rentline-sandbox 0.1.2 → 0.1.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 CHANGED
@@ -20,6 +20,32 @@ Requires Node.js ≥ 18.
20
20
 
21
21
  ---
22
22
 
23
+ ## Setup
24
+
25
+ Run once after install. Saves your credentials and automatically configures your AI client:
26
+
27
+ ```bash
28
+ sandbox setup
29
+ ```
30
+
31
+ The wizard will:
32
+ 1. Ask for your API key (get one at **sandbox.rentline.xyz/cli-auth**)
33
+ 2. Verify connectivity to the API
34
+ 3. Save credentials to `~/.rentline-sandbox/credentials.json`
35
+ 4. Detect your AI client (Claude Code, Cursor, Windsurf, OpenCode) and patch its MCP config
36
+ 5. Install `SKILL.md` so your agent understands the game
37
+
38
+ **Non-interactive:**
39
+ ```bash
40
+ sandbox setup --key sb_xxx --client opencode --yes
41
+ ```
42
+
43
+ Supported clients: `claude-code`, `claude-desktop`, `cursor`, `windsurf`, `opencode`, `zed`, `cline`
44
+
45
+ After setup, **restart your AI client** to load the MCP server.
46
+
47
+ ---
48
+
23
49
  ## Authentication
24
50
 
25
51
  ### Browser login (recommended)
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ 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-JPLRGUPW.js");
12
+ const { runSetup, parseSetupArgs } = await import("./setup-MMARJIKJ.js");
13
13
  const opts = parseSetupArgs(args.filter((a) => a !== "setup" && a !== "--setup"));
14
14
  await runSetup(opts);
15
15
  process.exit(0);
@@ -31,7 +31,7 @@ if (args.length === 0 || args[0] === "server" || args[0] === "--server") {
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-JPLRGUPW.js");
34
+ const { runSetup, parseSetupArgs } = await import("./setup-MMARJIKJ.js");
35
35
  const opts = parseSetupArgs(process.argv.slice(3));
36
36
  await runSetup(opts);
37
37
  });
@@ -13,6 +13,7 @@ import { createInterface } from "readline";
13
13
  import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from "fs";
14
14
  import { homedir, platform } from "os";
15
15
  import { join, dirname } from "path";
16
+ import { execSync } from "child_process";
16
17
  import { fileURLToPath } from "url";
17
18
  var __filename = fileURLToPath(import.meta.url);
18
19
  var __dirname = dirname(__filename);
@@ -51,7 +52,7 @@ async function runSetup(opts) {
51
52
  }
52
53
  }
53
54
  if (!apiKey) {
54
- console.error("API key is required. Get one from your sandbox-api admin.");
55
+ console.error("API key is required. Get one at: https://sandbox.rentline.xyz/cli-auth");
55
56
  process.exit(1);
56
57
  }
57
58
  const existing = loadConfig();
@@ -90,7 +91,7 @@ async function runSetup(opts) {
90
91
  const idx = parseInt(choice);
91
92
  clientName = isNaN(idx) ? choice : clients[idx - 1] ?? "other";
92
93
  }
93
- await installForClient(clientName, opts.scope ?? "user", apiKey, apiUrl, displayName);
94
+ await installForClient(clientName, opts.scope ?? "user", apiKey, apiUrl);
94
95
  rl?.close();
95
96
  console.log("\nSetup complete. Restart your AI client to load the Rentline Sandbox MCP server.\n");
96
97
  }
@@ -102,84 +103,66 @@ function detectClient() {
102
103
  if (env.OPENCODE_PROJECT || env.OPENCODE_SESSION) return "opencode";
103
104
  return void 0;
104
105
  }
