Files
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
7.3 MiB
Plaintext
Raw Permalink 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"}, "87288e7c0ecc339242cf043f0e5f8e4f91ce34ce": {"t": 1786067860.9110973, "n": 232385, "h": "92c908848b2a9c5b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <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"}, "a46f243b015da954b06ad4ebb34ee9f6a5af8cdb": {"t": 1786068486.9441342, "n": 232385, "h": "92c908848b2a9c5b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <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"}}, "lib/cli.js": {"a46f243b015da954b06ad4ebb34ee9f6a5af8cdb": {"t": 1786068492.6652336, "n": 276192, "h": "0f285e0a4599aef6", "c": "const fs = require(\"node:fs\");\nconst crypto = require(\"node:crypto\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\nconst { spawn, spawnSync } = require(\"node:child_process\");\n\n// --- Spinner minimale (solo TTY) per segnalare operazioni in corso ---\nfunction startSpinner(message) {\n const frames = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n let i = 0;\n process.stdout.write(`\\x1b[36m${frames[0]} ${message}\\x1b[0m`);\n const timer = setInterval(() => {\n i = (i + 1) % frames.length;\n process.stdout.write(`\\r\\x1b[36m${frames[i]} ${message}\\x1b[0m`);\n }, 100);\n return { timer, message };\n}\nfunction stopSpinner(spinner) {\n if (!spinner) return;\n clearInterval(spinner.timer);\n process.stdout.write(\"\\r\\x1b[2K\\r\");\n}\nconst { createPaths, ensureRuntimeDirs, getDockerHostProjectsRoot } = require(\"./paths\");\nconst { createTokenStore } = require(\"./token-store\");\nconst { createProviderRegistry } = require(\"./provider-registry\");\nconst { createCopilotModelCatalogStore, resolveAvailableCopilotModels } = require(\"./copilot-models\");\nconst { startDeviceFlow, pollForToken } = require(\"./copilot-auth\");\nconst { createServiceManager } = require(\"./service-manager\");\nconst { startServer } = require(\"./app\");\nconst { getAvailableModels, mapModel, DEFAULT_COPILOT_MODEL } = require(\"./openai-translate\");\nconst { probeApiKeyProviderModel, normalizeQwenEndpointVariant, parseProviderModelPreferences, makeProxyFetch } = require(\"./copilot-proxy\");\nconst {\n loadRuntimeEnv,\n normalizeRuntimeProfile,\n resolveProxyHostPort,\n resolveRuntimeProfile,\n resolveServiceUrlForProfile,\n} = require(\"./runtime-env\");\nconst { createJsonlMeteringSink } = require(\"./metering\");\nconst { resolveClaudeProjectSettings } = require(\"./project-context\");\nconst { resolveDockerComposeCommand, runDockerCompose } = require(\"./docker-compose\");\nconst { listPortListeners } = require(\"./port-guard\");\nconst { createProviderReordering, readReorderingStore, buildDefaultProbeFn, fetchSpeedMetric, fetchBenchmarkMetric, DEFAULT_REORDERING_FILE } = require(\"./provider-reordering\");\nconst {\n readJsonResponseSafe,\n normalizeCloudPriceModelCandidates,\n extractBenchmarkCodingScore,\n fetchCodingScore,\n mapProviderToCloudPriceIds,\n fetchModelPricing,\n pickCurrentPricingOption,\n} = require(\"./cloudprice-client\");\nconst {\n inferScopeFromKey,\n getGlobalClaudeSettingsFile,\n hasLocalClaudeFolder,\n listConfigSpecs,\n listScopeValues,\n getScopeValue,\n migrateManagedConfig,\n readServiceConfig,\n setScopeValue,\n unsetScopeValue,\n writeServiceConfig,\n} = require(\"./configuration\");\nfunction buildProxyAgentUrl(proxyUrlRaw, proxyApiKey) {\n if (!proxyUrlRaw && !proxyApiKey) return \"\";\n const normalizedRaw = String(proxyUrlRaw || \"\").trim();\n const withScheme = normalizedRaw && !/^[a-z][a-z0-9+.-]*:\\/\\//i.test(normalizedRaw)\n ? `http://${normalizedRaw}`\n : normalizedRaw;\n let parsed = null;\n try {\n parsed = withScheme ? new URL(withScheme) : null;\n } catch {\n parsed = null;\n }\n const key = (proxyApiKey || \"\").trim();\n if (parsed?.hostname) {\n const protocol = parsed.protocol || \"http:\";\n let auth = \"\";\n if (key) {\n const user = parsed.username || \"proxy\";\n auth = `${encodeURIComponent(user)}:${encodeURIComponent(key)}@`;\n } else if (parsed.username) {\n const pw = parsed.password ? encodeURIComponent(parsed.password) : \"\";\n auth = `${encodeURIComponent(parsed.username)}:${pw}@`;\n }\n const host = parsed.hostname.includes(\":\") ? `[${parsed.hostname}]` : parsed.hostname;\n const port = parsed.port || \"7064\";\n return `${protocol}//${auth}${host}:${port}`;\n }\n return \"\";\n}\n\nconst AVAILABLE_PROVIDER_SPECS = [\n { id: \"copilot\", auth: \"oauth\", displayName: \"GitHub Copilot\", aliases: [] },\n { id: \"openrouter\", auth: \"api_key\", displayName: \"OpenRouter\", aliases: [] },\n { id: \"zai\", auth: \"api_key\", displayName: \"Z.AI\", aliases: [\"z.ai\"] },\n { id: \"kimi\", auth: \"api_key\", displayName: \"Kimi (Moonshot)\", aliases: [] },\n { id: \"qwen\", auth: \"api_key\", displayName: \"Qwen (DashScope)\", aliases: [] },\n { id: \"opencode\", auth: \"api_key\", displayName: \"OpenCode Zen\", aliases: [\"zen\"] },\n { id: \"opencode-go\", auth: \"api_key\", displayName: \"OpenCode Go\", aliases: [\"go\", \"opencodego\"] },\n { id: \"openai\", auth: \"api_key\", displayName: \"OpenAI\", aliases: [] },\n { id: \"anthropic\", auth: \"api_key\", displayName: \"Anthropic\", aliases: [] },\n { id: \"deepseek\", auth: \"api_key\", displayName: \"DeepSeek\", aliases: [] },\n { id: \"groq\", auth: \"api_key\", displayName: \"Groq\", aliases: [] },\n { id: \"mistral\", auth: \"api_key\", displayName: \"Mistral\", aliases: [] },\n { id: \"xai\", auth: \"api_key\", displayName: \"xAI\", aliases: [] },\n { id: \"perplexity\", auth: \"api_key\", displayName: \"Perplexity\", aliases: [] },\n { id: \"together\", auth: \"api_key\", displayName: \"Together\", aliases: [] },\n { id: \"fireworks\", auth: \"api_key\", displayName: \"Fireworks\", aliases: [] },\n { id: \"commandcode\", auth: \"api_key\", displayName: \"Command Code\", aliases: [] },\n { id: \"nvidia\", auth: \"api_key\", displayName: \"NVIDIA\", aliases: [] },\n { id: \"vercel-ai-gateway\", auth: \"api_key\", displayName: \"Vercel AI Gateway\", aliases: [\"ai-gateway\", \"vercel\"] },\n { id: \"meta\", auth: \"api_key\", displayName: \"Meta AI\", aliases: [] },\n];\n\nconst KNOWN_PROVIDERS = AVAILABLE_PROVIDER_SPECS.reduce((acc, provider) => {\n const entry = { auth: provider.auth, displayName: provider.displayName };\n acc[provider.id] = entry;\n for (const alias of provider.aliases) acc[alias] = entry;\n return acc;\n}, {});\n\nconst SHORT_COMMAND_ALIASES = {\n // Comandi senza gruppo\n h: \"help\",\n su: \"setup\",\n v: \"version\",\n rn: \"release-notes\",\n up: \"update\",\n un: \"uninstall\",\n in: \"install\",\n li: \"login\",\n lo: \"logout\",\n sto: \"stop\",\n st: \"status\",\n sa: \"stats\",\n lg: \"logs\",\n t: \"test\",\n // Gruppo provider\n \"p:a\": \"provider:add\",\n \"p:k\": \"provider:key\",\n \"p:av\": \"provider:available\",\n \"p:l\": \"provider:list\",\n \"p:t\": \"provider:test\",\n \"p:s\": \"provider:status\",\n \"p:u\": \"provider:usage\",\n \"p:o\": \"provider:order\",\n \"p:ro\": \"provider:reorder\",\n \"p:up\": \"provider:update\",\n \"p:rn\": \"provider:rename\",\n \"p:rm\": \"provider:remove\",\n // Gruppo config\n \"c:l\": \"config:list\",\n \"c:g\": \"config:get\",\n \"c:s\": \"config:set\",\n \"c:u\": \"config:unset\",\n // Gruppo service\n \"sv:sta\": \"service:start\",\n \"sv:sto\": \"service:stop\",\n \"sv:r\": \"service:restart\",\n \"sv:rt\": \"service:runtime\",\n // Gruppo model/models\n \"m:l\": \"models:list\",\n \"m:s\": \"model:set\",\n // Gruppo claude\n \"cc:s\": \"claude:setup\",\n // Gruppo pi\n \"pi:s\": \"pi:setup\",\n // Gruppo vscode\n \"vsc:cs\": \"vscode-chat:setup\",\n \"vsc:cc\": \"vscode-claude:setup\",\n // Gruppo stats\n \"sa:r\": \"stats:reset\",\n // Gruppo install\n \"in:p\": \"install:persistent\",\n \"in:it\": \"install:persistent-it\",\n \"in:en\": \"install:persistent-en\",\n // Gruppo proxy\n \"px:a\": \"proxy:add\",\n \"px:l\": \"proxy:list\",\n \"px:rm\": \"proxy:remove\",\n \"px:ro\": \"proxy:reorder\",\n \"px:t\": \"proxy:test\",\n};\n\nconst INSTALL_LOCALE_FILE = \"install-locale.txt\";\n\nconst REVERSE_SHORT_ALIASES = Object.fromEntries(\n Object.entries(SHORT_COMMAND_ALIASES).map(([short, full]) => [full, short]),\n);\n\nconst RELEASE_NOTES = {};\n\nfunction normalizeLocale(value) {\n return String(value || \"\").trim().toLowerCase() === \"en\" ? \"en\" : \"it\";\n}\n\nfunction getInstallLocaleFilePath(paths) {\n return path.join(paths.dataRoot, INSTALL_LOCALE_FILE);\n}\n\nfunction persistInstallLocale(paths, locale) {\n try {\n fs.writeFileSync(getInstallLocaleFilePath(paths), `${normalizeLocale(locale)}\\n`, \"utf8\");\n } catch {\n // best effort\n }\n}\n\nfunction readPersistedInstallLocale(paths) {\n try {\n const raw = fs.readFileSync(getInstallLocaleFilePath(paths), \"utf8\").trim();\n return normalizeLocale(raw);\n } catch {\n return \"it\";\n }\n}\n\nfunction resolveOutputLocale({ env, paths }) {\n const envLocale = String(env?.LLMPROXY_LOCALE || \"\").trim().toLowerCase();\n if (envLocale === \"it\" || envLocale === \"en\") return envLocale;\n return readPersistedInstallLocale(paths);\n}\n\nfunction normalizeKnownProviderId(rawProviderId) {\n const normalized = String(rawProviderId || \"\").trim().toLowerCase();\n if (normalized === \"z.ai\") return \"zai\";\n if (normalized === \"zen\" || normalized === \"opencode\") return \"opencode\";\n if (normalized === \"go\" || normalized === \"opencodego\" || normalized === \"opencode-go\") return \"opencode-go\";\n return normalized;\n}\n\nfunction resolveApiKeyProviderKind(rawInstanceId, rawProviderFlag) {\n const explicitProviderKind = normalizeKnownProviderId(rawProviderFlag);\n if (explicitProviderKind && KNOWN_PROVIDERS[explicitProviderKind]?.auth === \"api_key\") {\n return explicitProviderKind;\n }\n\n const normalizedInstanceId = normalizeKnownProviderId(rawInstanceId);\n if (normalizedInstanceId && KNOWN_PROVIDERS[normalizedInstanceId]?.auth === \"api_key\") {\n return normalizedInstanceId;\n }\n\n const knownApiKeyIds = AVAILABLE_PROVIDER_SPECS\n .filter((provider) => provider.auth === \"api_key\")\n .flatMap((provider) => [provider.id, ...(provider.aliases || [])])\n .map((providerId) => normalizeKnownProviderId(providerId))\n .filter(Boolean)\n .sort((left, right) => right.length - left.length);\n\n for (const providerId of knownApiKeyIds) {\n if (normalizedInstanceId === providerId || normalizedInstanceId.startsWith(`${providerId}-`)) {\n return providerId;\n }\n }\n\n return \"\";\n}\n\nfunction getKnownProvider(rawProviderId) {\n const id = normalizeKnownProviderId(rawProviderId);\n if (KNOWN_PROVIDERS[id]) {\n return { id, ...KNOWN_PROVIDERS[id] };\n }\n // Backward compatibility: arbitrary ids are treated as Copilot aliases.\n return { id, auth: \"oauth\", displayName: id || \"GitHub Copilot\" };\n}\n\nfunction listAvailableProviders() {\n return AVAILABLE_PROVIDER_SPECS.map((provider) => ({ ...provider, aliases: provider.aliases.slice() }));\n}\n\nfunction formatAvailableProviders(providers = listAvailableProviders()) {\n return providers\n .map((provider, index) => {\n const aliasSuffix = provider.aliases.length > 0 ? ` aliases=${provider.aliases.join(\", \")}` : \"\";\n return `${index + 1}. ${provider.id} (${provider.displayName}) auth=${provider.auth}${aliasSuffix}`;\n })\n .join(\"\\n\");\n}\n\nfunction formatKnownProvidersInline(providers = listAvailableProviders()) {\n return providers\n .map((provider) => (provider.aliases.length > 0 ? `${provider.aliases[0]} (${provider.id})` : provider.id))\n .join(\", \");\n}\n\nfunction parseArgs(argv) {\n const tokens = Array.isArray(argv) ? argv.slice(2) : [];\n const args = [];\n const flags = {};\n\n if (tokens.length > 0 && (tokens[0] === \"--help\" || tokens[0] === \"-h\")) {\n return {\n command: \"help\",\n subcommand: tokens[1] || \"\",\n args: tokens.slice(1),\n flags: {},\n follow: false,\n };\n }\n\n if (tokens.length > 0 && (tokens[0] === \"--version\" || tokens[0] === \"-v\")) {\n return {\n command: \"version\",\n subcommand: \"\",\n args: [],\n flags: {},\n follow: false,\n };\n }\n\n for (let index = 0; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (token.startsWith(\"-\") && !token.startsWith(\"--\")) {\n const shortFlags = token.slice(1).trim();\n if (shortFlags) {\n // Se il token dopo il singolo dash contiene un hyphen, è un long flag\n // scritto con un dash solo per errore (es. -all-providers → --all-providers)\n if (shortFlags.includes(\"-\")) {\n flags[shortFlags] = true;\n continue;\n }\n for (const shortFlag of shortFlags) flags[shortFlag] = true;\n continue;\n }\n }\n if (!token.startsWith(\"--\")) {\n args.push(token);\n continue;\n }\n\n const withoutPrefix = token.slice(2);\n const [rawKey, inlineValue] = withoutPrefix.split(\"=\", 2);\n if (inlineValue !== undefined) {\n flags[rawKey] = inlineValue;\n continue;\n }\n\n const nextToken = tokens[index + 1];\n if (nextToken && !nextToken.startsWith(\"--\")) {\n flags[rawKey] = nextToken;\n index += 1;\n continue;\n }\n\n flags[rawKey] = true;\n }\n\n const command = args[0] || \"help\";\n const expandedCommand = SHORT_COMMAND_ALIASES[command] || command;\n\n return {\n command: expandedCommand,\n subcommand: args[1] || \"\",\n args: args.slice(1),\n flags,\n follow: Boolean(flags.follow),\n };\n}\n\nconst COMMAND_HELP = {\n \"help\": {\n usage: \"llmproxy help [comando]\",\n description: \"Mostra la guida generale oppure la scheda dettagliata di un comando specifico.\",\n when: \"Usalo quando vuoi capire rapidamente quale comando scegliere o ricordarti sintassi e flusso consigliato.\",\n example: \"llmproxy help claude:setup\",\n },\n \"setup\": {\n usage: \"llmproxy setup\",\n description: \"Verifica il runtime locale, mostra il data root e il service manager selezionato.\",\n when: \"Usalo subito dopo l'installazione o quando vuoi capire dove llmproxy salva dati e log.\",\n example: \"llmproxy setup\",\n },\n \"version\": {\n usage: \"llmproxy version\",\n description: \"Stampa la versione corrente della CLI installata.\",\n when: \"Usalo per verificare che l'update sia andato a buon fine o per debug rapido della versione attiva.\",\n example: \"llmproxy --version\",\n },\n \"release-notes\": {\n usage: \"llmproxy release-notes [--version <version>] [--locale <it|en>]\",\n description: \"Stampa il changelog di una versione specifica usando commit message, note embedded o fallback locale.\",\n when: \"Usalo per controllare cosa introduce una release oppure per verificare l'output che verra' mostrato dopo un update.\",\n example: \"llmproxy release-notes --version 0.2.61\",\n },\n \"update\": {\n usage: \"llmproxy update\",\n description: \"Scarica e installa l'ultima versione dalla repository GitHub e verifica il binario aggiornato.\",\n when: \"Usalo quando vuoi aggiornare la CLI mantenendo una sola installazione globale attiva.\",\n example: \"llmproxy update\",\n },\n \"install\": {\n usage: \"llmproxy install\",\n description: \"Alias inglese di install:persistent-en: installa globalmente la CLI corrente e registra il servizio persistente nativo dell'OS.\",\n when: \"Usalo quando vuoi un comando piu' corto e leggibile per il bootstrap persistente in inglese dal binario CLI.\",\n example: \"llmproxy install\",\n englishDescription: \"English alias for install:persistent-en: installs the current CLI globally and registers the native persistent service for the OS.\",\n englishWhen: \"Use it when you want a shorter, English-first command for the persistent bootstrap flow from the CLI.\",\n englishExample: \"llmproxy install\",\n locale: \"en\",\n },\n \"install:persistent\": {\n usage: \"llmproxy install:persistent\",\n description: \"Installa globalmente la CLI corrente e registra il servizio persistente nativo dell'OS.\",\n when: \"Usalo come bootstrap one-shot dal checkout locale quando vuoi avere llmproxy disponibile anche dopo reboot.\",\n example: \"npm run install:persistent\",\n locale: \"it\",\n },\n \"install:persistent-it\": {\n usage: \"llmproxy install:persistent-it\",\n description: \"Installa globalmente la CLI corrente e registra il servizio persistente nativo dell'OS.\",\n when: \"Usalo come percorso esplicito in italiano quando vuoi output, help ed errori in italiano.\",\n example: \"npm run install:persistent-it\",\n locale: \"it\",\n },\n \"install:persistent-en\": {\n usage: \"llmproxy install:persistent-en\",\n description: \"Installs the current CLI globally and registers the native persistent service for the OS.\",\n when: \"Use it when you want the explicit English install path with English output, help, and errors.\",\n example: \"npm run install:persistent-en\",\n englishDescription: \"Installs the current CLI globally and registers the native persistent service for the OS.\",\n englishWhen: \"Use it when you want the explicit English install path with English output, help, and errors.\",\n englishExample: \"npm run install:persistent-en\",\n locale: \"en\",\n },\n \"uninstall\": {\n usage: \"llmproxy uninstall\",\n description: \"Disinstallazione completa: ferma il servizio, rimuove container Docker, dati, file di servizio e pacchetto globale.\",\n when: \"Usalo quando vuoi rimuovere completamente llmproxy dal sistema (servizio, Docker, dati, pacchetto).\",\n example: \"llmproxy uninstall\",\n },\n \"login\": {\n usage: \"llmproxy login\",\n description: \"Alias legacy di `llmproxy provider:add copilot`: autentica GitHub Copilot via device flow.\",\n when: \"Usalo solo per compatibilita'; il flusso consigliato e' `llmproxy provider:add copilot`.\",\n example: \"llmproxy provider:add copilot\",\n },\n \"logout\": {\n usage: \"llmproxy logout\",\n description: \"Rimuove i token Copilot locali.\",\n when: \"Usalo per forzare un nuovo login o per pulire completamente le credenziali locali.\",\n example: \"llmproxy logout\",\n },\n \"provider:add\": {\n usage: \"llmproxy provider:add <id> --api-key <key> --model <model> --vision <true|false> [--free-model [true|false]] [--name <name>] [--plan <plan>]\",\n description: \"Aggiunge un provider noto: Copilot via device flow, gli altri provider tramite API key e modello predefinito. --vision e' obbligatorio per i provider API key. Usa --free-model per marcare la coppia provider/modello come gratuita e impedire all'auto-escalation di abbandonarla.\",\n when: \"Usalo quando vuoi configurare un provider di fallback aggiuntivo, per esempio openrouter o kimi, oltre al provider principale.\",\n example: \"llmproxy provider:add qwen --api-key sk-sp-... --model qwen3.7-plus --vision true --plan subscription\",\n },\n \"provider:key\": {\n usage: \"llmproxy provider:key <id> --api-key <key> [--model <model>] [--vision <true|false>] [--free-model <true|false>] [--plan <plan>]\",\n description: \"Aggiorna o imposta la API key di un provider gia' configurato con autenticazione api_key. --vision e' opzionale (mantiene il valore esistente se omesso). --free-model puo' attivare o disattivare il blocco dell'auto-escalation per quella coppia provider/modello.\",\n when: \"Usalo quando devi ruotare una chiave esistente senza ricreare da zero il provider.\",\n example: \"llmproxy provider:key qwen --api-key sk-sp-... --vision true --plan subscription\",\n },\n \"provider:available\": {\n usage: \"llmproxy provider:available\",\n description: \"Elenca i provider supportati dalla CLI, con id canonico, alias e tipo di autenticazione.\",\n when: \"Usalo quando vuoi sapere quali provider puoi configurare prima di eseguire provider:add.\",\n example: \"llmproxy provider:available\",\n },\n \"provider:list\": {\n usage: \"llmproxy provider:list\",\n description: \"Elenca i provider configurati o, dentro un progetto con override Claude, la chain effettiva di fallback del progetto.\",\n when: \"Usalo per controllare rapidamente quali provider verranno davvero provati, con i modelli effettivi derivati da ANTHROPIC_DEFAULT_MODEL quando presente.\",\n example: \"llmproxy provider:list\",\n },\n \"provider:test\": {\n usage: \"llmproxy provider:test [--vision] [--all-proxies] [--proxy <url>] [--provider <id>]\",\n description: \"Testa i provider configurati con inferenza testuale. Usa --vision per testare la visione immagini; con --all-proxies o --proxy esegue inferenza su ogni combinazione provider × proxy.\",\n when: \"Usalo dopo aver configurato i provider per verificare che funzionino con un test di inferenza completo, oppure con --vision per testare la visione immagini.\",\n example: \"llmproxy provider:test\",\n englishExample: \"llmproxy provider:test\",\n },\n \"provider:status\": {\n usage: \"llmproxy provider:status\",\n description: \"Mostra il provider attivo e lo stato sintetico di tutti i provider configurati.\",\n when: \"Usalo quando vuoi capire quale provider sta servendo le richieste e se i fallback sono completi o mancanti di modello/chiave.\",\n example: \"llmproxy provider:status\",\n },\n \"provider:usage\": {\n usage: \"llmproxy provider:usage\",\n description: \"Mostra il consumo di token giornaliero, settimanale e mensile con breakdown per provider e modello.\",\n when: \"Usalo quando vuoi monitorare il consumo di token e capire quale provider/modello costa di piu'.\",\n example: \"llmproxy provider:usage\",\n },\n \"provider:order\": {\n usage: \"llmproxy provider:order <id> <position>\",\n description: \"Sposta un provider nella posizione di fallback desiderata.\",\n when: \"Usalo quando vuoi dare priorita' a un provider diverso senza doverlo rimuovere e ricreare.\",\n example: \"llmproxy provider:order kimi 1\",\n },\n \"provider:reorder\": {\n usage: \"llmproxy provider:reorder\",\n description: \"Forza subito un ciclo di reordering automatico (price/power/speed) senza aspettare il timer.\",\n when: \"Usalo dopo aver cambiato LLMPROXY_REORDERING, o quando vuoi un ordine aggiornato subito.\",\n example: \"llmproxy provider:reorder\",\n },\n \"provider:update\": {\n usage: \"llmproxy provider:update <id> [--vision <true|false>] [--free-model <true|false>] [--name <name>]\",\n description: \"Aggiorna metadati di un provider gia' configurato senza toccare le credenziali o il modello. --vision abilita/disabilita la partecipazione al fallback delle richieste con immagini. --free-model attiva/disattiva il blocco dell'auto-escalation per quella coppia provider/modello. --name cambia il nome visualizzato.\",\n when: \"Usalo quando vuoi correggere velocemente il flag vision dopo un test, marcare un provider come gratuito gratuito, o rinominare un'istanza per distinguerla da altre dello stesso tipo.\",\n example: \"llmproxy provider:update opencode-bacin --vision false --free-model true --name 'OpenCode (Bacin)'\",\n },\n \"provider:rename\": {\n usage: \"llmproxy provider:rename <id> <name>\",\n description: \"Rinomina un provider configurato per distinguerlo meglio negli elenchi e nello stato.\",\n when: \"Usalo quando hai piu' provider simili e vuoi etichette piu' chiare per capire il fallback a colpo d'occhio.\",\n example: \"llmproxy provider:rename backup Backup Copilot\",\n },\n \"provider:remove\": {\n usage: \"llmproxy provider:remove <id>\",\n description: \"Rimuove un provider configurato dal fallback locale.\",\n when: \"Usalo quando un provider non serve piu' oppure vuoi eliminare una configurazione non valida.\",\n example: \"llmproxy provider:remove openrouter\",\n },\n \"run\": {\n usage: \"llmproxy run\",\n description: \"Avvia l'istanza locale/dev in foreground.\",\n when: \"Usalo per debug rapido locale sulla porta 5045 senza toccare il servizio persistente.\",\n example: \"llmproxy run\",\n },\n \"stop\": {\n usage: \"llmproxy stop\",\n description: \"Ferma solo l'istanza locale/dev avviata con `llmproxy run` sulla porta 5045.\",\n when: \"Usalo quando vuoi chiudere la run locale senza fermare il servizio persistente.\",\n example: \"llmproxy stop\",\n },\n \"status\": {\n usage: \"llmproxy status [--docker]\",\n description: \"Mostra stato del servizio, autenticazione e ordine dei provider. Con --docker mostra anche lo stato dei container Docker.\",\n when: \"Usalo quando il proxy non risponde o vuoi verificare quale provider sia attivo. Aggiungi --docker per vedere i container in esecuzione.\",\n example: \"llmproxy status --docker\",\n },\n \"stats\": {\n usage: \"llmproxy stats\",\n description: \"Mostra statistiche aggregate di utilizzo con breakdown per provider e modello e relativo consumo token.\",\n when: \"Usalo quando vuoi capire quali provider e modelli stanno assorbendo piu' traffico e token.\",\n example: \"llmproxy stats\",\n },\n \"stats:reset\": {\n usage: \"llmproxy stats:reset [--hard]\",\n description: \"Azzera tutte le statistiche di utilizzo (metering records). Con --hard resetta anche la cache dello smart router e il file di configurazione auto-rank.\",\n when: \"Usalo quando vuoi ripartire da zero con le statistiche, per esempio dopo un cambio configurazione o per fare test puliti.\",\n example: \"llmproxy stats:reset --hard\",\n },\n \"logs\": {\n usage: \"llmproxy logs [--follow]\",\n description: \"Mostra i log recenti del servizio e dell'ultimo audit log JSONL.\",\n when: \"Usalo per diagnosticare errori; aggiungi --follow per seguire il flusso in tempo reale.\",\n example: \"llmproxy logs --follow\",\n },\n \"service:start\": {\n usage: \"llmproxy service:start\",\n description: \"Installa e avvia il servizio utente persistente.\",\n when: \"Usalo quando vuoi che il proxy resti disponibile dopo il reboot senza avviarlo a mano.\",\n example: \"llmproxy service:start\",\n },\n \"service:stop\": {\n usage: \"llmproxy service:stop\",\n description: \"Ferma il servizio persistente.\",\n when: \"Usalo prima di manutenzione, riconfigurazioni o disinstallazione.\",\n example: \"llmproxy service:stop\",\n },\n \"service:restart\": {\n usage: \"llmproxy service:restart\",\n description: \"Riavvia il servizio persistente e verifica che il runtime Docker/locale sia tornato sano.\",\n when: \"Usalo dopo update o modifiche alla configurazione runtime.\",\n example: \"llmproxy service:restart\",\n },\n \"service:runtime\": {\n usage: \"llmproxy service:runtime <docker|native|launchd|systemd>\",\n description: \"Passa in modo esplicito tra runtime Docker e runtime nativo, pulendo quello precedente e verificando l'health finale.\",\n when: \"Usalo quando vuoi migrare la macchina dalla variante Docker a quella launchd/systemd o viceversa con un solo comando.\",\n example: \"llmproxy service:runtime docker\",\n },\n \"models:list\": {\n usage: \"llmproxy models:list\",\n description: \"Elenca i modelli disponibili in forma numerata.\",\n when: \"Usalo prima di llmproxy claude:setup per scegliere l'indice corretto del modello.\",\n example: \"llmproxy models:list\",\n },\n \"model:set\": {\n usage: \"llmproxy model:set <model>\",\n description: \"Aggiorna il modello Claude del progetto scrivendo il valore grezzo in .claude/settings.json.\",\n when: \"Usalo quando vuoi cambiare rapidamente routing o provider senza rifare claude:setup, ad esempio passando a deepseek:deepseek-v4-flash.\",\n example: \"llmproxy model:set deepseek:deepseek-v4-flash\",\n },\n \"config:list\": {\n usage: \"llmproxy config:list [--project|--service|--scope <project|global|service>]\",\n description: \"Mostra le variabili configurabili di progetto e/o servizio gestite da llmproxy.\",\n when: \"Usalo per verificare rapidamente gli override in .claude/settings.json e la configurazione persistita del servizio.\",\n example: \"llmproxy config:list --scope global\",\n },\n \"config:get\": {\n usage: \"llmproxy config:get <key> [--project|--service|--scope <project|global|service>]\",\n description: \"Legge una variabile configurabile di llmproxy dal progetto o dal servizio.\",\n when: \"Usalo quando vuoi verificare il valore effettivo di una variabile prima di cambiarla.\",\n example: \"llmproxy config:get LLMPROXY_REORDERING --scope service\",\n },\n \"config:set\": {\n usage: \"llmproxy config:set <key> <value> [--project|--service|--scope <project|global|service>]\",\n description: \"Imposta una variabile configurabile di llmproxy nello scope corretto.\",\n when: \"Usalo per aggiornare routing di progetto o parametri runtime senza editare i file a mano.\",\n example: \"llmproxy config:set LLMPROXY_REORDERING price-speed-power --scope service\",\n },\n \"config:unset\": {\n usage: \"llmproxy config:unset <key> [--project|--service|--scope <project|global|service>]\",\n description: \"Rimuove una variabile configurabile di llmproxy dal progetto o dal servizio.\",\n when: \"Usalo quando vuoi tornare al comportamento di default senza lasciare override residui.\",\n example: \"llmproxy config:unset ANTHROPIC_DEFAULT_MODEL --scope global\",\n },\n \"test\": {\n usage: \"llmproxy test [-i|--inference] [--all-providers] [--proxy <url>] [--provider <id>] [<id>|<index>]\",\n description: \"Esegue un probe rapido dei provider oppure, con -i, una vera inferenza contro il proxy locale. Con -i si puo' specificare un ID provider (es. opencode-alessio) o un indice numerico (es. 1) per testare un singolo provider invece della fallback chain. Con --proxy <url> si forza un proxy specifico.\",\n when: \"Usalo per verificare rapidamente lo stato sintetico dei provider oppure quale provider risponde davvero a una richiesta /v1/messages.\",\n example: \"llmproxy test -i --all-providers\",\n },\n \"claude:setup\": {\n usage: \"llmproxy claude:setup [--model <indice>]\",\n description: \"Scrive .claude/settings.json per usare llmproxy come backend locale di Claude Code.\",\n when: \"Usalo nel progetto che vuoi collegare a llmproxy, dopo aver configurato almeno un provider e dopo aver scelto il modello con models:list.\",\n example: \"llmproxy claude:setup --model 2\",\n },\n \"pi:setup\": {\n usage: \"llmproxy pi:setup\",\n description: \"Scrive .pi/models.json e .pi/settings.json per usare llmproxy come provider in PI Agent.\",\n when: \"Usalo nel progetto che vuoi usare con PI Agent, dopo aver configurato llmproxy con almeno un provider.\",\n example: \"llmproxy pi:setup\",\n },\n \"vscode-chat:setup\": {\n usage: \"llmproxy vscode-chat:setup\",\n description: \"Configura VS Code Chat con llmproxy come provider OpenAI, includendo tutti i modelli disponibili.\",\n when: \"Usalo per abilitare llmproxy come backend nella Chat di VS Code (Copilot Chat).\",\n example: \"llmproxy vscode-chat:setup\",\n },\n \"vscode-claude:setup\": {\n usage: \"llmproxy vscode-claude:setup [--model <indice>]\",\n description: \"Configura l'estensione Claude Code in VS Code con llmproxy come backend e tutti i modelli disponibili.\",\n when: \"Usalo per collegare l'estensione Claude Code in VS Code al proxy locale llmproxy.\",\n example: \"llmproxy vscode-claude:setup --model 2\",\n },\n \"proxy:add\": {\n usage: \"llmproxy proxy:add <url> [--name <nome>]\",\n description: \"Aggiunge un proxy alla registry. Il formato URL include credenziali: http://user:pass@host:port. Con --name assegna un nome leggibile.\",\n when: \"Usalo per registrare un proxy da usare in rotazione con --proxy (senza valore).\",\n example: \"llmproxy proxy:add http://user:key@dc.decodo.com:10001 --name 'Proxy primario'\",\n },\n \"proxy:list\": {\n usage: \"llmproxy proxy:list\",\n description: \"Elenca tutti i proxy registrati in ordine di failover.\",\n when: \"Usalo per vedere i proxy disponibili e il loro ordine.\",\n example: \"llmproxy proxy:list\",\n },\n \"proxy:remove\": {\n usage: \"llmproxy proxy:remove <id>\",\n description: \"Rimuove un proxy dalla registry per ID (dominio).\",\n when: \"Usalo per rimuovere un proxy non piu' valido.\",\n example: \"llmproxy proxy:remove dc.decodo.com\",\n },\n \"proxy:reorder\": {\n usage: \"llmproxy proxy:reorder <id1> <id2> ...\",\n description: \"Riordina i proxy specificando gli ID nel nuovo ordine di failover.\",\n when: \"Usalo per cambiare l'ordine di failover dei proxy.\",\n example: \"llmproxy proxy:reorder proxy-a.com proxy-b.com\",\n },\n \"proxy:test\": {\n usage: \"llmproxy proxy:test\",\n description: \"Testa tutti i proxy registrati verificando la connettivita'.\",\n when: \"Usalo per verificare quali proxy sono raggiungibili.\",\n example: \"llmproxy proxy:test\",\n },\n};\n\nfunction printCommandHelp(command, stdout = process.stdout) {\n const entry = COMMAND_HELP[command];\n if (!entry) {\n stdout.write(`Comando non documentato: ${command}\\n`);\n stdout.write(\"Usa `llmproxy help` per l'elenco completo.\\n\");\n return 1;\n }\n\n const shortAlias = REVERSE_SHORT_ALIASES[command] || \"\";\n const isEnglishInstallCommand = entry.locale === \"en\";\n const descriptionLabel = isEnglishInstallCommand ? \"Description\" : \"Descrizione\";\n const whenLabel = isEnglishInstallCommand ? \"When to use\" : \"Quando usarlo\";\n const exampleLabel = isEnglishInstallCommand ? \"Example\" : \"Esempio\";\n const description = isEnglishInstallCommand ? (entry.englishDescription || entry.description) : entry.description;\n const when = isEnglishInstallCommand ? (entry.englishWhen || entry.when) : entry.when;\n const example = isEnglishInstallCommand ? (entry.englishExample || entry.example) : entry.example;\n\n if (shortAlias) stdout.write(`Short: llmp ${shortAlias}\\n`);\n stdout.write(`${entry.usage}\\n\\n`);\n stdout.write(`${descriptionLabel}: ${description}\\n`);\n stdout.write(`${whenLabel}: ${when}\\n`);\n if (example) stdout.write(`${exampleLabel}: ${example}\\n`);\n return 0;\n}\n\nfunction printHelp(stdout = process.stdout) {\n const knownProvidersInline = formatKnownProvidersInline();\n const s = (cmd) => {\n const alias = REVERSE_SHORT_ALIASES[cmd];\n return alias ? `[llmp ${alias}]` : \"\";\n };\n stdout.write(`llmProxy CLI\n\nComandi principali:\n llmproxy help ${s(\"help\")} mostra questa guida\n llmproxy setup ${s(\"setup\")} verifica il runtime locale, il data root e il service manager selezionato\n llmproxy version ${s(\"version\")} mostra la versione corrente della CLI installata\n llmproxy install:persistent-it ${s(\"install:persistent-it\")} installa globalmente la CLI corrente e attiva il servizio persistente nativo dell'OS\n llmproxy install:persistent-en ${s(\"install:persistent-en\")} installs the current CLI globally and enables the native persistent service for the OS\n llmproxy install ${s(\"install\")} English alias for install:persistent-en\n llmproxy install:persistent ${s(\"install:persistent\")} alias legacy di install:persistent-it\n llmproxy update ${s(\"update\")} scarica e installa l'ultima versione dalla repo e verifica il binario aggiornato\n llmproxy uninstall ${s(\"uninstall\")} disinstallazione completa: servizio, Docker, dati e pacchetto globale\n\nEsecuzione proxy:\n llmproxy run avvia l'istanza locale/dev in foreground su 127.0.0.1:5045\n llmproxy stop ${s(\"stop\")} ferma solo l'istanza locale/dev su 127.0.0.1:5045\n llmproxy status ${s(\"status\")} mostra stato del servizio, autenticazione e ordine dei provider\n llmproxy stats ${s(\"stats\")} mostra statistiche aggregate di utilizzo per provider e modello\n llmproxy stats:reset [--hard] ${s(\"stats:reset\")} azzera le statistiche di utilizzo; con --hard resetta anche cache e auto-rank\n llmproxy logs [--follow] ${s(\"logs\")} legge i log recenti; usa --follow per seguire il flusso in tempo reale\n\nServizio persistente:\n llmproxy service:start ${s(\"service:start\")} installa e avvia il servizio utente persistente\n llmproxy service:stop ${s(\"service:stop\")} ferma il servizio persistente\n llmproxy service:restart ${s(\"service:restart\")} riavvia il servizio persistente dopo modifiche o update\n llmproxy service:runtime <target> ${s(\"service:runtime\")} passa tra runtime docker e runtime nativo pulendo quello precedente\n\nProvider:\n llmproxy provider:add <id> ... ${s(\"provider:add\")} aggiunge un provider noto (Copilot avvia il login; gli altri usano API key)\n llmproxy provider:key <id> --api-key <k> ${s(\"provider:key\")} aggiorna/imposta la API key per provider API-key\n llmproxy provider:available ${s(\"provider:available\")} elenca i provider supportati dalla CLI\n llmproxy provider:list ${s(\"provider:list\")} elenca i provider configurati o la chain effettiva del progetto corrente\n llmproxy provider:test [--vision] [--all-proxies] ${s(\"provider:test\")} inferenza testuale; con --vision testa la visione, con --all-proxies testa ogni proxy\n llmproxy provider:status ${s(\"provider:status\")} mostra quale provider e' attivo in questo momento\n llmproxy provider:usage ${s(\"provider:usage\")} mostra consumo token giornaliero, settimanale e mensile\n llmproxy provider:order <id> <position> ${s(\"provider:order\")} cambia la priorita' di fallback di un provider\n llmproxy provider:reorder ${s(\"provider:reorder\")} riordina i provider in base ai criteri LLMPROXY_REORDERING\n llmproxy provider:update <id> ... ${s(\"provider:update\")} aggiorna vision, free-model o name di un provider\n llmproxy provider:rename <id> <name> ${s(\"provider:rename\")} rinomina un provider per distinguerlo meglio\n llmproxy provider:remove <id> ${s(\"provider:remove\")} rimuove un provider configurato\n\nProxy (registro e rotazione):\n llmproxy proxy:add <url> [--name] ${s(\"proxy:add\")} aggiunge un proxy alla registry\n llmproxy proxy:list ${s(\"proxy:list\")} elenca i proxy registrati in ordine di failover\n llmproxy proxy:remove <id> ${s(\"proxy:remove\")} rimuove un proxy per ID (dominio)\n llmproxy proxy:reorder <id1> <id2> ... ${s(\"proxy:reorder\")} riordina i proxy (primo = prioritario)\n llmproxy proxy:test ${s(\"proxy:test\")} testa la connettivita' di tutti i proxy\n llmproxy provider:test --all-proxies ${s(\"provider:test\")} esegue un test reale di inferenza provider × proxy\n (usa --proxy senza valore per rotazione automatica al posto di --proxy <url>)\n\nProvider noti:\n ${knownProvidersInline}\n\nModelli e Claude Code:\n llmproxy models:list ${s(\"models:list\")} mostra i modelli disponibili in forma numerata\n llmproxy model:set <model> ${s(\"model:set\")} aggiorna il modello del progetto (es. deepseek:deepseek-v4-flash)\n llmproxy config:list [--scope ...] ${s(\"config:list\")} mostra le variabili configurabili gestite da llmproxy\n llmproxy config:get <key> [...] ${s(\"config:get\")} legge una variabile configurabile\n llmproxy config:set <key> <value> [...] ${s(\"config:set\")} imposta una variabile configurabile\n llmproxy config:unset <key> [...] ${s(\"config:unset\")} rimuove una variabile configurabile\n llmproxy test ${s(\"test\")} esegue un test rapido di inferenza contro il proxy locale\n llmproxy claude:setup [--model <indice>] ${s(\"claude:setup\")} scrive .claude/settings.json per usare llmproxy come backend locale\n llmproxy pi:setup ${s(\"pi:setup\")} scrive .pi/models.json e settings.json per PI Agent\n llmproxy vscode-chat:setup ${s(\"vscode-chat:setup\")} configura VS Code Chat con llmproxy (tutti i modelli)\n llmproxy vscode-claude:setup [--model <indice>] ${s(\"vscode-claude:setup\")} configura Claude Code in VS Code con llmproxy\n\nQuando usare cosa:\n 1. Prima installazione persistente: install:persistent-it oppure install:persistent-en -> provider:add <provider> -> models:list -> claude:setup\n 2. Uso come servizio: service:start -> status -> logs\n 3. Multi-account: provider:add -> provider:list -> provider:order\n 4. Proxy rotazione: proxy:add <url> -> proxy:list -> provider:add ... --proxy ...\n 5. Aggiornamento CLI: update\n 6. Rimozione completa: uninstall\n\nProblemi comuni:\n - llmproxy non risponde: usa llmproxy status e poi llmproxy logs --follow\n - Claude Code non vede il proxy: rilancia llmproxy claude:setup nel progetto corretto\n - un modello fallisce: controlla llmproxy models:list e scegli un indice valido\n - il token Copilot non funziona: riesegui llmproxy provider:add copilot\n`);\n}\n\nfunction readPackageVersion(packageRoot) {\n const packageFile = path.join(packageRoot, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(packageFile, \"utf8\"));\n return String(pkg.version || \"0.0.0\");\n}\n\nfunction compareReleaseVersions(left, right) {\n const normalize = (value) => String(value || \"\")\n .trim()\n .split(\".\")\n .map((part) => Number.parseInt(part, 10));\n const leftParts = normalize(left);\n const rightParts = normalize(right);\n if (leftParts.length === 0 || rightParts.length === 0) return null;\n if (leftParts.some((part) => !Number.isFinite(part)) || rightParts.some((part) => !Number.isFinite(part))) {\n return null;\n }\n const maxLength = Math.max(leftParts.length, rightParts.length);\n for (let index = 0; index < maxLength; index += 1) {\n const current = leftParts[index] ?? 0;\n const remote = rightParts[index] ?? 0;\n if (current > remote) return 1;\n if (current < remote) return -1;\n }\n return 0;\n}\n\nasync function readRemotePackageVersion(fetchFn, repo = \"alessiobacin/llmProxy\", options = {}) {\n if (typeof fetchFn !== \"function\") return \"\";\n const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Math.max(1, Number(options.timeoutMs)) : 3000;\n const versionRequest = fetchFn(`https://raw.githubusercontent.com/${repo}/main/package.json`, {\n method: \"GET\",\n headers: { \"content-type\": \"application/json\" },\n });\n const response = await Promise.race([\n Promise.resolve(versionRequest),\n new Promise((resolve) => setTimeout(() => resolve(null), timeoutMs)),\n ]);\n if (!response || !response.ok) return \"\";\n const payload = await response.json();\n return String(payload?.version || \"\").trim();\n}\n\nfunction readEmbeddedReleaseNotes(packageRoot, version, locale = \"it\") {\n try {\n const notesFile = path.join(packageRoot, \"release-notes.json\");\n const notes = JSON.parse(fs.readFileSync(notesFile, \"utf8\"));\n const versionNotes = notes[String(version || \"\").trim()];\n if (!versionNotes) return [];\n const normalizedLocale = normalizeLocale(locale);\n return Array.isArray(versionNotes[normalizedLocale]) ? versionNotes[normalizedLocale].filter((note) => String(note || \"\").trim()) : [];\n } catch {\n return [];\n }\n}\n\nfunction shellQuote(value) {\n return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;\n}\n\nfunction runCommand(commandRunner, command, args = []) {\n try {\n return commandRunner(command, args, { encoding: \"utf8\" });\n } catch (error) {\n return { status: 1, stdout: \"\", stderr: \"\", error };\n }\n}\n\nfunction commandIsAvailable(commandRunner, command, args = [\"--version\"]) {\n const result = runCommand(commandRunner, command, args);\n return result && result.status === 0;\n}\n\nfunction quoteWindowsCmdArg(value) {\n const raw = String(value ?? \"\");\n if (!raw) return \"\\\"\\\"\";\n if (/^[A-Za-z0-9_./:\\\\=-]+$/.test(raw)) return raw;\n return `\"${raw.replace(/\"/g, \"\\\"\\\"\")}\"`;\n}\n\nfunction getPlatformCommandCandidates(command, platform = process.platform) {\n const normalized = String(command || \"\").trim();\n if (!normalized) return [];\n if (platform !== \"win32\") return [normalized];\n if (normalized.includes(\".\") || normalized.includes(\"\\\\\") || normalized.includes(\"/\")) return [normalized];\n if (normalized === \"npm\" || normalized === \"pnpm\" || normalized === \"npx\") {\n return [`${normalized}.cmd`, normalized];\n }\n return [normalized];\n}\n\nfunction runPlatformCommand(commandRunner, command, args = [], platform = process.platform) {\n let lastResult = null;\n for (const candidate of getPlatformCommandCandidates(command, platform)) {\n const result = platform === \"win32\" && candidate.toLowerCase().endsWith(\".cmd\")\n ? runCommand(commandRunner, \"cmd.exe\", [\"/d\", \"/s\", \"/c\", [quoteWindowsCmdArg(candidate), ...args.map((arg) => quoteWindowsCmdArg(arg))].join(\" \")])\n : runCommand(commandRunner, candidate, args);\n lastResult = result;\n if (result && result.status === 0) return result;\n const errorCode = result?.error?.code || result?.code || \"\";\n const stderrText = String(result?.stderr || \"\");\n const missingCommand = errorCode === \"ENOENT\" || /not recognized|cannot find/i.test(stderrText);\n if (!missingCommand) return result;\n }\n return lastResult || { status: 1, stdout: \"\", stderr: \"\" };\n}\n\nfunction commandIsAvailableForPlatform(commandRunner, command, platform = process.platform, args = [\"--version\"]) {\n const result = runPlatformCommand(commandRunner, command, args, platform);\n return result && result.status === 0;\n}\n\nfunction canWritePath(targetPath) {\n if (!targetPath || !fs.existsSync(targetPath)) return false;\n try {\n fs.accessSync(targetPath, fs.constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction canUseSudoNonInteractively(commandRunner) {\n if (!commandIsAvailable(commandRunner, \"sudo\")) return false;\n const result = runCommand(commandRunner, \"sudo\", [\"-n\", \"true\"]);\n return !!result && result.status === 0;\n}\n\nfunction isWindowsSessionElevated(commandRunner = spawnSync) {\n const result = runCommand(commandRunner, \"powershell.exe\", [\n \"-NoProfile\",\n \"-Command\",\n \"$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()); if ($principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { '1' } else { '0' }\",\n ]);\n return result && result.status === 0 && String(result.stdout || \"\").trim() === \"1\";\n}\n\nfunction getWindowsServiceState(commandRunner = spawnSync, label = \"llmproxy\") {\n const result = runCommand(commandRunner, \"sc.exe\", [\"query\", label]);\n const stdout = String(result?.stdout || \"\");\n const stderr = String(result?.stderr || \"\");\n const combined = `${stdout}\\n${stderr}`;\n const normalized = combined.toLowerCase();\n const missing = /does not exist|1060/i.test(normalized);\n const active = normalized.includes(\"running\") || /\\b(stato|state)\\b[\\s:]+4\\b/i.test(combined);\n return {\n ok: !!result && result.status === 0,\n missing,\n active,\n stdout,\n stderr,\n };\n}\n\nfunction readOsReleaseInfo(options = {}) {\n const platform = String(options.platform || process.platform);\n if (platform !== \"linux\") return {};\n const content = typeof options.content === \"string\"\n ? options.content\n : (fs.existsSync(\"/etc/os-release\") ? fs.readFileSync(\"/etc/os-release\", \"utf8\") : \"\");\n if (!content) return {};\n\n const values = {};\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const separatorIndex = line.indexOf(\"=\");\n if (separatorIndex <= 0) continue;\n const key = line.slice(0, separatorIndex).trim();\n let value = line.slice(separatorIndex + 1).trim();\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n values[key] = value;\n }\n return values;\n}\n\nfunction detectLinuxPackageFamily(osRelease = {}) {\n const tokens = [\n osRelease.ID,\n osRelease.ID_LIKE,\n osRelease.NAME,\n osRelease.PRETTY_NAME,\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n\n if (/(ubuntu|debian|mint|pop)/.test(tokens)) return \"debian\";\n if (/(rhel|fedora|centos|rocky|almalinux)/.test(tokens)) return \"rhel\";\n if (/(arch|manjaro)/.test(tokens)) return \"arch\";\n return \"generic\";\n}\n\nfunction getPrerequisiteFixCommands(key, options = {}) {\n const locale = normalizeLocale(options.locale);\n const platform = String(options.platform || process.platform);\n const linuxFamily = detectLinuxPackageFamily(options.osRelease);\n const localPrefixCommands = locale === \"en\"\n ? [\n \"mkdir -p \\\"$HOME/.local/npm\\\"\",\n \"npm config set prefix \\\"$HOME/.local/npm\\\"\",\n \"echo 'export PATH=\\\"$HOME/.local/npm/bin:$PATH\\\"' >> ~/.profile\",\n ]\n : [\n \"mkdir -p \\\"$HOME/.local/npm\\\"\",\n \"npm config set prefix \\\"$HOME/.local/npm\\\"\",\n \"echo 'export PATH=\\\"$HOME/.local/npm/bin:$PATH\\\"' >> ~/.profile\",\n ];\n\n if (platform === \"darwin\") {\n if (key === \"npm\") return [\"brew install node\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"brew install --cask docker\"];\n if (key === \"docker-daemon\") return [\"open -a Docker\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (platform === \"win32\") {\n if (key === \"npm\") return [\"Scarica e installa Node.js da https://nodejs.org (versione LTS 22.x)\"];\n if (key === \"npm-write\") return locale === \"en\"\n ? [\"Run PowerShell as Administrator: npm config set prefix \\\"%APPDATA%\\\\npm\\\"\"]\n : [\"Esegui PowerShell come Amministratore: npm config set prefix \\\"%APPDATA%\\\\npm\\\"\"];\n if (key === \"windows-service-admin\") return locale === \"en\"\n ? [\"Run PowerShell as Administrator, then retry: llmp up\", \"Or stop the service as Administrator first: llmp service:stop\"]\n : [\"Esegui PowerShell come Amministratore, poi riprova: llmp up\", \"Oppure arresta prima il servizio come Amministratore: llmp service:stop\"];\n return [];\n }\n\n if (platform !== \"linux\") return [];\n\n if (key === \"systemd-user-bus\") return locale === \"en\"\n ? [\"loginctl enable-linger\", \"sudo loginctl enable-linger $USER\"]\n : [\"loginctl enable-linger\", \"sudo loginctl enable-linger $USER\"];\n\n if (linuxFamily === \"debian\") {\n if (key === \"npm\") return [\"sudo apt update && sudo apt install -y nodejs npm\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"sudo apt update && sudo apt install -y docker.io docker-compose-v2\"];\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") return [\"sudo apt update && sudo apt install -y systemd\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (linuxFamily === \"rhel\") {\n if (key === \"npm\") return [\"sudo dnf install -y nodejs npm\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"sudo dnf install -y docker docker-compose-plugin\"];\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") return [\"sudo dnf install -y systemd\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (linuxFamily === \"arch\") {\n if (key === \"npm\") return [\"sudo pacman -Sy --noconfirm nodejs npm\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"sudo pacman -Sy --noconfirm docker docker-compose\"];\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") return [\"sudo pacman -Sy --noconfirm systemd\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (key === \"npm\") return [locale === \"en\" ? \"Install Node.js + npm with your distro package manager.\" : \"Installa Node.js + npm con il package manager della tua distro.\"];\n if (key === \"docker\" || key === \"docker-compose\") {\n return [locale === \"en\"\n ? \"Install Docker Engine plus Docker Compose plugin (or the legacy docker-compose binary).\"\n : \"Installa Docker Engine piu' il plugin Docker Compose (oppure il binario legacy docker-compose).\"];\n }\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") {\n return [locale === \"en\"\n ? \"Use a Linux environment with systemd user services enabled.\"\n : \"Usa un ambiente Linux con servizi utente systemd abilitati.\"];\n }\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n}\n\nfunction resolveUpdatePrerequisites(options = {}) {\n const commandRunner = options.commandRunner || spawnSync;\n const targetPlatform = String(options.targetPlatform || process.platform);\n const env = options.env || process.env;\n const paths = options.paths || createPaths({ dataRoot: options.dataRoot, packageRoot: options.packageRoot, env });\n const packageRoot = paths.packageRoot;\n const runtimeEntry = resolveServiceEntryFile({ env, packageRoot, targetPlatform });\n const requiresDockerRuntime = path.basename(runtimeEntry) === \"docker-launchd-entry.js\";\n const dockerComposeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(packageRoot, \"docker-compose.production.yml\")).trim();\n const missing = [];\n\n const addMissing = (key, messageIt, messageEn) => {\n if (missing.some((item) => item.key === key)) return;\n missing.push({ key, messageIt, messageEn });\n };\n\n if (!commandIsAvailableForPlatform(commandRunner, \"git\", targetPlatform)) {\n addMissing(\"git\", \"Git (`git`) non trovato nel PATH.\", \"Git (`git`) is not available in PATH.\");\n }\n // pnpm is optional: we fall back to npm pack if pnpm is not available\n // if (!commandIsAvailable(commandRunner, \"pnpm\")) {\n // addMissing(\"pnpm\", \"pnpm non trovato nel PATH.\", \"pnpm is not available in PATH.\");\n // }\n const npmAvailable = commandIsAvailableForPlatform(commandRunner, \"npm\", targetPlatform);\n if (!npmAvailable) {\n addMissing(\"npm\", \"npm non trovato nel PATH.\", \"npm is not available in PATH.\");\n }\n\n if (npmAvailable) {\n const npmPrefixResult = runPlatformCommand(commandRunner, \"npm\", [\"prefix\", \"-g\"], targetPlatform);\n const npmPrefix = String(npmPrefixResult?.stdout || \"\").trim();\n if (!npmPrefixResult || npmPrefixResult.status !== 0 || !npmPrefix) {\n addMissing(\n \"npm-prefix\",\n \"Impossibile determinare il prefisso globale di npm (`npm prefix -g`).\",\n \"Unable to determine the global npm prefix (`npm prefix -g`).\",\n );\n } else if (!canWritePath(npmPrefix)) {\n if (!commandIsAvailableForPlatform(commandRunner, \"sudo\", targetPlatform)) {\n addMissing(\n \"npm-write\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e \\`sudo\\` non e' disponibile.`,\n `The global npm prefix is not writable (${npmPrefix}) and \\`sudo\\` is not available.`,\n );\n } else if (!canUseSudoNonInteractively(commandRunner)) {\n addMissing(\n \"sudo-ticket\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e questa CLI non puo' richiedere la password sudo in modo interattivo. Esegui prima \\`sudo -v\\` oppure rilancia \\`sudo llmproxy update\\`.`,\n `The global npm prefix is not writable (${npmPrefix}) and this CLI cannot prompt for a sudo password interactively. Run \\`sudo -v\\` first or rerun \\`sudo llmproxy update\\`.`,\n );\n }\n }\n }\n\n if (targetPlatform === \"win32\") {\n const serviceState = getWindowsServiceState(commandRunner, \"llmproxy\");\n if (serviceState.active && !isWindowsSessionElevated(commandRunner)) {\n addMissing(\n \"windows-service-admin\",\n \"Il servizio Windows `llmproxy` e' attivo e questa sessione PowerShell non e' elevata. Esegui `llmp up` da una PowerShell avviata come Amministratore, oppure arresta prima il servizio con privilegi amministrativi.\",\n \"The Windows `llmproxy` service is active and this PowerShell session is not elevated. Run `llmp up` from an Administrator PowerShell, or stop the service first with administrative privileges.\",\n );\n }\n }\n\n if (requiresDockerRuntime) {\n if (!fs.existsSync(dockerComposeFile)) {\n addMissing(\n \"docker-compose-file\",\n `File docker compose non trovato: ${dockerComposeFile}`,\n `Docker compose file not found: ${dockerComposeFile}`,\n );\n }\n const dockerAvailable = commandIsAvailableForPlatform(commandRunner, \"docker\", targetPlatform);\n if (!dockerAvailable) {\n addMissing(\"docker\", \"Docker non trovato nel PATH.\", \"Docker is not available in PATH.\");\n } else {\n if (!resolveDockerComposeCommand(commandRunner).ok) {\n addMissing(\n \"docker-compose\",\n \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\",\n \"Docker Compose is not available (`docker compose` and `docker-compose` both failed).\",\n );\n }\n if (!commandIsAvailableForPlatform(commandRunner, \"docker\", targetPlatform, [\"info\"])) {\n addMissing(\n \"docker-daemon\",\n \"Il daemon Docker non e' raggiungibile per l'utente corrente (`docker info` fallisce).\",\n \"The Docker daemon is not reachable for the current user (`docker info` failed).\",\n );\n }\n }\n }\n\n return {\n ok: missing.length === 0,\n requiresDockerRuntime,\n dockerComposeFile,\n missing,\n };\n}\n\nfunction formatUpdatePrerequisitesFailure(result, locale = \"it\") {\n const normalizedLocale = normalizeLocale(locale);\n const header = normalizedLocale === \"en\"\n ? \"Update prerequisites are not satisfied. Install/fix the following items and run `llmproxy update` again:\"\n : \"I prerequisiti per `llmproxy update` non sono soddisfatti. Installa/correggi questi elementi e rilancia `llmproxy update`:\";\n const lines = [header];\n for (const item of result.missing || []) {\n lines.push(`- ${normalizedLocale === \"en\" ? item.messageEn : item.messageIt}`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction resolvePersistentInstallPrerequisites(options = {}) {\n const commandRunner = options.commandRunner || spawnSync;\n const env = options.env || process.env;\n const targetPlatform = String(options.targetPlatform || process.platform);\n const packageRoot = path.resolve(String(options.packageRoot || path.join(__dirname, \"..\")));\n const runtimeEnv = {\n ...env,\n LLMPROXY_ENV: String(env.LLMPROXY_ENV || env.NODE_ENV || \"production\"),\n };\n const runtimeEntry = resolveServiceEntryFile({ env: runtimeEnv, packageRoot: path.join(\"/tmp\", \"node_modules\", \"llmproxy\"), targetPlatform });\n const requiresDockerRuntime = path.basename(runtimeEntry) === \"docker-launchd-entry.js\";\n const dockerComposeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(packageRoot, \"docker-compose.production.yml\")).trim();\n const missing = [];\n\n const addMissing = (key, messageIt, messageEn) => {\n if (missing.some((item) => item.key === key)) return;\n missing.push({ key, messageIt, messageEn });\n };\n\n if (!fs.existsSync(packageRoot)) {\n addMissing(\n \"package-root\",\n `Directory del progetto non trovata: ${packageRoot}`,\n `Project directory not found: ${packageRoot}`,\n );\n }\n\n const npmAvailable = commandIsAvailableForPlatform(commandRunner, \"npm\", targetPlatform);\n if (!npmAvailable) {\n addMissing(\"npm\", \"npm non trovato nel PATH.\", \"npm is not available in PATH.\");\n }\n\n if (targetPlatform === \"win32\") {\n if (!commandIsAvailableForPlatform(commandRunner, \"powershell.exe\", targetPlatform, [\"-NoProfile\", \"-Command\", \"Get-Command\", \"sc.exe\"])) {\n addMissing(\n \"sc\",\n \"Windows Service Control (`sc.exe`) non disponibile. Windows non e' supportato correttamente.\",\n \"Windows Service Control (`sc.exe`) is not available. Windows is not fully supported.\",\n );\n }\n }\n\n if (targetPlatform === \"linux\" && !commandIsAvailableForPlatform(commandRunner, \"systemctl\", targetPlatform, [\"--version\"])) {\n addMissing(\n \"systemctl\",\n \"systemd (`systemctl`) non e' disponibile: l'installazione persistente Linux richiede un servizio `systemd --user`.\",\n \"systemd (`systemctl`) is not available: persistent Linux installs require a `systemd --user` service.\",\n );\n }\n\n if (targetPlatform === \"linux\" && commandIsAvailableForPlatform(commandRunner, \"systemctl\", targetPlatform, [\"--version\"])) {\n // Under sudo, delegate to real user via sudo -u (D-Bus enforces uid match on the socket)\n const sudoUser = process.env.SUDO_USER;\n const checkCmd = sudoUser\n ? `sudo -u \"${sudoUser}\" env XDG_RUNTIME_DIR=/run/user/$(id -u \"${sudoUser}\") DBUS_SESSION_BUS_ADDRESS=\"unix:path=/run/user/$(id -u \"${sudoUser}\")/bus\" systemctl --user is-system-running --quiet`\n : \"systemctl --user is-system-running --quiet\";\n const userBusCheck = runCommand(commandRunner, \"bash\", [\"-c\", checkCmd]);\n if (userBusCheck && userBusCheck.status !== 0 && userBusCheck.stderr && userBusCheck.stderr.includes(\"bus\")) {\n addMissing(\n \"systemd-user-bus\",\n \"Il bus systemd --user non e' raggiungibile. Attiva `loginctl enable-linger` per mantenere attivo il manager utente.\",\n \"The systemd --user bus is not reachable. Enable linger with `loginctl enable-linger` to keep the user manager running.\",\n );\n }\n }\n\n if (npmAvailable) {\n const npmPrefixResult = runPlatformCommand(commandRunner, \"npm\", [\"prefix\", \"-g\"], targetPlatform);\n const npmPrefix = String(npmPrefixResult?.stdout || \"\").trim();\n if (!npmPrefixResult || npmPrefixResult.status !== 0 || !npmPrefix) {\n addMissing(\n \"npm-prefix\",\n \"Impossibile determinare il prefisso globale di npm (`npm prefix -g`).\",\n \"Unable to determine the global npm prefix (`npm prefix -g`).\",\n );\n } else if (!canWritePath(npmPrefix)) {\n if (!commandIsAvailableForPlatform(commandRunner, \"sudo\", targetPlatform)) {\n addMissing(\n \"npm-write\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e \\`sudo\\` non e' disponibile.`,\n `The global npm prefix is not writable (${npmPrefix}) and \\`sudo\\` is not available.`,\n );\n } else if (!canUseSudoNonInteractively(commandRunner)) {\n addMissing(\n \"sudo-ticket\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e questa CLI non puo' richiedere la password sudo in modo interattivo. Esegui prima \\`sudo -v\\` oppure rilancia il comando con \\`sudo\\`.`,\n `The global npm prefix is not writable (${npmPrefix}) and this CLI cannot prompt for a sudo password interactively. Run \\`sudo -v\\` first or rerun the command with \\`sudo\\`.`,\n );\n }\n }\n }\n\n if (requiresDockerRuntime) {\n if (!fs.existsSync(dockerComposeFile)) {\n addMissing(\n \"docker-compose-file\",\n `File docker compose non trovato: ${dockerComposeFile}`,\n `Docker compose file not found: ${dockerComposeFile}`,\n );\n }\n const dockerCompose = resolveDockerComposeCommand(commandRunner);\n if (!dockerCompose.dockerAvailable) {\n addMissing(\"docker\", \"Docker non trovato nel PATH.\", \"Docker is not available in PATH.\");\n } else if (!dockerCompose.ok) {\n addMissing(\n \"docker-compose\",\n \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\",\n \"Docker Compose is not available (`docker compose` and `docker-compose` both failed).\",\n );\n }\n if (dockerCompose.dockerAvailable && !commandIsAvailableForPlatform(commandRunner, \"docker\", targetPlatform, [\"info\"])) {\n addMissing(\n \"docker-daemon\",\n \"Il daemon Docker non e' raggiungibile per l'utente corrente (`docker info` fallisce).\",\n \"The Docker daemon is not reachable for the current user (`docker info` failed).\",\n );\n }\n }\n\n return {\n ok: missing.length === 0,\n missing,\n requiresDockerRuntime,\n dockerComposeFile,\n };\n}\n\nfunction formatPersistentInstallPrerequisitesFailure(result, options = {}) {\n const locale = normalizeLocale(options.locale);\n const platform = String(options.platform || process.platform);\n const osRelease = options.osRelease || {};\n const header = locale === \"en\"\n ? \"Persistent installation prerequisites are not satisfied. Install/fix the following items and run the command again:\"\n : \"I prerequisiti per l'installazione persistente non sono soddisfatti. Installa/correggi questi elementi e rilancia il comando:\";\n const lines = [header];\n\n for (const item of result.missing || []) {\n lines.push(`- ${locale === \"en\" ? item.messageEn : item.messageIt}`);\n const commands = getPrerequisiteFixCommands(item.key, { locale, platform, osRelease });\n for (const command of commands) {\n lines.push(` ${locale === \"en\" ? \"Command\" : \"Comando\"}: ${command}`);\n }\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction formatQwenPlanLabel(endpointVariant) {\n const variant = normalizeQwenEndpointVariant(endpointVariant);\n if (variant === \"token_plan\") return \"subscription\";\n if (variant === \"dashscope\") return \"payg\";\n return \"\";\n}\n\nfunction formatProviderPlanSuffix(provider, useColor = false) {\n if (String(provider?.provider || \"\").toLowerCase() !== \"qwen\") return \"\";\n const plan = formatQwenPlanLabel(provider?.endpoint_variant);\n return plan ? ` plan=${colorize(plan, \"cyan\", useColor)}` : \"\";\n}\n\nfunction formatVisionSuffix(provider, useColor = false) {\n if (provider.vision === true) return ` vision=${colorize(\"true\", \"green\", useColor)}`;\n if (provider.vision === false) return ` vision=${colorize(\"false\", \"red\", useColor)}`;\n return \"\";\n}\n\nfunction formatFreeModelSuffix(provider, useColor = false) {\n if (provider.free_model === true) return ` free=${colorize(\"true\", \"green\", useColor)}`;\n if (provider.free_model === false) return ` free=${colorize(\"false\", \"dim\", useColor)}`;\n return \"\";\n}\n\nfunction parseOptionalBooleanFlag(value, { defaultValue = null, allowBareTrue = false } = {}) {\n if (value === undefined || value === null || value === \"\") return defaultValue;\n if (value === true && allowBareTrue) return true;\n if (value === false) return false;\n const normalized = String(value).trim().toLowerCase();\n if (!normalized) return defaultValue;\n if (allowBareTrue && normalized === \"true\") return true;\n if (normalized === \"true\" || normalized === \"1\" || normalized === \"yes\" || normalized === \"on\") return true;\n if (normalized === \"false\" || normalized === \"0\" || normalized === \"no\" || normalized === \"off\") return false;\n return null;\n}\n\nfunction getProviderKind(provider) {\n return String(provider?.provider || \"copilot\").toLowerCase();\n}\n\nfunction resolveEffectiveProviderList(providers, configuredModel) {\n const baseProviders = Array.isArray(providers) ? providers.map((provider) => ({ ...provider })) : [];\n const preferences = parseProviderModelPreferences(configuredModel);\n if (!configuredModel || preferences.length === 0) {\n return {\n providers: baseProviders,\n configuredModel: \"\",\n projectOverrideActive: false,\n };\n }\n\n return {\n providers: baseProviders,\n configuredModel: String(configuredModel || \"\").trim(),\n projectOverrideActive: true,\n };\n}\n\nfunction formatProviderList(providers, options = {}) {\n const useColor = shouldUseColorOutput(options.stdout, options.env);\n const env = options.env || process.env;\n return providers.map((provider, index) => {\n const defaultModel = provider.effective_model || provider.default_model || (provider.provider === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n const linePrefix = colorize(`${index + 1}.`, \"dim\", useColor);\n const providerId = colorize(provider.id, \"cyan\", useColor);\n const providerName = colorize(provider.name, \"bright\", useColor);\n const modelValue = defaultModel\n ? colorize(defaultModel, \"yellow\", useColor)\n : colorize(\"missing\", \"red\", useColor);\n const codingValue = colorize(provider.coding_info?.label || \"n/a\", provider.coding_info?.color || \"dim\", useColor);\n const creditValue = colorize(provider.credit_info?.label || \"n/a\", provider.credit_info?.color || \"dim\", useColor);\n const currentPrice = colorize(provider.price_info?.currentPriceLabel || \"n/a\", provider.price_info?.currentPriceColor || \"dim\", useColor);\n const bestProvider = colorize(provider.price_info?.bestProviderLabel || \"n/a\", provider.price_info?.bestProviderColor || \"dim\", useColor);\n const bestPrice = colorize(provider.price_info?.bestPriceLabel || \"n/a\", provider.price_info?.bestPriceColor || \"dim\", useColor);\n\n let proxyValue = null;\n if (provider.proxy_rotation) {\n proxyValue = colorize(\"proxy:rotation\", \"magenta\", useColor);\n } else if (provider.proxy_url) {\n proxyValue = colorize(`proxy:${provider.proxy_url}`, \"magenta\", useColor);\n } else {\n proxyValue = colorize(\"proxy:no\", \"dim\", useColor);\n }\n const benchSpeed = provider.bench_metric || \"n/a\";\n const visionSuffix = formatVisionSuffix(provider, useColor);\n const freeModelSuffix = formatFreeModelSuffix(provider, useColor);\n const planSuffix = formatProviderPlanSuffix(provider, useColor);\n const visibilityLine = [visionSuffix.trim(), freeModelSuffix.trim()].filter(Boolean).join(\" \");\n const details = [\n ` model=${modelValue}`,\n ` credit=${creditValue}`,\n ` coding=${codingValue}${planSuffix}`,\n visibilityLine ? ` ${visibilityLine}` : null,\n ` price=${currentPrice}`,\n ` best=${bestProvider} (${bestPrice})`,\n proxyValue ? ` proxy=${proxyValue}` : null,\n ` bench=${benchSpeed}`,\n ].filter(Boolean);\n\n return `${linePrefix} ${providerId} (${providerName})\\n${details.join(\"\\n\")}`;\n }).join(\"\\n\\n\");\n}\n\nfunction shouldUseColorOutput(stream = process.stdout, env = process.env) {\n if (String(env?.NO_COLOR || \"\").trim()) return false;\n if (String(env?.FORCE_COLOR || \"\").trim()) return true;\n return Boolean(stream && stream.isTTY);\n}\n\nfunction colorize(text, style, enabled) {\n if (!enabled) return String(text);\n const STYLES = {\n dim: \"\\x1b[2m\",\n bright: \"\\x1b[1m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n };\n const prefix = STYLES[style];\n if (!prefix) return String(text);\n return `${prefix}${text}\\x1b[0m`;\n}\n\nfunction formatProviderCreditSuffix(creditInfo, useColor = false) {\n const label = String(creditInfo?.label || \"n/a\").trim() || \"n/a\";\n const color = label === \"n/a\"\n ? \"dim\"\n : label === \"unavailable\"\n ? \"red\"\n : label.includes(\"-\")\n ? \"red\"\n : \"blue\";\n return ` credit=${colorize(label, color, useColor)}`;\n}\n\nfunction formatProviderPriceSuffix(priceInfo, useColor = false) {\n const currentPrice = String(priceInfo?.currentPriceLabel || \"n/a\").trim() || \"n/a\";\n const bestProvider = String(priceInfo?.bestProviderLabel || \"n/a\").trim() || \"n/a\";\n const bestPrice = String(priceInfo?.bestPriceLabel || \"n/a\").trim() || \"n/a\";\n const currentColor = currentPrice === \"n/a\" || currentPrice === \"unavailable\" ? \"dim\" : \"magenta\";\n const bestProviderColor = bestProvider === \"n/a\" || bestProvider === \"unavailable\" ? \"dim\" : \"green\";\n const bestPriceColor = bestPrice === \"n/a\" || bestPrice === \"unavailable\" ? \"dim\" : \"green\";\n return ` price=${colorize(currentPrice, currentColor, useColor)} best=${colorize(bestProvider, bestProviderColor, useColor)} (${colorize(bestPrice, bestPriceColor, useColor)})`;\n}\n\nfunction formatCodingScoreLabel(value) {\n if (value == null || value === \"\") return \"n/a\";\n const numeric = Number(value);\n if (!Number.isFinite(numeric)) return \"n/a\";\n return numeric.toFixed(1);\n}\n\nfunction colorForCodingScore(label) {\n if (label === \"n/a\") return \"dim\";\n if (label === \"unavailable\") return \"red\";\n const numeric = Number(label);\n if (!Number.isFinite(numeric)) return \"dim\";\n if (numeric >= 0.8 || numeric >= 80) return \"green\";\n if (numeric >= 0.6 || numeric >= 60) return \"yellow\";\n return \"red\";\n}\n\nfunction normalizeModelLookupCandidates(model) {\n const raw = String(model || \"\").trim();\n if (!raw) return [];\n const withoutDate = raw.replace(/-\\d{8}$/i, \"\");\n const leaf = raw.includes(\"/\") ? raw.split(\"/\").pop() : raw;\n const leafWithoutDate = leaf.replace(/-\\d{8}$/i, \"\");\n return Array.from(new Set([raw, withoutDate, leaf, leafWithoutDate]\n .map((entry) => String(entry || \"\").trim().toLowerCase())\n .filter(Boolean)));\n}\n\nasync function fetchProviderCodingInfo(provider, fetchFn, cache = new Map()) {\n // Prefer stored coding_score (backfilled at provider insertion, no need to fetch live every time)\n if (typeof provider?.coding_score === \"number\" && Number.isFinite(provider.coding_score)) {\n const label = formatCodingScoreLabel(provider.coding_score);\n return { label, color: colorForCodingScore(label) };\n }\n const model = String(provider?.effective_model || provider?.default_model || \"\").trim();\n if (!model) return { label: \"n/a\", color: \"dim\" };\n const score = await fetchCodingScore(model, fetchFn, cache);\n if (score == null) return { label: \"n/a\", color: \"dim\" };\n const label = formatCodingScoreLabel(score);\n return { label, color: colorForCodingScore(label) };\n}\n\nfunction formatCreditAmount(amount, currency = \"\") {\n const numeric = Number(amount);\n if (!Number.isFinite(numeric)) return \"\";\n const formatted = numeric.toFixed(2);\n const normalizedCurrency = String(currency || \"\").trim().toUpperCase();\n return normalizedCurrency ? `${normalizedCurrency} ${formatted}` : formatted;\n}\n\nasync function fetchProviderCreditInfo(provider, fetchFn, cache = new Map()) {\n const providerKind = String(provider?.provider || provider?.id || \"\").trim().toLowerCase();\n const accessToken = String(provider?.access_token || \"\").trim();\n const endpointVariant = String(provider?.endpoint_variant || \"\").trim().toLowerCase();\n if (!providerKind || !accessToken) return { label: \"n/a\", color: \"dim\" };\n\n const cacheKey = crypto\n .createHash(\"sha1\")\n .update(`${providerKind}|${endpointVariant}|${accessToken}`)\n .digest(\"hex\");\n\n if (cache.has(cacheKey)) return cache.get(cacheKey);\n\n const promise = (async () => {\n const unsupported = { label: \"n/a\", color: \"dim\" };\n const unavailable = { label: \"unavailable\", color: \"red\" };\n const request = {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"content-type\": \"application/json\",\n },\n signal: typeof AbortSignal?.timeout === \"function\" ? AbortSignal.timeout(5000) : undefined,\n };\n\n try {\n if (providerKind === \"deepseek\") {\n const response = await fetchFn(\"https://api.deepseek.com/user/balance\", request);\n if (!response?.ok) return unavailable;\n const payload = await readJsonResponseSafe(response);\n const balances = Array.isArray(payload?.balance_infos) ? payload.balance_infos : [];\n const preferred = balances.find((entry) => String(entry?.currency || \"\").trim().toUpperCase() === \"USD\") || balances[0];\n const label = formatCreditAmount(preferred?.total_balance, preferred?.currency);\n return label ? { label, color: \"blue\" } : unavailable;\n }\n\n if (providerKind === \"kimi\") {\n const response = await fetchFn(\"https://api.moonshot.ai/v1/users/me/balance\", request);\n if (!response?.ok) return unavailable;\n const payload = await readJsonResponseSafe(response);\n const label = formatCreditAmount(payload?.data?.available_balance);\n return label ? { label, color: \"blue\" } : unavailable;\n }\n\n if (providerKind === \"openrouter\") {\n const response = await fetchFn(\"https://openrouter.ai/api/v1/credits\", request);\n if (!response?.ok) return unavailable;\n const payload = await readJsonResponseSafe(response);\n const totalCredits = Number(payload?.data?.total_credits);\n const totalUsage = Number(payload?.data?.total_usage);\n if (!Number.isFinite(totalCredits) || !Number.isFinite(totalUsage)) return unavailable;\n const remainingCredits = Math.max(0, totalCredits - totalUsage);\n return { label: `${remainingCredits.toFixed(2)} credits`, color: \"blue\" };\n }\n\n return unsupported;\n } catch {\n return unavailable;\n }\n })();\n\n cache.set(cacheKey, promise);\n return promise;\n}\n\nfunction mapCloudPriceProviderLabel(providerId) {\n const normalized = String(providerId || \"\").trim().toLowerCase();\n const mappings = {\n alibaba_qwen: \"qwen\",\n fireworks_ai: \"fireworks\",\n moonshot_ai: \"kimi\",\n moonshotai: \"kimi\",\n vercel_ai_gateway: \"vercel_ai_gateway\",\n azure_aifoundry: \"azure_aifoundry\",\n };\n return mappings[normalized] || normalized;\n}\n\nfunction formatCloudPriceBreakdownLabel(entry) {\n const breakdown = Array.isArray(entry?.breakdown) ? entry.breakdown : [];\n const input = breakdown.find((item) => String(item?.dimension || \"\").trim().toLowerCase() === \"input\");\n const output = breakdown.find((item) => String(item?.dimension || \"\").trim().toLowerCase() === \"output\");\n const inputPrice = Number(input?.unit_price);\n const outputPrice = Number(output?.unit_price);\n if (!Number.isFinite(inputPrice) && !Number.isFinite(outputPrice)) return \"n/a\";\n const parts = [];\n if (Number.isFinite(inputPrice)) parts.push(`in=USD ${inputPrice.toFixed(2)}/1M`);\n if (Number.isFinite(outputPrice)) parts.push(`out=USD ${outputPrice.toFixed(2)}/1M`);\n return parts.join(\" \");\n}\n\nfunction pickCloudPriceBestProvider(resultProviders, provider) {\n const candidates = Array.isArray(resultProviders) ? resultProviders : [];\n if (candidates.length === 0) return null;\n const currentIds = new Set(mapProviderToCloudPriceIds(provider));\n const llmProxySupported = new Set(AVAILABLE_PROVIDER_SPECS.map((entry) => entry.id));\n const preferredAlternative = candidates.find((candidate) => {\n const mapped = mapCloudPriceProviderLabel(candidate?.provider_id);\n return mapped && !currentIds.has(String(candidate?.provider_id || \"\").trim().toLowerCase()) && llmProxySupported.has(mapped);\n });\n if (preferredAlternative) return preferredAlternative;\n const nonCurrent = candidates.find((candidate) => !currentIds.has(String(candidate?.provider_id || \"\").trim().toLowerCase()));\n if (nonCurrent) return nonCurrent;\n return candidates[0];\n}\n\nasync function fetchProviderPriceInfo(provider, fetchFn, cache = new Map()) {\n if (provider?.free_model === true) {\n return {\n currentPriceLabel: \"free\",\n currentPriceColor: \"green\",\n bestProviderLabel: \"free\",\n bestProviderColor: \"green\",\n bestPriceLabel: \"free\",\n bestPriceColor: \"green\",\n };\n }\n const model = String(provider?.effective_model || provider?.default_model || \"\").trim();\n if (!model) {\n return {\n currentPriceLabel: \"n/a\",\n currentPriceColor: \"dim\",\n bestProviderLabel: \"n/a\",\n bestProviderColor: \"dim\",\n bestPriceLabel: \"n/a\",\n bestPriceColor: \"dim\",\n };\n }\n const data = await fetchModelPricing(model, fetchFn, cache);\n if (!data) {\n return {\n currentPriceLabel: \"unavailable\",\n currentPriceColor: \"dim\",\n bestProviderLabel: \"unavailable\",\n bestProviderColor: \"dim\",\n bestPriceLabel: \"unavailable\",\n bestPriceColor: \"dim\",\n };\n }\n\n const currentOption = pickCurrentPricingOption(data.options, provider);\n const bestProvider = pickCloudPriceBestProvider(data?.result?.providers, provider);\n const currentPriceLabel = currentOption ? formatCloudPriceBreakdownLabel(currentOption) : \"n/a\";\n const bestProviderLabel = bestProvider ? mapCloudPriceProviderLabel(bestProvider.provider_id) : \"n/a\";\n const bestPriceLabel = data?.result ? formatCloudPriceBreakdownLabel(data.result) : \"n/a\";\n return {\n currentPriceLabel,\n currentPriceColor: currentPriceLabel === \"n/a\" || currentPriceLabel === \"unavailable\" ? \"dim\" : \"magenta\",\n bestProviderLabel,\n bestProviderColor: bestProviderLabel === \"n/a\" || bestProviderLabel === \"unavailable\" ? \"dim\" : \"green\",\n bestPriceLabel,\n bestPriceColor: bestPriceLabel === \"n/a\" || bestPriceLabel === \"unavailable\" ? \"dim\" : \"green\",\n };\n}\n\nfunction formatProviderStatus(providers) {\n const activeProviderId = providers[0]?.id || \"none\";\n const lines = [`Active provider: ${activeProviderId}`];\n providers.forEach((provider, index) => {\n const activeSuffix = index === 0 ? \" [active]\" : \"\";\n const defaultModel = provider.effective_model || provider.default_model || (provider.provider === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n const state = provider.access_token && (provider.provider === \"copilot\" || defaultModel) ? \"configured\" : \"incomplete\";\n const modelSuffix = defaultModel ? ` model=${defaultModel}` : \" model=missing\";\n const visionSuffix = formatVisionSuffix(provider);\n const planSuffix = formatProviderPlanSuffix(provider);\n const visionLine = visionSuffix ? ` ${visionSuffix.trim()}` : \"\";\n lines.push(`${index + 1}. ${provider.id} (${provider.name})${activeSuffix} provider=${provider.provider || \"copilot\"} auth=${provider.auth_type || \"oauth\"} state=${state}${modelSuffix}${planSuffix}${visionLine ? \"\\n\" + visionLine : \"\"}`);\n });\n return lines.join(\"\\n\");\n}\n\nfunction formatConfigEntries(entries) {\n return entries\n .map((entry) => `${entry.scope}.${entry.key}=${entry.value == null ? \"\" : String(entry.value)}`)\n .join(\"\\n\");\n}\n\nfunction resolveQwenEndpointVariant(providerId, planFlag, apiKey) {\n if (providerId !== \"qwen\") return \"\";\n const explicit = normalizeQwenEndpointVariant(planFlag);\n if (explicit) return explicit;\n return /^sk-sp-/i.test(String(apiKey || \"\").trim()) ? \"token_plan\" : \"dashscope\";\n}\n\nfunction formatModelList(models) {\n return models.map((model, index) => `${index + 1}. ${model}`).join(\"\\n\");\n}\n\nfunction sortBreakdownEntries(entriesObject = {}) {\n return Object.entries(entriesObject)\n .map(([key, value]) => [key, value || {}])\n .sort((left, right) => {\n const leftTotal = Number(left[1].tokens_input || 0) + Number(left[1].tokens_output || 0);\n const rightTotal = Number(right[1].tokens_input || 0) + Number(right[1].tokens_output || 0);\n if (rightTotal !== leftTotal) return rightTotal - leftTotal;\n const leftRequests = Number(left[1].requests || 0);\n const rightRequests = Number(right[1].requests || 0);\n if (rightRequests !== leftRequests) return rightRequests - leftRequests;\n return String(left[0]).localeCompare(String(right[0]));\n });\n}\n\nfunction formatBreakdownSection(title, entriesObject = {}, useColor = false) {\n const entries = sortBreakdownEntries(entriesObject);\n if (entries.length === 0) {\n return {\n header: title,\n rows: [],\n columns: [\"Name\", \"Req\", \"Total\", \"In\", \"Out\"],\n };\n }\n const rows = entries.map(([key, value]) => {\n const input = Number(value.tokens_input || 0);\n const output = Number(value.tokens_output || 0);\n const total = input + output;\n const requests = Number(value.requests || 0);\n return { name: key, requests, total, input, output };\n });\n return { header: title, rows, columns: [\"Name\", \"Req\", \"Total\", \"In\", \"Out\"] };\n}\n\nfunction padRight(text, width) {\n const str = String(text);\n return str.length >= width ? str : str + \" \".repeat(width - str.length);\n}\n\nfunction padLeft(text, width) {\n const str = String(text);\n return str.length >= width ? str : \" \".repeat(width - str.length) + str;\n}\n\nfunction formatNumber(n) {\n return Number(n).toLocaleString(\"it-IT\");\n}\n\nfunction renderStatsTable(stats = {}, source = \"unknown\", useColor = false) {\n const totalRequests = Number(stats.total_requests || 0);\n const successCount = Number(stats.success_count || 0);\n const errorCount = Number(stats.error_count || 0);\n const input = Number(stats.total_tokens_input || 0);\n const output = Number(stats.total_tokens_output || 0);\n const total = Number(stats.total_tokens || 0);\n const providers = formatBreakdownSection(\"Providers\", stats.by_provider || {}, useColor);\n const models = formatBreakdownSection(\"Models\", stats.by_model || {}, useColor);\n\n const sections = [\n { label: \"Source\", value: source },\n { label: \"Requests\", value: `${formatNumber(totalRequests)} | Success: ${formatNumber(successCount)} | Errors: ${formatNumber(errorCount)}` },\n { label: \"Tokens\", value: `total=${formatNumber(total)} in=${formatNumber(input)} out=${formatNumber(output)}` },\n ];\n if (stats.earliest_timestamp || stats.latest_timestamp) {\n sections.push({ label: \"Range\", value: `${stats.earliest_timestamp || \"n/a\"} → ${stats.latest_timestamp || \"n/a\"}` });\n }\n\n const allRows = [\n ...providers.rows.map((r) => ({ ...r, section: \"Providers\" })),\n ...models.rows.map((r) => ({ ...r, section: \"Models\" })),\n ];\n\n const nameWidth = Math.max(\n 5,\n ...allRows.map((r) => r.name.length),\n ...[\"Name\", \"Provider\", \"Model\"].map((s) => s.length),\n );\n const numCols = [\"requests\", \"total\", \"input\", \"output\"];\n const colWidths = {};\n for (const col of numCols) {\n colWidths[col] = Math.max(\n 5,\n ...allRows.map((r) => formatNumber(r[col]).length),\n );\n }\n\n const border = \"─\";\n const pipe = \"│\";\n const cross = \"┼\";\n const topLeft = \"┌\";\n const topRight = \"┐\";\n const botLeft = \"└\";\n const botRight = \"┘\";\n const midLeft = \"├\";\n const midRight = \"┤\";\n\n const innerWidth = nameWidth + colWidths.requests + colWidths.total + colWidths.input + colWidths.output + 20;\n const width = Math.max(innerWidth, 50);\n\n function horizontalBar(left, mid, right, fill = border) {\n return left + fill.repeat(width - 2) + right;\n }\n\n function sectionDivider(label) {\n const labelText = `─ ${label} `;\n const remaining = width - 2 - labelText.length;\n return midLeft + labelText + border.repeat(Math.max(0, remaining)) + midRight;\n }\n\n function tableHeader() {\n const nameCol = padRight(\"Name\", nameWidth);\n const reqCol = padLeft(\"Req\", colWidths.requests);\n const totalCol = padLeft(\"Total\", colWidths.total);\n const inCol = padLeft(\"In\", colWidths.input);\n const outCol = padLeft(\"Out\", colWidths.output);\n const row = `${pipe} # ${nameCol} ${reqCol} ${totalCol} ${inCol} ${outCol} ${pipe}`;\n if (useColor) {\n return colorize(row, \"bright\", true);\n }\n return row;\n }\n\n function tableRow(index, row) {\n const nameCol = padRight(row.name, nameWidth);\n const reqCol = padLeft(formatNumber(row.requests), colWidths.requests);\n const totalCol = padLeft(formatNumber(row.total), colWidths.total);\n const inCol = padLeft(formatNumber(row.input), colWidths.input);\n const outCol = padLeft(formatNumber(row.output), colWidths.output);\n const line = `${pipe} ${index} ${nameCol} ${reqCol} ${totalCol} ${inCol} ${outCol} ${pipe}`;\n if (useColor) {\n return colorize(line, \"cyan\", true);\n }\n return line;\n }\n\n const lines = [];\n lines.push(horizontalBar(topLeft, border, topRight));\n for (const section of sections) {\n const labelText = colorize(section.label, \"bright\", useColor);\n const valueText = useColor ? colorize(section.value, \"dim\", true) : section.value;\n lines.push(`${pipe} ${labelText}: ${valueText}`);\n }\n\n lines.push(sectionDivider(\"Providers\"));\n if (providers.rows.length === 0) {\n lines.push(`${pipe} ${colorize(\"none\", \"dim\", useColor)}`);\n } else {\n lines.push(tableHeader());\n providers.rows.forEach((row, i) => {\n lines.push(tableRow(i + 1, row));\n });\n }\n\n lines.push(sectionDivider(\"Models\"));\n if (models.rows.length === 0) {\n lines.push(`${pipe} ${colorize(\"none\", \"dim\", useColor)}`);\n } else {\n if (providers.rows.length === 0) lines.push(tableHeader());\n models.rows.forEach((row, i) => {\n lines.push(tableRow(i + 1, row));\n });\n }\n\n lines.push(horizontalBar(botLeft, border, botRight));\n return lines.join(\"\\n\");\n}\n\nasync function fetchDbLayerStats(fetchFn, baseUrl) {\n const response = await fetchFn(`${String(baseUrl).replace(/\\/$/, \"\")}/metering/stats`);\n if (!response.ok) {\n throw new Error(`db-layer stats HTTP ${response.status}`);\n }\n return response.json();\n}\n\nfunction resolveDbLayerUrlForEnv(env = {}) {\n const explicit = String(env.DBLAYER_URL || \"\").trim();\n if (explicit) return explicit.replace(/\\/$/, \"\");\n const profile = resolveRuntimeProfile({ env });\n if (profile === \"staging\") return \"http://localhost:6001\";\n if (profile === \"production\") return \"http://localhost:7001\";\n return \"http://localhost:5001\";\n}\n\nfunction resolveEventBusUrlForEnv(env = {}) {\n const explicit = String(env.EVENTBUS_URL || \"\").trim();\n const profile = resolveRuntimeProfile({ env });\n const expectedPort = profile === \"production\" ? \"7048\" : profile === \"staging\" ? \"6048\" : \"5048\";\n if (explicit) return resolveServiceUrlForProfile(explicit.replace(/\\/$/, \"\"), expectedPort);\n return `http://localhost:${expectedPort}`;\n}\n\nasync function loadMeteringStats({ env, paths, fetchFn }) {\n const fallbackSink = createJsonlMeteringSink({ filePath: paths.meteringFile });\n const mode = String(env.LLMPROXY_MODE || \"standalone\").trim().toLowerCase();\n const mongoConnectionString = String(env.LLMPROXY_MONGODB_CONNECTION_STRING || \"\").trim();\n\n if (mode === \"platform\") {\n try {\n const stats = await fetchDbLayerStats(fetchFn, resolveDbLayerUrlForEnv(env));\n return { stats, source: \"dblayer\" };\n } catch {\n return { stats: await Promise.resolve(fallbackSink.computeStats({})), source: \"local-fallback\" };\n }\n }\n\n if (mongoConnectionString) {\n const { createMongoMeteringSink } = require(\"./metering-db\");\n const sink = createMongoMeteringSink({\n uri: mongoConnectionString,\n collectionName: \"llmproxy_metering\",\n });\n try {\n const stats = await Promise.resolve(sink.computeStats({}));\n return { stats, source: \"mongodb\" };\n } finally {\n await Promise.resolve(sink.close()).catch(() => {});\n }\n }\n\n return { stats: await Promise.resolve(fallbackSink.computeStats({})), source: \"jsonl\" };\n}\n\nfunction formatStatsReport(stats = {}, source = \"unknown\", useColor = false) {\n return renderStatsTable(stats, source, useColor);\n}\n\nfunction sanitizeTestAssistantText(text) {\n return String(text || \"\")\n .split(\"\\n\")\n .map((line) => line.trimEnd())\n .filter((line) => !line.startsWith(\"[llmproxy] \"))\n .join(\"\\n\")\n .trim();\n}\n\nfunction requestSummaryMatchesProvider(entry, provider) {\n const finalProvider = String(entry?.finalProvider || \"\").trim().toLowerCase();\n if (!finalProvider) return false;\n\n const providerId = String(provider?.id || \"\").trim().toLowerCase();\n const providerKind = String(provider?.provider || \"\").trim().toLowerCase();\n const providerMatch = finalProvider === providerId || (providerKind && finalProvider === providerKind);\n if (!providerMatch) return false;\n\n // Evita un summary stale di un'altra richiesta risolta dallo stesso\n // finalProvider (es. fallback): il modello richiesto deve corrispondere\n // al modello che il probe stava realmente inviando.\n const entryRequestedModel = String(entry?.requestedModel || \"\").trim().toLowerCase();\n if (entryRequestedModel) {\n const providerModel = String(provider?.effective_model || provider?.default_model || \"\").trim().toLowerCase();\n if (providerModel && entryRequestedModel !== providerModel) return false;\n }\n return true;\n}\n\nfunction decodeBase64Utf8(value) {\n const raw = String(value || \"\").trim();\n if (!raw) return \"\";\n try {\n return Buffer.from(raw, \"base64\").toString(\"utf8\");\n } catch {\n return \"\";\n }\n}\n\nconst LLM_STATS_API_KEY_REQUIRED_MESSAGE = [\n \"LLMPROXY_LLM_STATS_API_KEY is mandatory for llmProxy Claude Code inference.\",\n \"\",\n \"Get a free API key from https://llm-stats.com/developer\",\n \"Then set it in .claude/settings.json under env.LLMPROXY_LLM_STATS_API_KEY and retry.\",\n \"\",\n \"Example:\",\n \"{\",\n ' \"env\": {',\n ' \"LLMPROXY_LLM_STATS_API_KEY\": \"your-free-key\"',\n \" }\",\n \"}\",\n].join(\"\\n\");\n\nfunction parseCommitMessageReleaseNotes(commitMessage) {\n return String(commitMessage || \"\")\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => line.replace(/^[-*]\\s*/, \"\").trim())\n .filter(Boolean);\n}\n\nfunction formatReleaseNotes(version, locale = \"it\", options = {}) {\n const normalizedLocale = normalizeLocale(locale);\n const notesFromCommit = parseCommitMessageReleaseNotes(options.commitMessage);\n const notesFromPackage = readEmbeddedReleaseNotes(options.packageRoot, version, normalizedLocale);\n const value = RELEASE_NOTES[String(version || \"\").trim()];\n const fallbackNotes = Array.isArray(value)\n ? value\n : Array.isArray(value?.[normalizedLocale])\n ? value[normalizedLocale]\n : [];\n const notes = notesFromCommit.length > 0\n ? notesFromCommit\n : notesFromPackage.length > 0\n ? notesFromPackage\n : fallbackNotes;\n\n const title = normalizedLocale === \"en\" ? `Changelog ${version}:` : `Changelog ${version}:`;\n const lines = [title];\n if (!notes.length) {\n lines.push(normalizedLocale === \"en\"\n ? \"- Release notes are not available for this version yet.\"\n : \"- Note di rilascio non disponibili per questa versione.\");\n return lines.join(\"\\n\");\n }\n for (const note of notes) lines.push(`- ${note}`);\n return lines.join(\"\\n\");\n}\n\nfunction resolveDefaultModel(selection, availableModels = getAvailableModels()) {\n const models = Array.isArray(availableModels) && availableModels.length > 0 ? availableModels : getAvailableModels();\n const rawSelection = String(selection || \"\").trim();\n if (!rawSelection) return DEFAULT_COPILOT_MODEL;\n\n if (!/^\\d+$/.test(rawSelection)) {\n throw new Error(\"Usa l'indice numerico di `llmproxy models:list` con `llmproxy claude:setup --model <indice>`.\");\n }\n\n const index = Number(rawSelection) - 1;\n if (index < 0 || index >= models.length) {\n throw new Error(`Indice modello non valido: ${rawSelection}`);\n }\n return models[index];\n}\n\nfunction runSelfUpdate(commandRunner = spawnSync, repo = \"alessiobacin/llmProxy\", locale = \"it\") {\n const script = [\n \"set -e\",\n \"tmpdir=$(mktemp -d)\",\n \"cleanup() { rm -rf \\\"$tmpdir\\\"; }\",\n \"trap cleanup EXIT\",\n \"\",\n \"# Preserve the caller's current global npm bin before any nvm switch\",\n 'original_npm_prefix=$(npm prefix -g 2>/dev/null || true)',\n 'original_global_bin_path=\"\"',\n 'original_global_short_bin_path=\"\"',\n 'if [ -n \"$original_npm_prefix\" ]; then',\n ' original_global_bin_path=\"$original_npm_prefix/bin/llmproxy\"',\n ' original_global_short_bin_path=\"$original_npm_prefix/bin/llmp\"',\n 'fi',\n \"\",\n \"# Load nvm if available to use the correct Node version\",\n 'if [ -f \"$HOME/.nvm/nvm.sh\" ]; then',\n ' source \"$HOME/.nvm/nvm.sh\" 2>/dev/null || true',\n 'elif [ -f \"/usr/share/nvm/init-nvm.sh\" ]; then',\n ' source \"/usr/share/nvm/init-nvm.sh\" 2>/dev/null || true',\n 'fi',\n \"\",\n \"# Try to use a modern Node version (20+) if available\",\n 'if command -v nvm >/dev/null 2>&1; then',\n ' nvm use 22 >/dev/null 2>&1 || nvm use 20 >/dev/null 2>&1 || nvm use lts >/dev/null 2>&1 || true',\n 'fi',\n \"\",\n \"# Verify Node version is at least 20\",\n 'node_version=$(node -v 2>/dev/null | sed \"s/v//\" | cut -d. -f1)',\n 'if [ -n \"$node_version\" ] && [ \"$node_version\" -lt 20 ]; then',\n ' echo \"Warning: Node v$(node -v) may be too old. Attempting to use a newer version...\"',\n 'fi',\n \"\",\n \"existing_bins=$( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk '!seen[$0]++')\",\n \"current_bin=$(command -v llmproxy 2>/dev/null || true)\",\n \"current_short_bin=$(command -v llmp 2>/dev/null || true)\",\n 'current_version=$(llmproxy version 2>/dev/null || true)',\n 'original_npm_root=$(npm root -g 2>/dev/null || true)',\n 'current_bin_prefix=\"\"',\n 'if [ -n \"$current_bin\" ]; then',\n ' current_bin_dir=$(dirname \"$current_bin\")',\n ' current_bin_prefix=$(cd \"$current_bin_dir/..\" 2>/dev/null && pwd || true)',\n 'fi',\n \"used_sudo=0\",\n 'rollback_manifest=\"$tmpdir/rollback-manifest.txt\"',\n 'touch \"$rollback_manifest\"',\n 'rollback_primary_dir=\"\"',\n 'backup_install_dir() {',\n ' src=\"$1\"',\n ' [ -d \"$src\" ] || return 0',\n ' archive=\"$tmpdir/rollback-$(wc -l < \"$rollback_manifest\" | tr -d \" \")-$(basename \"$src\").tar\"',\n ' tar -cf \"$archive\" -C \"$(dirname \"$src\")\" \"$(basename \"$src\")\"',\n ' printf \"%s|%s\\\\n\" \"$src\" \"$archive\" >> \"$rollback_manifest\"',\n ' if [ -z \"$rollback_primary_dir\" ]; then',\n ' rollback_primary_dir=\"$src\"',\n ' fi',\n '}',\n 'restore_backups() {',\n ' if [ ! -s \"$rollback_manifest\" ]; then',\n ' return 1',\n ' fi',\n ' while IFS=\"|\" read -r target archive; do',\n ' [ -n \"$target\" ] || continue',\n ' parent_dir=$(dirname \"$target\")',\n ' rm -rf \"$target\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo rm -rf \"$target\" >/dev/null 2>&1 || return 1; else return 1; fi; }',\n ' mkdir -p \"$parent_dir\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo mkdir -p \"$parent_dir\" >/dev/null 2>&1 || return 1; else return 1; fi; }',\n ' tar -xf \"$archive\" -C \"$parent_dir\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo tar -xf \"$archive\" -C \"$parent_dir\" >/dev/null 2>&1 || return 1; else return 1; fi; }',\n ' done < \"$rollback_manifest\"',\n ' return 0',\n '}',\n 'rollback_and_exit() {',\n ' rollback_reason=\"$1\"',\n ' restored_version=\"$current_version\"',\n ' if restore_backups; then',\n ' if [ -n \"$rollback_primary_dir\" ] && [ -f \"$rollback_primary_dir/bin/llmproxy.js\" ]; then',\n ' package_cli=\"$rollback_primary_dir/bin/llmproxy.js\"',\n ' new_bin=\"$package_cli\"',\n ' new_bin_mode=\"node\"',\n ' if type ensure_wrapper_path >/dev/null 2>&1; then',\n ' ensure_wrapper_path \"${global_bin_path:-}\" >/dev/null 2>&1 || true',\n ' for preserved_bin in ${preserved_bin_paths:-}; do',\n ' ensure_wrapper_path \"$preserved_bin\" >/dev/null 2>&1 || true',\n ' done',\n ' fi',\n ' if [ \"$used_sudo\" -eq 1 ] && [ \"$(uname -s)\" = \"Linux\" ] && [ -n \"${SUDO_USER:-}\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo -u \"$SUDO_USER\" XDG_RUNTIME_DIR=\"/run/user/$(id -u \"$SUDO_USER\")\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=/run/user/$(id -u \"$SUDO_USER\")/bus\" node \"$package_cli\" service:restart >/dev/null 2>&1 || true',\n ' else',\n ' node \"$package_cli\" service:restart >/dev/null 2>&1 || true',\n ' fi',\n ' restored_version=$(node \"$package_cli\" version 2>/dev/null || printf \"%s\" \"$current_version\")',\n ' fi',\n ' fi',\n ' printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"',\n ' printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"$restored_version\"',\n ' printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"$rollback_reason\"',\n ' exit 0',\n '}',\n 'backup_install_dir \"${original_npm_root:+$original_npm_root/llmproxy}\"',\n 'backup_install_dir \"${current_bin_prefix:+$current_bin_prefix/lib/node_modules/llmproxy}\"',\n 'safe_remove_path() {',\n ' target_path=\"$1\"',\n ' [ -n \"$target_path\" ] || return 0',\n ' if [ -e \"$target_path\" ] || [ -L \"$target_path\" ]; then',\n ' rm -rf \"$target_path\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -rf \"$target_path\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' fi',\n '}',\n 'collect_install_target() {',\n ' candidate_dir=\"$1\"',\n ' [ -n \"$candidate_dir\" ] || return 0',\n ' [ -d \"$candidate_dir\" ] || return 0',\n ' printf \"%s\\\\n\" \"$candidate_dir\"',\n '}',\n 'purge_installation_targets() {',\n ' install_targets=$( {',\n ' collect_install_target \"${original_npm_root:+$original_npm_root/llmproxy}\"',\n ' collect_install_target \"${current_bin_prefix:+$current_bin_prefix/lib/node_modules/llmproxy}\"',\n ' for discovered_bin in $existing_bins; do',\n ' [ -n \"$discovered_bin\" ] || continue',\n ' discovered_prefix=$(cd \"$(dirname \"$discovered_bin\")/..\" 2>/dev/null && pwd || true)',\n ' collect_install_target \"${discovered_prefix:+$discovered_prefix/lib/node_modules/llmproxy}\"',\n ' done',\n ' current_npm_root=$(npm root -g 2>/dev/null || true)',\n ' collect_install_target \"${current_npm_root:+$current_npm_root/llmproxy}\"',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo_npm_root=$(sudo npm root -g 2>/dev/null || true)',\n ' collect_install_target \"${sudo_npm_root:+$sudo_npm_root/llmproxy}\"',\n ' fi',\n ' pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n ' collect_install_target \"${pnpm_root:+$pnpm_root/llmproxy}\"',\n ' collect_install_target \"/usr/lib/node_modules/llmproxy\"',\n ' collect_install_target \"/usr/local/lib/node_modules/llmproxy\"',\n ' collect_install_target \"/opt/homebrew/lib/node_modules/llmproxy\"',\n ' } | awk \\'NF && !seen[$0]++\\' )',\n ' for install_target in $install_targets; do',\n ' backup_install_dir \"$install_target\"',\n ' safe_remove_path \"$install_target\"',\n ' done',\n '}',\n 'purge_existing_wrappers() {',\n ' wrapper_targets=$(printf \"%s\\\\n\" \"$current_bin\" \"$current_short_bin\" \"$original_global_bin_path\" \"$original_global_short_bin_path\" \"/usr/bin/llmproxy\" \"/usr/local/bin/llmproxy\" \"/opt/homebrew/bin/llmproxy\" \"/usr/bin/llmp\" \"/usr/local/bin/llmp\" \"/opt/homebrew/bin/llmp\" $existing_bins | awk \\'NF && !seen[$0]++\\')',\n ' for wrapper_target in $wrapper_targets; do',\n ' safe_remove_path \"$wrapper_target\"',\n ' done',\n '}',\n 'cleanup_global_service_port() {',\n ' service_port=\"${1:-7045}\"',\n ' [ -n \"$service_port\" ] || return 0',\n ' if command -v docker >/dev/null 2>&1; then',\n ' docker_ids=$(docker ps --format \\'{{.ID}} {{.Ports}}\\' 2>/dev/null | awk -v port=\"$service_port\" \\'index($0, \":\" port \"->\") > 0 { print $1 }\\')',\n ' if [ -n \"$docker_ids\" ]; then',\n ' for docker_id in $docker_ids; do',\n ' docker stop \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker stop \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' docker rm \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker rm \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' if command -v lsof >/dev/null 2>&1; then',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' sleep 1',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill -9 \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill -9 \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' fi',\n '}',\n 'ensure_runtime_home_permissions() {',\n ' runtime_home=\"$1\"',\n ' runtime_owner=\"$2\"',\n ' [ -n \"$runtime_home\" ] || return 0',\n ' mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' if [ -n \"$runtime_owner\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo chown -R \"$runtime_owner\":\"$runtime_owner\" \"$runtime_home\" >/dev/null 2>&1 || true',\n ' fi',\n ' chmod -R u+rwX \"$runtime_home\" >/dev/null 2>&1 || true',\n '}',\n 'cleanup_global_service_port \"${PORT:-7045}\"',\n 'npm uninstall -g llmproxy >/dev/null 2>&1 || true',\n 'if command -v sudo >/dev/null 2>&1; then',\n ' sudo npm uninstall -g llmproxy >/dev/null 2>&1 || true',\n 'fi',\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'purge_installation_targets',\n 'purge_existing_wrappers',\n `git clone --depth=1 https://github.com/${repo}.git \\\"$tmpdir/repo\\\" >/dev/null 2>&1`,\n 'cd \"$tmpdir/repo\"',\n 'target_version=$(node -p \"require(\\'./package.json\\').version\")',\n \"commit_message_base64=$(git log -1 --pretty=%B | base64 | tr -d '\\\\n')\",\n \"\",\n \"# Try pnpm pack first, fall back to npm pack\",\n 'if command -v pnpm >/dev/null 2>&1 && pnpm pack --pack-destination \"$tmpdir\" >/dev/null 2>&1; then',\n ' package_file=$(find \"$tmpdir\" -maxdepth 1 -name \"*.tgz\" -print | head -n 1)',\n 'else',\n ' npm pack --pack-destination \"$tmpdir\" >/dev/null 2>&1',\n ' package_file=$(find \"$tmpdir\" -maxdepth 1 -name \"*.tgz\" -print | head -n 1)',\n 'fi',\n '[ -n \"$package_file\" ]',\n \"\",\n '# Check npm prefix: if user-local, force system-wide install via sudo for multi-user server',\n 'npm_prefix_global=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'case \"$npm_prefix_global\" in',\n ' \"$HOME\"/*)',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' if sudo npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' used_sudo=1',\n ' else',\n ' echo \"Warning: could not install system-wide. Falling back to user-local.\" >&2',\n ' if ! npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' fi',\n ' else',\n ' if ! npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' fi',\n ' ;;',\n ' *)',\n ' if ! npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' echo \"Standard npm install failed, trying with sudo...\"',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' if sudo npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' used_sudo=1',\n ' else',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' else',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' fi',\n ' ;;',\n 'esac',\n \"\",\n \"# Clean up pnpm global if present\",\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n 'if [ -n \"$pnpm_root\" ]; then',\n ' pnpm_home=$(dirname \"$(dirname \"$pnpm_root\")\")',\n ' rm -f \"$pnpm_home/bin/llmproxy\" >/dev/null 2>&1 || true',\n 'fi',\n \"\",\n \"# Determine npm global prefix\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' npm_prefix=$(sudo npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'else',\n ' npm_prefix=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'fi',\n 'install_dir=\"$npm_prefix/lib/node_modules/llmproxy\"',\n 'package_cli=\"$install_dir/bin/llmproxy.js\"',\n 'global_bin_path=\"$npm_prefix/bin/llmproxy\"',\n 'global_short_bin_path=\"$npm_prefix/bin/llmp\"',\n 'new_bin_mode=\"bin\"',\n 'run_new_llmproxy() {',\n ' if [ \"$new_bin_mode\" = \"node\" ] && [ -f \"$new_bin\" ]; then',\n ' node \"$new_bin\" \"$@\"',\n ' elif [ -x \"$new_bin\" ]; then',\n ' \"$new_bin\" \"$@\"',\n ' elif [ -f \"$package_cli\" ]; then',\n ' node \"$package_cli\" \"$@\"',\n ' else',\n ' return 127',\n ' fi',\n '}',\n 'build_wrapper_payload() {',\n ' if [ \"$new_bin_mode\" = \"node\" ] && [ -f \"$package_cli\" ]; then',\n ' printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec node \\\\\"$package_cli\\\\\" \\\\\"\\\\$@\\\\\"\"',\n ' return 0',\n ' fi',\n ' if [ -n \"$new_bin\" ] && [ -e \"$new_bin\" ]; then',\n ' printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\"',\n ' return 0',\n ' fi',\n ' return 1',\n '}',\n 'ensure_wrapper_path() {',\n ' target_path=\"$1\"',\n ' if [ -z \"$target_path\" ] || [ \"$target_path\" = \"$new_bin\" ]; then',\n ' return 0',\n ' fi',\n ' target_dir=$(dirname \"$target_path\")',\n ' if [ ! -d \"$target_dir\" ]; then',\n ' return 0',\n ' fi',\n ' wrapper_payload=$(build_wrapper_payload) || return 1',\n ' if [ -w \"$target_dir\" ]; then',\n ' rm -f \"$target_path\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$wrapper_payload\" > \"$target_path\"',\n ' chmod +x \"$target_path\"',\n ' return 0',\n ' fi',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -f \"$target_path\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$wrapper_payload\" | sudo tee \"$target_path\" >/dev/null',\n ' sudo chmod +x \"$target_path\"',\n ' return 0',\n ' fi',\n ' return 1',\n '}',\n 'ensure_global_bin() {',\n ' if [ -x \"$global_bin_path\" ]; then',\n ' return 0',\n ' fi',\n ' ensure_wrapper_path \"$global_bin_path\"',\n '}',\n 'ensure_global_short_bin() {',\n ' if [ -x \"$global_short_bin_path\" ]; then',\n ' return 0',\n ' fi',\n ' ensure_wrapper_path \"$global_short_bin_path\"',\n '}',\n \"\",\n \"# System-wide install: make installed package world-readable (keep root ownership)\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' if [ -d \"$install_dir\" ]; then',\n ' sudo chmod -R a+rX \"$install_dir\" 2>/dev/null || echo \"Warning: could not make $install_dir world-readable. Non-root users may not be able to use llmproxy.\" >&2',\n ' fi',\n 'fi',\n \"\",\n \"# Find the newly installed binary\",\n \"resolved_bins=$( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk '!seen[$0]++')\",\n 'new_bin=\"\"',\n 'version_output=\"\"',\n 'if [ -f \"$package_cli\" ]; then',\n ' package_cli_version=$(node \"$package_cli\" version 2>/dev/null || true)',\n ' if [ \"$package_cli_version\" = \"$target_version\" ]; then',\n ' new_bin=\"$package_cli\"',\n ' new_bin_mode=\"node\"',\n ' version_output=\"$package_cli_version\"',\n ' fi',\n 'fi',\n 'if [ \"$version_output\" != \"$target_version\" ] && [ -x \"$global_bin_path\" ]; then',\n ' global_bin_version=$(\"$global_bin_path\" version 2>/dev/null || true)',\n ' if [ \"$global_bin_version\" = \"$target_version\" ]; then',\n ' new_bin=\"$global_bin_path\"',\n ' new_bin_mode=\"bin\"',\n ' version_output=\"$global_bin_version\"',\n ' fi',\n 'fi',\n 'if [ \"$version_output\" != \"$target_version\" ]; then',\n ' for candidate_bin in $resolved_bins; do',\n ' if [ -x \"$candidate_bin\" ]; then',\n ' candidate_version=$(\"$candidate_bin\" version 2>/dev/null || true)',\n ' if [ \"$candidate_version\" = \"$target_version\" ]; then',\n ' new_bin=\"$candidate_bin\"',\n ' new_bin_mode=\"bin\"',\n ' version_output=\"$candidate_version\"',\n ' break',\n ' fi',\n ' fi',\n ' done',\n 'fi',\n 'if [ \"$version_output\" != \"$target_version\" ]; then',\n ' rollback_and_exit \"installed binary verification failed for $target_version\"',\n 'fi',\n 'ensure_global_bin >/dev/null 2>&1 || true',\n 'ensure_global_short_bin >/dev/null 2>&1 || true',\n 'preserved_bin_paths=$(printf \"%s\\\\n\" \"$current_bin\" \"$current_short_bin\" \"$global_bin_path\" \"$global_short_bin_path\" \"$original_global_bin_path\" \"$original_global_short_bin_path\" \"/usr/bin/llmproxy\" \"/usr/local/bin/llmproxy\" \"/usr/bin/llmp\" \"/usr/local/bin/llmp\" $existing_bins | awk \\'NF && !seen[$0]++\\')',\n 'for preserved_bin in $preserved_bin_paths; do',\n ' ensure_wrapper_path \"$preserved_bin\" >/dev/null 2>&1 || true',\n 'done',\n 'is_preserved_bin_path() {',\n ' candidate_path=\"$1\"',\n ' for preserved_bin in $preserved_bin_paths; do',\n ' if [ \"$preserved_bin\" = \"$candidate_path\" ]; then',\n ' return 0',\n ' fi',\n ' done',\n ' return 1',\n '}',\n \"\",\n \"# Remove old duplicate binaries\",\n 'for installed_bin in $resolved_bins; do',\n ' if [ -n \"$installed_bin\" ] && [ \"$installed_bin\" != \"$new_bin\" ] && ! is_preserved_bin_path \"$installed_bin\"; then',\n ' rm -f \"$installed_bin\" >/dev/null 2>&1 || { if [ \"$used_sudo\" -eq 1 ]; then sudo rm -f \"$installed_bin\" >/dev/null 2>&1 || true; else true; fi; }',\n ' fi',\n 'done',\n \"\",\n \"# Update current bin if it points to an old location\",\n 'if [ -n \"$current_bin\" ] && [ \"$current_bin\" != \"$new_bin\" ] && [ \"$current_bin\" != \"$global_bin_path\" ]; then',\n ' current_bin_dir=$(dirname \"$current_bin\")',\n ' if [ \"$new_bin_mode\" = \"node\" ]; then',\n ' current_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec node \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' else',\n ' current_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' fi',\n ' if [ -w \"$current_bin_dir\" ]; then',\n ' rm -f \"$current_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_wrapper_payload\" > \"$current_bin\"',\n ' chmod +x \"$current_bin\"',\n ' elif command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -f \"$current_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_wrapper_payload\" | sudo tee \"$current_bin\" >/dev/null',\n ' sudo chmod +x \"$current_bin\"',\n ' fi',\n 'fi',\n 'if [ -n \"$current_short_bin\" ] && [ \"$current_short_bin\" != \"$new_bin\" ] && [ \"$current_short_bin\" != \"$global_short_bin_path\" ]; then',\n ' current_short_bin_dir=$(dirname \"$current_short_bin\")',\n ' if [ \"$new_bin_mode\" = \"node\" ]; then',\n ' current_short_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec node \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' else',\n ' current_short_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' fi',\n ' if [ -w \"$current_short_bin_dir\" ]; then',\n ' rm -f \"$current_short_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_short_wrapper_payload\" > \"$current_short_bin\"',\n ' chmod +x \"$current_short_bin\"',\n ' elif command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -f \"$current_short_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_short_wrapper_payload\" | sudo tee \"$current_short_bin\" >/dev/null',\n ' sudo chmod +x \"$current_short_bin\"',\n ' fi',\n 'fi',\n \"\",\n \"# Ensure systemd --user bus is available before restarting\",\n 'if [ \"$(uname -s)\" = \"Linux\" ] && [ -z \"${XDG_RUNTIME_DIR:-}\" ]; then',\n ' uid=$(id -u)',\n ' export XDG_RUNTIME_DIR=\"/run/user/$uid\"',\n 'fi',\n 'if [ \"$(uname -s)\" = \"Linux\" ] && [ -d \"$XDG_RUNTIME_DIR\" ]; then',\n ' bus_socket=\"$XDG_RUNTIME_DIR/bus\"',\n ' if [ ! -S \"$bus_socket\" ] && command -v systemctl >/dev/null 2>&1; then',\n ' systemctl --user start dbus 2>/dev/null || true',\n ' waited=0',\n ' while [ ! -S \"$bus_socket\" ] && [ \"$waited\" -lt 5 ]; do',\n ' sleep 1',\n ' waited=$((waited + 1))',\n ' done',\n ' fi',\n 'fi',\n \"\",\n 'cleanup_global_service_port \"${PORT:-7045}\"',\n \"\",\n \"# Migrate legacy managed config variables to the current schema\",\n 'run_new_llmproxy config:migrate >/dev/null 2>&1 || true',\n \"\",\n \"# Restart service — use sudo -u when npm install used sudo (systemd --user needs real user session bus)\",\n 'if [ \"$used_sudo\" -eq 1 ] && [ \"$(uname -s)\" = \"Linux\" ] && [ -n \"${SUDO_USER:-}\" ] && command -v sudo >/dev/null 2>&1; then',\n ' if [ \"$new_bin_mode\" = \"node\" ]; then',\n ' service_restart_output=$(sudo -u \"$SUDO_USER\" XDG_RUNTIME_DIR=\"/run/user/$(id -u \"$SUDO_USER\")\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=/run/user/$(id -u \"$SUDO_USER\")/bus\" node \"$new_bin\" service:restart 2>&1 >/dev/null) || service_restart_status=$?',\n ' else',\n ' service_restart_output=$(sudo -u \"$SUDO_USER\" XDG_RUNTIME_DIR=\"/run/user/$(id -u \"$SUDO_USER\")\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=/run/user/$(id -u \"$SUDO_USER\")/bus\" \"$new_bin\" service:restart 2>&1 >/dev/null) || service_restart_status=$?',\n ' fi',\n 'else',\n ' service_restart_output=$(run_new_llmproxy service:restart 2>&1 >/dev/null) || service_restart_status=$?',\n 'fi',\n \"\",\n \"# Update Docker container if running\",\n 'docker_compose_file=\"$npm_prefix/lib/node_modules/llmproxy/docker-compose.production.yml\"',\n 'if [ -z \"$LLMPROXY_HOME\" ]; then',\n ' if [ \"$(uname -s)\" = \"Darwin\" ]; then',\n ' export LLMPROXY_HOME=\"$HOME/Library/Application Support/llmProxy\"',\n ' else',\n ' export LLMPROXY_HOME=\"$HOME/.local/share/llmProxy\"',\n ' fi',\n 'fi',\n 'if [ -z \"$LLMPROXY_HOST_PROJECTS_ROOT\" ]; then',\n ' if [ \"$(uname -s)\" = \"Darwin\" ]; then',\n ' export LLMPROXY_HOST_PROJECTS_ROOT=\"/Users\"',\n ' else',\n ' export LLMPROXY_HOST_PROJECTS_ROOT=\"/home\"',\n ' fi',\n 'fi',\n 'runtime_owner=\"\"',\n 'if [ \"$used_sudo\" -eq 1 ] && [ -n \"${SUDO_USER:-}\" ]; then',\n ' runtime_owner=\"$SUDO_USER\"',\n 'else',\n ' runtime_owner=\"$(whoami)\"',\n 'fi',\n 'ensure_runtime_home_permissions \"$LLMPROXY_HOME\" \"$runtime_owner\"',\n 'compose_cmd=\"\"',\n 'compose_args=\"\"',\n 'if command -v docker >/dev/null 2>&1 && [ -f \"$docker_compose_file\" ]; then',\n ' if docker compose version >/dev/null 2>&1; then',\n ' compose_cmd=\"docker\"',\n ' compose_args=\"compose\"',\n ' elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then',\n ' compose_cmd=\"docker-compose\"',\n ' fi',\n ' if [ -n \"$compose_cmd\" ]; then',\n ' if [ \"$compose_cmd\" = \"docker\" ]; then',\n ' if docker compose -f \"$docker_compose_file\" ps --services --status running 2>/dev/null | grep -qx \"llmproxy\"; then',\n ' docker compose -f \"$docker_compose_file\" up -d --build llmproxy >/dev/null || true',\n ' fi',\n ' else',\n ' if docker-compose -f \"$docker_compose_file\" ps --services --status running 2>/dev/null | grep -qx \"llmproxy\"; then',\n ' docker-compose -f \"$docker_compose_file\" up -d --build llmproxy >/dev/null || true',\n ' fi',\n ' fi',\n ' fi',\n 'fi',\n \"\",\n \"# Smoke test the freshly installed CLI and managed runtime before declaring success\",\n 'post_update_check() {',\n ' run_new_llmproxy version >/dev/null 2>&1 || return 1',\n ' run_new_llmproxy config:list >/dev/null 2>&1 || return 1',\n ' attempts=0',\n ' while [ \"$attempts\" -lt 5 ]; do',\n ' if run_new_llmproxy status >/dev/null 2>&1; then',\n ' return 0',\n ' fi',\n ' attempts=$((attempts + 1))',\n ' sleep 2',\n ' done',\n ' return 1',\n '}',\n 'if ! post_update_check; then',\n ' rollback_and_exit \"post-update smoke test failed for $target_version\"',\n 'fi',\n \"\",\n \"# Output results\",\n `release_notes_output=$(run_new_llmproxy release-notes --version \"$version_output\" --locale ${shellQuote(normalizeLocale(locale))} --commit-message-base64 \"$commit_message_base64\")`,\n 'printf \"__LLMPROXY_VERSION__=%s\\\\n\" \"$version_output\"',\n 'printf \"__LLMPROXY_RELEASE_NOTES_START__\\\\n%s\\\\n__LLMPROXY_RELEASE_NOTES_END__\\\\n\" \"$release_notes_output\"',\n 'if [ \"$service_restart_status\" -ne 0 ]; then',\n ' printf \"__LLMPROXY_SERVICE_RESTART_WARNING__=%s\\\\n\" \"$service_restart_output\"',\n 'fi',\n ].join(\"\\n\");\n return commandRunner(\"bash\", [\"-c\", script], { encoding: \"utf8\" });\n}\n\nfunction runSelfUninstall(commandRunner = spawnSync) {\n const script = [\n 'set -e',\n 'npm uninstall -g llmproxy >/dev/null 2>&1 || true',\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'npm_prefix=$(npm prefix -g 2>/dev/null || true)',\n 'if [ -n \"$npm_prefix\" ]; then',\n ' rm -f \"$npm_prefix/bin/llmproxy\" >/dev/null 2>&1 || true',\n ' rm -f \"$npm_prefix/bin/llmp\" >/dev/null 2>&1 || true',\n 'fi',\n 'pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n 'if [ -n \"$pnpm_root\" ]; then',\n ' pnpm_home=$(dirname \"$(dirname \"$pnpm_root\")\")',\n ' rm -f \"$pnpm_home/bin/llmproxy\" >/dev/null 2>&1 || true',\n ' rm -f \"$pnpm_home/bin/llmp\" >/dev/null 2>&1 || true',\n 'fi',\n ].join(\"\\n\");\n return commandRunner(\"bash\", [\"-c\", script], { encoding: \"utf8\" });\n}\n\nfunction runSelfUpdateWindows(commandRunner = spawnSync, repo = \"alessiobacin/llmProxy\", locale = \"it\") {\n const psScript = [\n \"$ErrorActionPreference = 'Stop'\",\n 'if ($PSVersionTable.PSVersion.Major -ge 7) { $PSNativeCommandUseErrorActionPreference = $false }',\n '$ProgressPreference = \"SilentlyContinue\"',\n '$tmpdir = [System.IO.Path]::GetTempPath() + [System.IO.Path]::GetRandomFileName()',\n 'New-Item -ItemType Directory -Path $tmpdir -Force | Out-Null',\n '',\n 'function Quote-CmdArgument([string]$Value) {',\n ' if ($null -eq $Value) { return \"\\\"\\\"\" }',\n ' $escaped = $Value -replace \\'\"\\', \\'\"\"\\'',\n ' if ($escaped -match \\'[\\\\s\"&|<>()^]\\') { return \\'\"\\' + $escaped + \\'\"\\' }',\n ' return $escaped',\n '}',\n '',\n 'function Invoke-QuietNative([string]$FilePath, [string[]]$ArgumentList) {',\n ' $stdoutFile = Join-Path $tmpdir ([System.IO.Path]::GetRandomFileName())',\n ' $stderrFile = Join-Path $tmpdir ([System.IO.Path]::GetRandomFileName())',\n ' try {',\n ' $resolvedFilePath = $FilePath',\n ' $resolvedArguments = @($ArgumentList)',\n ' $extension = [System.IO.Path]::GetExtension($FilePath).ToLowerInvariant()',\n ' if ($FilePath -ieq \"npm\" -or $FilePath -ieq \"npm.cmd\" -or $extension -eq \".cmd\" -or $extension -eq \".bat\") {',\n ' $resolvedFilePath = \"cmd.exe\"',\n ' $cmdLine = (@($FilePath) + @($ArgumentList) | ForEach-Object { Quote-CmdArgument $_ }) -join \" \"',\n ' $resolvedArguments = @(\"/d\", \"/s\", \"/c\", $cmdLine)',\n ' } elseif ($extension -eq \".ps1\") {',\n ' $resolvedFilePath = \"powershell.exe\"',\n ' $resolvedArguments = @(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", $FilePath) + @($ArgumentList)',\n ' } elseif ($extension -eq \".js\") {',\n ' $resolvedFilePath = \"node.exe\"',\n ' $resolvedArguments = @($FilePath) + @($ArgumentList)',\n ' }',\n ' $process = Start-Process -FilePath $resolvedFilePath -ArgumentList $resolvedArguments -NoNewWindow -PassThru -Wait -RedirectStandardOutput $stdoutFile -RedirectStandardError $stderrFile',\n ' if ($process.ExitCode -ne 0) {',\n ' $stderrText = if (Test-Path $stderrFile) { (Get-Content $stderrFile -Raw -ErrorAction SilentlyContinue) } else { \"\" }',\n ' $stdoutText = if (Test-Path $stdoutFile) { (Get-Content $stdoutFile -Raw -ErrorAction SilentlyContinue) } else { \"\" }',\n ' throw (\"Native command failed: \" + $resolvedFilePath + \" \" + ($resolvedArguments -join \" \") + \" :: \" + ($stderrText + $stdoutText).Trim())',\n ' }',\n ' } finally {',\n ' Remove-Item -Force $stdoutFile -ErrorAction SilentlyContinue',\n ' Remove-Item -Force $stderrFile -ErrorAction SilentlyContinue',\n ' }',\n '}',\n '',\n '$currentVersion = ((& llmproxy version 2>$null) | Out-String).Trim()',\n '$currentNpmRoot = ((npm root -g 2>$null) | Out-String).Trim()',\n '$currentInstallDir = if ($currentNpmRoot) { Join-Path $currentNpmRoot \"llmproxy\" } else { \"\" }',\n '$rollbackDir = Join-Path $tmpdir \"rollback-llmproxy\"',\n '$rollbackAvailable = $false',\n 'if ($currentInstallDir -and (Test-Path $currentInstallDir)) {',\n ' Copy-Item -Recurse -Force $currentInstallDir $rollbackDir',\n ' $rollbackAvailable = $true',\n '}',\n '',\n `Invoke-QuietNative \"git\" @(\"clone\", \"--depth=1\", \"https://github.com/${repo}.git\", \"$tmpdir/repo\")`,\n 'Set-Location \"$tmpdir/repo\"',\n '',\n '$targetVersion = node -p \"require(\\'./package.json\\').version\"',\n '$commitMessageBytes = [Text.Encoding]::UTF8.GetBytes((git log -1 --pretty=%B 2>$null))',\n '$commitMessageBase64 = [Convert]::ToBase64String($commitMessageBytes)',\n '',\n '# Stop the currently running Windows service before replacing the global package files',\n '& llmproxy service:stop 2>$null | Out-Null',\n '',\n '# npm pack',\n 'Invoke-QuietNative \"npm\" @(\"pack\", \"--pack-destination\", \"$tmpdir\")',\n '$packageFile = Get-ChildItem \"$tmpdir/*.tgz\" | Select-Object -First 1 -ExpandProperty FullName',\n '',\n '# Install globally (always same user on Windows)',\n 'Invoke-QuietNative \"npm\" @(\"install\", \"-g\", \"--force\", \"$packageFile\")',\n '',\n '# Clean up pnpm global if present',\n 'pnpm remove -g llmproxy 2>$null | Out-Null',\n '$pnpmRoot = pnpm root -g 2>$null',\n 'if ($pnpmRoot) {',\n ' $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent',\n ' Remove-Item -Force \"$pnpmHome/bin/llmproxy\" -ErrorAction SilentlyContinue',\n '}',\n '',\n 'function Resolve-LlmproxyGlobalBin([string]$Prefix) {',\n ' $candidates = @(',\n ' (Join-Path $Prefix \"llmproxy.cmd\"),',\n ' (Join-Path $Prefix \"llmproxy.ps1\"),',\n ' (Join-Path $Prefix \"llmproxy\"),',\n ' (Join-Path $Prefix \"llmp.cmd\"),',\n ' (Join-Path $Prefix \"llmp.ps1\"),',\n ' (Join-Path $Prefix \"llmp\")',\n ' )',\n ' foreach ($candidate in $candidates) {',\n ' if (Test-Path $candidate) { return $candidate }',\n ' }',\n ' return (Join-Path $Prefix \"llmproxy.cmd\")',\n '}',\n '',\n '# Find the newly installed binary',\n '$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()',\n '$newBin = Resolve-LlmproxyGlobalBin $npmPrefix',\n '$installDir = Join-Path ((npm root -g).Trim()) \"llmproxy\"',\n '$packageCli = Join-Path $installDir \"bin/llmproxy.js\"',\n '',\n `$localeArg = ${locale === \"en\" ? '\"en\"' : '\"it\"'}`,\n '',\n 'function Invoke-Rollback([string]$Reason) {',\n ' if ($rollbackAvailable -and (Test-Path $rollbackDir)) {',\n ' if ($installDir -and (Test-Path $installDir)) { Remove-Item -Recurse -Force $installDir -ErrorAction SilentlyContinue }',\n ' $parentDir = Split-Path $installDir -Parent',\n ' if ($parentDir) { New-Item -ItemType Directory -Path $parentDir -Force | Out-Null }',\n ' Copy-Item -Recurse -Force $rollbackDir $installDir',\n ' $restoredCli = Join-Path $installDir \"bin/llmproxy.js\"',\n ' if (Test-Path $restoredCli) {',\n ' node $restoredCli service:restart 2>$null | Out-Null',\n ' $restoredVersion = ((node $restoredCli version 2>$null) | Out-String).Trim()',\n ' Write-Output \"__LLMPROXY_ROLLBACK__=1\"',\n ' Write-Output \"__LLMPROXY_ROLLBACK_VERSION__=$restoredVersion\"',\n ' Write-Output \"__LLMPROXY_ROLLBACK_REASON__=$Reason\"',\n ' exit 0',\n ' }',\n ' }',\n ' throw \"Update verification failed: $Reason\"',\n '}',\n '',\n '# Migrate legacy managed config variables to the current schema',\n 'Invoke-QuietNative \"$newBin\" @(\"config:migrate\")',\n '',\n '# Restart service via the new binary',\n 'Invoke-QuietNative \"$newBin\" @(\"service:restart\")',\n '$resolvedVersion = ((& \"$newBin\" version 2>$null) | Out-String).Trim()',\n 'if ($resolvedVersion -ne $targetVersion) { Invoke-Rollback \"installed binary verification failed for $targetVersion\" }',\n '& \"$newBin\" config:list 2>$null | Out-Null',\n 'if ($LASTEXITCODE -ne 0) { Invoke-Rollback \"config:list failed after installing $targetVersion\" }',\n '& \"$newBin\" status 2>$null | Out-Null',\n 'if ($LASTEXITCODE -ne 0) { Invoke-Rollback \"post-update smoke test failed for $targetVersion\" }',\n '',\n '# Output results',\n '$versionOutput = $resolvedVersion',\n '$releaseNotesOutput = & \"$newBin\" release-notes --version $targetVersion --locale ' + (locale === \"en\" ? '\"en\"' : '\"it\"') + ' --commit-message-base64 $commitMessageBase64 2>&1',\n 'Write-Output \"__LLMPROXY_VERSION__=$versionOutput\"',\n 'Write-Output \"__LLMPROXY_RELEASE_NOTES_START__\"',\n 'Write-Output \"$releaseNotesOutput\"',\n 'Write-Output \"__LLMPROXY_RELEASE_NOTES_END__\"',\n ].join(\"\\n\");\n return commandRunner(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], { encoding: \"utf8\" });\n}\n\nfunction runSelfUninstallWindows(commandRunner = spawnSync) {\n const psScript = [\n '$ErrorActionPreference = \"Stop\"',\n 'npm uninstall -g llmproxy 2>$null | Out-Null',\n 'pnpm remove -g llmproxy 2>$null | Out-Null',\n '$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()',\n 'if ($npmPrefix) {',\n ' @(',\n ' \"llmproxy\", \"llmproxy.cmd\", \"llmproxy.ps1\",',\n ' \"llmp\", \"llmp.cmd\", \"llmp.ps1\"',\n ' ) | ForEach-Object {',\n ' $candidate = Join-Path $npmPrefix $_',\n ' Remove-Item -Force $candidate -ErrorAction SilentlyContinue',\n ' }',\n '}',\n '$pnpmRoot = pnpm root -g 2>$null',\n 'if ($pnpmRoot) {',\n ' $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent',\n ' @(',\n ' \"llmproxy\", \"llmproxy.cmd\", \"llmproxy.ps1\",',\n ' \"llmp\", \"llmp.cmd\", \"llmp.ps1\"',\n ' ) | ForEach-Object {',\n ' Remove-Item -Force (Join-Path \"$pnpmHome/bin\" $_) -ErrorAction SilentlyContinue',\n ' }',\n '}',\n ].join(\"\\n\");\n return commandRunner(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], { encoding: \"utf8\" });\n}\n\nfunction buildWindowsPersistentInstallScript(options = {}) {\n const packageRoot = path.resolve(String(options.packageRoot || path.join(__dirname, \"..\")));\n const locale = normalizeLocale(options.locale);\n const lines = locale === \"en\"\n ? [\n \"$ErrorActionPreference = 'Stop'\",\n \"$tmpdir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())\",\n \"New-Item -ItemType Directory -Path $tmpdir -Force | Out-Null\",\n `$packageRoot = ${JSON.stringify(packageRoot)}`,\n \"& llmproxy service:stop 2>$null | Out-Null\",\n \"npm pack $packageRoot --pack-destination $tmpdir 2>&1 | Out-Null\",\n \"$packageFile = Get-ChildItem (Join-Path $tmpdir '*.tgz') | Select-Object -First 1 -ExpandProperty FullName\",\n \"if (-not $packageFile) { throw 'Unable to prepare llmproxy package archive for persistent install.' }\",\n \"# Remove previous global installs\",\n \"npm uninstall -g llmproxy 2>$null | Out-Null\",\n \"pnpm remove -g llmproxy 2>$null | Out-Null\",\n \"# Install globally\",\n \"npm install -g $packageFile 2>&1 | Out-Null\",\n \"# Remove pnpm global wrapper if it exists\",\n \"$pnpmRoot = pnpm root -g 2>$null\",\n \"if ($pnpmRoot) {\",\n \" $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent\",\n \" Remove-Item -Force \\\"$pnpmHome/bin/llmproxy\\\" -ErrorAction SilentlyContinue\",\n \"}\",\n \"function Resolve-LlmproxyGlobalBin([string]$Prefix) {\",\n \" $candidates = @(\",\n \" (Join-Path $Prefix \\\"llmproxy.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmp\\\")\",\n \" )\",\n \" foreach ($candidate in $candidates) {\",\n \" if (Test-Path $candidate) { return $candidate }\",\n \" }\",\n \" return (Join-Path $Prefix \\\"llmproxy.cmd\\\")\",\n \"}\",\n \"# Remove other llmproxy binaries from PATH (keep the npm one)\",\n \"$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()\",\n \"$globalBin = Resolve-LlmproxyGlobalBin $npmPrefix\",\n \"$existing = Get-Command llmproxy -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source\",\n \"foreach ($bin in $existing) {\",\n \" if ($bin -ne $globalBin -and (Test-Path $bin)) { Remove-Item -Force $bin -ErrorAction SilentlyContinue }\",\n \"}\",\n \"# Start persistent service via the newly installed global binary\",\n \"$env:LLMPROXY_MODE = 'standalone'\",\n \"$env:LLMPROXY_SERVICE_RUNTIME = 'native'\",\n \"& \\\"$globalBin\\\" service:start 2>&1\",\n \"Write-Output \\\"__LLMPROXY_GLOBAL_BIN__=$globalBin\\\"\",\n \"Write-Output \\\"__LLMPROXY_BIN_SCOPE__=user\\\"\",\n \"Remove-Item -Recurse -Force $tmpdir -ErrorAction SilentlyContinue\",\n ]\n : [\n \"$ErrorActionPreference = 'Stop'\",\n \"$tmpdir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())\",\n \"New-Item -ItemType Directory -Path $tmpdir -Force | Out-Null\",\n `$packageRoot = ${JSON.stringify(packageRoot)}`,\n \"& llmproxy service:stop 2>$null | Out-Null\",\n \"npm pack $packageRoot --pack-destination $tmpdir 2>&1 | Out-Null\",\n \"$packageFile = Get-ChildItem (Join-Path $tmpdir '*.tgz') | Select-Object -First 1 -ExpandProperty FullName\",\n \"if (-not $packageFile) { throw 'Impossibile preparare l\\\\'archivio del package llmproxy per l\\\\'installazione persistente.' }\",\n \"# Rimuovi installazioni globali precedenti\",\n \"npm uninstall -g llmproxy 2>$null | Out-Null\",\n \"pnpm remove -g llmproxy 2>$null | Out-Null\",\n \"# Installa globalmente\",\n \"npm install -g $packageFile 2>&1 | Out-Null\",\n \"# Rimuovi wrapper pnpm globale se esiste\",\n \"$pnpmRoot = pnpm root -g 2>$null\",\n \"if ($pnpmRoot) {\",\n \" $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent\",\n \" Remove-Item -Force \\\"$pnpmHome/bin/llmproxy\\\" -ErrorAction SilentlyContinue\",\n \"}\",\n \"function Resolve-LlmproxyGlobalBin([string]$Prefix) {\",\n \" $candidates = @(\",\n \" (Join-Path $Prefix \\\"llmproxy.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmp\\\")\",\n \" )\",\n \" foreach ($candidate in $candidates) {\",\n \" if (Test-Path $candidate) { return $candidate }\",\n \" }\",\n \" return (Join-Path $Prefix \\\"llmproxy.cmd\\\")\",\n \"}\",\n \"# Rimuovi altri binari llmproxy dal PATH (mantieni quello npm)\",\n \"$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()\",\n \"$globalBin = Resolve-LlmproxyGlobalBin $npmPrefix\",\n \"$existing = Get-Command llmproxy -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source\",\n \"foreach ($bin in $existing) {\",\n \" if ($bin -ne $globalBin -and (Test-Path $bin)) { Remove-Item -Force $bin -ErrorAction SilentlyContinue }\",\n \"}\",\n \"# Avvia servizio persistente tramite il binario globale appena installato\",\n \"$env:LLMPROXY_MODE = 'standalone'\",\n \"$env:LLMPROXY_SERVICE_RUNTIME = 'native'\",\n \"& \\\"$globalBin\\\" service:start 2>&1\",\n \"Write-Output \\\"__LLMPROXY_GLOBAL_BIN__=$globalBin\\\"\",\n \"Write-Output \\\"__LLMPROXY_BIN_SCOPE__=user\\\"\",\n \"Remove-Item -Recurse -Force $tmpdir -ErrorAction SilentlyContinue\",\n ];\n\n return lines.join(\"\\n\");\n}\n\nfunction buildPersistentInstallScript(options = {}) {\n const platform = String(options.platform || process.platform);\n const packageRoot = path.resolve(String(options.packageRoot || path.join(__dirname, \"..\")));\n const locale = options.locale === \"en\" ? \"en\" : \"it\";\n\n if (platform === \"win32\") {\n return buildWindowsPersistentInstallScript(options);\n }\n\n if (platform !== \"darwin\" && platform !== \"linux\") {\n if (locale === \"en\") {\n throw new Error(`Unsupported platform for persistent installation: ${platform}`);\n }\n throw new Error(`Piattaforma non supportata per installazione persistente: ${platform}`);\n }\n\n return [\n \"set -e\",\n `platform=${shellQuote(platform)}`,\n 'case \"$platform\" in',\n ' darwin|linux) ;;',\n ' *) echo \"Unsupported platform: $platform\" >&2; exit 1 ;;',\n 'esac',\n 'append_path_export_once() {',\n ' profile_file=\"$1\"',\n ' bin_dir=\"$2\"',\n ' [ -n \"$profile_file\" ] || return 0',\n ' [ -n \"$bin_dir\" ] || return 0',\n ' line=\"export PATH=\\\\\"$bin_dir:\\\\$PATH\\\\\"\"',\n ' if [ ! -f \"$profile_file\" ]; then',\n ' : > \"$profile_file\"',\n ' fi',\n ' grep -F \"$line\" \"$profile_file\" >/dev/null 2>&1 && return 0',\n ' printf \"\\\\n%s\\\\n\" \"$line\" >> \"$profile_file\"',\n '}',\n 'persist_user_npm_bin_path() {',\n ' prefix=\"$1\"',\n ' case \"$prefix\" in',\n ' \"$HOME\"/*) ;;',\n ' *) return 0 ;;',\n ' esac',\n ' bin_dir=\"$prefix/bin\"',\n ' [ -d \"$bin_dir\" ] || return 0',\n ' append_path_export_once \"$HOME/.profile\" \"$bin_dir\"',\n ' append_path_export_once \"$HOME/.bash_profile\" \"$bin_dir\"',\n ' append_path_export_once \"$HOME/.zprofile\" \"$bin_dir\"',\n ' PATH=\"$bin_dir:$PATH\"',\n ' export PATH',\n ' hash -r 2>/dev/null || true',\n '}',\n 'cleanup_global_service_port() {',\n ' service_port=\"${1:-7045}\"',\n ' [ -n \"$service_port\" ] || return 0',\n ' if command -v docker >/dev/null 2>&1; then',\n ' docker_ids=$(docker ps --format \\'{{.ID}} {{.Ports}}\\' 2>/dev/null | awk -v port=\"$service_port\" \\'index($0, \":\" port \"->\") > 0 { print $1 }\\')',\n ' if [ -n \"$docker_ids\" ]; then',\n ' for docker_id in $docker_ids; do',\n ' docker stop \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker stop \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' docker rm \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker rm \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' if command -v lsof >/dev/null 2>&1; then',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' sleep 1',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill -9 \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill -9 \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' fi',\n '}',\n 'ensure_runtime_home_permissions() {',\n ' runtime_home=\"$1\"',\n ' runtime_owner=\"$2\"',\n ' [ -n \"$runtime_home\" ] || return 0',\n ' mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' if [ -n \"$runtime_owner\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo chown -R \"$runtime_owner\":\"$runtime_owner\" \"$runtime_home\" >/dev/null 2>&1 || true',\n ' fi',\n ' chmod -R u+rwX \"$runtime_home\" >/dev/null 2>&1 || true',\n '}',\n \"\",\n \"# Block reinstall if llmproxy is already installed system-wide by another user\",\n 'existing_global_bin=\"\"',\n 'for candidate in $( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk \\'!seen[$0]++\\'); do',\n ' case \"$candidate\" in',\n ' \"$HOME\"/*) ;;',\n ' *) existing_global_bin=\"$candidate\"; break ;;',\n ' esac',\n 'done',\n 'if [ -n \"$existing_global_bin\" ]; then',\n ' bin_owner=$(stat -c \"%U\" \"$existing_global_bin\" 2>/dev/null || stat -f \"%Su\" \"$existing_global_bin\" 2>/dev/null || true)',\n ' if [ -n \"$bin_owner\" ] && [ \"$bin_owner\" != \"$(whoami)\" ] && [ \"$bin_owner\" != \"root\" ]; then',\n ` echo \"llmproxy e' gia' installato globalmente da un altro utente ($bin_owner).\" >&2`,\n ' echo \"Usa llmproxy update per aggiornare, oppure contatta l\\'amministratore di sistema.\" >&2',\n ' echo \"Per forzare la reinstallazione usa --force (sovrascrive l\\'installazione esistente).\" >&2',\n ' exit 1',\n ' fi',\n 'fi',\n \"\",\n \"existing_bins=$( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk '!seen[$0]++')\",\n \"used_sudo=0\",\n 'npm_prefix_global=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'case \"$npm_prefix_global\" in',\n ' \"$HOME\"/*)',\n ' # User-local npm prefix: prefer the existing writable prefix first',\n ` if ! npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' if command -v sudo >/dev/null 2>&1; then',\n ` if sudo npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' used_sudo=1',\n ' echo \"System-wide install via sudo (user-local prefix install failed).\"',\n ' else',\n ' echo \"Error: npm install failed.\" >&2',\n ' exit 1',\n ' fi',\n ' else',\n ' echo \"Error: npm install failed. Install sudo or fix npm permissions.\" >&2',\n ' exit 1',\n ' fi',\n ' fi',\n ' ;;',\n ' *)',\n ' # System-level npm prefix: normal install, fall back to sudo if permissions fail',\n ` if ! npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' echo \"Standard npm install failed, trying with sudo...\"',\n ' if command -v sudo >/dev/null 2>&1; then',\n ` if sudo npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' used_sudo=1',\n ' else',\n ' echo \"Error: Cannot install llmproxy globally. Run with sudo or fix npm permissions.\" >&2',\n ' exit 1',\n ' fi',\n ' else',\n ' echo \"Error: npm install failed and sudo is not available. Fix npm permissions or run as root.\" >&2',\n ' exit 1',\n ' fi',\n ' fi',\n ' ;;',\n 'esac',\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n 'if [ -n \"$pnpm_root\" ]; then',\n ' pnpm_home=$(dirname \"$(dirname \"$pnpm_root\")\")',\n ' rm -f \"$pnpm_home/bin/llmproxy\"',\n ' rm -f \"$pnpm_home/bin/llmp\"',\n 'fi',\n \"# Re-read npm prefix: use sudo when we installed system-wide\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' npm_prefix=$(sudo npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'else',\n ' npm_prefix=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'fi',\n 'persist_user_npm_bin_path \"$npm_prefix\"',\n \"\",\n \"# System-wide install: make installed package world-readable (keep root ownership)\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' install_dir=\"$npm_prefix/lib/node_modules/llmproxy\"',\n ' if [ -d \"$install_dir\" ]; then',\n ' sudo chmod -R a+rX \"$install_dir\" 2>/dev/null || echo \"Warning: could not make $install_dir world-readable. Non-root users may not be able to use llmproxy.\" >&2',\n ' fi',\n 'fi',\n \"\",\n 'global_bin=\"$npm_prefix/bin/llmproxy\"',\n 'global_short_bin=\"$npm_prefix/bin/llmp\"',\n '[ -x \"$global_bin\" ]',\n 'linger_user=${SUDO_USER:-$USER}',\n 'for installed_bin in $existing_bins; do',\n ' if [ -n \"$installed_bin\" ] && [ \"$installed_bin\" != \"$global_bin\" ] && [ \"$installed_bin\" != \"$global_short_bin\" ]; then',\n ' rm -f \"$installed_bin\"',\n ' fi',\n 'done',\n 'if [ \"$platform\" = \"linux\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo -n loginctl enable-linger \"$linger_user\" >/dev/null 2>&1 || true',\n 'fi',\n '# On Linux, wait for systemd --user bus to become available after enable-linger',\n 'if [ \"$platform\" = \"linux\" ] && [ -z \"${XDG_RUNTIME_DIR:-}\" ]; then',\n ' uid=$(id -u)',\n ' export XDG_RUNTIME_DIR=\"/run/user/$uid\"',\n 'fi',\n 'if [ \"$platform\" = \"linux\" ] && [ -d \"$XDG_RUNTIME_DIR\" ]; then',\n ' bus_socket=\"$XDG_RUNTIME_DIR/bus\"',\n ' if [ ! -S \"$bus_socket\" ] && command -v systemctl >/dev/null 2>&1; then',\n ' systemctl --user start dbus 2>/dev/null || true',\n ' # Wait up to 5s for the user bus socket to appear',\n ' waited=0',\n ' while [ ! -S \"$bus_socket\" ] && [ \"$waited\" -lt 5 ]; do',\n ' sleep 1',\n ' waited=$((waited + 1))',\n ' done',\n ' fi',\n 'fi',\n 'if [ -z \"$LLMPROXY_HOME\" ]; then',\n ' if [ \"$platform\" = \"darwin\" ]; then',\n ' export LLMPROXY_HOME=\"$HOME/Library/Application Support/llmProxy\"',\n ' else',\n ' export LLMPROXY_HOME=\"$HOME/.local/share/llmProxy\"',\n ' fi',\n 'fi',\n 'runtime_owner=\"\"',\n 'if [ \"$used_sudo\" -eq 1 ] && [ -n \"${SUDO_USER:-}\" ]; then',\n ' runtime_owner=\"$SUDO_USER\"',\n 'else',\n ' runtime_owner=\"$(whoami)\"',\n 'fi',\n 'ensure_runtime_home_permissions \"$LLMPROXY_HOME\" \"$runtime_owner\"',\n 'cleanup_global_service_port \"${PORT:-7045}\"',\n '# When npm install used sudo, run service:start as the original user (systemd --user needs the real user session bus)',\n 'if [ \"$used_sudo\" -eq 1 ] && [ \"$platform\" = \"linux\" ] && [ -n \"${SUDO_USER:-}\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo -u \"$SUDO_USER\" XDG_RUNTIME_DIR=\"/run/user/$(id -u \"$SUDO_USER\")\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=/run/user/$(id -u \"$SUDO_USER\")/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"$global_bin\" service:start',\n 'else',\n ' LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"$global_bin\" service:start',\n 'fi',\n 'printf \"__LLMPROXY_GLOBAL_BIN__=%s\\\\n\" \"$global_bin\"',\n 'case \"$global_bin\" in',\n ' \"$HOME\"/*)',\n ' printf \"__LLMPROXY_BIN_SCOPE__=user\\\\n\"',\n ` printf \"__LLMPROXY_BIN_HINT__=%s\\\\n\" \"Il binario globale e' sotto \\\\$HOME ($global_bin). Altri utenti non vedranno llmproxy. Per installarlo a livello server, imposta il prefix npm su un path di sistema (es. /usr/local) e riesegui con sudo.\" ;;`,\n ' *) printf \"__LLMPROXY_BIN_SCOPE__=server\\\\n\" ;;',\n 'esac',\n \"\",\n \"# Suggest cleanup of clone directory\",\n `clone_dir=${shellQuote(packageRoot)}`,\n 'case \"$clone_dir\" in',\n ' /tmp/*|\"$HOME\"/llmProxy|\"$HOME\"/llmproxy|\"$HOME\"/Modules-Platform/*)',\n ' printf \"__LLMPROXY_CLONE_DIR__=%s\\\\n\" \"$clone_dir\"',\n ' echo \"\"',\n ' echo \"Il codice e\\' stato installato globalmente. La directory di clone non serve piu\\'.\"',\n ` echo \"Puoi rimuoverla con: rm -rf $clone_dir\"`,\n ' echo \"\"',\n ' ;;',\n 'esac',\n ].join(\"\\n\");\n}\n\nfunction resolveInstallLocale(command) {\n if (command === \"install\" || command === \"install:persistent-en\") return \"en\";\n return \"it\";\n}\n\nfunction isPersistentInstallCommand(command) {\n return command === \"install\"\n || command === \"install:persistent\"\n || command === \"install:persistent-it\"\n || command === \"install:persistent-en\";\n}\n\nfunction runPersistentInstall(commandRunner = spawnSync, options = {}) {\n const platform = String(options.platform || process.platform);\n if (platform === \"win32\") {\n const psScript = buildPersistentInstallScript(options);\n return commandRunner(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], { encoding: \"utf8\" });\n }\n return commandRunner(\"sh\", [\"-c\", buildPersistentInstallScript(options)], { encoding: \"utf8\" });\n}\n\nfunction getProxyBaseUrl(envOrOptions = process.env) {\n const options = envOrOptions && typeof envOrOptions === \"object\"\n && (Object.prototype.hasOwnProperty.call(envOrOptions, \"env\")\n || Object.prototype.hasOwnProperty.call(envOrOptions, \"dataRoot\")\n || Object.prototype.hasOwnProperty.call(envOrOptions, \"host\")\n || Object.prototype.hasOwnProperty.call(envOrOptions, \"port\"))\n ? envOrOptions\n : { env: envOrOptions };\n const { host, port } = resolveProxyHostPort(options);\n return `http://${host}:${port}`;\n}\n\nfunction resolveCliServiceManagerOptions({ env, paths, targetPlatform }) {\n const dockerComposeFile = path.join(paths.packageRoot, \"docker-compose.production.yml\");\n const proxyBinding = resolveProxyHostPort({ env, dataRoot: paths.dataRoot });\n\n return {\n platform: targetPlatform,\n packageRoot: paths.packageRoot,\n entryFile: resolveServiceEntryFile({ env, packageRoot: paths.packageRoot, targetPlatform }),\n serviceFile: targetPlatform === \"darwin\" ? paths.launchAgentFile : targetPlatform === \"win32\" ? paths.serviceRunnerFile : paths.systemdUnitFile,\n wrapperPath: targetPlatform === \"win32\" ? paths.serviceRunnerFile : undefined,\n stdoutPath: paths.stdoutLogFile,\n stderrPath: paths.stderrLogFile,\n environment: resolveServiceEnvironment({\n env: { ...env, HOST: proxyBinding.host, PORT: proxyBinding.port },\n paths,\n dockerComposeFile,\n }),\n };\n}\n\nfunction resolveServiceEnvironment({ env = process.env, paths, dockerComposeFile }) {\n const binding = resolveProxyHostPort({ env, dataRoot: paths.dataRoot });\n return {\n PORT: String(binding.port),\n HOST: String(binding.host),\n LLMPROXY_GLOBAL_SERVICE: \"1\",\n NODE_ENV: String(env.NODE_ENV || \"production\"),\n LLMPROXY_ENV: String(env.LLMPROXY_ENV || env.NODE_ENV || \"production\"),\n LLMPROXY_RUNTIME_PROFILE: String(env.LLMPROXY_RUNTIME_PROFILE || env.LLMPROXY_ENV || env.NODE_ENV || \"production\"),\n LLMPROXY_HOME: paths.dataRoot,\n LLMPROXY_MODE: String(env.LLMPROXY_MODE || \"standalone\"),\n LLMPROXY_SERVICE_RUNTIME: String(env.LLMPROXY_SERVICE_RUNTIME || \"native\"),\n LLMPROXY_MONGODB_CONNECTION_STRING: String(env.LLMPROXY_MONGODB_CONNECTION_STRING || \"\"),\n DBLAYER_URL: resolveDbLayerUrlForEnv(env),\n EVENTBUS_URL: resolveEventBusUrlForEnv(env),\n LLMPROXY_LOG_RETENTION_DAYS: String(env.LLMPROXY_LOG_RETENTION_DAYS || \"30\"),\n LLMPROXY_HOST_PROJECTS_ROOT: String(\n env.LLMPROXY_HOST_PROJECTS_ROOT || getDockerHostProjectsRoot({ env }),\n ),\n LLMPROXY_PROXY_REGISTRY: paths.proxyRegistryFile,\n LLMPROXY_DOCKER_COMPOSE_FILE: dockerComposeFile,\n LLMPROXY_DOCKER_SERVICE: String(env.LLMPROXY_DOCKER_SERVICE || \"llmproxy\"),\n LLMPROXY_DOCKER_POLL_MS: String(env.LLMPROXY_DOCKER_POLL_MS || \"30000\"),\n PATH: String(env.PATH || \"/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\"),\n };\n}\n\nasync function checkServiceHealth(fetchFn, baseUrl) {\n const response = await fetchFn(`${String(baseUrl).replace(/\\/$/, \"\")}/health`, {\n signal: AbortSignal.timeout(3000),\n });\n return response.ok;\n}\n\nasync function validatePlatformModeSwitch({ fetchFn, env, serviceConfigFile, nextValue }) {\n const normalized = String(nextValue || \"\").trim().toLowerCase();\n if (normalized !== \"platform\") return;\n\n const currentServiceConfig = readServiceConfig(serviceConfigFile);\n const mergedEnv = {\n ...env,\n ...(currentServiceConfig?.env || {}),\n LLMPROXY_MODE: \"platform\",\n };\n const dbLayerUrl = resolveDbLayerUrlForEnv(mergedEnv);\n const eventBusUrl = resolveEventBusUrlForEnv(mergedEnv);\n\n const [dbLayerOk, eventBusOk] = await Promise.all([\n checkServiceHealth(fetchFn, dbLayerUrl).catch(() => false),\n checkServiceHealth(fetchFn, eventBusUrl).catch(() => false),\n ]);\n\n if (dbLayerOk && eventBusOk) return;\n\n const missing = [];\n if (!dbLayerOk) missing.push(`db-layer (${dbLayerUrl})`);\n if (!eventBusOk) missing.push(`event-bus (${eventBusUrl})`);\n throw new Error(`Non e' possibile passare a LLMPROXY_MODE=platform: dipendenze non raggiungibili: ${missing.join(\", \")}`);\n}\n\nfunction resolveServiceEntryFile({ env = process.env, packageRoot, targetPlatform = process.platform }) {\n const forcedRuntime = String(env.LLMPROXY_SERVICE_RUNTIME || \"\").trim().toLowerCase();\n if (forcedRuntime === \"docker\") {\n return path.join(packageRoot, \"lib\", \"service\", \"docker-launchd-entry.js\");\n }\n // Native (default): node, native, o non impostato → server.js diretto\n return path.join(packageRoot, \"server.js\");\n}\n\nfunction writeClaudeSettings(options = {}) {\n const cwd = path.resolve(String(options.cwd || process.cwd()));\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const selectedModel = String(options.model || \"\").trim();\n const settingsDir = path.join(cwd, \".claude\");\n const settingsFile = path.join(settingsDir, \"settings.json\");\n let existingConfig = {};\n\n if (fs.existsSync(settingsFile)) {\n existingConfig = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n }\n\n const proxyEnvSource = env.LLMPROXY_HOME && options.port == null\n ? { ...env, PORT: \"\" }\n : env;\n const proxyEnv = {\n ANTHROPIC_BASE_URL: getProxyBaseUrl({\n env: proxyEnvSource,\n dataRoot: env.LLMPROXY_HOME || options.dataRoot,\n host: options.host,\n port: options.port,\n }),\n API_TIMEOUT_MS: \"3000000\",\n CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: \"1\",\n LLMPROXY_LLM_STATS_API_KEY: \"\",\n LLMPROXY_SENDGRID_API_KEY: \"\",\n LLMPROXY_SENDGRID_FROM_EMAIL: \"\",\n LLMPROXY_SENDGRID_TO_EMAIL: \"\",\n LLMPROXY_SENDGRID_TO_MESSAGE_TYPE: \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\",\n LLMPROXY_METERING_INLINE: \"0\",\n LLMPROXY_INFERENCE_INFO_INLINE: \"1\",\n LLMPROXY_PROVIDER_CREDIT_INLINE: \"1\",\n LLMPROXY_SHORT_ANSWER: \"0\",\n };\n if (options.includeDefaultModel !== false) {\n proxyEnv.ANTHROPIC_DEFAULT_MODEL = selectedModel;\n }\n const uiModelLabel = String(options.uiModelLabel || \"llmProxy\").trim() || \"llmProxy\";\n const existingEnv = existingConfig.env && typeof existingConfig.env === \"object\" ? existingConfig.env : {};\n if (options.includeDefaultModel === false) {\n delete existingEnv.ANTHROPIC_DEFAULT_MODEL;\n }\n delete existingEnv.ANTHROPIC_AUTH_TOKEN;\n const nextConfig = {\n ...existingConfig,\n model: uiModelLabel,\n env: {\n ...existingEnv,\n ...proxyEnv,\n },\n };\n\n fs.mkdirSync(settingsDir, { recursive: true });\n fs.writeFileSync(settingsFile, `${JSON.stringify(nextConfig, null, 2)}\\n`, \"utf8\");\n stdout.write(`Configurazione Claude scritta in ${settingsFile}\\n`);\n stdout.write(`ANTHROPIC_BASE_URL: ${proxyEnv.ANTHROPIC_BASE_URL}\\n`);\n stdout.write(`Default model: ${selectedModel}\\n`);\n syncClaudeHomeSettings({\n env,\n proxyEnv: {\n ...proxyEnv,\n ANTHROPIC_AUTH_TOKEN: \"proxy-local\",\n },\n stdout,\n });\n return 0;\n}\n\nfunction syncClaudeHomeSettings(options = {}) {\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const proxyEnv = options.proxyEnv && typeof options.proxyEnv === \"object\" ? options.proxyEnv : {};\n const homeDir = path.resolve(String(env.HOME || os.homedir()));\n const settingsDir = path.join(homeDir, \".claude\");\n const settingsFile = path.join(settingsDir, \"settings.json\");\n let existingConfig = {};\n\n if (fs.existsSync(settingsFile)) {\n existingConfig = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n }\n\n const existingEnv = existingConfig.env && typeof existingConfig.env === \"object\" ? existingConfig.env : {};\n const nextConfig = {\n ...existingConfig,\n env: {\n ...existingEnv,\n ANTHROPIC_BASE_URL: proxyEnv.ANTHROPIC_BASE_URL || existingEnv.ANTHROPIC_BASE_URL,\n API_TIMEOUT_MS: proxyEnv.API_TIMEOUT_MS || existingEnv.API_TIMEOUT_MS,\n CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: proxyEnv.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS || existingEnv.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS,\n ANTHROPIC_AUTH_TOKEN: proxyEnv.ANTHROPIC_AUTH_TOKEN,\n },\n };\n\n delete nextConfig.env.ANTHROPIC_DEFAULT_MODEL;\n\n fs.mkdirSync(settingsDir, { recursive: true });\n fs.writeFileSync(settingsFile, `${JSON.stringify(nextConfig, null, 2)}\\n`, \"utf8\");\n stdout.write(\"Supporto globale Claude sincronizzato automaticamente (placeholder auth locale).\\n\");\n}\n\nfunction writePiSettings(options = {}) {\n const cwd = path.resolve(String(options.cwd || process.cwd()));\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const piDir = path.join(cwd, \".pi\");\n const modelsFile = path.join(piDir, \"models.json\");\n const settingsFile = path.join(piDir, \"settings.json\");\n\n const proxyEnvSource = env.LLMPROXY_HOME && options.port == null\n ? { ...env, PORT: \"\" }\n : env;\n const baseUrl = getProxyBaseUrl({\n env: proxyEnvSource,\n dataRoot: env.LLMPROXY_HOME || options.dataRoot,\n host: options.host,\n port: options.port,\n });\n\n const models = {\n providers: {\n llmproxy: {\n api: \"anthropic-messages\",\n baseUrl,\n apiKey: \"proxy-local\",\n models: [{ id: \"llmproxy\", name: \"llmProxy\", contextWindow: 1000000 }],\n },\n },\n };\n\n const settings = {\n defaultProvider: \"llmproxy\",\n defaultModel: \"llmproxy\",\n };\n\n fs.mkdirSync(piDir, { recursive: true });\n fs.writeFileSync(modelsFile, `${JSON.stringify(models, null, 2)}\\n`, \"utf8\");\n fs.writeFileSync(settingsFile, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n stdout.write(`Configurazione PI Agent scritta in ${piDir}/\\n`);\n stdout.write(` ${modelsFile}\\n`);\n stdout.write(` ${settingsFile}\\n`);\n stdout.write(`baseUrl: ${baseUrl}\\n`);\n return 0;\n}\n\nfunction resolveVscodeChatModelsFile(env = process.env) {\n const platform = String(env.LLMPROXY_PLATFORM_OVERRIDE || process.platform);\n const homeDir = path.resolve(String(env.HOME || os.homedir()));\n if (platform === \"win32\") {\n const appData = String(env.APPDATA || path.join(homeDir, \"AppData\", \"Roaming\"));\n return path.join(appData, \"Code\", \"User\", \"chatLanguageModels.json\");\n }\n if (platform === \"darwin\") {\n return path.join(homeDir, \"Library\", \"Application Support\", \"Code\", \"User\", \"chatLanguageModels.json\");\n }\n return path.join(homeDir, \".config\", \"Code\", \"User\", \"chatLanguageModels.json\");\n}\n\nfunction resolveModelsFromTokenStore(tokenStore) {\n if (!tokenStore || typeof tokenStore.listProviders !== \"function\") return [];\n const providers = tokenStore.listProviders();\n const seen = new Set();\n const models = [];\n const now = Math.floor(Date.now() / 1000);\n for (const provider of providers) {\n const modelId = String(provider.default_model || \"\").trim();\n if (!modelId || seen.has(modelId)) continue;\n seen.add(modelId);\n models.push({\n id: modelId,\n object: \"model\",\n created: now,\n owned_by: String(provider.provider || provider.id || \"unknown\").trim().toLowerCase(),\n });\n }\n return models;\n}\n\nfunction writeVscodeChatSettings(options = {}) {\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const modelsFile = resolveVscodeChatModelsFile(env);\n const modelsDir = path.dirname(modelsFile);\n\n const proxyEnvSource = env.LLMPROXY_HOME && options.port == null\n ? { ...env, PORT: \"\" }\n : env;\n const baseUrl = getProxyBaseUrl({\n env: proxyEnvSource,\n dataRoot: env.LLMPROXY_HOME || options.dataRoot,\n host: options.host,\n port: options.port,\n });\n const openaiUrl = `${baseUrl}/v1`;\n\n const models = options.availableModels && Array.isArray(options.availableModels)\n ? options.availableModels\n : resolveModelsFromTokenStore(options.tokenStore);\n\n let existing = [];\n if (fs.existsSync(modelsFile)) {\n try {\n const raw = JSON.parse(fs.readFileSync(modelsFile, \"utf8\"));\n existing = Array.isArray(raw) ? raw : [];\n } catch (_parseError) {\n existing = [];\n }\n }\n\n const modelEntries = {};\n for (const model of models) {\n const id = String(model.id || \"\").trim();\n if (!id) continue;\n modelEntries[id] = {\n name: String(model.name || id).trim(),\n url: openaiUrl,\n };\n }\n\n const llmproxyEntry = {\n name: \"llmProxy\",\n vendor: \"openai\",\n url: openaiUrl,\n headers: {},\n models: modelEntries,\n };\n\n const next = existing.filter((entry) => {\n if (!entry || typeof entry !== \"object\") return true;\n const name = String(entry.name || \"\").trim();\n const vendor = String(entry.vendor || \"\").trim().toLowerCase();\n return !(name === \"llmProxy\" || name === \"llmproxy\" || (vendor === \"openai\" && name.toLowerCase().includes(\"llmproxy\")));\n });\n next.push(llmproxyEntry);\n\n fs.mkdirSync(modelsDir, { recursive: true });\n fs.writeFileSync(modelsFile, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n stdout.write(`Configurazione VS Code Chat scritta in ${modelsFile}\\n`);\n stdout.write(` URL: ${openaiUrl}\\n`);\n stdout.write(` Modelli: ${models.length} (${Object.keys(modelEntries).join(\", \")})\\n`);\n stdout.write(`Riavvia VS Code e seleziona \"llmProxy\" nel picker della Chat.\\n`);\n return 0;\n}\n\nfunction writeVscodeClaudeSettings(options = {}) {\n const defaultModel = resolveDefaultModel(options.model, options.availableModels);\n return writeClaudeSettings({\n cwd: options.cwd,\n env: options.env,\n stdout: options.stdout,\n model: defaultModel,\n includeDefaultModel: false,\n omitAuthToken: true,\n uiModelLabel: \"llmProxy\",\n dataRoot: options.dataRoot,\n });\n}\n\nfunction configureClaudeSettings(options = {}) {\n const defaultModel = resolveDefaultModel(options.model, options.availableModels);\n return writeClaudeSettings({\n cwd: options.cwd,\n env: options.env,\n stdout: options.stdout,\n model: defaultModel,\n includeDefaultModel: false,\n omitAuthToken: true,\n uiModelLabel: \"llmProxy\",\n dataRoot: options.dataRoot,\n });\n}\n\nfunction setClaudeModel(options = {}) {\n const selectedModel = String(options.model || \"\").trim();\n if (!selectedModel) {\n throw new Error(\"Uso: llmproxy model:set <model>\");\n }\n return writeClaudeSettings({\n cwd: options.cwd,\n env: options.env,\n stdout: options.stdout,\n model: selectedModel,\n uiModelLabel: selectedModel,\n });\n}\n\nfunction resolveConfigScopeFromParsed(parsed, key = \"\") {\n const wantsProject = Boolean(parsed.flags.project);\n const wantsService = Boolean(parsed.flags.service);\n const wantsGlobal = Boolean(parsed.flags.global);\n const rawScope = String(parsed.flags.scope || \"\").trim().toLowerCase();\n if (rawScope && ![\"project\", \"global\", \"service\"].includes(rawScope)) {\n throw new Error(\"Scope non valido. Usa uno tra: project, global, service\");\n }\n const explicitScope = wantsProject\n ? \"project\"\n : rawScope === \"project\"\n ? \"project\"\n : wantsGlobal\n ? \"global\"\n : rawScope === \"global\"\n ? \"global\"\n : wantsService\n ? \"service\"\n : rawScope === \"service\"\n ? \"service\"\n : \"\";\n if (!key) return explicitScope;\n return inferScopeFromKey(key, explicitScope);\n}\n\nfunction resolveConfigListScope(parsed, cwd, env = process.env) {\n const explicitScope = resolveConfigScopeFromParsed(parsed);\n if (explicitScope) return explicitScope;\n if (hasLocalClaudeFolder(cwd)) return \"\";\n return fs.existsSync(getGlobalClaudeSettingsFile(env)) ? \"\" : \"service\";\n}\n\nfunction persistServiceEnvironmentConfig(serviceConfigFile, environment = {}) {\n if (!serviceConfigFile) return;\n writeServiceConfig(serviceConfigFile, { env: { ...environment } });\n}\n\nfunction writeForegroundRunState(filePath, payload = {}) {\n if (!filePath) return;\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\\n`, \"utf8\");\n}\n\nfunction readForegroundRunState(filePath) {\n if (!filePath || !fs.existsSync(filePath)) return null;\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction removeForegroundRunState(filePath) {\n if (!filePath) return;\n try {\n fs.unlinkSync(filePath);\n } catch {\n // ignore\n }\n}\n\nfunction stopForegroundDevInstance(options = {}) {\n const {\n paths,\n stdout = process.stdout,\n stderr = process.stderr,\n execCommand,\n killProcess,\n } = options;\n const devPort = 5045;\n const state = readForegroundRunState(paths.foregroundRunPidFile);\n const listeners = listPortListeners({ port: devPort, execCommand });\n const statePid = Number(state?.pid || 0);\n const listenerByStatePid = statePid > 0\n ? listeners.find((listener) => Number(listener.pid) === statePid)\n : null;\n\n const killFn = typeof killProcess === \"function\" ? killProcess : process.kill.bind(process);\n\n if (listenerByStatePid) {\n killFn(listenerByStatePid.pid, \"SIGTERM\");\n removeForegroundRunState(paths.foregroundRunPidFile);\n stdout.write(`Istanza dev fermata su http://127.0.0.1:${devPort}\\n`);\n return 0;\n }\n\n const fallbackListeners = listeners.filter((listener) => /node|bun|deno/i.test(String(listener.command || \"\")));\n if (fallbackListeners.length === 1) {\n killFn(fallbackListeners[0].pid, \"SIGTERM\");\n removeForegroundRunState(paths.foregroundRunPidFile);\n stdout.write(`Istanza dev fermata su http://127.0.0.1:${devPort}\\n`);\n return 0;\n }\n\n if (listeners.length === 0) {\n removeForegroundRunState(paths.foregroundRunPidFile);\n stdout.write(`Nessuna istanza dev attiva su http://127.0.0.1:${devPort}\\n`);\n return 0;\n }\n\n const summary = listeners.map((listener) => `${listener.command || \"unknown\"} pid=${listener.pid}`).join(\", \");\n stderr.write(`Porta ${devPort} occupata da processi non riconosciuti come run locale llmproxy: ${summary}\\n`);\n return 1;\n}\n\nfunction normalizeServiceRuntimeTarget(value, targetPlatform = process.platform) {\n const normalized = String(value || \"\").trim().toLowerCase();\n if (!normalized) return \"\";\n if (normalized === \"docker\") return \"docker\";\n if ([\"native\", \"launchd\", \"systemd\", \"windows\"].includes(normalized)) {\n if (normalized === \"launchd\" && targetPlatform !== \"darwin\") {\n throw new Error(\"`launchd` e' valido solo su macOS.\");\n }\n if (normalized === \"systemd\" && targetPlatform !== \"linux\") {\n throw new Error(\"`systemd` e' valido solo su Linux.\");\n }\n if (normalized === \"windows\" && targetPlatform !== \"win32\") {\n throw new Error(\"`windows` e' valido solo su Windows.\");\n }\n return \"native\";\n }\n throw new Error(\"Runtime non valido. Usa uno tra: docker, native, launchd, systemd.\");\n}\n\nfunction removeNativeServiceArtifacts({ targetPlatform, paths, env, managerOverride }) {\n const nativeEnv = { ...env, LLMPROXY_SERVICE_RUNTIME: \"native\" };\n const manager = managerOverride || createServiceManager(resolveCliServiceManagerOptions({ env: nativeEnv, paths, targetPlatform }));\n manager.stop();\n const serviceFile = targetPlatform === \"darwin\"\n ? paths.launchAgentFile\n : targetPlatform === \"win32\"\n ? paths.serviceRunnerFile\n : paths.systemdUnitFile;\n try { fs.unlinkSync(serviceFile); } catch { /* ignore */ }\n if (targetPlatform === \"win32\") {\n try {\n spawnSync(\"sc.exe\", [\"delete\", \"llmproxy\"], { encoding: \"utf8\", shell: true });\n } catch { /* ignore */ }\n }\n}\n\nfunction removeDockerRuntime({ commandRunner, env, paths }) {\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n runDockerCompose(commandRunner, composeFile, [\"down\", \"--remove-orphans\"], { spawnOptions });\n}\n\nfunction formatConfigList(entries, scope = \"\") {\n const normalizedScope = String(scope || \"\").trim();\n if (normalizedScope === \"project\") {\n return `Project configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"project\"))}\\n\\nGlobal Claude configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"global\"))}`;\n }\n if (normalizedScope === \"global\") {\n return `Global Claude configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"global\"))}`;\n }\n if (normalizedScope === \"service\") {\n return `Service configuration:\\n${formatConfigEntries(entries)}`;\n }\n return `Project configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"project\"))}\\n\\nGlobal Claude configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"global\"))}\\n\\nService configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"service\"))}`;\n}\n\nfunction buildRestCommandRequest(parsed, options = {}) {\n const cwd = path.resolve(String(options.cwd || process.cwd()));\n const headers = { \"content-type\": \"application/json\", \"x-project-path\": cwd };\n const body = {};\n const query = new URLSearchParams();\n const command = parsed.command;\n\n switch (command) {\n case \"version\":\n return { method: \"GET\", path: \"/api/version\" };\n case \"help\":\n if (parsed.args[0]) query.set(\"command\", SHORT_COMMAND_ALIASES[parsed.args[0]] || parsed.args[0]);\n return { method: \"GET\", path: `/api/help${query.toString() ? `?${query.toString()}` : \"\"}` };\n case \"setup\":\n return { method: \"GET\", path: \"/api/setup\" };\n case \"release-notes\":\n if (parsed.flags.version || parsed.args[0]) query.set(\"version\", String(parsed.flags.version || parsed.args[0]).trim());\n if (parsed.flags.locale) query.set(\"locale\", String(parsed.flags.locale).trim());\n return { method: \"GET\", path: `/api/release-notes${query.toString() ? `?${query.toString()}` : \"\"}` };\n case \"login\":\n return { method: \"POST\", path: \"/api/auth/login\" };\n case \"logout\":\n return { method: \"POST\", path: \"/api/auth/logout\" };\n case \"status\":\n return { method: \"GET\", path: \"/api/service/status\" };\n case \"logs\":\n if (parsed.follow) return null;\n return { method: \"GET\", path: \"/api/logs\" };\n case \"models:list\":\n return { method: \"GET\", path: \"/api/models\" };\n case \"claude:setup\":\n if (parsed.flags.model) body.model = String(parsed.flags.model).trim();\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/claude/setup\", headers, body };\n case \"pi:setup\":\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/pi/setup\", headers, body };\n case \"vscode-chat:setup\":\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/vscode/chat/setup\", headers, body };\n case \"vscode-claude:setup\":\n if (parsed.flags.model) body.model = String(parsed.flags.model).trim();\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/vscode/claude/setup\", headers, body };\n case \"model:set\":\n body.model = String(parsed.args[0] || \"\").trim();\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/model/set\", headers, body };\n case \"provider:add\": {\n const providerId = normalizeKnownProviderId(parsed.args[0]);\n if (!providerId) return null;\n const apiKey = String(parsed.flags[\"api-key\"] || \"\").trim();\n const providerName = String(parsed.flags.name || providerId).trim();\n const model = String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim();\n const plan = String(parsed.flags.plan || \"\").trim();\n const freeModel = parsed.flags[\"free-model\"];\n if (apiKey) {\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(providerId)}/api-key`,\n headers,\n body: {\n name: providerName,\n apiKey,\n model,\n plan,\n vision: parsed.flags.vision,\n freeModel,\n },\n };\n }\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(providerId)}/login`,\n headers,\n body: {\n name: providerName,\n model,\n plan,\n freeModel,\n },\n };\n }\n case \"provider:key\": {\n const providerId = normalizeKnownProviderId(parsed.args[0]);\n if (!providerId) return null;\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(providerId)}/api-key`,\n headers,\n body: {\n name: providerId,\n apiKey: String(parsed.flags[\"api-key\"] || \"\").trim(),\n model: String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim(),\n plan: String(parsed.flags.plan || \"\").trim(),\n vision: parsed.flags.vision,\n freeModel: parsed.flags[\"free-model\"],\n },\n };\n }\n case \"provider:list\":\n return { method: \"GET\", path: \"/api/providers\", headers };\n case \"provider:available\":\n return { method: \"GET\", path: \"/api/providers/available\" };\n case \"provider:status\":\n return { method: \"GET\", path: \"/api/providers/status\" };\n case \"provider:usage\":\n return { method: \"GET\", path: \"/api/providers/usage\" };\n case \"provider:order\":\n return {\n method: \"POST\",\n path: \"/api/providers/order\",\n headers,\n body: {\n id: String(parsed.args[0] || \"\").trim(),\n position: String(parsed.args[1] || \"\").trim(),\n },\n };\n case \"provider:reorder\":\n return {\n method: \"POST\",\n path: \"/api/providers/reorder\",\n headers,\n body: {},\n };\n case \"provider:rename\":\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(String(parsed.args[0] || \"\").trim())}/rename`,\n headers,\n body: { name: parsed.args.slice(1).join(\" \").trim() },\n };\n case \"provider:update\": {\n const providerId = String(parsed.args[0] || \"\").trim();\n const flags = parsed.flags || {};\n const patch = {};\n if (flags.vision !== undefined && flags.vision !== null && flags.vision !== \"\") patch.vision = flags.vision;\n if (flags[\"free-model\"] !== undefined && flags[\"free-model\"] !== null && flags[\"free-model\"] !== \"\") patch.free_model = flags[\"free-model\"];\n if (flags.name !== undefined && flags.name !== null && flags.name !== \"\") patch.name = String(flags.name).trim();\n return {\n method: \"PATCH\",\n path: `/api/providers/${encodeURIComponent(providerId)}`,\n headers,\n body: { patch },\n };\n }\n case \"provider:remove\":\n return {\n method: \"DELETE\",\n path: `/api/providers/${encodeURIComponent(String(parsed.args[0] || \"\").trim())}`,\n headers,\n };\n case \"service:stop\":\n return { method: \"POST\", path: \"/api/service/stop\" };\n case \"service:restart\":\n return { method: \"POST\", path: \"/api/service/restart\" };\n case \"service:runtime\":\n body.runtime = String(parsed.args[0] || \"\").trim();\n return { method: \"POST\", path: \"/api/service/runtime\", body };\n case \"stats\":\n return { method: \"GET\", path: \"/api/stats\" };\n case \"update\":\n return { method: \"POST\", path: \"/api/update\" };\n case \"uninstall\":\n return { method: \"POST\", path: \"/api/uninstall\" };\n case \"config:list\": {\n const scope = resolveConfigScopeFromParsed(parsed);\n if (scope) query.set(\"scope\", scope);\n query.set(\"projectPath\", cwd);\n return { method: \"GET\", path: `/api/config${query.toString() ? `?${query.toString()}` : \"\"}`, headers };\n }\n case \"config:get\": {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) return null;\n const scope = resolveConfigScopeFromParsed(parsed, key);\n if (scope) query.set(\"scope\", scope);\n query.set(\"projectPath\", cwd);\n return { method: \"GET\", path: `/api/config/${encodeURIComponent(key)}?${query.toString()}`, headers };\n }\n case \"config:set\": {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) return null;\n const scope = resolveConfigScopeFromParsed(parsed, key);\n return {\n method: \"POST\",\n path: `/api/config/${encodeURIComponent(key)}`,\n headers,\n body: {\n value: String(parsed.args[1] || \"\"),\n scope,\n projectPath: cwd,\n },\n };\n }\n case \"config:unset\": {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) return null;\n const scope = resolveConfigScopeFromParsed(parsed, key);\n return {\n method: \"DELETE\",\n path: `/api/config/${encodeURIComponent(key)}`,\n headers,\n body: {\n scope,\n projectPath: cwd,\n },\n };\n }\n case \"proxy:add\": {\n const url = String(parsed.args[0] || \"\").trim();\n if (!url) return null;\n return { method: \"POST\", path: \"/api/proxies\", headers, body: { url } };\n }\n case \"proxy:list\":\n return { method: \"GET\", path: \"/api/proxies\", headers };\n case \"proxy:remove\":\n return { method: \"DELETE\", path: `/api/proxies/${encodeURIComponent(String(parsed.args[0] || \"\").trim())}`, headers };\n case \"proxy:reorder\":\n return { method: \"POST\", path: \"/api/proxies/reorder\", headers, body: {} };\n case \"proxy:test\":\n return { method: \"POST\", path: \"/api/proxies/test\", headers };\n default:\n return null;\n }\n}\n\nasync function tryRunViaRest(parsed, options = {}) {\n if (options.preferLocalExecution) return null;\n if ([\n \"version\",\n \"status\",\n \"config:list\",\n \"config:get\",\n \"config:set\",\n \"config:unset\",\n \"update\",\n \"uninstall\",\n \"service:stop\",\n \"service:restart\",\n \"service:runtime\",\n ].includes(parsed.command)) return null;\n if (options.tokenStore || options.modelCatalogStore || options.serviceManager || options.commandRunner) {\n return null;\n }\n let request = null;\n try {\n request = buildRestCommandRequest(parsed, options);\n } catch {\n return null;\n }\n if (!request) return null;\n\n const restFetchFn = options.restFetchFn === undefined ? fetch : options.restFetchFn;\n if (typeof restFetchFn !== \"function\") return null;\n\n const baseUrl = getProxyBaseUrl({\n env: options.env || process.env,\n dataRoot: options.dataRoot,\n });\n\n try {\n const healthResponse = await restFetchFn(`${baseUrl}/health`);\n if (!healthResponse?.ok) return null;\n\n const response = await restFetchFn(`${baseUrl}${request.path}`, {\n method: request.method,\n headers: request.headers,\n body: request.body ? JSON.stringify(request.body) : undefined,\n });\n const payload = await response.json();\n const data = payload?.data || {};\n if (data.output) {\n options.stdout.write(`${String(data.output).trimEnd()}\\n`);\n }\n if (data.error) {\n options.stderr.write(`${String(data.error).trimEnd()}\\n`);\n }\n return Number.isFinite(Number(payload?.exitCode)) ? Number(payload.exitCode) : (payload?.success ? 0 : 1);\n } catch {\n return null;\n }\n}\n\nfunction tailFile(filePath, lines = 80) {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf8\").trimEnd();\n if (!content) return \"\";\n const chunks = content.split(/\\r?\\n/);\n return chunks.slice(-lines).join(\"\\n\");\n}\n\nfunction tailLatestRequestLog(logsDir, lines = 80) {\n if (!fs.existsSync(logsDir)) return \"\";\n const requestLogs = fs.readdirSync(logsDir)\n .filter((fileName) => /^requests-\\d{4}-\\d{2}-\\d{2}\\.jsonl$/.test(fileName))\n .sort();\n\n const latestLog = requestLogs.at(-1);\n if (!latestLog) return \"\";\n return tailFile(path.join(logsDir, latestLog), lines);\n}\n\nfunction readLatestRequestSummary(logsDir, sinceIso = null) {\n if (!fs.existsSync(logsDir)) return null;\n const requestLogs = fs.readdirSync(logsDir)\n .filter((fileName) => /^requests-\\d{4}-\\d{2}-\\d{2}\\.jsonl(?:\\.\\d+)?$/.test(fileName))\n .sort();\n\n let latest = null;\n for (const fileName of requestLogs) {\n const filePath = path.join(logsDir, fileName);\n let content = \"\";\n try {\n content = fs.readFileSync(filePath, \"utf8\");\n } catch {\n continue;\n }\n for (const line of content.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n let entry;\n try {\n entry = JSON.parse(trimmed);\n } catch {\n continue;\n }\n if (entry?.event !== \"request_summary\") continue;\n const ts = String(entry?.ts || \"\").trim();\n if (!ts) continue;\n if (sinceIso && ts < sinceIso) continue;\n if (!latest || ts >= String(latest.ts || \"\")) latest = entry;\n }\n }\n return latest;\n}\n\nasync function waitForLatestRequestSummary(logsDir, options = {}) {\n const sinceIso = options.sinceIso || null;\n const matcher = typeof options.matcher === \"function\" ? options.matcher : null;\n const retries = Number.isInteger(options.retries) ? options.retries : 40;\n const delayMs = Number.isFinite(options.delayMs) ? options.delayMs : 50;\n const sleepFn = typeof options.sleepFn === \"function\"\n ? options.sleepFn\n : (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\n for (let retry = 0; retry < retries; retry += 1) {\n const summary = readLatestRequestSummary(logsDir, sinceIso);\n if (summary && (!matcher || matcher(summary))) return summary;\n if (retry < retries - 1) await sleepFn(delayMs);\n }\n return null;\n}\n\nfunction formatInferenceProviderAttempt(attempt = {}) {\n const provider = String(attempt.provider || \"unknown\").trim();\n const model = String(attempt.actual_model || attempt.effective_model || \"\").trim();\n if (attempt.success === true) {\n return `${provider}[ok${model ? ` ${model}` : \"\"}]`;\n }\n if (typeof attempt.status === \"number\") {\n return `${provider}[HTTP ${attempt.status}${model ? ` ${model}` : \"\"}]`;\n }\n return `${provider}[fail${model ? ` ${model}` : \"\"}]`;\n}\n\nfunction normalizeInferenceDisplayModel(model) {\n const raw = String(model || \"\").trim();\n if (!raw) return \"\";\n const leaf = raw.includes(\"/\") ? raw.split(\"/\").pop() : raw;\n return leaf.replace(/-\\d{8}$/i, \"\");\n}\n\nfunction buildRemainingFallbackProviders(providers, finalProviderId, finalModel) {\n const orderedProviders = Array.isArray(providers) ? providers.slice() : [];\n if (orderedProviders.length === 0) return [];\n\n const normalizedFinalProviderId = String(finalProviderId || \"\").trim().toLowerCase();\n const normalizedFinalModel = String(finalModel || \"\").trim().toLowerCase();\n let winnerIndex = orderedProviders.findIndex((provider) => {\n const providerId = String(provider?.id || \"\").trim().toLowerCase();\n const model = String(provider?.effective_model || provider?.default_model || \"\").trim().toLowerCase();\n return providerId === normalizedFinalProviderId && (!normalizedFinalModel || model === normalizedFinalModel);\n });\n\n if (winnerIndex === -1) {\n winnerIndex = orderedProviders.findIndex((provider) => String(provider?.id || \"\").trim().toLowerCase() === normalizedFinalProviderId);\n }\n if (winnerIndex === -1) return [];\n return orderedProviders.slice(winnerIndex + 1);\n}\n\nfunction formatOrdinalLabel(position) {\n const index = Number(position || 0);\n if (index === 1) return \"1st\";\n if (index === 2) return \"2nd\";\n if (index === 3) return \"3rd\";\n return `${index}th`;\n}\n\nfunction followLogs(paths, stdout, stderr) {\n const requestLogs = fs.existsSync(paths.logsDir)\n ? fs.readdirSync(paths.logsDir)\n .filter((fileName) => /^requests-\\d{4}-\\d{2}-\\d{2}\\.jsonl$/.test(fileName))\n .sort()\n .map((fileName) => path.join(paths.logsDir, fileName))\n : [];\n const files = [paths.stdoutLogFile, paths.stderrLogFile, ...requestLogs].filter((filePath) => fs.existsSync(filePath));\n if (files.length === 0) {\n stdout.write(\"Nessun file di log disponibile. Avvia prima il servizio.\\n\");\n return Promise.resolve(0);\n }\n\n return new Promise((resolve, reject) => {\n const child = spawn(\"tail\", [\"-n\", \"50\", \"-f\", ...files], {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n child.stdout.on(\"data\", (chunk) => stdout.write(chunk));\n child.stderr.on(\"data\", (chunk) => stderr.write(chunk));\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => resolve(code ?? 0));\n });\n}\n\nfunction isTransientLocalFetchError(error) {\n const message = String(error?.message || error || \"\").trim();\n return /fetch failed|socket connection was closed unexpectedly|socket.*closed|econnreset|econnrefused|etimedout|timeout|network error/i.test(message);\n}\n\nasync function runLocalProxyTestRequest(fetchFn, url, request, sleepFn) {\n const delay = typeof sleepFn === \"function\"\n ? sleepFn\n : (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\n for (let attempt = 0; attempt < 4; attempt += 1) {\n try {\n return await fetchFn(url, request);\n } catch (error) {\n if (!isTransientLocalFetchError(error) || attempt === 3) throw error;\n await delay(250);\n }\n }\n}\n\nasync function withTemporaryProviderProxy(providerStore, provider, proxyUrl, fn) {\n if (!providerStore || !provider || !provider.id || !proxyUrl) return fn(provider);\n\n const originalProvider = providerStore.getProvider(provider.id);\n if (!originalProvider) return fn(provider);\n\n providerStore.saveProvider(provider.id, {\n ...originalProvider,\n proxy_url: proxyUrl,\n proxy_rotation: false,\n }, { name: originalProvider.name });\n\n try {\n return await fn({\n ...provider,\n ...originalProvider,\n proxy_url: proxyUrl,\n proxy_rotation: false,\n });\n } finally {\n providerStore.saveProvider(provider.id, originalProvider, { name: originalProvider.name });\n }\n}\n\nfunction extractAssistantTextFromPayload(payload) {\n if (payload?.choices?.[0]?.message) {\n const message = payload.choices[0].message;\n const content = message?.content;\n if (typeof content === \"string\") return content.trim();\n if (Array.isArray(content)) {\n return content\n .map((item) => {\n if (typeof item === \"string\") return item;\n if (item && typeof item.text === \"string\") return item.text;\n return \"\";\n })\n .filter(Boolean)\n .join(\"\\n\")\n .trim();\n }\n }\n\n if (Array.isArray(payload?.content)) {\n return payload.content\n .filter((item) => item && item.type === \"text\" && typeof item.text === \"string\")\n .map((item) => item.text)\n .join(\"\\n\")\n .trim();\n }\n\n return \"\";\n}\n\nasync function readResponseErrorSnippet(response) {\n if (!response) return \"\";\n try {\n if (typeof response.text !== \"function\") return \"\";\n const rawText = String(await response.text() || \"\").trim();\n if (!rawText) return \"\";\n try {\n const parsed = JSON.parse(rawText);\n const nestedMessage = parsed?.error?.message\n || parsed?.error?.error\n || parsed?.message\n || parsed?.detail\n || parsed?.details;\n const normalizedNested = typeof nestedMessage === \"string\"\n ? nestedMessage.trim()\n : nestedMessage != null\n ? JSON.stringify(nestedMessage)\n : \"\";\n if (normalizedNested) return normalizedNested.slice(0, 240);\n } catch {\n // fall back to the raw text\n }\n return rawText.replace(/\\s+/g, \" \").slice(0, 240);\n } catch {\n return \"\";\n }\n}\n\nasync function probeProviderForTest({ fetchFn, proxyBaseUrl, provider, sleepFn, logsDir }) {\n const model = provider.default_model || (String(provider.provider || \"copilot\") === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n if (!model) {\n return {\n ok: false,\n kind: \"missing-model\",\n message: \"default model mancante\",\n model,\n displayModel: model,\n };\n }\n\n const providerStartedAtIso = new Date().toISOString();\n const response = await runLocalProxyTestRequest(\n fetchFn,\n `${proxyBaseUrl}/v1/messages`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n provider: provider.id === \"auto\" ? undefined : provider.id,\n model,\n stream: false,\n max_tokens: 1024,\n messages: [\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Rispondi solo: llmproxy-test-${provider.id}` }],\n },\n ],\n }),\n },\n sleepFn,\n );\n\n if (!response.ok && response.status !== 429 && response.status !== 402) {\n const errorSnippet = await readResponseErrorSnippet(response);\n return {\n ok: false,\n kind: \"http\",\n status: response.status,\n message: errorSnippet ? `HTTP ${response.status}: ${errorSnippet}` : `HTTP ${response.status}`,\n model,\n displayModel: model,\n };\n }\n\n if (!response.ok && (response.status === 429 || response.status === 402)) {\n return {\n ok: true,\n model,\n displayModel: model,\n finalProvider: provider.id,\n finalModel: model,\n degradedStatus: response.status,\n };\n }\n\n const payload = await response.json();\n const assistantText = Array.isArray(payload?.content)\n ? payload.content\n .filter((item) => item && item.type === \"text\" && typeof item.text === \"string\")\n .map((item) => item.text)\n .join(\"\\n\")\n .trim()\n : \"\";\n const visibleAssistantText = sanitizeTestAssistantText(assistantText);\n let summary = readLatestRequestSummary(logsDir, providerStartedAtIso);\n if (summary && !requestSummaryMatchesProvider(summary, provider)) {\n summary = null;\n }\n if (!visibleAssistantText && !(summary?.success && requestSummaryMatchesProvider(summary, provider))) {\n summary = await waitForLatestRequestSummary(logsDir, {\n sinceIso: providerStartedAtIso,\n sleepFn,\n matcher: (entry) => entry?.success === true && requestSummaryMatchesProvider(entry, provider),\n });\n }\n const finalProvider = String(summary?.finalProvider || \"\").trim();\n const finalModel = String(summary?.finalModel || payload?.model || \"\").trim();\n if (!visibleAssistantText && !(summary?.success && finalProvider)) {\n return {\n ok: false,\n kind: \"empty\",\n message: \"risposta vuota\",\n model,\n displayModel: finalModel || model,\n };\n }\n\n return {\n ok: true,\n model,\n displayModel: finalModel || model,\n finalProvider,\n finalModel,\n };\n}\n\nfunction isDockerManagedRuntime(env, paths, targetPlatform) {\n const entryFile = resolveServiceEntryFile({ env, packageRoot: paths.packageRoot, targetPlatform });\n return path.basename(entryFile) === \"docker-launchd-entry.js\";\n}\n\nfunction isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions = {}) {\n const result = runDockerCompose(commandRunner, composeFile, [\"ps\", \"--status\", \"running\", \"--services\", serviceName], { spawnOptions });\n if (!result || result.status !== 0) return false;\n return String(result.stdout || \"\")\n .split(/\\r?\\n/)\n .map((entry) => entry.trim())\n .includes(serviceName);\n}\n\nfunction ensureDockerServiceRunning({ commandRunner, env, paths, targetPlatform, forceRecreate = false }) {\n if (!isDockerManagedRuntime(env, paths, targetPlatform)) {\n return { ok: true, checked: false };\n }\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const serviceName = String(env.LLMPROXY_DOCKER_SERVICE || \"llmproxy\").trim() || \"llmproxy\";\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n if (!fs.existsSync(composeFile)) {\n return { ok: false, error: `File docker compose non trovato: ${composeFile}` };\n }\n try {\n fs.accessSync(composeFile, fs.constants.R_OK);\n } catch {\n const installDir = path.dirname(composeFile);\n return {\n ok: false,\n error: `Permesso negato nella lettura di ${composeFile}. Esegui: sudo chmod -R a+rX \"${installDir}\"`,\n };\n }\n const dockerCompose = resolveDockerComposeCommand(commandRunner, { spawnOptions });\n if (!dockerCompose.dockerAvailable) {\n return { ok: false, error: \"Docker non trovato nel PATH.\" };\n }\n if (!dockerCompose.ok) {\n return { ok: false, error: \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\" };\n }\n const alreadyRunning = isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions);\n if (alreadyRunning && !forceRecreate) {\n return { ok: true, checked: true, restarted: false, composeLabel: dockerCompose.label };\n }\n const upArgs = forceRecreate\n ? [\"up\", \"-d\", \"--build\", \"--force-recreate\", serviceName]\n : [\"up\", \"-d\", \"--build\", serviceName];\n const upResult = runDockerCompose(commandRunner, composeFile, upArgs, { invocation: dockerCompose, spawnOptions });\n if (!upResult || upResult.status !== 0) {\n return {\n ok: false,\n error: String(upResult?.stderr || upResult?.stdout || `${dockerCompose.label} up failed`).trim(),\n };\n }\n if (!isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions)) {\n return { ok: false, error: `Il container Docker ${serviceName} non risulta attivo dopo il restart.` };\n }\n return { ok: true, checked: true, restarted: true, composeLabel: dockerCompose.label };\n}\n\nfunction getManagedServiceStatus({ commandRunner, env, paths, targetPlatform, serviceManager }) {\n if (!isDockerManagedRuntime(env, paths, targetPlatform)) {\n return {\n kind: serviceManager.kind,\n status: serviceManager.status(),\n };\n }\n\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const serviceName = String(env.LLMPROXY_DOCKER_SERVICE || \"llmproxy\").trim() || \"llmproxy\";\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n const dockerCompose = resolveDockerComposeCommand(commandRunner, { spawnOptions });\n\n if (!dockerCompose.dockerAvailable) {\n return {\n kind: \"docker\",\n status: { ok: false, active: false, stdout: \"\", stderr: \"Docker non trovato nel PATH.\" },\n };\n }\n\n if (!dockerCompose.ok) {\n return {\n kind: \"docker\",\n status: { ok: false, active: false, stdout: \"\", stderr: \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\" },\n };\n }\n\n const active = isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions);\n return {\n kind: \"docker\",\n status: {\n ok: true,\n active,\n stdout: active ? `${serviceName} container active via ${dockerCompose.label}` : `${serviceName} container not running`,\n stderr: \"\",\n },\n };\n}\n\nasync function waitForLocalProxyHealth({ fetchFn, baseUrl, sleepFn, retries = 20, delayMs = 250 }) {\n const delay = typeof sleepFn === \"function\"\n ? sleepFn\n : (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n let lastError = null;\n\n for (let attempt = 0; attempt < retries; attempt += 1) {\n try {\n const response = await fetchFn(`${baseUrl}/health`, {\n method: \"GET\",\n headers: { \"content-type\": \"application/json\" },\n });\n if (response.ok) {\n const payload = await response.json();\n if (payload?.ok !== false) return { ok: true, payload };\n }\n lastError = new Error(`health HTTP ${response.status}`);\n } catch (error) {\n lastError = error;\n }\n await delay(delayMs);\n }\n\n return {\n ok: false,\n error: lastError ? String(lastError.message || lastError) : \"health check failed\",\n };\n}\n\nasync function runCli(argv, options = {}) {\n const stdout = options.stdout || process.stdout;\n const stderr = options.stderr || process.stderr;\n const fetchFn = options.fetchFn || fetch;\n const sleep = options.sleep;\n const parsed = parseArgs(argv || process.argv);\n const targetPlatform = options.platform || process.platform;\n const env = loadRuntimeEnv({\n env: options.env || process.env,\n packageRoot: options.packageRoot,\n dataRoot: options.dataRoot,\n homeDir: options.homeDir,\n platform: targetPlatform,\n });\n const paths = createPaths({ dataRoot: options.dataRoot, packageRoot: options.packageRoot, env, homeDir: options.homeDir, platform: targetPlatform });\n ensureRuntimeDirs(paths);\n const restExitCode = await tryRunViaRest(parsed, {\n ...options,\n env,\n dataRoot: paths.dataRoot,\n stdout,\n stderr,\n });\n if (restExitCode !== null) {\n return restExitCode;\n }\n const tokenStore = options.tokenStore || createTokenStore({ filePath: paths.tokenFile });\n const providerRegistry = options.providerRegistry || createProviderRegistry({\n filePath: paths.providerRegistryFile,\n secret: env.LLMPROXY_SECRET || null,\n });\n const proxyStore = options.proxyStore || require(\"./proxy-store\").createProxyStore({\n filePath: paths.proxyRegistryFile,\n });\n const providerStore = tokenStore;\n const modelCatalogStore = options.modelCatalogStore || createCopilotModelCatalogStore({ filePath: paths.modelCatalogFile });\n const serviceManagerOptions = resolveCliServiceManagerOptions({ env, paths, targetPlatform });\n const serviceManager = options.serviceManager || createServiceManager(serviceManagerOptions);\n\n if (parsed.command === \"help\") {\n if (parsed.args[0]) return printCommandHelp(SHORT_COMMAND_ALIASES[parsed.args[0]] || parsed.args[0], stdout);\n printHelp(stdout);\n return 0;\n }\n\n if (parsed.command === \"setup\") {\n stdout.write(`Runtime root: ${paths.dataRoot}\\n`);\n stdout.write(`Service manager: ${serviceManager.kind}\\n`);\n return 0;\n }\n\n if (parsed.command === \"version\") {\n stdout.write(`${readPackageVersion(paths.packageRoot)}\\n`);\n return 0;\n }\n\n if (parsed.command === \"release-notes\") {\n const version = String(parsed.flags.version || parsed.args[0] || readPackageVersion(paths.packageRoot)).trim();\n const locale = parsed.flags.locale\n ? normalizeLocale(parsed.flags.locale)\n : resolveOutputLocale({ env, paths });\n const commitMessage = decodeBase64Utf8(parsed.flags[\"commit-message-base64\"] || parsed.flags.commitMessageBase64);\n stdout.write(`${formatReleaseNotes(version, locale, { commitMessage, packageRoot: paths.packageRoot })}\\n`);\n return 0;\n }\n\n if (isPersistentInstallCommand(parsed.command)) {\n const installLocale = resolveInstallLocale(parsed.command);\n const isEnglishInstallCommand = installLocale === \"en\";\n if (targetPlatform !== \"darwin\" && targetPlatform !== \"linux\" && targetPlatform !== \"win32\") {\n if (isEnglishInstallCommand) {\n stderr.write(`Unsupported platform for persistent installation: ${targetPlatform}\\n`);\n } else {\n stderr.write(`Piattaforma non supportata per installazione persistente: ${targetPlatform}\\n`);\n }\n return 1;\n }\n\n const preflight = resolvePersistentInstallPrerequisites({\n commandRunner: options.commandRunner,\n env,\n packageRoot: paths.packageRoot,\n targetPlatform,\n });\n if (!preflight.ok) {\n stderr.write(formatPersistentInstallPrerequisitesFailure(preflight, {\n locale: installLocale,\n platform: targetPlatform,\n osRelease: readOsReleaseInfo({ platform: targetPlatform, content: options.osReleaseContent }),\n }));\n return 1;\n }\n\n const result = runPersistentInstall(options.commandRunner, {\n packageRoot: paths.packageRoot,\n platform: targetPlatform,\n locale: installLocale,\n });\n\n if (result?.status === 0) {\n persistServiceEnvironmentConfig(paths.serviceConfigFile, serviceManagerOptions.environment);\n const rawStdout = String(result.stdout || \"\");\n const globalBinMatch = rawStdout.match(/^__LLMPROXY_GLOBAL_BIN__=(.+)$/m);\n const binScopeMatch = rawStdout.match(/^__LLMPROXY_BIN_SCOPE__=(.+)$/m);\n const binHintMatch = rawStdout.match(/^__LLMPROXY_BIN_HINT__=(.+)$/m);\n const visibleStdout = rawStdout\n .replace(/^__LLMPROXY_GLOBAL_BIN__=.+$/m, \"\")\n .replace(/^__LLMPROXY_BIN_SCOPE__=.+$/m, \"\")\n .replace(/^__LLMPROXY_BIN_HINT__=.+$/m, \"\")\n .trim();\n\n const binScope = binScopeMatch?.[1]?.trim() || \"unknown\";\n\n if (isEnglishInstallCommand) {\n stdout.write(\"Persistent installation completed.\\n\");\n if (globalBinMatch?.[1]) stdout.write(`Global binary: ${String(globalBinMatch[1]).trim()}\\n`);\n stdout.write(`Binary scope: ${binScope === \"server\" ? \"server-wide (available to all users)\" : binScope === \"user\" ? \"user-only (current user)\" : binScope}\\n`);\n if (binScope === \"user\" && binHintMatch?.[1]) {\n stdout.write(`Hint: ${binHintMatch[1].trim()}\\n`);\n }\n stdout.write(`Persistent service enabled with ${serviceManager.kind}.\\n`);\n } else {\n stdout.write(\"Installazione persistente completata.\\n\");\n if (globalBinMatch?.[1]) stdout.write(`Binario globale: ${String(globalBinMatch[1]).trim()}\\n`);\n stdout.write(`Visibilità binario: ${binScope === \"server\" ? \"server-wide (disponibile a tutti gli utenti)\" : binScope === \"user\" ? \"solo utente corrente\" : binScope}\\n`);\n if (binScope === \"user\" && binHintMatch?.[1]) {\n stdout.write(`Suggerimento: ${binHintMatch[1].trim()}\\n`);\n }\n stdout.write(`Servizio persistente attivato con ${serviceManager.kind}.\\n`);\n }\n if (visibleStdout) stdout.write(`${visibleStdout}\\n`);\n if (targetPlatform === \"linux\") {\n if (isEnglishInstallCommand) {\n stdout.write(\"Linux note: to restart even without a user login, enable linger with `sudo loginctl enable-linger $USER`.\\n\");\n } else {\n stdout.write(\"Nota Linux: per riavvio anche senza login utente abilita linger con `sudo loginctl enable-linger $USER`.\\n\");\n }\n }\n persistInstallLocale(paths, installLocale);\n return 0;\n }\n\n if (isEnglishInstallCommand) {\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Persistent installation failed.\\n\");\n } else {\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Installazione persistente fallita.\\n\");\n }\n return 1;\n }\n\n if (parsed.command === \"models:list\") {\n const showAll = Boolean(parsed.flags.all || parsed.flags[\"all-providers\"]);\n if (showAll) {\n const models = await resolveAvailableCopilotModels({\n tokenStore,\n fetchFn,\n catalogStore: modelCatalogStore,\n fallbackModels: getAvailableModels(),\n });\n stdout.write(`${formatModelList(models)}\\n`);\n } else {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stderr.write(\"Nessun provider configurato. Usa llmproxy provider:add <provider> per aggiungerne uno.\\n\");\n return 1;\n }\n const seenModels = new Set();\n const entries = providers.map((p, idx) => {\n const providerId = String(p.id || \"\").trim();\n const modelId = String(p.default_model || \"\").trim();\n const key = modelId ? `${providerId}#${modelId}` : providerId;\n const intelligence = String(p.intelligence_score || p.coding || \"\").trim();\n const credit = String(p.credit || \"\").trim();\n const bench = String(p.bench || \"\").trim();\n return { idx: idx + 1, key, providerId, modelId, intelligence, credit, bench };\n }).filter((e) => {\n if (seenModels.has(e.key)) return false;\n seenModels.add(e.key);\n return true;\n });\n for (const e of entries) {\n const extras = [];\n if (e.credit && e.credit !== \"n/a\") extras.push(`credit=${e.credit}`);\n if (e.intelligence) extras.push(`coding=${e.intelligence}`);\n if (e.bench && !e.bench.includes(\"errore\")) extras.push(`bench=${e.bench}`);\n const extra = extras.length > 0 ? ` (${extras.join(\", \")})` : \"\";\n stdout.write(`${e.idx}. ${e.key}${extra}\\n`);\n }\n }\n return 0;\n }\n\n if (parsed.command === \"test\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stderr.write(\"Non ci sono LLM configurati! Usa llmproxy provider:add <provider> per configurarne uno.\\n\");\n stderr.write(\" Esempi: llmproxy provider:add copilot\\n\");\n stderr.write(\" llmproxy provider:add openrouter --api-key <key> --model <model> --vision true\\n\");\n stderr.write(\" Provider disponibili: llmproxy provider:available\\n\");\n return 1;\n }\n const inferenceMode = Boolean(parsed.flags.i || parsed.flags.inference);\n const allProviders = Boolean(parsed.flags[\"all-providers\"]);\n const targetProviderArg = inferenceMode ? String(parsed.args[0] || \"\").trim() : \"\";\n let targetProvider = null;\n let targetModelOverride = null;\n if (targetProviderArg) {\n const numericIndex = /^\\d+$/.test(targetProviderArg) ? parseInt(targetProviderArg, 10) : -1;\n if (numericIndex >= 1 && numericIndex <= providers.length) {\n targetProvider = providers[numericIndex - 1];\n } else {\n const byId = providers.find((p) => String(p.id || \"\").toLowerCase() === targetProviderArg.toLowerCase());\n if (byId) {\n targetProvider = byId;\n } else {\n // Se non è un id/indice, trattalo come nome di modello (es. tencent/hy3:free, opencode-bacin2, qwen)\n const byModel = providers.find(\n (p) => String(p.effective_model || p.default_model || \"\").toLowerCase() === targetProviderArg.toLowerCase(),\n );\n if (byModel) {\n targetProvider = byModel;\n targetModelOverride = targetProviderArg;\n }\n }\n }\n if (!targetProvider) {\n stderr.write(`Provider o modello non trovato: ${targetProviderArg}\\nUsa llmproxy provider:list per vedere i provider configurati.\\n`);\n return 1;\n }\n }\n const selectedTargets = inferenceMode\n ? []\n : (allProviders ? providers : providers.slice(0, 1));\n\n // Verifica che il proxy sia raggiungibile prima di iniziare i test\n const proxyBaseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n stdout.write(`Proxy endpoint: ${proxyBaseUrl}\\n`);\n\n try {\n const healthCheck = await fetchFn(`${proxyBaseUrl}/v1/llm/health`, {\n method: \"GET\",\n headers: { \"content-type\": \"application/json\" },\n });\n if (!healthCheck.ok) {\n stderr.write(`Errore: Il proxy non è raggiungibile su ${proxyBaseUrl}\\n`);\n stderr.write(` Status: ${healthCheck.status}\\n`);\n stderr.write(` Verifica che il servizio llmproxy sia in esecuzione:\\n`);\n stderr.write(` llmproxy service:start\\n`);\n stderr.write(` # oppure\\n`);\n stderr.write(` systemctl --user status llmproxy\\n`);\n return 1;\n }\n const healthData = await healthCheck.json();\n stdout.write(`Proxy stato: ${healthData.ok ? 'OK' : 'ERRORE'} (versione: ${healthData.manifest_version || 'sconosciuta'})\\n\\n`);\n } catch (error) {\n stderr.write(`Errore: Il proxy non è raggiungibile su ${proxyBaseUrl}\\n`);\n stderr.write(` ${error.message}\\n`);\n stderr.write(` Verifica che il servizio llmproxy sia in esecuzione:\\n`);\n stderr.write(` llmproxy service:start\\n`);\n stderr.write(` # oppure\\n`);\n stderr.write(` systemctl --user status llmproxy\\n`);\n stderr.write(` Se il servizio è in esecuzione, verifica la porta:\\n`);\n stderr.write(` ss -tlnp | grep node\\n`);\n return 1;\n }\n\n const commandCwd = path.resolve(String(options.cwd || process.cwd()));\n const projectSettings = resolveClaudeProjectSettings(\n commandCwd,\n { env },\n );\n const effectiveStatsKey = String(getScopeValue({\n key: \"LLMPROXY_LLM_STATS_API_KEY\",\n cwd: commandCwd,\n serviceConfigFile: paths.serviceConfigFile,\n env,\n }).value || \"\").trim();\n if (!effectiveStatsKey) {\n stderr.write(`${LLM_STATS_API_KEY_REQUIRED_MESSAGE}\\n`);\n return 1;\n }\n\n let failures = 0;\n\n if (inferenceMode) {\n const infProxyFlag = String(parsed.flags.proxy || \"\").trim();\n const infProviderFlag = String(parsed.flags.provider || \"\").trim();\n const infFetch = infProxyFlag ? makeProxyFetch(fetchFn, infProxyFlag) : fetchFn;\n const infProxyLabel = infProxyFlag ? ` via proxy ${infProxyFlag}` : \"\";\n\n // Se --provider flag specificato, sovrascrive targetProvider\n let infTargetProvider = targetProvider;\n if (infProviderFlag && !targetProvider) {\n const byId = providers.find((p) => String(p.id || \"\").toLowerCase() === infProviderFlag.toLowerCase());\n const byName = providers.find((p) => String(p.name || \"\").toLowerCase() === infProviderFlag.toLowerCase());\n infTargetProvider = byId || byName || null;\n if (!infTargetProvider) {\n stderr.write(`Provider non trovato: ${infProviderFlag}\\n`);\n return 1;\n }\n }\n const startedAtIso = new Date().toISOString();\n const startedAtMs = Date.now();\n try {\n const effectiveProviders = resolveEffectiveProviderList(providers, projectSettings.configuredModel).providers;\n const response = await runLocalProxyTestRequest(\n infFetch,\n `${proxyBaseUrl}/v1/messages`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n stream: false,\n max_tokens: 256,\n ...(targetProvider ? { provider: targetProvider.id } : {}),\n ...(targetModelOverride\n ? { model: targetModelOverride }\n : (targetProvider ? { model: targetProvider.effective_model || targetProvider.default_model } : {})),\n messages: [\n {\n role: \"user\",\n content: [{ type: \"text\", text: \"Rispondi solo: llmproxy-test-inference\" }],\n },\n ],\n }),\n },\n sleep,\n );\n\n if (!response.ok) {\n stdout.write(`inference: fail HTTP ${response.status}${infProxyLabel}\\n`);\n return 1;\n }\n\n const payload = await response.json();\n const assistantText = Array.isArray(payload?.content)\n ? payload.content\n .filter((item) => item && item.type === \"text\" && typeof item.text === \"string\")\n .map((item) => item.text)\n .join(\"\\n\")\n .trim()\n : \"\";\n const visibleAssistantText = sanitizeTestAssistantText(assistantText);\n const summary = await waitForLatestRequestSummary(paths.logsDir, {\n sinceIso: startedAtIso,\n sleepFn: sleep,\n matcher: (entry) => entry?.success === true || String(entry?.finalProvider || \"\").trim() !== \"\" || String(entry?.finalModel || \"\").trim() !== \"\",\n });\n const finalProvider = String(summary?.finalProvider || \"\").trim();\n const finalModel = String(summary?.finalModel || payload?.model || \"\").trim();\n\n if (!visibleAssistantText && !summary?.success && !finalProvider && !finalModel) {\n stdout.write(\"inference: fail risposta vuota\\n\");\n return 1;\n }\n\n const elapsedMs = Date.now() - startedAtMs;\n stdout.write(`inference: ok${finalProvider || finalModel ? ` (${[finalProvider, finalModel].filter(Boolean).join(\" | \")})` : \"\"}${infProxyLabel} [${elapsedMs}ms]\\n`);\n if (allProviders) {\n const remainingFallbackProviders = buildRemainingFallbackProviders(effectiveProviders, finalProvider, finalModel);\n const workingFallbacks = [];\n const brokenFallbacks = [];\n for (const provider of remainingFallbackProviders) {\n try {\n const probe = await probeProviderForTest({\n fetchFn,\n proxyBaseUrl,\n provider,\n sleepFn: sleep,\n logsDir: paths.logsDir,\n });\n if (probe.ok) {\n workingFallbacks.push({\n id: provider.id,\n model: normalizeInferenceDisplayModel(probe.displayModel || provider.effective_model || provider.default_model || \"\"),\n degraded: probe.degradedStatus || null,\n });\n } else {\n brokenFallbacks.push({\n id: provider.id,\n reason: probe.kind === \"http\"\n ? `HTTP ${probe.status}`\n : probe.message,\n model: probe.displayModel || provider.effective_model || provider.default_model || \"\",\n });\n }\n } catch (error) {\n brokenFallbacks.push({\n id: provider.id,\n reason: error.message || String(error || \"\"),\n model: provider.effective_model || provider.default_model || \"\",\n });\n }\n }\n workingFallbacks.forEach((provider, index) => {\n const degradedLabel = provider.degraded ? ` degraded HTTP ${provider.degraded}` : \"\";\n stdout.write(`${formatOrdinalLabel(index + 1)} fallback: ${provider.id}${degradedLabel} | ${provider.model || \"missing\"}\\n`);\n });\n brokenFallbacks.forEach((provider) => {\n const model = provider.model ? ` (${provider.model})` : \"\";\n stdout.write(`invalid fallback: ${provider.id} -> ${provider.reason}${model}\\n`);\n });\n if (brokenFallbacks.length > 0) {\n if (visibleAssistantText) stdout.write(`response: ${visibleAssistantText}\\n`);\n return 1;\n }\n }\n if (visibleAssistantText) stdout.write(`response: ${visibleAssistantText}\\n`);\n return 0;\n } catch (error) {\n const errorMessage = error.message || String(error || \"\");\n stdout.write(`inference: fail ${errorMessage}\\n`);\n return 1;\n }\n }\n\n for (const provider of selectedTargets) {\n try {\n const probe = await probeProviderForTest({\n fetchFn,\n proxyBaseUrl,\n provider,\n sleepFn: sleep,\n logsDir: paths.logsDir,\n });\n if (!probe.ok) {\n const model = probe.displayModel || probe.model || provider.default_model || \"\";\n if (probe.kind === \"missing-model\") {\n stdout.write(`${provider.id}: skipped (${probe.message})\\n`);\n } else if (probe.kind === \"http\") {\n stdout.write(`${provider.id}: fail ${probe.message} (${model})\\n`);\n } else {\n stdout.write(`${provider.id}: fail ${probe.message} (${model})\\n`);\n }\n failures += 1;\n continue;\n }\n const degradedLabel = probe.degradedStatus ? ` degraded HTTP ${probe.degradedStatus}` : \"\";\n stdout.write(`${provider.id}: ok${degradedLabel} (${probe.displayModel || probe.model || provider.default_model || \"\"})\\n`);\n } catch (error) {\n const model = provider.default_model || (String(provider.provider || \"copilot\") === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n stdout.write(`${provider.id}: fail ${error.message || String(error || \"\")} (${model})\\n`);\n failures += 1;\n }\n }\n\n return failures === 0 ? 0 : 1;\n }\n\n if (parsed.command === \"claude:setup\") {\n try {\n const models = await resolveAvailableCopilotModels({\n tokenStore,\n fetchFn,\n catalogStore: modelCatalogStore,\n fallbackModels: getAvailableModels(),\n });\n return configureClaudeSettings({ cwd: options.cwd, env, stdout, model: parsed.flags.model, availableModels: models, dataRoot: paths.dataRoot });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"pi:setup\") {\n try {\n return writePiSettings({ cwd: options.cwd, env, stdout, dataRoot: paths.dataRoot });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"vscode-chat:setup\") {\n try {\n const models = resolveModelsFromTokenStore(tokenStore);\n return writeVscodeChatSettings({\n env,\n stdout,\n dataRoot: paths.dataRoot,\n availableModels: models,\n tokenStore,\n });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"vscode-claude:setup\") {\n try {\n const models = await resolveAvailableCopilotModels({\n tokenStore,\n fetchFn,\n catalogStore: modelCatalogStore,\n fallbackModels: getAvailableModels(),\n });\n return writeVscodeClaudeSettings({\n cwd: options.cwd,\n env,\n stdout,\n model: parsed.flags.model,\n availableModels: models,\n dataRoot: paths.dataRoot,\n });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"model:set\") {\n try {\n return setClaudeModel({ cwd: options.cwd, env, stdout, model: parsed.args[0] });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:list\") {\n try {\n const scope = resolveConfigListScope(parsed, options.cwd, { ...process.env, ...env });\n const entries = listScopeValues({\n scope,\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n packageRoot: paths.packageRoot,\n dataRoot: paths.dataRoot,\n });\n stdout.write(`${formatConfigList(entries, scope)}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:migrate\") {\n try {\n const migrated = migrateManagedConfig({\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env,\n packageRoot: paths.packageRoot,\n dataRoot: paths.dataRoot,\n });\n const changedScopes = [\n migrated.project ? \"project\" : \"\",\n migrated.global ? \"global\" : \"\",\n migrated.service ? \"service\" : \"\",\n ].filter(Boolean);\n stdout.write(changedScopes.length > 0\n ? `Config migration completed: ${changedScopes.join(\", \")}\\n`\n : \"Config migration completed: no changes\\n\");\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:get\") {\n try {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) {\n throw new Error(\"Uso: llmproxy config:get <key> [--project|--service|--scope <project|global|service>]\");\n }\n const entry = getScopeValue({\n key,\n scope: resolveConfigScopeFromParsed(parsed, key),\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n packageRoot: paths.packageRoot,\n dataRoot: paths.dataRoot,\n });\n stdout.write(`${entry.scope}.${entry.key}=${entry.value == null ? \"\" : String(entry.value)}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:set\") {\n try {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key || parsed.args.length < 2) {\n throw new Error(\"Uso: llmproxy config:set <key> <value> [--project|--service|--scope <project|global|service>]\");\n }\n const scope = resolveConfigScopeFromParsed(parsed, key);\n if (scope === \"service\" && key === \"LLMPROXY_MODE\") {\n await validatePlatformModeSwitch({\n fetchFn,\n env,\n serviceConfigFile: paths.serviceConfigFile,\n nextValue: parsed.args[1],\n });\n }\n const entry = setScopeValue({\n key,\n value: parsed.args[1],\n scope,\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n });\n stdout.write(`Configurazione aggiornata: ${entry.scope}.${entry.key}=${entry.value}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:unset\") {\n try {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) {\n throw new Error(\"Uso: llmproxy config:unset <key> [--project|--service|--scope <project|global|service>]\");\n }\n const entry = unsetScopeValue({\n key,\n scope: resolveConfigScopeFromParsed(parsed, key),\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n });\n stdout.write(`Configurazione rimossa: ${entry.scope}.${entry.key}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"update\") {\n const locale = resolveOutputLocale({ env, paths });\n const currentVersion = readPackageVersion(paths.packageRoot).trim();\n try {\n const remoteVersion = await readRemotePackageVersion(fetchFn, undefined, {\n timeoutMs: options.remoteVersionTimeoutMs,\n });\n const versionComparison = remoteVersion ? compareReleaseVersions(currentVersion, remoteVersion) : null;\n if (remoteVersion && (remoteVersion === currentVersion || (versionComparison != null && versionComparison >= 0))) {\n if (locale === \"en\") {\n stdout.write(`Already up to date. Current version: ${currentVersion}\\n`);\n } else {\n stdout.write(`Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n }\n return 0;\n }\n } catch {\n // If the remote version check fails, continue with the standard update flow.\n }\n const preflight = resolveUpdatePrerequisites({\n commandRunner: options.commandRunner || spawnSync,\n env,\n paths,\n targetPlatform,\n });\n if (!preflight.ok) {\n stderr.write(formatUpdatePrerequisitesFailure(preflight, locale));\n return 1;\n }\n const result = targetPlatform === \"win32\"\n ? runSelfUpdateWindows(options.commandRunner, undefined, locale)\n : runSelfUpdate(options.commandRunner, undefined, locale);\n if (result?.status === 0) {\n const rawStdout = String(result.stdout || \"\");\n const versionMatch = rawStdout.match(/^__LLMPROXY_VERSION__=(.+)$/m);\n const releaseNotesMatch = rawStdout.match(/__LLMPROXY_RELEASE_NOTES_START__\\n([\\s\\S]*?)\\n__LLMPROXY_RELEASE_NOTES_END__/m);\n const serviceRestartWarningMatch = rawStdout.match(/^__LLMPROXY_SERVICE_RESTART_WARNING__=(.+)$/m);\n const rollbackMatch = rawStdout.match(/^__LLMPROXY_ROLLBACK__=(.+)$/m);\n const rollbackVersionMatch = rawStdout.match(/^__LLMPROXY_ROLLBACK_VERSION__=(.+)$/m);\n const rollbackReasonMatch = rawStdout.match(/^__LLMPROXY_ROLLBACK_REASON__=(.+)$/m);\n const visibleStdout = rawStdout\n .replace(/^__LLMPROXY_VERSION__=.+$/m, \"\")\n .replace(/__LLMPROXY_RELEASE_NOTES_START__\\n[\\s\\S]*?\\n__LLMPROXY_RELEASE_NOTES_END__\\n?/m, \"\")\n .replace(/^__LLMPROXY_SERVICE_RESTART_WARNING__=.+$/m, \"\")\n .replace(/^__LLMPROXY_ROLLBACK__=.+$/m, \"\")\n .replace(/^__LLMPROXY_ROLLBACK_VERSION__=.+$/m, \"\")\n .replace(/^__LLMPROXY_ROLLBACK_REASON__=.+$/m, \"\")\n .trim();\n if (rollbackMatch?.[1] && rollbackMatch[1].trim() !== \"0\") {\n if (visibleStdout) stdout.write(`${visibleStdout}\\n`);\n const restoredVersion = String(rollbackVersionMatch?.[1] || \"\").trim();\n const rollbackReason = String(rollbackReasonMatch?.[1] || \"\").trim();\n const prefix = locale === \"en\"\n ? \"Update failed after installation. The previous version has been restored.\"\n : \"Aggiornamento fallito dopo l'installazione. La versione precedente e' stata ripristinata.\";\n stderr.write(`${prefix}\\n`);\n if (rollbackReason) {\n stderr.write(`${locale === \"en\" ? \"Rollback reason\" : \"Motivo rollback\"}: ${rollbackReason}\\n`);\n }\n if (restoredVersion) {\n stderr.write(`${locale === \"en\" ? \"Restored version\" : \"Versione ripristinata\"}: ${restoredVersion}\\n`);\n }\n return 1;\n }\n stdout.write(\"Aggiornamento completato.\\n\");\n if (visibleStdout) stdout.write(`${visibleStdout}\\n`);\n if (versionMatch?.[1]) {\n const currentVersion = String(versionMatch[1]).trim();\n stdout.write(`Versione corrente: ${currentVersion}\\n`);\n const releaseNotes = releaseNotesMatch?.[1]?.trim() || formatReleaseNotes(currentVersion, locale, { packageRoot: paths.packageRoot });\n stdout.write(`${releaseNotes}\\n`);\n if (serviceRestartWarningMatch?.[1]) {\n stdout.write(`Warning riavvio servizio: ${String(serviceRestartWarningMatch[1]).trim()}\\n`);\n }\n return 0;\n }\n stderr.write(\"Aggiornamento completato, ma il rilancio di llmproxy e' fallito.\\n\");\n return 1;\n }\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Aggiornamento fallito.\\n\");\n return 1;\n }\n\n if (parsed.command === \"uninstall\") {\n const commandRunner = options.commandRunner || spawnSync;\n\n serviceManager.stop();\n\n const serviceFile = targetPlatform === \"darwin\"\n ? paths.launchAgentFile\n : targetPlatform === \"win32\"\n ? paths.serviceRunnerFile\n : paths.systemdUnitFile;\n try { fs.unlinkSync(serviceFile); } catch { /* ignore if missing */ }\n\n // On Windows, also delete the Windows service via sc.exe\n if (targetPlatform === \"win32\") {\n try {\n spawnSync(\"sc.exe\", [\"delete\", \"llmproxy\"], { encoding: \"utf8\", shell: true });\n } catch { /* ignore */ }\n }\n\n if (isDockerManagedRuntime(env, paths, targetPlatform)) {\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n runDockerCompose(commandRunner, composeFile, [\"down\", \"--remove-orphans\"], { spawnOptions });\n }\n\n try { fs.rmSync(paths.dataRoot, { recursive: true, force: true }); } catch { /* ignore */ }\n\n const result = targetPlatform === \"win32\"\n ? runSelfUninstallWindows(commandRunner)\n : runSelfUninstall(commandRunner);\n if (result?.status === 0) {\n stdout.write(\"Disinstallazione completata.\\n\");\n return 0;\n }\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Disinstallazione fallita.\\n\");\n return 1;\n }\n\n if (parsed.command === \"login\") {\n stdout.write(\"Warning: `llmproxy login` e' un alias legacy. Usa `llmproxy provider:add copilot`.\\n\");\n const deviceData = await startDeviceFlow({ fetchFn });\n stdout.write(`Apri ${deviceData.verification_uri} e inserisci il codice ${deviceData.user_code}\\n`);\n const result = await pollForToken(deviceData.device_code, deviceData.interval || 5, { fetchFn, store: tokenStore, sleep });\n if (!result.success) {\n stderr.write(`Login fallito: ${result.error}\\n`);\n return 1;\n }\n stdout.write(\"Login completato.\\n\");\n return 0;\n }\n\n if (parsed.command === \"provider:add\") {\n const rawProviderArg = String(parsed.args[0] || \"\").trim();\n const providerId = normalizeKnownProviderId(rawProviderArg);\n const apiKey = String(parsed.flags[\"api-key\"] || \"\").trim();\n const apiKeyProviderKind = resolveApiKeyProviderKind(rawProviderArg, parsed.flags.provider);\n const providerKind = apiKey ? apiKeyProviderKind : providerId;\n const instanceId = apiKey && providerId && providerId !== providerKind ? providerId : providerKind;\n const providerName = String(parsed.flags.name || getKnownProvider(providerKind || providerId).displayName || instanceId || providerKind || providerId || \"\").trim();\n const defaultModel = String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim();\n const explicitQwenPlan = normalizeQwenEndpointVariant(parsed.flags.plan);\n const endpointVariant = resolveQwenEndpointVariant(providerKind || providerId, parsed.flags.plan, apiKey);\n const visionFlag = parsed.flags.vision;\n const freeModelFlag = parsed.flags[\"free-model\"];\n const visionEnabled = visionFlag === \"true\" || visionFlag === true;\n const visionDisabled = visionFlag === \"false\" || visionFlag === false;\n // --proxy senza valore = rotazione sui proxy registrati\n // --proxy <url> = proxy specifico (comportamento esistente)\n const proxyBareFlag = parsed.flags.proxy === true;\n const proxyUrlRaw = proxyBareFlag ? \"\" : String(parsed.flags.proxy || \"\").trim();\n const proxyApiKey = String(parsed.flags[\"proxy-key\"] || \"\").trim();\n if (!providerId) {\n stderr.write(\"Provider id richiesto. Uso: llmproxy provider:add <id> --api-key <key> --model <model> --vision <true|false> [--name <name>] [--plan <plan>] [--proxy <url>] [--proxy-key <key>]\\n\");\n return 1;\n }\n\n const providerInfo = getKnownProvider(providerKind || providerId);\n if (apiKey && !apiKeyProviderKind) {\n stderr.write(`Provider API-key non supportato: ${providerId}. Usa un id canonico oppure --provider <tipo>, ad esempio: llmproxy provider:add opencode-bacin --provider opencode --api-key <key> --model <model> --vision false\\n`);\n return 1;\n }\n if (!providerInfo.auth) {\n stderr.write(`Provider non supportato: ${providerId}. Esempi: copilot, openrouter, kimi, qwen, z.ai, opencode, opencode-go\\n`);\n return 1;\n }\n\n if (providerInfo.auth === \"api_key\") {\n if (!apiKey) {\n stderr.write(`Il provider ${providerId} richiede --api-key. Uso: llmproxy provider:add ${providerId} --api-key <key> --model <model> --vision <true|false> [--proxy <url>] [--proxy-key <key>]\\n`);\n return 1;\n }\n if (providerKind === \"qwen\" && parsed.flags.plan && !explicitQwenPlan) {\n stderr.write(\"Per `qwen` usa --plan subscription oppure --plan payg.\\n\");\n return 1;\n }\n if (!defaultModel) {\n stderr.write(`Il provider ${providerKind} richiede --model per salvare e verificare il modello di default.\\n`);\n return 1;\n }\n if (visionFlag === undefined || visionFlag === null || visionFlag === \"\") {\n stderr.write(`Il provider ${providerKind} richiede --vision <true|false>. Esempio: llmproxy provider:add ${instanceId || providerKind} --api-key <key> --model ${defaultModel} --vision true\\n`);\n return 1;\n }\n if (!visionEnabled && !visionDisabled) {\n stderr.write(`--vision deve essere 'true' oppure 'false'. Valore ricevuto: '${visionFlag}'\\n`);\n return 1;\n }\n const freeModel = parseOptionalBooleanFlag(freeModelFlag, { defaultValue: false, allowBareTrue: true });\n if (freeModel === null) {\n stderr.write(`--free-model deve essere 'true' oppure 'false'. Valore ricevuto: '${freeModelFlag}'\\n`);\n return 1;\n }\n const proxyUrl = (proxyUrlRaw || proxyApiKey) ? buildProxyAgentUrl(proxyUrlRaw, proxyApiKey) : \"\";\n const probe = await probeApiKeyProviderModel({ provider: { provider: providerKind, endpoint_variant: endpointVariant }, apiKey, model: defaultModel, fetchFn, proxyUrl });\n if (!probe.ok) {\n stderr.write(`Test provider fallito per ${providerKind}/${defaultModel}: ${probe.status || \"network\"} ${probe.error || \"\"}\\n`);\n return 1;\n }\n const allProviders = providerStore.listProviders();\n const sameKindProviders = allProviders.filter((p) => String(p.provider || \"\").toLowerCase() === providerKind);\n const hasExplicitInstanceId = Boolean(providerId && providerId !== providerKind);\n const hasExplicitName = Boolean(providerName && providerName !== (getKnownProvider(providerKind || providerId)?.displayName || \"\"));\n const nameBasedId = hasExplicitName && !hasExplicitInstanceId\n ? providerName.toLowerCase().replace(/[^a-z0-9_-]/g, \"-\").replace(/^-+|-+$/g, \"\")\n : null;\n const existingWithSameModel = (hasExplicitInstanceId || nameBasedId) ? null : sameKindProviders.find((p) => p.default_model === defaultModel);\n const targetInstanceId = existingWithSameModel\n ? existingWithSameModel.id\n : (hasExplicitInstanceId ? providerId : (nameBasedId || (sameKindProviders.length > 0 ? defaultModel : providerKind)));\n\n providerStore.saveProvider(targetInstanceId, {\n access_token: apiKey,\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: providerKind,\n auth_type: \"api_key\",\n default_model: defaultModel,\n endpoint_variant: endpointVariant,\n vision: visionEnabled,\n free_model: freeModel,\n proxy_url: proxyUrl || undefined,\n proxy_api_key: proxyApiKey || undefined,\n proxy_rotation: proxyBareFlag ? true : undefined,\n }, { name: providerName || providerId });\n // Fetch and store coding score from live API (once at provider insertion)\n fetchCodingScore(defaultModel, fetchFn).then((score) => {\n if (typeof score === \"number\" && Number.isFinite(score)) {\n providerStore.saveProvider(targetInstanceId, { coding_score: score }, { name: providerName || providerId });\n }\n return null;\n }).catch(() => {});\n const planSuffix = providerKind === \"qwen\" ? `, plan: ${formatQwenPlanLabel(endpointVariant)}` : \"\";\n stdout.write(`Provider configurato con API key: ${targetInstanceId} (default model: ${defaultModel}, vision: ${visionEnabled}, free: ${freeModel}${planSuffix}).\\n`);\n return 0;\n }\n\n const deviceData = await startDeviceFlow({ fetchFn });\n stdout.write(`Apri ${deviceData.verification_uri} e inserisci il codice ${deviceData.user_code}\\n`);\n const result = await pollForToken(deviceData.device_code, deviceData.interval || 5, {\n fetchFn,\n sleep,\n store: {\n save(data) {\n return providerStore.saveProvider(providerId, {\n ...data,\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: defaultModel || DEFAULT_COPILOT_MODEL,\n }, { name: providerName || providerId });\n },\n },\n });\n if (!result.success) {\n stderr.write(`Login fallito: ${result.error}\\n`);\n return 1;\n }\n // Fetch and store coding score after successful login\n const copilotModel = defaultModel || DEFAULT_COPILOT_MODEL;\n fetchCodingScore(copilotModel, fetchFn).then((score) => {\n if (typeof score === \"number\" && Number.isFinite(score)) {\n providerStore.saveProvider(providerId, { coding_score: score }, { name: providerName || providerId });\n }\n return null;\n }).catch(() => {});\n stdout.write(`Login completato per provider ${providerId}.\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:key\") {\n const providerId = normalizeKnownProviderId(parsed.args[0]);\n const apiKey = String(parsed.flags[\"api-key\"] || \"\").trim();\n const defaultModel = String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim();\n const explicitQwenPlan = normalizeQwenEndpointVariant(parsed.flags.plan);\n const visionFlag = parsed.flags.vision;\n const freeModelFlag = parsed.flags[\"free-model\"];\n if (!providerId || !apiKey) {\n stderr.write(\"Uso: llmproxy provider:key <id> --api-key <key> [--model <model>] [--vision <true|false>] [--free-model <true|false>] [--plan <plan>]\\n\");\n return 1;\n }\n\n const providerInfo = getKnownProvider(providerId);\n if (!providerInfo.auth) {\n stderr.write(`Provider non supportato: ${providerId}.\\n`);\n return 1;\n }\n if (providerInfo.auth !== \"api_key\") {\n stderr.write(`Il provider ${providerId} non usa API key. Usa \\`llmproxy provider:add ${providerId}\\` per autenticazione OAuth.\\n`);\n return 1;\n }\n\n let existing = providerStore.getProvider(providerId);\n if (!existing && defaultModel) {\n const allProviders = providerStore.listProviders();\n existing = allProviders.find((p) => String(p.provider || \"\").toLowerCase() === providerId && p.default_model === defaultModel) || null;\n }\n const effectiveId = existing ? existing.id : providerId;\n const endpointVariant = resolveQwenEndpointVariant(providerId, parsed.flags.plan, apiKey) || existing?.endpoint_variant || \"\";\n const nextDefaultModel = defaultModel || existing?.default_model || \"\";\n if (!nextDefaultModel) {\n stderr.write(`Il provider ${providerId} non ha un modello di default salvato. Usa --model <model>.\\n`);\n return 1;\n }\n if (providerId === \"qwen\" && parsed.flags.plan && !explicitQwenPlan) {\n stderr.write(\"Per `qwen` usa --plan subscription oppure --plan payg.\\n\");\n return 1;\n }\n let nextVision = existing?.vision;\n if (visionFlag !== undefined && visionFlag !== null && visionFlag !== \"\") {\n if (visionFlag !== \"true\" && visionFlag !== true && visionFlag !== \"false\" && visionFlag !== false) {\n stderr.write(`--vision deve essere 'true' oppure 'false'. Valore ricevuto: '${visionFlag}'\\n`);\n return 1;\n }\n nextVision = visionFlag === \"true\" || visionFlag === true;\n }\n let nextFreeModel = existing?.free_model;\n if (freeModelFlag !== undefined && freeModelFlag !== null && freeModelFlag !== \"\") {\n nextFreeModel = parseOptionalBooleanFlag(freeModelFlag, { defaultValue: null, allowBareTrue: true });\n if (nextFreeModel === null) {\n stderr.write(`--free-model deve essere 'true' oppure 'false'. Valore ricevuto: '${freeModelFlag}'\\n`);\n return 1;\n }\n }\n const probe = await probeApiKeyProviderModel({ provider: { provider: providerId, endpoint_variant: endpointVariant }, apiKey, model: nextDefaultModel, fetchFn });\n if (!probe.ok) {\n stderr.write(`Test provider fallito per ${providerId}/${nextDefaultModel}: ${probe.status || \"network\"} ${probe.error || \"\"}\\n`);\n return 1;\n }\n providerStore.saveProvider(effectiveId, {\n ...(existing || {}),\n access_token: apiKey,\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: providerId,\n auth_type: \"api_key\",\n default_model: nextDefaultModel,\n endpoint_variant: endpointVariant,\n vision: nextVision,\n free_model: nextFreeModel,\n }, { name: existing?.name || providerInfo.displayName || providerId });\n // Fetch and store coding score (model scores are static — fetch once)\n fetchCodingScore(nextDefaultModel, fetchFn).then((score) => {\n if (typeof score === \"number\" && Number.isFinite(score)) {\n providerStore.saveProvider(effectiveId, { coding_score: score }, { name: existing?.name || providerInfo.displayName || providerId });\n }\n return null;\n }).catch(() => {});\n\n const planSuffix = providerId === \"qwen\" ? `, plan: ${formatQwenPlanLabel(endpointVariant)}` : \"\";\n const visionSuffix = nextVision !== undefined ? `, vision: ${nextVision}` : \"\";\n const freeModelSuffix = nextFreeModel !== undefined ? `, free: ${nextFreeModel}` : \"\";\n stdout.write(`API key aggiornata per provider ${effectiveId} (default model: ${nextDefaultModel}${visionSuffix}${freeModelSuffix}${planSuffix}).\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:list\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stdout.write(\"Nessun provider configurato.\\n\");\n return 0;\n }\n const projectSettings = resolveClaudeProjectSettings(path.resolve(String(options.cwd || process.cwd())), { env });\n const effectiveProviders = resolveEffectiveProviderList(providers, projectSettings.configuredModel);\n const creditCache = new Map();\n const priceCache = new Map();\n const codingCache = new Map();\n const reorderingCache = providerStore.__llmproxyProviderReordering\n ? providerStore.__llmproxyProviderReordering\n : (() => {\n const storePath = path.join(paths.dataRoot, DEFAULT_REORDERING_FILE);\n const store = readReorderingStore(storePath);\n return { getStore: () => store };\n })();\n const reorderingStore = reorderingCache.getStore();\n const probeFn = buildDefaultProbeFn({ proxyStore });\n // Spinner: segnala che il benchmarking dei provider è in corso\n const benchSpinner = (process.stdout.isTTY)\n ? startSpinner(\"We are benchmarking each provider/model for you: please wait\")\n : null;\n const providersWithCredit = await Promise.all(\n effectiveProviders.providers.map(async (provider) => {\n const [coding_info, credit_info, price_info, reorder_info] = await Promise.all([\n fetchProviderCodingInfo(provider, fetchFn, codingCache),\n fetchProviderCreditInfo(provider, fetchFn, creditCache),\n fetchProviderPriceInfo(provider, fetchFn, priceCache),\n Promise.resolve(reorderingStore.scores?.[provider.id] || null),\n ]);\n let benchMetric = reorder_info?.speed != null\n ? `${reorder_info.speed} ms`\n : \"n/a\";\n // Fallback: fetch speed probe live if not in reorder info\n if (benchMetric === \"n/a\") {\n const speedResult = await fetchSpeedMetric(provider, probeFn, fetchFn);\n if (typeof speedResult === \"number\") {\n benchMetric = `${speedResult} ms`;\n } else if (typeof speedResult === \"string\") {\n benchMetric = speedResult; // error message like \"errore 429\"\n } else {\n benchMetric = \"n/a\";\n }\n }\n // Add benchmark_score field for display\n const benchmarkScore = reorder_info?.power != null\n ? reorder_info.power\n : (await fetchBenchmarkMetric(provider, fetchFn));\n return {\n ...provider,\n coding_info,\n credit_info,\n price_info,\n reorder_info,\n bench_metric: benchMetric,\n benchmark_score: benchmarkScore,\n };\n }),\n );\n if (benchSpinner) stopSpinner(benchSpinner);\n if (effectiveProviders.projectOverrideActive) {\n stdout.write(`Provider effettivi per il progetto (override: ${effectiveProviders.configuredModel}):\\n`);\n }\n if (reorderingStore.criteria.length > 0) {\n const ageMinutes = reorderingStore.lastUpdatedMs ? Math.round((Date.now() - reorderingStore.lastUpdatedMs) / 60000) : null;\n stdout.write(`reorder=${reorderingStore.criteria.join(\">\")}${ageMinutes != null ? ` (ultimo: ${ageMinutes}m fa)` : \"\"}\\n`);\n }\n stdout.write(`${formatProviderList(providersWithCredit, { stdout, env })}\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:test\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stdout.write(\"Nessun provider configurato. Usa 'llmproxy provider:add' per aggiungere un provider.\\n\");\n return 1;\n }\n\n const visionMode = parsed.flags[\"vision\"] === true;\n const allProxiesMode = parsed.flags[\"all-proxies\"] === true;\n const forcedProxyUrl = String(parsed.flags.proxy || \"\").trim();\n const forcedProviderId = String(parsed.flags.provider || \"\").trim();\n const proxyInferenceMode = allProxiesMode || Boolean(forcedProxyUrl);\n let passCount = 0;\n let failCount = 0;\n let skipCount = 0;\n let proxyUnreachableCount = 0;\n let proxiesList = [];\n let testImageBase64 = \"\";\n let visionPrompt = \"\";\n let visionKeywords = [];\n if (forcedProxyUrl) {\n stdout.write(`Test di inferenza con proxy forzato: ${forcedProxyUrl}\\n\\n`);\n proxiesList = [{ url: forcedProxyUrl }];\n } else if (allProxiesMode) {\n proxiesList = proxyStore.listProxies();\n if (proxiesList.length === 0) {\n stdout.write(\"Nessun proxy registrato. Usa 'llmproxy proxy:add <url>' per aggiungere proxy.\\n\");\n return 1;\n }\n stdout.write(\"Test di inferenza provider × proxy...\\n\\n\");\n } else if (visionMode) {\n // Test visione: leggi l'immagine di test dal file assets\n const testImagePath = path.join(paths.packageRoot, \"assets\", \"test-vision.png\");\n try {\n const imageBuffer = fs.readFileSync(testImagePath);\n testImageBase64 = imageBuffer.toString('base64');\n } catch (err) {\n stdout.write(`Errore: impossibile leggere l'immagine di test da ${testImagePath}\\n`);\n return 1;\n }\n\n // Prompt che chiede esplicitamente di descrivere i colori\n visionPrompt = \"Descrivi questa immagine. Quali colori vedi e dove sono posizionati?\";\n\n // Parole chiave che indicano visione effettiva\n visionKeywords = [\"rosso\", \"red\", \"blu\", \"blue\", \"colore\", \"color\", \"immagine\", \"image\", \"alto\", \"top\", \"basso\", \"bottom\", \"sopra\", \"above\", \"sotto\", \"below\"];\n stdout.write(\"Test visione provider...\\n\\n\");\n } else {\n // Default: inferenza testuale semplice\n stdout.write(\"Test inferenza provider...\\n\\n\");\n }\n\n for (const provider of providers) {\n const model = provider.default_model;\n const vision = provider.vision;\n const providerName = provider.name || provider.id;\n\n if (!model) {\n stdout.write(`⏭️ ${providerName}: saltato (modello non configurato)\\n`);\n skipCount++;\n continue;\n }\n\n // Filtra per --provider esplicito\n if (forcedProviderId && provider.id !== forcedProviderId && (provider.name || \"\").toLowerCase() !== forcedProviderId.toLowerCase()) {\n continue;\n }\n\n const proxyUrls = proxyInferenceMode\n ? proxiesList.map((p) => p.url)\n : [\"\"];\n\n if (proxyInferenceMode) {\n stdout.write(`${providerName} (${model})\\n`);\n }\n\n for (const proxyUrl of proxyUrls) {\n const providerStartedAtIso = new Date().toISOString();\n if (proxyInferenceMode) {\n const proxyHost = proxyUrl ? new URL(proxyUrl).hostname : \"direct\";\n stdout.write(` ${proxyHost}\\n`);\n } else if (visionMode) {\n const proxyLabel = proxyUrl ? ` (via ${new URL(proxyUrl).hostname})` : \"\";\n const expectedStatus = vision === true ? \"visione ✅\" : vision === false ? \"testo ❌\" : \"visione sconosciuta ⚠️\";\n stdout.write(`🔍 ${providerName} (${model})${proxyLabel} - atteso: ${expectedStatus}\\n`);\n } else {\n // Default text inference: simple label\n const proxyLabel = proxyUrl ? ` (via ${new URL(proxyUrl).hostname})` : \"\";\n stdout.write(`🔍 ${providerName} (${model})${proxyLabel}\\n`);\n }\n\n try {\n const baseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n const testTokenPrefix = proxyInferenceMode ? \"llmproxy-proxy-test-\" : \"llmproxy-test-\";\n // Determine messages based on mode\n const buildMessages = (providerUnderTest) => visionMode\n ? [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: visionPrompt },\n {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: \"image/png\",\n data: testImageBase64,\n },\n },\n ],\n },\n ]\n : [\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Rispondi solo: ${testTokenPrefix}${providerUnderTest.id}` }],\n },\n ];\n const response = await withTemporaryProviderProxy(providerStore, provider, proxyUrl, async (providerUnderTest) => (\n runLocalProxyTestRequest(\n fetchFn,\n `${baseUrl}/v1/messages`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n provider: providerUnderTest.id,\n model,\n stream: false,\n max_tokens: 150,\n messages: buildMessages(providerUnderTest),\n }),\n },\n sleep,\n )\n ));\n\n if (!response.ok) {\n const errorText = await response.text();\n if (visionMode && vision === false) {\n stdout.write(` ✅ PASS - Visione correttamente disabilitata (errore HTTP ${response.status})\\n`);\n passCount++;\n continue;\n }\n if (proxyInferenceMode) {\n stdout.write(` FAIL HTTP ${response.status}: ${errorText.replace(/\\s+/g, \" \").slice(0, 180)}\\n`);\n } else {\n stdout.write(` ❌ Errore HTTP ${response.status}: ${errorText.slice(0, 100)}\\n`);\n }\n failCount++;\n continue;\n }\n\n const payload = await response.json();\n const assistantText = extractAssistantTextFromPayload(payload);\n const visibleAssistantText = sanitizeTestAssistantText(assistantText);\n const summary = proxyInferenceMode\n ? await waitForLatestRequestSummary(paths.logsDir, {\n sinceIso: providerStartedAtIso,\n sleepFn: sleep,\n matcher: (entry) => entry?.success === true && requestSummaryMatchesProvider(entry, provider),\n })\n : null;\n\n if (proxyInferenceMode) {\n if (!visibleAssistantText && !summary?.success) {\n stdout.write(\" FAIL risposta vuota\\n\");\n failCount++;\n continue;\n }\n const finalModel = String(summary?.finalModel || payload?.model || model || \"\").trim();\n stdout.write(` OK ${finalModel}\\n`);\n if (visibleAssistantText) {\n stdout.write(` ${visibleAssistantText.replace(/\\s+/g, \" \").slice(0, 160)}\\n`);\n } else {\n stdout.write(\" risposta testuale non visibile, ma inferenza completata correttamente\\n\");\n }\n passCount++;\n continue;\n }\n\n if (visionMode) {\n // Vision mode: check response for vision indicators\n const reasoningText = payload.reasoning_content || \"\";\n const combinedText = [assistantText, reasoningText].filter(t => t).join(\"\\n\");\n\n if (!combinedText) {\n stdout.write(` ❌ Risposta vuota\\n`);\n failCount++;\n continue;\n }\n\n const responseLower = combinedText.toLowerCase();\n const hasVisionIndicators = visionKeywords.some((keyword) => responseLower.includes(keyword));\n\n if (hasVisionIndicators) {\n if (vision === true) {\n stdout.write(` ✅ PASS - Visione confermata\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 100)}...\\n`);\n passCount++;\n } else {\n stdout.write(` ⚠️ WARN - Visione rilevata ma flag vision=false\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 100)}...\\n`);\n passCount++;\n }\n } else {\n if (vision === false) {\n stdout.write(` ✅ PASS - Visione correttamente disabilitata\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 80)}...\\n`);\n passCount++;\n } else {\n stdout.write(` ❌ FAIL - Visione attesa ma non rilevata\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 80)}...\\n`);\n failCount++;\n }\n }\n continue;\n }\n\n // Default text inference mode\n if (!visibleAssistantText) {\n stdout.write(` ❌ Risposta vuota\\n`);\n failCount++;\n continue;\n }\n const finalModel = String(payload?.model || model || \"\").trim();\n stdout.write(` ✅ PASS (${finalModel})\\n`);\n stdout.write(` ${visibleAssistantText.replace(/\\s+/g, \" \").slice(0, 160)}\\n`);\n passCount++;\n } catch (error) {\n const isProxyError = String(error.message || \"\").includes(\"fetch failed\");\n if (isProxyError && allProxiesMode) {\n stdout.write(proxyInferenceMode\n ? \" Proxy non raggiungibile\\n\"\n : \" ℹ️ Proxy non raggiungibile (ignorato in modalità probe)\\n\");\n proxyUnreachableCount++;\n } else {\n stdout.write(proxyInferenceMode\n ? ` FAIL ${error.message}\\n`\n : ` ❌ Errore: ${error.message}\\n`);\n failCount++;\n }\n }\n } // fine loop proxyUrl\n\n if (proxyInferenceMode) {\n stdout.write(\"\\n\");\n }\n }\n\n stdout.write(`Risultati: ${passCount} pass, ${failCount} fail, ${skipCount} skip`);\n if (proxyUnreachableCount > 0) {\n stdout.write(`, ${proxyUnreachableCount} proxy non raggiungibili (non contati come fallimento)`);\n }\n stdout.write(\"\\n\");\n\n return failCount === 0 ? 0 : 1;\n }\n\n if (parsed.command === \"provider:available\") {\n stdout.write(`${formatAvailableProviders()}\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:status\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stdout.write(\"Nessun provider configurato.\\n\");\n return 0;\n }\n stdout.write(`${formatProviderStatus(providers)}\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:usage\") {\n try {\n const meteringSink = createJsonlMeteringSink({ filePath: paths.meteringFile });\n const allRecords = meteringSink.query({ filters: {}, limit: 1000, offset: 0, order: \"desc\" }).records;\n\n const now = new Date();\n const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();\n const weekStart = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();\n const monthStart = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString();\n\n function filterByPeriod(records, from) {\n return records.filter((r) => r.timestamp >= from);\n }\n\n function computePeriodStats(records) {\n const byProvider = {};\n const byModel = {};\n let totalInput = 0;\n let totalOutput = 0;\n let totalRequests = 0;\n\n for (const r of records) {\n if (r.success === false) continue;\n totalRequests++;\n const input = Number(r.tokens_input || r.prompt_tokens || 0);\n const output = Number(r.tokens_output || r.completion_tokens || 0);\n totalInput += input;\n totalOutput += output;\n\n const provider = r.provider || \"unknown\";\n if (!byProvider[provider]) byProvider[provider] = { requests: 0, input: 0, output: 0 };\n byProvider[provider].requests++;\n byProvider[provider].input += input;\n byProvider[provider].output += output;\n\n const model = r.model_used || \"unknown\";\n if (!byModel[model]) byModel[model] = { requests: 0, input: 0, output: 0 };\n byModel[model].requests++;\n byModel[model].input += input;\n byModel[model].output += output;\n }\n\n return { totalRequests, totalInput, totalOutput, byProvider, byModel };\n }\n\n const todayRecords = filterByPeriod(allRecords, todayStart);\n const weekRecords = filterByPeriod(allRecords, weekStart);\n const monthRecords = filterByPeriod(allRecords, monthStart);\n\n const todayStats = computePeriodStats(todayRecords);\n const weekStats = computePeriodStats(weekRecords);\n const monthStats = computePeriodStats(monthRecords);\n\n function formatPeriodStats(label, stats) {\n const lines = [\n `\\n${label}:`,\n ` Requests: ${stats.totalRequests} | Tokens: ${stats.totalInput + stats.totalOutput} (in: ${stats.totalInput}, out: ${stats.totalOutput})`,\n ];\n\n const providers = Object.entries(stats.byProvider).sort((a, b) => b[1].requests - a[1].requests);\n if (providers.length > 0) {\n lines.push(` Per provider:`);\n for (const [provider, data] of providers) {\n lines.push(` - ${provider}: ${data.requests} req, ${data.input + data.output} tokens (in: ${data.input}, out: ${data.output})`);\n }\n }\n\n const models = Object.entries(stats.byModel).sort((a, b) => b[1].requests - a[1].requests);\n if (models.length > 0) {\n lines.push(` Per modello:`);\n for (const [model, data] of models.slice(0, 10)) {\n lines.push(` - ${model}: ${data.requests} req, ${data.input + data.output} tokens`);\n }\n if (models.length > 10) {\n lines.push(` ... e altri ${models.length - 10} modelli`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n stdout.write(\"Consumo token per periodo:\\n\");\n stdout.write(formatPeriodStats(\"Oggi (dalle 00:00)\", todayStats));\n stdout.write(formatPeriodStats(\"Ultimi 7 giorni\", weekStats));\n stdout.write(formatPeriodStats(\"Ultimi 30 giorni\", monthStats));\n stdout.write(\"\\n\");\n\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:order\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n const position = Number(parsed.args[1] || 0);\n if (!providerId || !Number.isFinite(position) || position <= 0) {\n stderr.write(\"Uso: llmproxy provider:order <id> <position>\\n\");\n return 1;\n }\n try {\n const providers = providerStore.moveProvider(providerId, position);\n stdout.write(`Nuovo ordine provider: ${providers.map((provider) => provider.id).join(\", \")}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:reorder\") {\n const reordering = createProviderReordering({\n tokenStore: providerStore,\n filePath: path.join(paths.dataRoot, DEFAULT_REORDERING_FILE),\n fetchFn,\n probeFn: buildDefaultProbeFn(),\n });\n const result = await reordering.runCycle(env);\n if (!result) {\n stdout.write(\"LLMPROXY_REORDERING non configurata (o vuota): nessun reordering automatico da eseguire.\\n\");\n return 0;\n }\n stdout.write(`Criteri: ${result.criteria.join(\">\")}\\n`);\n stdout.write(`Nuovo ordine provider: ${result.order.join(\", \")}\\n`);\n for (const providerId of result.order) {\n const score = result.scores[providerId] || {};\n const parts = Object.entries(score).map(([key, value]) => `${key}=${value == null ? \"n/a\" : value}`);\n stdout.write(` ${providerId}: ${parts.join(\" \")}\\n`);\n }\n return 0;\n }\n\n if (parsed.command === \"provider:rename\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n const nextName = parsed.args.slice(1).join(\" \").trim();\n if (!providerId || !nextName) {\n stderr.write(\"Uso: llmproxy provider:rename <id> <name>\\n\");\n return 1;\n }\n try {\n const provider = providerStore.renameProvider(providerId, nextName);\n stdout.write(`Provider rinominato: ${provider.id} -> ${provider.name}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:update\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n if (!providerId) {\n stderr.write(\"Uso: llmproxy provider:update <id> [--vision <true|false>] [--free-model <true|false>] [--name <name>]\\n\");\n return 1;\n }\n const patch = {};\n const visionRaw = parsed.flags.vision;\n if (visionRaw !== undefined && visionRaw !== null && visionRaw !== \"\") {\n if (visionRaw !== \"true\" && visionRaw !== true && visionRaw !== \"false\" && visionRaw !== false) {\n stderr.write(`--vision deve essere 'true' oppure 'false'. Valore ricevuto: '${visionRaw}'\\n`);\n return 1;\n }\n patch.vision = visionRaw === \"true\" || visionRaw === true;\n }\n const freeRaw = parsed.flags[\"free-model\"];\n if (freeRaw !== undefined && freeRaw !== null && freeRaw !== \"\") {\n const parsedFree = parseOptionalBooleanFlag(freeRaw, { defaultValue: null, allowBareTrue: true });\n if (parsedFree === null) {\n stderr.write(`--free-model deve essere 'true' oppure 'false'. Valore ricevuto: '${freeRaw}'\\n`);\n return 1;\n }\n patch.free_model = parsedFree;\n }\n const nameRaw = parsed.flags.name;\n if (nameRaw !== undefined && nameRaw !== null && nameRaw !== \"\") {\n patch.name = String(nameRaw).trim();\n }\n if (Object.keys(patch).length === 0) {\n stderr.write(\"Nessun campo da aggiornare. Specifica almeno uno tra --vision, --free-model, --name.\\n\");\n return 1;\n }\n try {\n const provider = providerStore.updateProvider(providerId, patch);\n stdout.write(`Provider aggiornato: ${provider.id}\\n`);\n if (patch.vision !== undefined) stdout.write(` vision: ${provider.vision}\\n`);\n if (patch.free_model !== undefined) stdout.write(` free: ${provider.free_model}\\n`);\n if (patch.name) stdout.write(` name: ${provider.name}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:remove\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n if (!providerId) {\n stderr.write(\"Uso: llmproxy provider:remove <id>\\n\");\n return 1;\n }\n providerStore.clearProvider(providerId);\n stdout.write(`Provider rimosso: ${providerId}\\n`);\n return 0;\n }\n\n if (parsed.command === \"proxy:add\") {\n const url = String(parsed.args[0] || \"\").trim();\n const proxyName = String(parsed.flags.name || \"\").trim();\n if (!url) {\n stderr.write(\"URL proxy richiesto. Uso: llmproxy proxy:add <url> [--name <nome>]\\n\");\n return 1;\n }\n try {\n const proxy = proxyStore.addProxy(url, proxyName || undefined);\n const nameSuffix = proxy.name ? ` (${proxy.name})` : \"\";\n stdout.write(`Proxy aggiunto: ${proxy.id}${nameSuffix} (${proxy.url})\\n`);\n return 0;\n } catch (err) {\n stderr.write(`Errore: ${err.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"proxy:list\") {\n const proxies = proxyStore.listProxies();\n if (proxies.length === 0) {\n stdout.write(\"Nessun proxy registrato.\\n\");\n return 0;\n }\n stdout.write(\"Proxy registrati:\\n\");\n proxies.forEach((p, i) => {\n const nameTag = p.name ? ` [${p.name}]` : \"\";\n stdout.write(` ${i + 1}. ${p.id}${nameTag} (${p.url})\\n`);\n });\n return 0;\n }\n\n if (parsed.command === \"proxy:remove\") {\n const id = String(parsed.args[0] || \"\").trim();\n if (!id) {\n stderr.write(\"ID proxy richiesto. Uso: llmproxy proxy:remove <id>\\n\");\n return 1;\n }\n const removed = proxyStore.removeProxy(id);\n if (!removed) {\n stderr.write(`Proxy non trovato: ${id}\\n`);\n return 1;\n }\n stdout.write(`Proxy rimosso: ${id}\\n`);\n return 0;\n }\n\n if (parsed.command === \"proxy:reorder\") {\n const proxies = proxyStore.listProxies();\n if (proxies.length < 2) {\n stdout.write(\"Servono almeno 2 proxy per il riordino.\\n\");\n return 0;\n }\n const ids = parsed.args.filter((id) => proxies.some((p) => p.id === id));\n if (ids.length === 0) {\n stdout.write(\"Uso: llmproxy proxy:reorder <id1> <id2> ...\\n\");\n stdout.write(\"ID disponibili: \" + proxies.map((p) => p.id).join(\", \") + \"\\n\");\n return 1;\n }\n proxyStore.setProxyOrder(ids);\n stdout.write(\"Ordine proxy aggiornato.\\n\");\n return 0;\n }\n\n if (parsed.command === \"proxy:test\") {\n const proxies = proxyStore.listProxies();\n if (proxies.length === 0) {\n stdout.write(\"Nessun proxy registrato. Usa 'llmproxy proxy:add <url>' per aggiungere un proxy.\\n\");\n return 1;\n }\n\n stdout.write(\"Test proxy...\\n\\n\");\n let passCount = 0;\n let failCount = 0;\n\n for (let i = 0; i < proxies.length; i++) {\n const p = proxies[i];\n stdout.write(` ${i + 1}. ${p.id}... `);\n try {\n const probeFetch = makeProxyFetch(fetchFn, p.url);\n // Prova piu' host per resilienza a blocchi/rate-limit\n const testUrls = [\n \"https://api.github.com/\",\n \"https://www.google.com/\",\n \"https://cloudflare.com/cdn-cgi/trace\",\n ];\n let ok = false;\n let lastStatus = \"\";\n for (const url of testUrls) {\n try {\n const resp = await probeFetch(url, { signal: AbortSignal.timeout(10000) });\n if (resp.ok) { ok = true; break; }\n lastStatus = `HTTP ${resp.status}`;\n } catch { lastStatus = \"timeout\"; }\n }\n if (ok) {\n stdout.write(\"✅ OK\\n\");\n passCount++;\n } else {\n stdout.write(`❌ ${lastStatus}\\n`);\n failCount++;\n }\n } catch (err) {\n stdout.write(`❌ ${err.message}\\n`);\n failCount++;\n }\n }\n\n stdout.write(`\\nRisultati: ${passCount} pass, ${failCount} fail\\n`);\n return failCount === 0 ? 0 : 1;\n }\n\n if (parsed.command === \"logout\") {\n tokenStore.clear();\n stdout.write(\"Token Copilot rimosso.\\n\");\n return 0;\n }\n\n if (parsed.command === \"stop\") {\n try {\n return stopForegroundDevInstance({\n paths,\n stdout,\n stderr,\n execCommand: options.execCommand,\n killProcess: options.killProcess,\n });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"run\") {\n const cleanupForegroundState = () => removeForegroundRunState(paths.foregroundRunPidFile);\n const { host, port } = await startServer({\n dataRoot: paths.dataRoot,\n packageRoot: paths.packageRoot,\n env,\n host: \"127.0.0.1\",\n port: 5045,\n });\n writeForegroundRunState(paths.foregroundRunPidFile, {\n pid: process.pid,\n host,\n port,\n startedAt: new Date().toISOString(),\n });\n process.once(\"exit\", cleanupForegroundState);\n process.once(\"SIGTERM\", cleanupForegroundState);\n process.once(\"SIGINT\", cleanupForegroundState);\n stdout.write(`llmProxy in ascolto su http://${host}:${port}\\n`);\n return new Promise(() => {});\n }\n\n if (parsed.command === \"status\") {\n const commandRunner = options.commandRunner || spawnSync;\n const managedStatus = getManagedServiceStatus({\n commandRunner,\n env,\n paths,\n targetPlatform,\n serviceManager,\n });\n const status = managedStatus.status;\n const providers = providerStore.listProviders();\n stdout.write(`Service manager: ${managedStatus.kind}\\n`);\n stdout.write(`Service active: ${status.active ? \"yes\" : \"no\"}\\n`);\n stdout.write(`Providers configurati: ${providers.length}\\n`);\n if (providers.length > 0) {\n stdout.write(`Active provider: ${providers[0].id}\\n`);\n stdout.write(`Fallback order: ${providers.map((provider) => provider.id).join(\", \")}\\n`);\n stdout.write(\"Providers:\\n\");\n providers.forEach((provider, index) => {\n const defaultModel = provider.default_model || (provider.provider === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n const state = provider.access_token && (provider.provider === \"copilot\" || defaultModel) ? \"configured\" : \"incomplete\";\n stdout.write(`${index + 1}. ${provider.id} (${provider.name}) provider=${provider.provider || \"copilot\"} auth=${provider.auth_type || \"oauth\"} state=${state} model=${defaultModel || \"missing\"}\\n`);\n });\n }\n if (status.stdout) stdout.write(`${status.stdout}\\n`);\n if (!status.ok && status.stderr) stderr.write(`${status.stderr}\\n`);\n if (parsed.flags.docker) {\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n stdout.write(`\\nDocker containers (${path.basename(composeFile)}):\\n`);\n const dockerCompose = resolveDockerComposeCommand(spawnSync, { spawnOptions });\n if (!dockerCompose.dockerAvailable) {\n stderr.write(\"Docker non disponibile nel PATH.\\n\");\n } else if (!dockerCompose.ok) {\n stderr.write(\"Docker Compose non disponibile (`docker compose` e `docker-compose` falliscono).\\n\");\n } else {\n const dockerResult = runDockerCompose(spawnSync, composeFile, [\"ps\", \"--format\", \"table\"], { invocation: dockerCompose, spawnOptions });\n if (dockerResult.status !== 0) {\n stderr.write(dockerResult.stderr || `Errore ${dockerCompose.label} ps.\\n`);\n } else {\n stdout.write(dockerResult.stdout || \"(nessun container trovato)\\n\");\n }\n }\n }\n return status.ok ? 0 : 1;\n }\n\n if (parsed.command === \"stats\") {\n try {\n const result = await loadMeteringStats({ env, paths, fetchFn });\n const useColor = shouldUseColorOutput(stdout, env);\n stdout.write(`${formatStatsReport(result.stats, result.source, useColor)}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"stats:reset\") {\n try {\n const truncated = [];\n // 1) Metering records\n if (fs.existsSync(paths.meteringFile)) {\n fs.writeFileSync(paths.meteringFile, \"\", \"utf8\");\n truncated.push(\"metering\");\n }\n if (truncated.length === 0) {\n stdout.write(\"Nessun file di statistiche trovato. Nessuna azione necessaria.\\n\");\n } else {\n stdout.write(`Statistiche azzerate: ${truncated.join(\", \")}.\\n`);\n }\n return 0;\n } catch (error) {\n stderr.write(`Errore: ${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"logs\") {\n if (parsed.follow) {\n return followLogs(paths, stdout, stderr);\n }\n const outLog = tailFile(paths.stdoutLogFile);\n const errLog = tailFile(paths.stderrLogFile);\n const requestLog = tailLatestRequestLog(paths.logsDir);\n if (outLog) stdout.write(`${outLog}\\n`);\n if (errLog) stderr.write(`${errLog}\\n`);\n if (requestLog) stdout.write(`${requestLog}\\n`);\n if (!outLog && !errLog && !requestLog) stdout.write(\"Nessun log disponibile.\\n\");\n return 0;\n }\n\n if (parsed.command === \"service:start\") {\n const result = serviceManager.install();\n if (!result.ok) {\n stderr.write((result.stderr && String(result.stderr).trim()) || \"Installazione servizio fallita.\\n\");\n return 1;\n }\n persistServiceEnvironmentConfig(paths.serviceConfigFile, serviceManagerOptions.environment);\n const commandRunner = options.commandRunner || spawnSync;\n const dockerRuntimeCheck = ensureDockerServiceRunning({\n commandRunner,\n env,\n paths,\n targetPlatform,\n });\n if (!dockerRuntimeCheck.ok) {\n stderr.write(`${dockerRuntimeCheck.error}\\n`);\n return 1;\n }\n const proxyBaseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n const healthResult = await waitForLocalProxyHealth({\n fetchFn,\n baseUrl: proxyBaseUrl,\n sleepFn: sleep,\n });\n if (!healthResult.ok) {\n stderr.write(`Health check fallito dopo l'avvio del servizio: ${healthResult.error}\\n`);\n return 1;\n }\n if (serviceManager.kind === \"windows\" && typeof serviceManager.status === \"function\") {\n const postInstallStatus = serviceManager.status();\n if (!postInstallStatus.ok || !postInstallStatus.active) {\n stderr.write(\"Il servizio Windows e' stato registrato ma non risulta attivo dopo l'avvio.\\n\");\n return 1;\n }\n }\n stdout.write(`Servizio installato con ${serviceManager.kind}.\\n`);\n stdout.write(`stdout: ${result.stdoutPath}\\n`);\n stdout.write(`stderr: ${result.stderrPath}\\n`);\n if (dockerRuntimeCheck.checked) {\n stdout.write(`Runtime Docker: ${dockerRuntimeCheck.restarted ? \"container ricreato\" : \"container gia' attivo\"}.\\n`);\n }\n stdout.write(`Health check OK: ${proxyBaseUrl}/health\\n`);\n return 0;\n }\n\n if (parsed.command === \"service:stop\") {\n const result = serviceManager.stop();\n if (!result.ok) {\n stderr.write(result.stderr || \"Arresto servizio fallito.\\n\");\n return 1;\n }\n stdout.write(\"Servizio arrestato.\\n\");\n return 0;\n }\n\n if (parsed.command === \"service:restart\") {\n let stopResult = { ok: true, stdout: \"\", stderr: \"\" };\n let startResult;\n const commandRunner = options.commandRunner || spawnSync;\n\n if (serviceManager.kind === \"launchd\" || serviceManager.kind === \"windows\") {\n if (typeof serviceManager.install === \"function\") {\n startResult = serviceManager.install();\n } else {\n startResult = serviceManager.start();\n }\n } else {\n stopResult = serviceManager.stop();\n startResult = serviceManager.start();\n }\n\n if (!stopResult.ok || !startResult.ok) {\n stderr.write(((startResult.stderr || stopResult.stderr) && String(startResult.stderr || stopResult.stderr).trim()) || \"Riavvio servizio fallito.\\n\");\n return 1;\n }\n persistServiceEnvironmentConfig(paths.serviceConfigFile, serviceManagerOptions.environment);\n\n const dockerRuntimeCheck = ensureDockerServiceRunning({\n commandRunner,\n env,\n paths,\n targetPlatform,\n forceRecreate: true,\n });\n if (!dockerRuntimeCheck.ok) {\n stderr.write(`${dockerRuntimeCheck.error}\\n`);\n return 1;\n }\n\n const proxyBaseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n const healthResult = await waitForLocalProxyHealth({\n fetchFn,\n baseUrl: proxyBaseUrl,\n sleepFn: sleep,\n });\n if (!healthResult.ok) {\n stderr.write(`Health check fallito dopo il restart: ${healthResult.error}\\n`);\n return 1;\n }\n if (serviceManager.kind === \"windows\" && typeof serviceManager.status === \"function\") {\n const postRestartStatus = serviceManager.status();\n if (!postRestartStatus.ok || !postRestartStatus.active) {\n stderr.write(\"Il servizio Windows e' stato riavviato ma non risulta attivo.\\n\");\n return 1;\n }\n }\n\n stdout.write(\"Servizio riavviato.\\n\");\n if (dockerRuntimeCheck.checked) {\n stdout.write(`Runtime Docker: ${dockerRuntimeCheck.restarted ? \"container ricreato\" : \"container gia' attivo\"}.\\n`);\n }\n stdout.write(`Health check OK: ${proxyBaseUrl}/health\\n`);\n return 0;\n }\n\n if (parsed.command === \"service:runtime\") {\n try {\n const targetRuntime = normalizeServiceRuntimeTarget(parsed.args[0], targetPlatform);\n if (!targetRuntime) {\n throw new Error(\"Uso: llmproxy service:runtime <docker|native|launchd|systemd>\");\n }\n\n const commandRunner = options.commandRunner || spawnSync;\n const runtimeProfile = normalizeRuntimeProfile(\n env.LLMPROXY_RUNTIME_PROFILE || env.LLMPROXY_ENV || env.NODE_ENV || \"production\",\n ) || \"production\";\n const targetEnv = {\n ...env,\n LLMPROXY_SERVICE_RUNTIME: targetRuntime,\n LLMPROXY_RUNTIME_PROFILE: runtimeProfile,\n LLMPROXY_ENV: runtimeProfile,\n NODE_ENV: runtimeProfile,\n };\n const targetOptions = resolveCliServiceManagerOptions({ env: targetEnv, paths, targetPlatform });\n const targetManager = targetRuntime === \"native\" && options.serviceManager\n ? options.serviceManager\n : createServiceManager(targetOptions);\n\n if (targetRuntime === \"docker\") {\n removeNativeServiceArtifacts({ targetPlatform, paths, env, managerOverride: serviceManager });\n removeDockerRuntime({ commandRunner, env: targetEnv, paths });\n persistServiceEnvironmentConfig(paths.serviceConfigFile, targetOptions.environment);\n const dockerRuntimeCheck = ensureDockerServiceRunning({\n commandRunner,\n env: targetEnv,\n paths,\n targetPlatform,\n });\n if (!dockerRuntimeCheck.ok) {\n stderr.write(`${dockerRuntimeCheck.error}\\n`);\n return 1;\n }\n } else {\n removeDockerRuntime({ commandRunner, env: targetEnv, paths });\n const result = typeof targetManager.install === \"function\"\n ? targetManager.install()\n : targetManager.start();\n if (!result.ok) {\n stderr.write((result.stderr && String(result.stderr).trim()) || \"Installazione servizio fallita.\\n\");\n return 1;\n }\n persistServiceEnvironmentConfig(paths.serviceConfigFile, targetOptions.environment);\n }\n\n const proxyBaseUrl = getProxyBaseUrl({ env: targetEnv, dataRoot: paths.dataRoot });\n const healthResult = await waitForLocalProxyHealth({\n fetchFn,\n baseUrl: proxyBaseUrl,\n sleepFn: sleep,\n });\n if (!healthResult.ok) {\n stderr.write(`Health check fallito dopo il cambio runtime: ${healthResult.error}\\n`);\n return 1;\n }\n\n stdout.write(`Runtime attivo: ${targetRuntime}\\n`);\n stdout.write(`Health check OK: ${proxyBaseUrl}/health\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n printHelp(stderr);\n return 1;\n}\n\nmodule.exports = {\n runCli,\n parseArgs,\n printHelp,\n configureClaudeSettings,\n setClaudeModel,\n formatAvailableProviders,\n formatBreakdownSection,\n formatModelList,\n formatProviderList,\n formatProviderStatus,\n formatStatsReport,\n loadMeteringStats,\n sanitizeTestAssistantText,\n getProxyBaseUrl,\n resolveDefaultModel,\n runSelfUpdate,\n runSelfUpdateWindows,\n runSelfUninstall,\n runSelfUninstallWindows,\n buildPersistentInstallScript,\n runPersistentInstall,\n resolveServiceEntryFile,\n resolveServiceEnvironment,\n resolveCliServiceManagerOptions,\n buildProxyAgentUrl,\n};\n"}}}}