setclaw 1.0.4 → 1.0.6
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 +16 -2
- package/bin/postinstall.mjs +15 -26
- package/bin/setclaw.js +33 -22
- package/package.json +4 -2
- package/bin/setup.mjs +0 -213
package/README.md
CHANGED
|
@@ -39,15 +39,29 @@ Connect [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and [Facto
|
|
|
39
39
|
|
|
40
40
|
### Install & Configure
|
|
41
41
|
|
|
42
|
+
Pass your API key during install — everything configures automatically in one command:
|
|
43
|
+
|
|
44
|
+
**Windows (PowerShell):**
|
|
45
|
+
```powershell
|
|
46
|
+
$env:QUATARLY_API_KEY="qua_your-key-here"; npm i -g setclaw --foreground-scripts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**macOS / Linux:**
|
|
50
|
+
```bash
|
|
51
|
+
QUATARLY_API_KEY=qua_your-key-here npm i -g setclaw --foreground-scripts
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or install first, then run the `setclaw` command separately:
|
|
55
|
+
|
|
42
56
|
```bash
|
|
43
57
|
npm i -g setclaw
|
|
44
|
-
setclaw
|
|
58
|
+
setclaw qua_your-key-here
|
|
45
59
|
```
|
|
46
60
|
|
|
47
61
|
That's it. When you run `setclaw`, it will:
|
|
48
62
|
|
|
49
63
|
```
|
|
50
|
-
setclaw — Quatarly setup for Claude Code & Factory
|
|
64
|
+
setclaw — BOA & Quatarly setup for Claude Code & Factory
|
|
51
65
|
─────────────────────────────────────────────────
|
|
52
66
|
|
|
53
67
|
> Adding models to Factory...
|
package/bin/postinstall.mjs
CHANGED
|
@@ -1,33 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* postinstall — If QUATARLY_API_KEY env var is set, run setup automatically.
|
|
5
|
-
* Otherwise, print instructions telling the user to run `setclaw`.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
3
|
import { platform } from "os";
|
|
4
|
+
import { writeFileSync, join, homedir } from "fs";
|
|
9
5
|
|
|
10
|
-
|
|
6
|
+
// Always write a "pending setup" marker so setclaw can detect first run
|
|
7
|
+
import { join as pathJoin } from "path";
|
|
8
|
+
import { homedir as getHome } from "os";
|
|
9
|
+
import { writeFileSync as wf, mkdirSync } from "fs";
|
|
10
|
+
|
|
11
|
+
const markerDir = pathJoin(getHome(), ".setclaw");
|
|
12
|
+
const markerFile = pathJoin(markerDir, "pending");
|
|
11
13
|
|
|
14
|
+
try {
|
|
15
|
+
mkdirSync(markerDir, { recursive: true });
|
|
16
|
+
wf(markerFile, "1", "utf-8");
|
|
17
|
+
} catch {}
|
|
18
|
+
|
|
19
|
+
const apiKey = process.env.QUATARLY_API_KEY;
|
|
12
20
|
if (apiKey) {
|
|
13
|
-
|
|
14
|
-
await import("./setup.mjs");
|
|
15
|
-
} else {
|
|
16
|
-
// Interactive install: can't prompt during npm postinstall, so guide the user
|
|
17
|
-
const w = (msg) => process.stderr.write(msg + "\n");
|
|
18
|
-
w("");
|
|
19
|
-
w("\x1b[1m setclaw\x1b[0m — BOA & Quatarly — installed successfully!");
|
|
20
|
-
w("");
|
|
21
|
-
w(" Run this command to configure your Quatarly API key:");
|
|
22
|
-
w("");
|
|
23
|
-
w(" \x1b[36msetclaw \x1b[33m<your-api-key>\x1b[0m");
|
|
24
|
-
w("");
|
|
25
|
-
w(" Or with an env var:");
|
|
26
|
-
w("");
|
|
27
|
-
if (platform() === "win32") {
|
|
28
|
-
w(' \x1b[36m$env:QUATARLY_API_KEY="your-key"; setclaw\x1b[0m');
|
|
29
|
-
} else {
|
|
30
|
-
w(" \x1b[36mQUATARLY_API_KEY=your-key setclaw\x1b[0m");
|
|
31
|
-
}
|
|
32
|
-
w("");
|
|
21
|
+
await import("./setclaw.js");
|
|
33
22
|
}
|
package/bin/setclaw.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* setclaw (prompts interactively)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { readFileSync, writeFileSync, copyFileSync, appendFileSync, existsSync } from "fs";
|
|
12
|
+
import { readFileSync, writeFileSync, copyFileSync, appendFileSync, existsSync, rmSync, mkdirSync } from "fs";
|
|
13
13
|
import { join } from "path";
|
|
14
14
|
import { homedir, platform } from "os";
|
|
15
15
|
import { execSync } from "child_process";
|
|
@@ -27,33 +27,41 @@ function ask(question) {
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
30
|
+
const out = (msg) => process.stdout.write(msg + "\n");
|
|
31
|
+
const err = (msg) => process.stderr.write(msg + "\n");
|
|
33
32
|
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
}
|
|
33
|
+
function log(msg) { err(`\x1b[36m>\x1b[0m ${msg}`); }
|
|
34
|
+
function success(msg) { err(`\x1b[32m✔\x1b[0m ${msg}`); }
|
|
35
|
+
function warn(msg) { err(`\x1b[33m!\x1b[0m ${msg}`); }
|
|
36
|
+
function fail(msg) { err(`\x1b[31m✖\x1b[0m ${msg}`); }
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
process.stderr.write(`\x1b[33m!\x1b[0m ${msg}\n`);
|
|
40
|
-
}
|
|
38
|
+
// ─── First-run detection ──────────────────────────────────────────────
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
40
|
+
const markerFile = join(homedir(), ".setclaw", "pending");
|
|
41
|
+
const isFirstRun = existsSync(markerFile);
|
|
45
42
|
|
|
46
43
|
// ─── Banner ──────────────────────────────────────────────────────────
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
out("");
|
|
46
|
+
out("\x1b[1m setclaw\x1b[0m — BOA & Quatarly setup for Claude Code & Factory");
|
|
47
|
+
out(" \x1b[2m─────────────────────────────────────────────────\x1b[0m");
|
|
48
|
+
out("");
|
|
52
49
|
|
|
53
50
|
// ─── Get API key ─────────────────────────────────────────────────────
|
|
54
51
|
|
|
55
52
|
let apiKey = process.env.QUATARLY_API_KEY || process.argv[2];
|
|
56
53
|
|
|
54
|
+
// If no key and this is first run (just installed), show clear instructions
|
|
55
|
+
if (!apiKey && isFirstRun) {
|
|
56
|
+
out(" \x1b[33m⚡ Setup required!\x1b[0m Run with your Quatarly API key:");
|
|
57
|
+
out("");
|
|
58
|
+
out(" \x1b[36msetclaw\x1b[0m \x1b[33m<your-api-key>\x1b[0m");
|
|
59
|
+
out("");
|
|
60
|
+
out(" Get your key at \x1b[4mhttps://api.quatarly.cloud\x1b[0m");
|
|
61
|
+
out("");
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
if (!apiKey) {
|
|
58
66
|
try {
|
|
59
67
|
apiKey = await ask(" Enter your Quatarly API key: ");
|
|
@@ -202,12 +210,15 @@ if (os === "win32") {
|
|
|
202
210
|
|
|
203
211
|
// ─── Done ────────────────────────────────────────────────────────────
|
|
204
212
|
|
|
205
|
-
|
|
206
|
-
|
|
213
|
+
// Clear the first-run marker
|
|
214
|
+
try { rmSync(markerFile); } catch {}
|
|
215
|
+
|
|
216
|
+
out("");
|
|
217
|
+
out(" \x1b[1mEnvironment variables set:\x1b[0m");
|
|
207
218
|
for (const [key, value] of Object.entries(vars)) {
|
|
208
219
|
const display = key === "ANTHROPIC_AUTH_TOKEN" ? value.slice(0, 8) + "..." : value;
|
|
209
|
-
|
|
220
|
+
out(` ${key} = ${display}`);
|
|
210
221
|
}
|
|
211
|
-
|
|
222
|
+
out("");
|
|
212
223
|
success("All done! Restart your terminal, then launch Claude Code.");
|
|
213
|
-
|
|
224
|
+
out("");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "setclaw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "BOA & Quatarly setup for Claude Code and Factory",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
"postinstall": "node bin/postinstall.mjs"
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
|
-
"bin"
|
|
13
|
+
"bin/setclaw.js",
|
|
14
|
+
"bin/postinstall.mjs"
|
|
14
15
|
],
|
|
15
16
|
"keywords": [
|
|
16
17
|
"claude",
|
|
17
18
|
"claude-code",
|
|
18
19
|
"quatarly",
|
|
20
|
+
"boa",
|
|
19
21
|
"factory",
|
|
20
22
|
"ai",
|
|
21
23
|
"llm",
|
package/bin/setup.mjs
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
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");
|