Created
April 16, 2026 11:32
-
-
Save jez500/e4e80c6a899264cddaf2637ae70d477d to your computer and use it in GitHub Desktop.
PaperclipAi get/set agent model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env node | |
| /** | |
| * set-agent-model.js | |
| * | |
| * Changes the model and/or adaptorType for a Paperclip agent. | |
| * | |
| * Usage: | |
| * node set-agent-model.js --agent-id <id> --model <model-name> [options] | |
| * node set-agent-model.js --agent-id <id> --adaptor-type <type> [options] | |
| * | |
| * Options: | |
| * --agent-id Agent UUID (required) | |
| * --model Model name to set, e.g. claude-sonnet-4-6 | |
| * --adaptor-type Adaptor type to set, e.g. claude-code | |
| * --company-id Company UUID (defaults to PAPERCLIP_COMPANY_ID env var) | |
| * --api-url API base URL (defaults to PAPERCLIP_API_URL or http://localhost:3100) | |
| * --api-key Bearer token (defaults to PAPERCLIP_API_KEY env var) | |
| * --list List agents and their current models and adaptor types, then exit | |
| * --dry-run Show what would be changed without applying it | |
| * | |
| * Auth: | |
| * Set PAPERCLIP_API_KEY to a valid bearer token. | |
| * To get one, run: | |
| * npx paperclipai agent local-cli <agent-id-or-shortname> --company-id <company-id> | |
| * That command prints PAPERCLIP_API_KEY — export it, then re-run this script. | |
| */ | |
| const https = require("https"); | |
| const http = require("http"); | |
| const { URL } = require("url"); | |
| // --- Arg parsing --- | |
| const args = process.argv.slice(2); | |
| const get = (flag) => { | |
| const i = args.indexOf(flag); | |
| return i !== -1 ? args[i + 1] : null; | |
| }; | |
| const has = (flag) => args.includes(flag); | |
| const agentId = get("--agent-id"); | |
| const model = get("--model"); | |
| const adaptorType = get("--adaptor-type"); | |
| const companyId = get("--company-id") || process.env.PAPERCLIP_COMPANY_ID; | |
| const apiUrl = (get("--api-url") || process.env.PAPERCLIP_API_URL || "http://localhost:3100").replace(/\/$/, ""); | |
| const apiKey = get("--api-key") || process.env.PAPERCLIP_API_KEY; | |
| const dryRun = has("--dry-run"); | |
| const listMode = has("--list"); | |
| const helpMode = has("--help") || has("-h"); | |
| function printUsage(exitCode = 0) { | |
| console.log(` | |
| Usage: | |
| node set-agent-model.js --agent-id <id> --model <model-name> [options] | |
| node set-agent-model.js --agent-id <id> --adaptor-type <type> [options] | |
| node set-agent-model.js --list --company-id <id> | |
| Options: | |
| --agent-id Agent UUID (required unless --list) | |
| --model Model name to set (required unless --adaptor-type or --list) | |
| --adaptor-type Adaptor type to set (can be combined with --model) | |
| --company-id Company UUID (defaults to PAPERCLIP_COMPANY_ID env var) | |
| --api-url API base URL (defaults to PAPERCLIP_API_URL or http://localhost:3100) | |
| --api-key Bearer token (defaults to PAPERCLIP_API_KEY env var) | |
| --list List agents and their current models and adaptor types, then exit | |
| --dry-run Show what would be changed without applying it | |
| --help, -h Show this help message | |
| Examples: | |
| # Set model for a specific agent | |
| node set-agent-model.js --agent-id abc-123 --model claude-sonnet-4-6 | |
| # Set adaptor type for a specific agent | |
| node set-agent-model.js --agent-id abc-123 --adaptor-type claude-code | |
| # Set both model and adaptor type at once | |
| node set-agent-model.js --agent-id abc-123 --model claude-opus-4-6 --adaptor-type claude-code | |
| # Preview change without applying | |
| node set-agent-model.js --agent-id abc-123 --model claude-opus-4-6 --dry-run | |
| # List all agents and their current models and adaptor types | |
| node set-agent-model.js --list --company-id xyz-456 | |
| # Use explicit API key and URL | |
| node set-agent-model.js --agent-id abc-123 --model claude-haiku-4-5-20251001 \\ | |
| --api-key <token> --api-url https://api.paperclip.ai | |
| Auth: | |
| Set PAPERCLIP_API_KEY to a valid bearer token. | |
| To get one, run: | |
| npx paperclipai agent local-cli <agent-id-or-shortname> --company-id <company-id> | |
| That command prints PAPERCLIP_API_KEY — export it, then re-run this script. | |
| `); | |
| process.exit(exitCode); | |
| } | |
| if (helpMode) printUsage(0); | |
| if (!listMode && (!agentId || (!model && !adaptorType))) { | |
| console.error("Error: --agent-id and at least one of --model or --adaptor-type are required (or use --list)\n"); | |
| printUsage(1); | |
| } | |
| if (!apiKey) { | |
| console.error("Error: PAPERCLIP_API_KEY is not set.\n"); | |
| console.error("Get one by running:"); | |
| console.error(" npx paperclipai agent local-cli <agent-id-or-shortname> --company-id <company-id>\n"); | |
| console.error("Then export PAPERCLIP_API_KEY=<value> and re-run."); | |
| process.exit(1); | |
| } | |
| // --- HTTP helpers --- | |
| function request(method, path, body) { | |
| return new Promise((resolve, reject) => { | |
| const url = new URL(apiUrl + path); | |
| const isHttps = url.protocol === "https:"; | |
| const lib = isHttps ? https : http; | |
| const payload = body ? JSON.stringify(body) : null; | |
| const options = { | |
| hostname: url.hostname, | |
| port: url.port || (isHttps ? 443 : 80), | |
| path: url.pathname + url.search, | |
| method, | |
| headers: { | |
| Authorization: `Bearer ${apiKey}`, | |
| "Content-Type": "application/json", | |
| ...(payload ? { "Content-Length": Buffer.byteLength(payload) } : {}), | |
| }, | |
| }; | |
| const req = lib.request(options, (res) => { | |
| let data = ""; | |
| res.on("data", (chunk) => (data += chunk)); | |
| res.on("end", () => { | |
| let parsed; | |
| try { | |
| parsed = JSON.parse(data); | |
| } catch { | |
| parsed = data; | |
| } | |
| if (res.statusCode >= 400) { | |
| reject(new Error(`HTTP ${res.statusCode}: ${JSON.stringify(parsed)}`)); | |
| } else { | |
| resolve(parsed); | |
| } | |
| }); | |
| }); | |
| req.on("error", reject); | |
| if (payload) req.write(payload); | |
| req.end(); | |
| }); | |
| } | |
| // --- Main --- | |
| async function main() { | |
| if (listMode) { | |
| if (!companyId) { | |
| console.error("Error: --company-id or PAPERCLIP_COMPANY_ID is required for --list"); | |
| process.exit(1); | |
| } | |
| console.log(`Fetching agents for company ${companyId}...`); | |
| const agents = await request("GET", `/api/companies/${companyId}/agents`); | |
| const list = Array.isArray(agents) ? agents : agents.agents || agents.data || []; | |
| if (!list.length) { | |
| console.log("No agents found."); | |
| return; | |
| } | |
| console.log(`\n${"ID".padEnd(38)} ${"Name".padEnd(20)} ${"Adaptor Type".padEnd(20)} Model`); | |
| console.log("-".repeat(100)); | |
| for (const a of list) { | |
| const m = a.adapterConfig?.model || "(not set)"; | |
| const t = a.adapterType || a.adapterConfig?.adapterType || "(not set)"; | |
| console.log(`${(a.id || "").padEnd(38)} ${(a.name || a.urlKey || "").padEnd(20)} ${t.padEnd(20)} ${m}`); | |
| } | |
| return; | |
| } | |
| // --- Get current agent --- | |
| console.log(`Fetching agent ${agentId}...`); | |
| const agent = await request("GET", `/api/agents/${agentId}`); | |
| const currentModel = agent.adapterConfig?.model || "(not set)"; | |
| const currentAdaptorType = agent.adapterType || agent.adapterConfig?.adapterType || "(not set)"; | |
| console.log(` Name: ${agent.name || agent.urlKey || agentId}`); | |
| console.log(` Adapter: ${agent.adapter}`); | |
| if (model !== null) { | |
| console.log(` Current model: ${currentModel}`); | |
| console.log(` New model: ${model}`); | |
| } | |
| if (adaptorType !== null) { | |
| console.log(` Current adaptorType: ${currentAdaptorType}`); | |
| console.log(` New adaptorType: ${adaptorType}`); | |
| } | |
| const modelUnchanged = model === null || currentModel === model; | |
| const adaptorTypeUnchanged = adaptorType === null || currentAdaptorType === adaptorType; | |
| if (modelUnchanged && adaptorTypeUnchanged) { | |
| console.log("\nAll values already set. Nothing to do."); | |
| return; | |
| } | |
| if (dryRun) { | |
| if (!modelUnchanged) console.log("\n[dry-run] Would PATCH adapterConfig.model — skipping."); | |
| if (!adaptorTypeUnchanged) console.log("[dry-run] Would PATCH adaptorType — skipping."); | |
| return; | |
| } | |
| // Merge changes into existing adapterConfig / top-level fields | |
| const newAdapterConfig = { ...(agent.adapterConfig || {}), ...(model !== null ? { model } : {}) }; | |
| const patchBody = { adapterConfig: newAdapterConfig, ...(adaptorType !== null ? { adapterType: adaptorType } : {}) }; | |
| console.log("\nApplying update..."); | |
| const updated = await request("PATCH", `/api/agents/${agentId}`, patchBody); | |
| const appliedModel = updated.adapterConfig?.model; | |
| const appliedAdaptorType = updated.adapterType || updated.adapterConfig?.adapterType; | |
| const modelOk = model === null || appliedModel === model; | |
| const adaptorTypeOk = adaptorType === null || appliedAdaptorType === adaptorType; | |
| if (modelOk && adaptorTypeOk) { | |
| if (model !== null) console.log(`\nDone. Model updated to: ${appliedModel}`); | |
| if (adaptorType !== null) console.log(`Done. adaptorType updated to: ${appliedAdaptorType}`); | |
| } else { | |
| // Fallback: try the dashboard endpoint the user discovered | |
| console.log("PATCH did not apply all changes. Trying dashboard endpoint..."); | |
| if (!companyId) { | |
| console.error("Error: --company-id or PAPERCLIP_COMPANY_ID is required for dashboard fallback"); | |
| process.exit(1); | |
| } | |
| await request("POST", `/api/companies/${companyId}/dashboard`, patchBody); | |
| if (model !== null) console.log(`\nDone via dashboard endpoint. Model set to: ${model}`); | |
| if (adaptorType !== null) console.log(`Done via dashboard endpoint. adaptorType set to: ${adaptorType}`); | |
| } | |
| } | |
| main().catch((err) => { | |
| console.error("\nFailed:", err.message); | |
| process.exit(1); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment