rentline-sandbox 0.1.4 → 0.1.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/dist/{admin-YB2G7ZYZ.js → admin-YJ5Z6XHO.js} +1 -1
- package/dist/{auth-M7RU2YB3.js → auth-226EXWKD.js} +1 -1
- package/dist/config-7QGCVVHS.js +58 -0
- package/dist/config-YHSAFZLC.js +19 -0
- package/dist/{game-KCQW5KOZ.js → game-3X5O5Z5U.js} +1 -1
- package/dist/index.js +8 -8
- package/dist/{mortgage-7UCDSSUV.js → mortgage-7ZPW4FND.js} +1 -1
- package/dist/{server-NRWOBJJN.js → server-T7AXCBL4.js} +14 -2
- package/dist/server.js +14 -2
- package/dist/setup-4O73QXTY.js +199 -0
- package/dist/{trade-5BAZ6QFQ.js → trade-RZGTOO5G.js} +1 -1
- package/package.json +1 -1
- package/dist/setup-MMARJIKJ.js +0 -212
- /package/dist/{chunk-Q5AKRRZ2.js → chunk-JQL6X6FZ.js} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/config.ts
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync, unlinkSync } from "fs";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
var CONFIG_DIR = join(homedir(), ".rentline-sandbox");
|
|
8
|
+
var CONFIG_FILE = join(CONFIG_DIR, "credentials.json");
|
|
9
|
+
var DEFAULT_API_URL = "https://sandbox-api.rentline.xyz";
|
|
10
|
+
function loadConfig() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = readFileSync(CONFIG_FILE, "utf-8");
|
|
13
|
+
return JSON.parse(raw);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function requireConfig() {
|
|
19
|
+
const cfg = loadConfig();
|
|
20
|
+
if (!cfg) {
|
|
21
|
+
console.error(
|
|
22
|
+
"Not authenticated. Run:\n\n sandbox auth login --key <your-api-key>\n"
|
|
23
|
+
);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
return cfg;
|
|
27
|
+
}
|
|
28
|
+
function saveConfig(cfg) {
|
|
29
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
30
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
31
|
+
try {
|
|
32
|
+
chmodSync(CONFIG_FILE, 384);
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function deleteConfig() {
|
|
37
|
+
try {
|
|
38
|
+
unlinkSync(CONFIG_FILE);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function getApiUrl(override) {
|
|
43
|
+
const cfg = loadConfig();
|
|
44
|
+
return override || process.env.SANDBOX_API_URL || cfg?.api_url || DEFAULT_API_URL;
|
|
45
|
+
}
|
|
46
|
+
function getApiKey(override) {
|
|
47
|
+
const cfg = loadConfig();
|
|
48
|
+
return override || process.env.SANDBOX_API_KEY || cfg?.api_key;
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
DEFAULT_API_URL,
|
|
52
|
+
deleteConfig,
|
|
53
|
+
getApiKey,
|
|
54
|
+
getApiUrl,
|
|
55
|
+
loadConfig,
|
|
56
|
+
requireConfig,
|
|
57
|
+
saveConfig
|
|
58
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_API_URL,
|
|
4
|
+
deleteConfig,
|
|
5
|
+
getApiKey,
|
|
6
|
+
getApiUrl,
|
|
7
|
+
loadConfig,
|
|
8
|
+
requireConfig,
|
|
9
|
+
saveConfig
|
|
10
|
+
} from "./chunk-JQL6X6FZ.js";
|
|
11
|
+
export {
|
|
12
|
+
DEFAULT_API_URL,
|
|
13
|
+
deleteConfig,
|
|
14
|
+
getApiKey,
|
|
15
|
+
getApiUrl,
|
|
16
|
+
loadConfig,
|
|
17
|
+
requireConfig,
|
|
18
|
+
saveConfig
|
|
19
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -9,29 +9,29 @@ var _require = createRequire(import.meta.url);
|
|
|
9
9
|
var { version } = _require("../package.json");
|
|
10
10
|
var args = process.argv.slice(2);
|
|
11
11
|
if (args[0] === "setup" || args[0] === "--setup") {
|
|
12
|
-
const { runSetup, parseSetupArgs } = await import("./setup-
|
|
12
|
+
const { runSetup, parseSetupArgs } = await import("./setup-4O73QXTY.js");
|
|
13
13
|
const opts = parseSetupArgs(args.filter((a) => a !== "setup" && a !== "--setup"));
|
|
14
14
|
await runSetup(opts);
|
|
15
15
|
process.exit(0);
|
|
16
16
|
}
|
|
17
17
|
if (args.length === 0 || args[0] === "server" || args[0] === "--server") {
|
|
18
|
-
const { startServer } = await import("./server-
|
|
18
|
+
const { startServer } = await import("./server-T7AXCBL4.js");
|
|
19
19
|
await startServer();
|
|
20
20
|
} else {
|
|
21
21
|
const program = new Command();
|
|
22
22
|
program.name("sandbox").description("Rentline Sandbox \u2014 CLI and MCP server for the real estate simulation game").version(version).option("--url <url>", "Sandbox API base URL (overrides saved config)").option("--api-key <key>", "API key (overrides saved config)");
|
|
23
|
-
const { registerAuth } = await import("./auth-
|
|
24
|
-
const { registerGame } = await import("./game-
|
|
25
|
-
const { registerTrade } = await import("./trade-
|
|
26
|
-
const { registerMortgage } = await import("./mortgage-
|
|
27
|
-
const { registerAdmin } = await import("./admin-
|
|
23
|
+
const { registerAuth } = await import("./auth-226EXWKD.js");
|
|
24
|
+
const { registerGame } = await import("./game-3X5O5Z5U.js");
|
|
25
|
+
const { registerTrade } = await import("./trade-RZGTOO5G.js");
|
|
26
|
+
const { registerMortgage } = await import("./mortgage-7ZPW4FND.js");
|
|
27
|
+
const { registerAdmin } = await import("./admin-YJ5Z6XHO.js");
|
|
28
28
|
registerAuth(program);
|
|
29
29
|
registerGame(program);
|
|
30
30
|
registerTrade(program);
|
|
31
31
|
registerMortgage(program);
|
|
32
32
|
registerAdmin(program);
|
|
33
33
|
program.command("mcp-setup", { hidden: true }).allowUnknownOption().action(async () => {
|
|
34
|
-
const { runSetup, parseSetupArgs } = await import("./setup-
|
|
34
|
+
const { runSetup, parseSetupArgs } = await import("./setup-4O73QXTY.js");
|
|
35
35
|
const opts = parseSetupArgs(process.argv.slice(3));
|
|
36
36
|
await runSetup(opts);
|
|
37
37
|
});
|
|
@@ -544,8 +544,20 @@ function err(msg) {
|
|
|
544
544
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
545
545
|
}
|
|
546
546
|
async function startServer() {
|
|
547
|
-
|
|
548
|
-
|
|
547
|
+
let apiUrl = process.env.SANDBOX_API_URL;
|
|
548
|
+
let apiKey = process.env.SANDBOX_API_KEY;
|
|
549
|
+
if (!apiUrl || !apiKey) {
|
|
550
|
+
try {
|
|
551
|
+
const { loadConfig } = await import("./config-YHSAFZLC.js");
|
|
552
|
+
const cfg = loadConfig();
|
|
553
|
+
if (cfg) {
|
|
554
|
+
apiUrl = apiUrl ?? cfg.api_url;
|
|
555
|
+
apiKey = apiKey ?? cfg.api_key;
|
|
556
|
+
}
|
|
557
|
+
} catch {
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
apiUrl = apiUrl ?? "https://sandbox-api.rentline.xyz";
|
|
549
561
|
const client = createClient({ apiUrl, apiKey });
|
|
550
562
|
const server = new Server(
|
|
551
563
|
{ name: "rentline-sandbox", version: "0.1.0" },
|
package/dist/server.js
CHANGED
|
@@ -626,8 +626,20 @@ function err(msg) {
|
|
|
626
626
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
627
627
|
}
|
|
628
628
|
async function startServer() {
|
|
629
|
-
|
|
630
|
-
|
|
629
|
+
let apiUrl = process.env.SANDBOX_API_URL;
|
|
630
|
+
let apiKey = process.env.SANDBOX_API_KEY;
|
|
631
|
+
if (!apiUrl || !apiKey) {
|
|
632
|
+
try {
|
|
633
|
+
const { loadConfig } = await import("./config-7QGCVVHS.js");
|
|
634
|
+
const cfg = loadConfig();
|
|
635
|
+
if (cfg) {
|
|
636
|
+
apiUrl = apiUrl ?? cfg.api_url;
|
|
637
|
+
apiKey = apiKey ?? cfg.api_key;
|
|
638
|
+
}
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
apiUrl = apiUrl ?? "https://sandbox-api.rentline.xyz";
|
|
631
643
|
const client = createClient({ apiUrl, apiKey });
|
|
632
644
|
const server = new Server(
|
|
633
645
|
{ name: "rentline-sandbox", version: "0.1.0" },
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_API_URL,
|
|
4
|
+
loadConfig,
|
|
5
|
+
saveConfig
|
|
6
|
+
} from "./chunk-JQL6X6FZ.js";
|
|
7
|
+
import {
|
|
8
|
+
createClient
|
|
9
|
+
} from "./chunk-X3OZHOPM.js";
|
|
10
|
+
|
|
11
|
+
// src/setup.ts
|
|
12
|
+
import { createInterface } from "readline";
|
|
13
|
+
import { homedir, platform } from "os";
|
|
14
|
+
import { join, dirname } from "path";
|
|
15
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync } from "fs";
|
|
16
|
+
import { execSync } from "child_process";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
var SKILL_SRC = join(dirname(fileURLToPath(import.meta.url)), "..", "SKILL.md");
|
|
19
|
+
var MCP_ENTRY = {
|
|
20
|
+
command: "npx",
|
|
21
|
+
args: ["-y", "rentline-sandbox"]
|
|
22
|
+
};
|
|
23
|
+
function openCodePath(scope) {
|
|
24
|
+
if (scope === "project") return join(process.cwd(), "opencode.json");
|
|
25
|
+
const a = join(homedir(), ".config", "opencode", "opencode.json");
|
|
26
|
+
const b = join(homedir(), ".config", "opencode", "config.json");
|
|
27
|
+
return existsSync(b) && !existsSync(a) ? b : a;
|
|
28
|
+
}
|
|
29
|
+
function patchMcpJson(filePath, serverName, entry, key) {
|
|
30
|
+
let config = {};
|
|
31
|
+
if (existsSync(filePath)) {
|
|
32
|
+
try {
|
|
33
|
+
config = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
38
|
+
if (key === "mcp") {
|
|
39
|
+
if (!config["$schema"]) config["$schema"] = "https://opencode.ai/config.json";
|
|
40
|
+
const mcp = config.mcp ?? {};
|
|
41
|
+
mcp[serverName] = { type: "local", command: entry.args ? [entry.command, ...entry.args] : [entry.command], enabled: true };
|
|
42
|
+
config.mcp = mcp;
|
|
43
|
+
} else {
|
|
44
|
+
const servers = config[key] ?? {};
|
|
45
|
+
servers[serverName] = entry;
|
|
46
|
+
config[key] = servers;
|
|
47
|
+
}
|
|
48
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2));
|
|
49
|
+
}
|
|
50
|
+
function installSkill(dirs) {
|
|
51
|
+
if (!existsSync(SKILL_SRC)) return;
|
|
52
|
+
for (const dir of dirs) {
|
|
53
|
+
mkdirSync(dir, { recursive: true });
|
|
54
|
+
copyFileSync(SKILL_SRC, join(dir, "SKILL.md"));
|
|
55
|
+
console.log(`SKILL.md \u2192 ${dir}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function installForClient(client, scope) {
|
|
59
|
+
switch (client) {
|
|
60
|
+
case "claude-code": {
|
|
61
|
+
try {
|
|
62
|
+
execSync(`claude mcp add rentline-sandbox --scope ${scope} -- npx -y rentline-sandbox`, { stdio: "pipe" });
|
|
63
|
+
console.log(`Added via claude CLI (scope=${scope})`);
|
|
64
|
+
} catch {
|
|
65
|
+
const file = join(homedir(), ".claude.json");
|
|
66
|
+
patchMcpJson(file, "rentline-sandbox", MCP_ENTRY, "mcpServers");
|
|
67
|
+
console.log(`Patched ${file}`);
|
|
68
|
+
}
|
|
69
|
+
installSkill([
|
|
70
|
+
join(homedir(), ".claude", "skills", "rentline-sandbox"),
|
|
71
|
+
join(homedir(), ".agents", "skills", "rentline-sandbox")
|
|
72
|
+
]);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "claude-desktop": {
|
|
76
|
+
const file = platform() === "win32" ? join(process.env.APPDATA ?? homedir(), "Claude", "claude_desktop_config.json") : platform() === "darwin" ? join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json") : join(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
77
|
+
patchMcpJson(file, "rentline-sandbox", MCP_ENTRY, "mcpServers");
|
|
78
|
+
console.log(`Patched ${file}`);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "cursor": {
|
|
82
|
+
const file = scope === "project" ? join(process.cwd(), ".cursor", "mcp.json") : join(homedir(), ".cursor", "mcp.json");
|
|
83
|
+
patchMcpJson(file, "rentline-sandbox", MCP_ENTRY, "mcpServers");
|
|
84
|
+
console.log(`Patched ${file}`);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case "windsurf": {
|
|
88
|
+
const file = platform() === "win32" ? join(process.env.APPDATA ?? homedir(), ".codeium", "windsurf", "mcp_config.json") : join(homedir(), ".codeium", "windsurf", "mcp_config.json");
|
|
89
|
+
patchMcpJson(file, "rentline-sandbox", MCP_ENTRY, "mcpServers");
|
|
90
|
+
console.log(`Patched ${file}`);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case "opencode": {
|
|
94
|
+
const file = openCodePath(scope);
|
|
95
|
+
patchMcpJson(file, "rentline-sandbox", MCP_ENTRY, "mcp");
|
|
96
|
+
console.log(`Patched ${file}`);
|
|
97
|
+
installSkill([
|
|
98
|
+
join(homedir(), ".config", "opencode", "skills", "rentline-sandbox"),
|
|
99
|
+
join(homedir(), ".claude", "skills", "rentline-sandbox"),
|
|
100
|
+
join(homedir(), ".agents", "skills", "rentline-sandbox")
|
|
101
|
+
]);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "zed": {
|
|
105
|
+
console.log("\nAdd to your Zed settings.json:");
|
|
106
|
+
console.log(JSON.stringify({ context_servers: { "rentline-sandbox": { command: { path: "npx", args: ["-y", "rentline-sandbox"] } } } }, null, 2));
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
default: {
|
|
110
|
+
console.log("\nAdd to your MCP client config:");
|
|
111
|
+
console.log(JSON.stringify({ mcpServers: { "rentline-sandbox": MCP_ENTRY } }, null, 2));
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function parseSetupArgs(args) {
|
|
117
|
+
const opts = {};
|
|
118
|
+
for (let i = 0; i < args.length; i++) {
|
|
119
|
+
const a = args[i];
|
|
120
|
+
if ((a === "--key" || a === "-k") && args[i + 1]) opts.key = args[++i];
|
|
121
|
+
else if ((a === "--url" || a === "-u") && args[i + 1]) opts.url = args[++i];
|
|
122
|
+
else if (a === "--name" && args[i + 1]) opts.name = args[++i];
|
|
123
|
+
else if ((a === "--client" || a === "-c") && args[i + 1]) opts.client = args[++i];
|
|
124
|
+
else if (a === "--scope" && args[i + 1]) opts.scope = args[++i];
|
|
125
|
+
else if (a === "--yes" || a === "-y") opts.yes = true;
|
|
126
|
+
}
|
|
127
|
+
return opts;
|
|
128
|
+
}
|
|
129
|
+
function detectClient() {
|
|
130
|
+
const e = process.env;
|
|
131
|
+
if (e.CLAUDE_CODE || e.ANTHROPIC_CLAUDE_CODE) return "claude-code";
|
|
132
|
+
if (e.CURSOR_TRACE_ID || e.CURSOR_SESSION_ID) return "cursor";
|
|
133
|
+
if (e.WINDSURF_EXTENSION_ID || e.CODEIUM_API_KEY) return "windsurf";
|
|
134
|
+
if (e.OPENCODE_SESSION || e.OPENCODE_PROJECT) return "opencode";
|
|
135
|
+
if (e.ZED_TERM) return "zed";
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
async function runSetup(opts = {}) {
|
|
139
|
+
const rl = opts.yes ? null : createInterface({ input: process.stdin, output: process.stdout });
|
|
140
|
+
const ask = (q, fallback = "") => {
|
|
141
|
+
if (opts.yes || !rl) return Promise.resolve(fallback);
|
|
142
|
+
return new Promise((resolve) => rl.question(q, (ans) => resolve(ans.trim() || fallback)));
|
|
143
|
+
};
|
|
144
|
+
console.log("\nRentline Sandbox \u2014 MCP Setup\n");
|
|
145
|
+
const existing = loadConfig();
|
|
146
|
+
let apiKey = opts.key ?? (opts.yes ? existing?.api_key : void 0);
|
|
147
|
+
if (!apiKey) {
|
|
148
|
+
apiKey = await ask(
|
|
149
|
+
`API key${existing?.api_key ? " (Enter to keep existing)" : " (get one at sandbox.rentline.xyz/cli-auth)"}: `,
|
|
150
|
+
existing?.api_key ?? ""
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
if (!apiKey) {
|
|
154
|
+
console.error("API key required. Get one at: https://sandbox.rentline.xyz/cli-auth");
|
|
155
|
+
rl?.close();
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const apiUrl = opts.url ?? await ask(
|
|
159
|
+
`API URL [${existing?.api_url ?? DEFAULT_API_URL}]: `,
|
|
160
|
+
existing?.api_url ?? DEFAULT_API_URL
|
|
161
|
+
);
|
|
162
|
+
const displayName = opts.name ?? await ask(
|
|
163
|
+
`Display name [${existing?.display_name ?? "Player"}]: `,
|
|
164
|
+
existing?.display_name ?? "Player"
|
|
165
|
+
);
|
|
166
|
+
process.stdout.write("Verifying connectivity\u2026 ");
|
|
167
|
+
try {
|
|
168
|
+
const h = await createClient({ apiUrl, apiKey }).health();
|
|
169
|
+
console.log(`OK (${h.service})`);
|
|
170
|
+
} catch (e) {
|
|
171
|
+
console.log("FAILED");
|
|
172
|
+
console.error(`Cannot reach ${apiUrl}: ${e}`);
|
|
173
|
+
if (!opts.yes) {
|
|
174
|
+
const cont = await ask("Save anyway? [y/N]: ", "n");
|
|
175
|
+
if (cont.toLowerCase() !== "y") {
|
|
176
|
+
rl?.close();
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
saveConfig({ api_key: apiKey, api_url: apiUrl, display_name: displayName, created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
182
|
+
console.log("Credentials saved to ~/.rentline-sandbox/credentials.json\n");
|
|
183
|
+
let client = opts.client ?? detectClient();
|
|
184
|
+
if (!client) {
|
|
185
|
+
const clients = ["claude-code", "claude-desktop", "cursor", "windsurf", "opencode", "zed", "cline", "other"];
|
|
186
|
+
console.log("Which MCP client do you use?");
|
|
187
|
+
clients.forEach((c, i) => console.log(` ${i + 1}. ${c}`));
|
|
188
|
+
const choice = await ask("Enter number or name: ", "other");
|
|
189
|
+
const idx = parseInt(choice);
|
|
190
|
+
client = isNaN(idx) ? choice : clients[idx - 1] ?? "other";
|
|
191
|
+
}
|
|
192
|
+
installForClient(client, opts.scope ?? "user");
|
|
193
|
+
rl?.close();
|
|
194
|
+
console.log("\nSetup complete. Restart your AI client to load the Rentline Sandbox MCP server.\n");
|
|
195
|
+
}
|
|
196
|
+
export {
|
|
197
|
+
parseSetupArgs,
|
|
198
|
+
runSetup
|
|
199
|
+
};
|
package/package.json
CHANGED
package/dist/setup-MMARJIKJ.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_API_URL,
|
|
4
|
-
loadConfig,
|
|
5
|
-
saveConfig
|
|
6
|
-
} from "./chunk-Q5AKRRZ2.js";
|
|
7
|
-
import {
|
|
8
|
-
createClient
|
|
9
|
-
} from "./chunk-X3OZHOPM.js";
|
|
10
|
-
|
|
11
|
-
// src/setup.ts
|
|
12
|
-
import { createInterface } from "readline";
|
|
13
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from "fs";
|
|
14
|
-
import { homedir, platform } from "os";
|
|
15
|
-
import { join, dirname } from "path";
|
|
16
|
-
import { execSync } from "child_process";
|
|
17
|
-
import { fileURLToPath } from "url";
|
|
18
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
19
|
-
var __dirname = dirname(__filename);
|
|
20
|
-
function parseSetupArgs(args) {
|
|
21
|
-
const opts = {};
|
|
22
|
-
for (let i = 0; i < args.length; i++) {
|
|
23
|
-
const a = args[i];
|
|
24
|
-
if (a === "--key" || a === "-k") opts.key = args[++i];
|
|
25
|
-
else if (a === "--url" || a === "-u") opts.url = args[++i];
|
|
26
|
-
else if (a === "--name") opts.name = args[++i];
|
|
27
|
-
else if (a === "--client" || a === "-c") opts.client = args[++i];
|
|
28
|
-
else if (a === "--scope") opts.scope = args[++i];
|
|
29
|
-
else if (a === "--yes" || a === "-y") opts.yes = true;
|
|
30
|
-
}
|
|
31
|
-
return opts;
|
|
32
|
-
}
|
|
33
|
-
async function runSetup(opts) {
|
|
34
|
-
console.log("\nRentline Sandbox \u2014 MCP Setup\n");
|
|
35
|
-
const rl = opts.yes ? null : createInterface({ input: process.stdin, output: process.stdout });
|
|
36
|
-
const ask = (q, fallback = "") => {
|
|
37
|
-
if (opts.yes || !rl) return Promise.resolve(fallback);
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
rl.question(q, (ans) => resolve(ans.trim() || fallback));
|
|
40
|
-
});
|
|
41
|
-
};
|
|
42
|
-
let apiKey = opts.key;
|
|
43
|
-
if (!apiKey) {
|
|
44
|
-
const existing2 = loadConfig();
|
|
45
|
-
if (existing2?.api_key && opts.yes) {
|
|
46
|
-
apiKey = existing2.api_key;
|
|
47
|
-
} else {
|
|
48
|
-
apiKey = await ask(
|
|
49
|
-
`Enter your sandbox API key${existing2?.api_key ? " (press Enter to keep existing)" : ""}: `,
|
|
50
|
-
existing2?.api_key ?? ""
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (!apiKey) {
|
|
55
|
-
console.error("API key is required. Get one at: https://sandbox.rentline.xyz/cli-auth");
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
const existing = loadConfig();
|
|
59
|
-
const apiUrl = opts.url ?? await ask(
|
|
60
|
-
`Sandbox API URL [${existing?.api_url ?? DEFAULT_API_URL}]: `,
|
|
61
|
-
existing?.api_url ?? DEFAULT_API_URL
|
|
62
|
-
);
|
|
63
|
-
const displayName = opts.name ?? await ask(
|
|
64
|
-
`Your default display name [${existing?.display_name ?? "Player"}]: `,
|
|
65
|
-
existing?.display_name ?? "Player"
|
|
66
|
-
);
|
|
67
|
-
process.stdout.write("Verifying connectivity\u2026 ");
|
|
68
|
-
try {
|
|
69
|
-
const c = createClient({ apiUrl, apiKey });
|
|
70
|
-
const h = await c.health();
|
|
71
|
-
console.log(`OK (${h.service})`);
|
|
72
|
-
} catch (e) {
|
|
73
|
-
console.log("FAILED");
|
|
74
|
-
console.error(`Cannot reach ${apiUrl}: ${e}`);
|
|
75
|
-
if (!opts.yes) {
|
|
76
|
-
const cont = await ask("Save config anyway? [y/N]: ", "n");
|
|
77
|
-
if (cont.toLowerCase() !== "y") {
|
|
78
|
-
rl?.close();
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
saveConfig({ api_key: apiKey, api_url: apiUrl, display_name: displayName, created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
84
|
-
console.log("Credentials saved.\n");
|
|
85
|
-
let clientName = opts.client ?? detectClient();
|
|
86
|
-
if (!clientName) {
|
|
87
|
-
console.log("Which MCP client do you use?");
|
|
88
|
-
const clients = ["claude-code", "claude-desktop", "cursor", "windsurf", "opencode", "zed", "cline", "other"];
|
|
89
|
-
clients.forEach((c, i) => console.log(` ${i + 1}. ${c}`));
|
|
90
|
-
const choice = await ask("Enter number or name: ", "other");
|
|
91
|
-
const idx = parseInt(choice);
|
|
92
|
-
clientName = isNaN(idx) ? choice : clients[idx - 1] ?? "other";
|
|
93
|
-
}
|
|
94
|
-
await installForClient(clientName, opts.scope ?? "user", apiKey, apiUrl);
|
|
95
|
-
rl?.close();
|
|
96
|
-
console.log("\nSetup complete. Restart your AI client to load the Rentline Sandbox MCP server.\n");
|
|
97
|
-
}
|
|
98
|
-
function detectClient() {
|
|
99
|
-
const env = process.env;
|
|
100
|
-
if (env.CLAUDE_CODE || env.ANTHROPIC_CLAUDE_CODE) return "claude-code";
|
|
101
|
-
if (env.CURSOR_TRACE_ID || env.CURSOR_SESSION_ID) return "cursor";
|
|
102
|
-
if (env.WINDSURF_SESSION) return "windsurf";
|
|
103
|
-
if (env.OPENCODE_PROJECT || env.OPENCODE_SESSION) return "opencode";
|
|
104
|
-
return void 0;
|
|
105
|
-
}
|
|
106
|
-
var NPX_CMD = ["npx", "-y", "rentline-sandbox"];
|
|
107
|
-
async function installForClient(clientName, scope, apiKey, apiUrl) {
|
|
108
|
-
const env = { SANDBOX_API_KEY: apiKey, SANDBOX_API_URL: apiUrl };
|
|
109
|
-
switch (clientName) {
|
|
110
|
-
case "claude-code": {
|
|
111
|
-
const envFlags = Object.entries(env).map(([k, v]) => `-e ${k}="${v}"`).join(" ");
|
|
112
|
-
const cmd = `claude mcp add rentline-sandbox --scope ${scope} ${envFlags} -- ${NPX_CMD.join(" ")}`;
|
|
113
|
-
try {
|
|
114
|
-
execSync(cmd, { stdio: "pipe" });
|
|
115
|
-
console.log(`Installed via claude CLI (scope=${scope})`);
|
|
116
|
-
} catch {
|
|
117
|
-
const file = join(homedir(), ".claude.json");
|
|
118
|
-
patchMcpJson(file, "rentline-sandbox", { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }, "mcpServers");
|
|
119
|
-
console.log(`Patched ${file}`);
|
|
120
|
-
}
|
|
121
|
-
installSkill([
|
|
122
|
-
join(homedir(), ".claude", "skills", "rentline-sandbox"),
|
|
123
|
-
join(homedir(), ".agents", "skills", "rentline-sandbox")
|
|
124
|
-
]);
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
case "claude-desktop": {
|
|
128
|
-
let file;
|
|
129
|
-
if (platform() === "win32") {
|
|
130
|
-
file = join(process.env.APPDATA ?? homedir(), "Claude", "claude_desktop_config.json");
|
|
131
|
-
} else if (platform() === "darwin") {
|
|
132
|
-
file = join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
133
|
-
} else {
|
|
134
|
-
file = join(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
135
|
-
}
|
|
136
|
-
patchMcpJson(file, "rentline-sandbox", { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }, "mcpServers");
|
|
137
|
-
console.log(`Patched ${file}`);
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
case "cursor": {
|
|
141
|
-
const file = scope === "project" ? join(process.cwd(), ".cursor", "mcp.json") : join(homedir(), ".cursor", "mcp.json");
|
|
142
|
-
patchMcpJson(file, "rentline-sandbox", { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }, "mcpServers");
|
|
143
|
-
console.log(`Patched ${file}`);
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
case "windsurf": {
|
|
147
|
-
const file = platform() === "win32" ? join(process.env.APPDATA ?? homedir(), ".codeium", "windsurf", "mcp_config.json") : join(homedir(), ".codeium", "windsurf", "mcp_config.json");
|
|
148
|
-
patchMcpJson(file, "rentline-sandbox", { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }, "mcpServers");
|
|
149
|
-
console.log(`Patched ${file}`);
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
case "opencode": {
|
|
153
|
-
const file = scope === "project" ? join(process.cwd(), "opencode.json") : join(homedir(), ".config", "opencode", "opencode.json");
|
|
154
|
-
patchMcpJson(file, "rentline-sandbox", {
|
|
155
|
-
type: "local",
|
|
156
|
-
command: NPX_CMD,
|
|
157
|
-
enabled: true,
|
|
158
|
-
environment: env
|
|
159
|
-
}, "mcp");
|
|
160
|
-
console.log(`Patched ${file}`);
|
|
161
|
-
installSkill([
|
|
162
|
-
join(homedir(), ".config", "opencode", "skills", "rentline-sandbox"),
|
|
163
|
-
join(homedir(), ".claude", "skills", "rentline-sandbox"),
|
|
164
|
-
join(homedir(), ".agents", "skills", "rentline-sandbox")
|
|
165
|
-
]);
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
case "zed":
|
|
169
|
-
case "cline":
|
|
170
|
-
case "warp":
|
|
171
|
-
case "other":
|
|
172
|
-
default: {
|
|
173
|
-
console.log(`
|
|
174
|
-
Add the following to your MCP client config:
|
|
175
|
-
`);
|
|
176
|
-
console.log(JSON.stringify({
|
|
177
|
-
"rentline-sandbox": { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }
|
|
178
|
-
}, null, 2));
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
function installSkill(dirs) {
|
|
184
|
-
const skillSrc = join(__dirname, "../SKILL.md");
|
|
185
|
-
if (!existsSync(skillSrc)) return;
|
|
186
|
-
for (const dir of dirs) {
|
|
187
|
-
mkdirSync(dir, { recursive: true });
|
|
188
|
-
copyFileSync(skillSrc, join(dir, "SKILL.md"));
|
|
189
|
-
console.log(`SKILL.md \u2192 ${dir}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
function patchMcpJson(filePath, serverName, entry, key) {
|
|
193
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
194
|
-
let config = {};
|
|
195
|
-
if (existsSync(filePath)) {
|
|
196
|
-
try {
|
|
197
|
-
config = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
198
|
-
} catch {
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
if (key === "mcp" && !config["$schema"]) {
|
|
202
|
-
config["$schema"] = "https://opencode.ai/config.json";
|
|
203
|
-
}
|
|
204
|
-
const servers = config[key] ?? {};
|
|
205
|
-
servers[serverName] = entry;
|
|
206
|
-
config[key] = servers;
|
|
207
|
-
writeFileSync(filePath, JSON.stringify(config, null, 2));
|
|
208
|
-
}
|
|
209
|
-
export {
|
|
210
|
-
parseSetupArgs,
|
|
211
|
-
runSetup
|
|
212
|
-
};
|
|
File without changes
|