105
- var MCP_SERVER_ENTRY = {
106
- command: "npx",
107
- args: ["-y", "rentline-sandbox"],
108
- env: {}
109
- };
110
- function mcpEntry(apiKey, apiUrl) {
111
- return {
112
- ...MCP_SERVER_ENTRY,
113
- env: {
114
- SANDBOX_API_KEY: apiKey,
115
- SANDBOX_API_URL: apiUrl
116
- }
117
- };
118
- }
119
- async function installForClient(clientName, scope, apiKey, apiUrl, displayName) {
120
- const entry = mcpEntry(apiKey, apiUrl);
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 };
121
109
  switch (clientName) {
122
110
  case "claude-code": {
123
- const { execSync } = await import("child_process");
124
- const envFlags = Object.entries(entry.env ?? {}).map(([k, v]) => `-e ${k}=${v}`).join(" ");
125
- const cmd = `claude mcp add rentline-sandbox --scope ${scope} ${envFlags} -- npx -y rentline-sandbox`;
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(" ")}`;
126
113
  try {
127
114
  execSync(cmd, { stdio: "pipe" });
128
115
  console.log(`Installed via claude CLI (scope=${scope})`);
129
116
  } catch {
130
117
  const file = join(homedir(), ".claude.json");
131
- patchMcpJson(file, "rentline-sandbox", entry, "mcpServers");
118
+ patchMcpJson(file, "rentline-sandbox", { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }, "mcpServers");
132
119
  console.log(`Patched ${file}`);
133
120
  }
134
- const skillSrc = join(__dirname, "../SKILL.md");
135
- if (existsSync(skillSrc)) {
136
- const targets = [
137
- join(homedir(), ".claude", "skills", "rentline-sandbox"),
138
- join(homedir(), ".agents", "skills", "rentline-sandbox")
139
- ];
140
- for (const dir of targets) {
141
- mkdirSync(dir, { recursive: true });
142
- copyFileSync(skillSrc, join(dir, "SKILL.md"));
143
- console.log(`SKILL.md \u2192 ${dir}`);
144
- }
145
- }
121
+ installSkill([
122
+ join(homedir(), ".claude", "skills", "rentline-sandbox"),
123
+ join(homedir(), ".agents", "skills", "rentline-sandbox")
124
+ ]);
146
125
  break;
147
126
  }
148
127
  case "claude-desktop": {
149
- const file = platform() === "win32" ? join(process.env.APPDATA ?? homedir(), "Claude", "claude_desktop_config.json") : join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
150
- patchMcpJson(file, "rentline-sandbox", entry, "mcpServers");
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");
151
137
  console.log(`Patched ${file}`);
152
138
  break;
153
139
  }
154
140
  case "cursor": {
155
141
  const file = scope === "project" ? join(process.cwd(), ".cursor", "mcp.json") : join(homedir(), ".cursor", "mcp.json");
156
- patchMcpJson(file, "rentline-sandbox", entry, "mcpServers");
142
+ patchMcpJson(file, "rentline-sandbox", { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }, "mcpServers");
157
143
  console.log(`Patched ${file}`);
158
144
  break;
159
145
  }
160
146
  case "windsurf": {
161
- const file = join(process.cwd(), ".windsurf", "mcp.json");
162
- patchMcpJson(file, "rentline-sandbox", entry, "mcpServers");
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");
163
149
  console.log(`Patched ${file}`);
164
150
  break;
165
151
  }
166
152
  case "opencode": {
167
- const file = scope === "project" ? join(process.cwd(), "opencode.json") : join(homedir(), ".config", "opencode", "config.json");
168
- patchMcpJson(file, "rentline-sandbox", entry, "mcp");
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");
169
160
  console.log(`Patched ${file}`);
170
- const skillSrc = join(__dirname, "../SKILL.md");
171
- if (existsSync(skillSrc)) {
172
- const targets = [
173
- join(homedir(), ".config", "opencode", "skills", "rentline-sandbox"),
174
- join(homedir(), ".claude", "skills", "rentline-sandbox"),
175
- join(homedir(), ".agents", "skills", "rentline-sandbox")
176
- ];
177
- for (const dir of targets) {
178
- mkdirSync(dir, { recursive: true });
179
- copyFileSync(skillSrc, join(dir, "SKILL.md"));
180
- console.log(`SKILL.md \u2192 ${dir}`);
181
- }
182
- }
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
+ ]);
183
166
  break;
184
167
  }
185
168
  case "zed":
@@ -190,11 +173,22 @@ async function installForClient(clientName, scope, apiKey, apiUrl, displayName)
190
173
  console.log(`
191
174
  Add the following to your MCP client config:
192
175
  `);
193
- console.log(JSON.stringify({ "rentline-sandbox": entry }, null, 2));
176
+ console.log(JSON.stringify({
177
+ "rentline-sandbox": { command: NPX_CMD[0], args: NPX_CMD.slice(1), env }
178
+ }, null, 2));
194
179
  break;
195
180
  }
196
181
  }
197
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
+ }
198
192
  function patchMcpJson(filePath, serverName, entry, key) {
199
193
  mkdirSync(dirname(filePath), { recursive: true });
200
194
  let config = {};
@@ -204,6 +198,9 @@ function patchMcpJson(filePath, serverName, entry, key) {
204
198
  } catch {
205
199
  }
206
200
  }
201
+ if (key === "mcp" && !config["$schema"]) {
202
+ config["$schema"] = "https://opencode.ai/config.json";
203
+ }
207
204
  const servers = config[key] ?? {};
208
205
  servers[serverName] = entry;
209
206
  config[key] = servers;
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "rentline-sandbox",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "CLI and MCP server for the Rentline Sandbox real estate investment simulation game",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "sandbox": "dist/index.js",
8
- "sandbox-mcp": "dist/server.js"
8
+ "sandbox-mcp": "dist/server.js",
9
+ "rentline-sandbox": "dist/server.js"
9
10
  },
10
11
  "main": "./dist/index.js",
11
12
  "exports": {