setclaw 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/postinstall.mjs +1 -1
- package/bin/setclaw.js +213 -0
- package/bin/setup.mjs +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# setclaw
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**BOA & Quatarly setup for Claude Code & Factory**
|
|
6
6
|
|
|
7
7
|
Connect [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and [Factory AI Droid](https://app.factory.ai) to [Quatarly](https://api.quatarly.cloud) — access Claude, Gemini, and GPT models with a single API key.
|
|
8
8
|
|
package/bin/postinstall.mjs
CHANGED
|
@@ -16,7 +16,7 @@ if (apiKey) {
|
|
|
16
16
|
// Interactive install: can't prompt during npm postinstall, so guide the user
|
|
17
17
|
const w = (msg) => process.stderr.write(msg + "\n");
|
|
18
18
|
w("");
|
|
19
|
-
w("\x1b[1m setclaw\x1b[0m installed successfully!");
|
|
19
|
+
w("\x1b[1m setclaw\x1b[0m — BOA & Quatarly — installed successfully!");
|
|
20
20
|
w("");
|
|
21
21
|
w(" Run this command to configure your Quatarly API key:");
|
|
22
22
|
w("");
|
package/bin/setclaw.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* setclaw — BOA & Quatarly setup for Claude Code & Factory
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* setclaw <API_KEY>
|
|
8
|
+
* QUATARLY_API_KEY=<key> setclaw
|
|
9
|
+
* setclaw (prompts interactively)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync, writeFileSync, copyFileSync, appendFileSync, existsSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { homedir, platform } from "os";
|
|
15
|
+
import { execSync } from "child_process";
|
|
16
|
+
import { createInterface } from "readline";
|
|
17
|
+
|
|
18
|
+
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
function ask(question) {
|
|
21
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr, terminal: true });
|
|
22
|
+
return new Promise((resolve) =>
|
|
23
|
+
rl.question(question, (answer) => {
|
|
24
|
+
rl.close();
|
|
25
|
+
resolve(answer.trim());
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function log(msg) {
|
|
31
|
+
process.stderr.write(`\x1b[36m>\x1b[0m ${msg}\n`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function success(msg) {
|
|
35
|
+
process.stderr.write(`\x1b[32m✔\x1b[0m ${msg}\n`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function warn(msg) {
|
|
39
|
+
process.stderr.write(`\x1b[33m!\x1b[0m ${msg}\n`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function fail(msg) {
|
|
43
|
+
process.stderr.write(`\x1b[31m✖\x1b[0m ${msg}\n`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── Banner ──────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
process.stderr.write("\n");
|
|
49
|
+
process.stderr.write("\x1b[1m setclaw\x1b[0m — BOA & Quatarly setup for Claude Code & Factory\n");
|
|
50
|
+
process.stderr.write(" ─────────────────────────────────────────────────\n");
|
|
51
|
+
process.stderr.write("\n");
|
|
52
|
+
|
|
53
|
+
// ─── Get API key ─────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
let apiKey = process.env.QUATARLY_API_KEY || process.argv[2];
|
|
56
|
+
|
|
57
|
+
if (!apiKey) {
|
|
58
|
+
try {
|
|
59
|
+
apiKey = await ask(" Enter your Quatarly API key: ");
|
|
60
|
+
} catch (err) {
|
|
61
|
+
fail(err.message);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!apiKey) {
|
|
67
|
+
fail("API key cannot be empty.");
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
process.stderr.write("\n");
|
|
72
|
+
|
|
73
|
+
// ─── Step 1: Add models to Factory ──────────────────────────────────
|
|
74
|
+
|
|
75
|
+
const settingsPath = join(homedir(), ".factory", "settings.json");
|
|
76
|
+
|
|
77
|
+
if (existsSync(settingsPath)) {
|
|
78
|
+
log("Adding models to Factory...");
|
|
79
|
+
|
|
80
|
+
copyFileSync(settingsPath, `${settingsPath}.backup`);
|
|
81
|
+
|
|
82
|
+
let raw = readFileSync(settingsPath, "utf-8");
|
|
83
|
+
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);
|
|
84
|
+
const settings = JSON.parse(raw.trim() || "{}");
|
|
85
|
+
|
|
86
|
+
const newModels = [
|
|
87
|
+
{ model: "claude-sonnet-4-6-20250929", baseUrl: "https://api.quatarly.cloud/", provider: "anthropic" },
|
|
88
|
+
{ model: "claude-opus-4-6-thinking", baseUrl: "https://api.quatarly.cloud/", provider: "anthropic" },
|
|
89
|
+
{ model: "claude-haiku-4-5-20251001", baseUrl: "https://api.quatarly.cloud/", provider: "anthropic" },
|
|
90
|
+
{ model: "gemini-3.1-pro", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
91
|
+
{ model: "gemini-3-flash", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
92
|
+
{ model: "gpt-5.1", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
93
|
+
{ model: "gpt-5.1-codex", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
94
|
+
{ model: "gpt-5.1-codex-max", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
95
|
+
{ model: "gpt-5.2", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
96
|
+
{ model: "gpt-5.2-codex", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
97
|
+
{ model: "gpt-5.3-codex", baseUrl: "https://api.quatarly.cloud/v1", provider: "openai" },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const existing = settings.customModels || [];
|
|
101
|
+
const existingNames = new Set(existing.map((m) => m.model));
|
|
102
|
+
let maxIndex = existing.reduce((max, m) => Math.max(max, m.index ?? -1), -1);
|
|
103
|
+
let nextIndex = maxIndex + 1;
|
|
104
|
+
|
|
105
|
+
let added = 0;
|
|
106
|
+
let updated = 0;
|
|
107
|
+
|
|
108
|
+
for (const m of newModels) {
|
|
109
|
+
if (existingNames.has(m.model)) {
|
|
110
|
+
for (const e of existing) {
|
|
111
|
+
if (e.model === m.model) {
|
|
112
|
+
e.apiKey = apiKey;
|
|
113
|
+
e.baseUrl = m.baseUrl;
|
|
114
|
+
e.provider = m.provider;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
updated++;
|
|
118
|
+
} else {
|
|
119
|
+
existing.push({
|
|
120
|
+
model: m.model,
|
|
121
|
+
id: `custom:${m.model}-${nextIndex}`,
|
|
122
|
+
index: nextIndex,
|
|
123
|
+
baseUrl: m.baseUrl,
|
|
124
|
+
apiKey,
|
|
125
|
+
displayName: m.model,
|
|
126
|
+
noImageSupport: false,
|
|
127
|
+
provider: m.provider,
|
|
128
|
+
});
|
|
129
|
+
nextIndex++;
|
|
130
|
+
added++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
settings.customModels = existing;
|
|
135
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
136
|
+
|
|
137
|
+
success(`Factory: ${added} models added, ${updated} updated (${existing.length} total)`);
|
|
138
|
+
success(`Backup saved to ${settingsPath}.backup`);
|
|
139
|
+
} else {
|
|
140
|
+
warn(`Factory settings not found at ${settingsPath} — skipped.`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Step 2: Set Claude Code env vars ────────────────────────────────
|
|
144
|
+
|
|
145
|
+
log("Setting Claude Code environment variables...");
|
|
146
|
+
|
|
147
|
+
const vars = {
|
|
148
|
+
ANTHROPIC_BASE_URL: "https://api.quatarly.cloud/",
|
|
149
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
150
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "claude-haiku-4-5-20251001",
|
|
151
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "claude-sonnet-4-6-20250929",
|
|
152
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "claude-opus-4-6-thinking",
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const os = platform();
|
|
156
|
+
|
|
157
|
+
if (os === "win32") {
|
|
158
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
159
|
+
execSync(`reg add "HKCU\\Environment" /v "${key}" /t REG_SZ /d "${value}" /f`, {
|
|
160
|
+
stdio: "ignore",
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
success("Env vars written to Windows registry (HKCU\\Environment).");
|
|
164
|
+
} else {
|
|
165
|
+
const marker = "# --- Quatarly / Claude Code env ---";
|
|
166
|
+
const endMarker = "# --- end Quatarly ---";
|
|
167
|
+
const block = [
|
|
168
|
+
"",
|
|
169
|
+
marker,
|
|
170
|
+
...Object.entries(vars).map(([k, v]) => `export ${k}="${v}"`),
|
|
171
|
+
endMarker,
|
|
172
|
+
"",
|
|
173
|
+
].join("\n");
|
|
174
|
+
|
|
175
|
+
const rcFiles = [join(homedir(), ".bashrc"), join(homedir(), ".zshrc")];
|
|
176
|
+
const written = [];
|
|
177
|
+
|
|
178
|
+
for (const rc of rcFiles) {
|
|
179
|
+
if (!existsSync(rc)) continue;
|
|
180
|
+
|
|
181
|
+
const content = readFileSync(rc, "utf-8");
|
|
182
|
+
if (content.includes(marker)) {
|
|
183
|
+
const regex = new RegExp(
|
|
184
|
+
`\\n?${marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${endMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`,
|
|
185
|
+
"g"
|
|
186
|
+
);
|
|
187
|
+
writeFileSync(rc, content.replace(regex, block), "utf-8");
|
|
188
|
+
} else {
|
|
189
|
+
appendFileSync(rc, block, "utf-8");
|
|
190
|
+
}
|
|
191
|
+
written.push(rc);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (written.length === 0) {
|
|
195
|
+
const fallback = join(homedir(), ".bashrc");
|
|
196
|
+
appendFileSync(fallback, block, "utf-8");
|
|
197
|
+
written.push(fallback);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
success(`Env vars written to: ${written.join(", ")}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ─── Done ────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
process.stderr.write("\n");
|
|
206
|
+
process.stderr.write(" \x1b[1mEnvironment variables set:\x1b[0m\n");
|
|
207
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
208
|
+
const display = key === "ANTHROPIC_AUTH_TOKEN" ? value.slice(0, 8) + "..." : value;
|
|
209
|
+
process.stderr.write(` ${key} = ${display}\n`);
|
|
210
|
+
}
|
|
211
|
+
process.stderr.write("\n");
|
|
212
|
+
success("All done! Restart your terminal, then launch Claude Code.");
|
|
213
|
+
process.stderr.write("\n");
|
package/bin/setup.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* setclaw —
|
|
4
|
+
* setclaw — BOA & Quatarly setup for Claude Code & Factory
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* setclaw <API_KEY>
|
|
@@ -46,7 +46,7 @@ function fail(msg) {
|
|
|
46
46
|
// ─── Banner ──────────────────────────────────────────────────────────
|
|
47
47
|
|
|
48
48
|
process.stderr.write("\n");
|
|
49
|
-
process.stderr.write("\x1b[1m setclaw\x1b[0m — Quatarly setup for Claude Code & Factory\n");
|
|
49
|
+
process.stderr.write("\x1b[1m setclaw\x1b[0m — BOA & Quatarly setup for Claude Code & Factory\n");
|
|
50
50
|
process.stderr.write(" ─────────────────────────────────────────────────\n");
|
|
51
51
|
process.stderr.write("\n");
|
|
52
52
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "setclaw",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "BOA & Quatarly setup for Claude Code and Factory",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"setclaw": "
|
|
7
|
+
"setclaw": "bin/setclaw.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"postinstall": "node bin/postinstall.mjs"
|