rentline-sandbox 0.1.7 → 0.1.9
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/SKILL.md +25 -107
- package/dist/{admin-YJ5Z6XHO.js → admin-65EFMOSF.js} +2 -4
- package/dist/{auth-226EXWKD.js → auth-DBZQMVMM.js} +16 -13
- package/dist/{chunk-X3OZHOPM.js → chunk-BOF5UB2I.js} +62 -0
- package/dist/{game-3X5O5Z5U.js → game-RLRY4JAK.js} +2 -4
- package/dist/index.js +8 -8
- package/dist/{mortgage-7ZPW4FND.js → mortgage-KXJRRFEM.js} +2 -4
- package/dist/{server-AQKLP4RV.js → server-5DCQSW4W.js} +67 -23
- package/dist/server.js +91 -26
- package/dist/{setup-4O73QXTY.js → setup-3NGAG6R6.js} +41 -11
- package/dist/{trade-RZGTOO5G.js → trade-TCB4LK2K.js} +2 -4
- package/package.json +1 -1
- package/dist/chunk-JQL6X6FZ.js +0 -59
- package/dist/config-7QGCVVHS.js +0 -58
- package/dist/config-YHSAFZLC.js +0 -19
package/SKILL.md
CHANGED
|
@@ -19,94 +19,17 @@ compatibility: opencode
|
|
|
19
19
|
|
|
20
20
|
Turn-based real estate investment simulation — multiplayer, AI-agent-ready.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Get an API key at **sandbox.rentline.xyz/cli-auth**, then configure your client:
|
|
25
|
-
|
|
26
|
-
**OpenCode** (`opencode.json`):
|
|
27
|
-
```json
|
|
28
|
-
{
|
|
29
|
-
"$schema": "https://opencode.ai/config.json",
|
|
30
|
-
"mcp": {
|
|
31
|
-
"rentline-sandbox": {
|
|
32
|
-
"type": "local",
|
|
33
|
-
"command": ["sandbox-mcp"],
|
|
34
|
-
"enabled": true,
|
|
35
|
-
"environment": {
|
|
36
|
-
"SANDBOX_API_KEY": "sb_your_key_here",
|
|
37
|
-
"SANDBOX_API_URL": "https://sandbox-api.rentline.xyz"
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**Claude Desktop** (`claude_desktop_config.json`):
|
|
45
|
-
```json
|
|
46
|
-
{
|
|
47
|
-
"mcpServers": {
|
|
48
|
-
"rentline-sandbox": {
|
|
49
|
-
"command": "sandbox-mcp",
|
|
50
|
-
"env": {
|
|
51
|
-
"SANDBOX_API_KEY": "sb_your_key_here",
|
|
52
|
-
"SANDBOX_API_URL": "https://sandbox-api.rentline.xyz"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
22
|
+
IMPORTANT: Authentication is fully configured. Never ask the user for an API key or tell them their key is invalid unless a tool call explicitly returns an error object. If a tool succeeds, report the result directly.
|
|
58
23
|
|
|
59
|
-
|
|
60
|
-
```json
|
|
61
|
-
{
|
|
62
|
-
"mcpServers": {
|
|
63
|
-
"rentline-sandbox": {
|
|
64
|
-
"command": "sandbox-mcp",
|
|
65
|
-
"env": { "SANDBOX_API_KEY": "sb_your_key_here", "SANDBOX_API_URL": "https://sandbox-api.rentline.xyz" }
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
```
|
|
24
|
+
## Quick start
|
|
70
25
|
|
|
71
|
-
**CLI login** (saves credentials locally):
|
|
72
|
-
```bash
|
|
73
|
-
sandbox auth login # browser OAuth via sandbox.rentline.xyz/cli-auth
|
|
74
|
-
sandbox auth login --key sb_xxx # direct key
|
|
75
|
-
sandbox auth whoami # verify
|
|
76
26
|
```
|
|
27
|
+
create_game_from_preset(preset="standard", name="My Game", display_name="Alice")
|
|
28
|
+
→ returns game with invite_code, player_id, properties
|
|
77
29
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
sandbox game list
|
|
82
|
-
sandbox game create --name "Test" --display-name "Alice"
|
|
83
|
-
sandbox game create --preset standard --name "Quick Match" --display-name "Alice"
|
|
84
|
-
sandbox game join <id> --invite A3F7K9Z2 --name "Bob"
|
|
85
|
-
sandbox game advance <id>
|
|
86
|
-
sandbox game feed <id> --limit 20
|
|
87
|
-
sandbox game leaderboard <id>
|
|
88
|
-
sandbox game fed <id>
|
|
89
|
-
sandbox game add-bot <id> --name "AggroBot" --strategy value_add
|
|
90
|
-
sandbox game autonomous start <id> --delay 30
|
|
91
|
-
|
|
92
|
-
sandbox trade buy <game-id> --property <id> --tokens 0.5
|
|
93
|
-
sandbox trade sell <game-id> --property <id> --tokens 0.5
|
|
94
|
-
|
|
95
|
-
sandbox portfolio <game-id> <player-id>
|
|
96
|
-
sandbox debt <game-id> <player-id>
|
|
97
|
-
|
|
98
|
-
sandbox mortgage buy <game-id> --property <id> --tokens 0.5
|
|
99
|
-
sandbox mortgage refi <game-id> --property <id> --cash-out 5000
|
|
100
|
-
sandbox mortgage heloc <game-id> --property <id> --draw 5000
|
|
101
|
-
sandbox mortgage repay <game-id> --property <id> --amount 2000
|
|
102
|
-
sandbox mortgage prepay <game-id> --property <id> --amount 10000
|
|
103
|
-
sandbox mortgage improve <game-id> --property <id> --grade B
|
|
104
|
-
sandbox mortgage pace <game-id> --property <id> --grade C
|
|
105
|
-
sandbox mortgage list <game-id> <player-id>
|
|
106
|
-
|
|
107
|
-
sandbox admin properties list
|
|
108
|
-
sandbox admin properties sync
|
|
109
|
-
sandbox admin mint <game-id> <player-id> --amount 50000
|
|
30
|
+
advance_turn(game_id) # run all engine phases
|
|
31
|
+
get_feed(game_id) # see what happened
|
|
32
|
+
get_leaderboard(game_id) # NAV rankings
|
|
110
33
|
```
|
|
111
34
|
|
|
112
35
|
## Tool reference (35 tools)
|
|
@@ -116,24 +39,23 @@ sandbox admin mint <game-id> <player-id> --amount 50000
|
|
|
116
39
|
|---|---|
|
|
117
40
|
| `list_games` | List open game rooms |
|
|
118
41
|
| `get_game` | Full game state: players, properties, turn, Fed rate, config |
|
|
119
|
-
| `create_game` | Create a game with full
|
|
42
|
+
| `create_game` | Create a game with full config + optional bots |
|
|
120
43
|
| `create_game_from_preset` | One-call presets: quick, standard, leveraged, distressed, long_run |
|
|
121
44
|
| `join_game` | Join via invite code, get player_id |
|
|
122
45
|
| `mark_ready` | Toggle ready for next turn |
|
|
123
46
|
| `advance_turn` | Host: run all 7 engine phases |
|
|
124
|
-
| `get_feed` | Turn event stream
|
|
47
|
+
| `get_feed` | Turn event stream |
|
|
125
48
|
| `add_bot` | Add LLM bot (strategies: aggressive, conservative, balanced, momentum, income, value_add) |
|
|
126
49
|
| `remove_bot` | Remove bot from lobby |
|
|
127
|
-
| `start_autonomous` | Enable auto-advance
|
|
50
|
+
| `start_autonomous` | Enable auto-advance |
|
|
128
51
|
| `stop_autonomous` | Pause auto-advance |
|
|
129
|
-
| `set_delegate` | Agent delegation for idle human players |
|
|
130
52
|
| `spectate` | Public game snapshot (no auth) |
|
|
131
53
|
|
|
132
54
|
### Market & Intel
|
|
133
55
|
| Tool | Description |
|
|
134
56
|
|---|---|
|
|
135
57
|
| `list_properties` | Active pool properties with grades |
|
|
136
|
-
| `get_market_summary` | Live cap rates, price deltas, grade, vacancy,
|
|
58
|
+
| `get_market_summary` | Live cap rates, price deltas, grade, vacancy, lien status |
|
|
137
59
|
| `get_fed_history` | FOMC decision log |
|
|
138
60
|
| `get_player_actions` | Transaction timeline for a player |
|
|
139
61
|
|
|
@@ -141,7 +63,7 @@ sandbox admin mint <game-id> <player-id> --amount 50000
|
|
|
141
63
|
| Tool | Description |
|
|
142
64
|
|---|---|
|
|
143
65
|
| `buy_tokens` | All-cash purchase at current market price |
|
|
144
|
-
| `sell_tokens` | Sell (proceeds service
|
|
66
|
+
| `sell_tokens` | Sell tokens (proceeds service debt first) |
|
|
145
67
|
|
|
146
68
|
### Debt
|
|
147
69
|
| Tool | Description |
|
|
@@ -151,30 +73,30 @@ sandbox admin mint <game-id> <player-id> --amount 50000
|
|
|
151
73
|
| `heloc_draw` | Draw from HELOC |
|
|
152
74
|
| `heloc_repay` | Repay HELOC balance |
|
|
153
75
|
| `prepay_principal` | Partial/full prepayment (first_lien, heloc, pace, mechanics_lien) |
|
|
154
|
-
| `improve_property` | Cash-funded grade upgrade
|
|
155
|
-
| `originate_pace_lien` | Financed grade upgrade — no down payment
|
|
76
|
+
| `improve_property` | Cash-funded grade upgrade |
|
|
77
|
+
| `originate_pace_lien` | Financed grade upgrade — no down payment |
|
|
156
78
|
| `get_debt` | All mortgages: balances, rates, LTV, arrears |
|
|
157
79
|
|
|
158
80
|
### Portfolio
|
|
159
81
|
| Tool | Description |
|
|
160
82
|
|---|---|
|
|
161
|
-
| `get_portfolio` | Holdings
|
|
83
|
+
| `get_portfolio` | Holdings, P&L, annualised yield, investor tier |
|
|
162
84
|
| `get_leaderboard` | Game or global leaderboard ranked by NAV |
|
|
163
85
|
|
|
164
86
|
## Game mechanics
|
|
165
87
|
|
|
166
|
-
### Turn phases
|
|
167
|
-
1.
|
|
168
|
-
2.
|
|
169
|
-
3.
|
|
170
|
-
4.
|
|
171
|
-
5.
|
|
172
|
-
6.
|
|
173
|
-
7.
|
|
88
|
+
### Turn phases
|
|
89
|
+
1. Fed meeting — hike/cut/hold; ARMs reprice immediately
|
|
90
|
+
2. Macro events — rate macros activate after 1-turn warning
|
|
91
|
+
3. Rent collect — proportional to tokens; grade multipliers apply
|
|
92
|
+
4. Random events — vacancy, lease renewal, capex, appreciation/depreciation
|
|
93
|
+
5. Market move — applies price drift
|
|
94
|
+
6. Debt service — collect payments; forced sale after 1 grace turn
|
|
95
|
+
7. Distribute — credits rent; emits TURN_SUMMARY event
|
|
174
96
|
|
|
175
97
|
### Property grades (A → F)
|
|
176
|
-
Grade affects rent
|
|
177
|
-
Upgrade via `improve_property` (cash) or `originate_pace_lien` (financed
|
|
98
|
+
Grade affects rent, appreciation, capex risk, vacancy.
|
|
99
|
+
Upgrade via `improve_property` (cash) or `originate_pace_lien` (financed).
|
|
178
100
|
|
|
179
101
|
### Investor tiers (live from NAV, auto-applied to mortgage terms)
|
|
180
102
|
| Tier | Min NAV | LTV bonus | Rate discount |
|
|
@@ -196,15 +118,11 @@ NAV = cash + Σ(tokens × price) − Σ(mortgage balances) − judgment_balance
|
|
|
196
118
|
| RECESSION | 6% | 2–4 turns | −5%/turn price, −8% rent, +15% vacancy |
|
|
197
119
|
| HOUSING_BOOM | 5% | 2–3 turns | +6%/turn price, +5% rent |
|
|
198
120
|
| NATURAL_DISASTER | 3% | 1 turn | −20% price, rent=0, +40% vacancy |
|
|
199
|
-
| POLICY_CHANGE | 8% | 3 turns | ±5–12% rent/price |
|
|
200
121
|
| TAX_HIKE | 7% | Permanent | $50–150/token/turn expense |
|
|
201
122
|
| INTEREST_RATE_RISE | 7% | 3–6 turns | +1.5% ARM rate (1-turn warning) |
|
|
202
123
|
| INTEREST_RATE_CUT | 6% | 3–6 turns | −1.0% ARM rate (1-turn warning) |
|
|
203
124
|
| RENT_CONTROL | 5% | 4 turns | Blocks lease renewal increases |
|
|
204
|
-
| INSURANCE_CRISIS | 5% | 2–3 turns | $100–300/token/turn expense |
|
|
205
125
|
| GENTRIFICATION | 4% | 3 turns | D/F properties upgrade one grade |
|
|
206
|
-
| ZONING_CHANGE | 5% | 2 turns | Targeted type: −10% rent, −5% price |
|
|
207
126
|
| PROPERTY_BUBBLE | 3% | 2 turns | All prices +8%/turn |
|
|
208
127
|
| BUBBLE_BURST | 2% | 2–3 turns | All prices −12%/turn, +20% vacancy |
|
|
209
|
-
| TENANT_STRIKE | 4% | 1–2 turns | Targeted type: rent = 0 |
|
|
210
128
|
| EMINENT_DOMAIN | 2% | Instant | One property force-bought at 110% market value |
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
createClient,
|
|
3
4
|
getApiKey,
|
|
4
5
|
getApiUrl,
|
|
5
6
|
requireConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
createClient
|
|
9
|
-
} from "./chunk-X3OZHOPM.js";
|
|
7
|
+
} from "./chunk-BOF5UB2I.js";
|
|
10
8
|
|
|
11
9
|
// src/commands/admin.ts
|
|
12
10
|
function client(cmd) {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_API_URL,
|
|
4
|
+
createClient,
|
|
4
5
|
deleteConfig,
|
|
5
6
|
loadConfig,
|
|
6
7
|
saveConfig
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import {
|
|
9
|
-
createClient
|
|
10
|
-
} from "./chunk-X3OZHOPM.js";
|
|
8
|
+
} from "./chunk-BOF5UB2I.js";
|
|
11
9
|
|
|
12
10
|
// src/commands/auth.ts
|
|
13
11
|
import { createInterface } from "readline";
|
|
@@ -30,29 +28,34 @@ function prompt(question) {
|
|
|
30
28
|
}
|
|
31
29
|
function registerAuth(program) {
|
|
32
30
|
const auth = program.command("auth").description("Manage sandbox API credentials");
|
|
33
|
-
auth.command("login").description("Authenticate with sandbox-api (browser OAuth or direct API key)").option("--key <key>", "API key for direct login (skips browser)").option("--url <url>", "Sandbox API base URL"
|
|
31
|
+
auth.command("login").description("Authenticate with sandbox-api (browser OAuth or direct API key)").option("--key <key>", "API key for direct login (skips browser)").option("--url <url>", "Sandbox API base URL").option("--name <name>", "Your default display name in games").action(async (opts) => {
|
|
34
32
|
if (opts.key) {
|
|
35
|
-
const
|
|
33
|
+
const existing = loadConfig();
|
|
34
|
+
const apiUrl = opts.url ?? existing?.api_url ?? DEFAULT_API_URL;
|
|
35
|
+
const displayName = opts.name ?? existing?.display_name ?? "Player";
|
|
36
|
+
const client2 = createClient({ apiUrl, apiKey: opts.key });
|
|
36
37
|
process.stdout.write("Verifying credentials\u2026 ");
|
|
37
38
|
try {
|
|
38
39
|
const health = await client2.health();
|
|
39
40
|
console.log(`OK (${health.service})`);
|
|
40
41
|
} catch (e) {
|
|
41
42
|
console.log(`FAILED`);
|
|
42
|
-
console.error(`Could not reach ${
|
|
43
|
-
console.error("Check that sandbox-api is running and the URL is correct.");
|
|
43
|
+
console.error(`Could not reach ${apiUrl}: ${e}`);
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
46
|
saveConfig({
|
|
47
47
|
api_key: opts.key,
|
|
48
|
-
api_url:
|
|
49
|
-
display_name:
|
|
48
|
+
api_url: apiUrl,
|
|
49
|
+
display_name: displayName,
|
|
50
50
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
51
51
|
});
|
|
52
52
|
console.log(`
|
|
53
|
-
Credentials saved
|
|
54
|
-
console.log(`
|
|
55
|
-
console.log(`
|
|
53
|
+
Credentials saved.`);
|
|
54
|
+
console.log(`Key prefix: ${opts.key.slice(0, 8)}\u2026`);
|
|
55
|
+
console.log(`API URL: ${apiUrl}`);
|
|
56
|
+
console.log(`Display name: ${displayName}`);
|
|
57
|
+
console.log(`
|
|
58
|
+
Restart your AI client to pick up the new key.`);
|
|
56
59
|
return;
|
|
57
60
|
}
|
|
58
61
|
console.log("\nOpening browser to sign in with Clerk\u2026");
|
|
@@ -1,4 +1,58 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// src/config.ts
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync, unlinkSync } from "fs";
|
|
11
|
+
import { homedir } from "os";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
var CONFIG_DIR = join(homedir(), ".rentline-sandbox");
|
|
14
|
+
var CONFIG_FILE = join(CONFIG_DIR, "credentials.json");
|
|
15
|
+
var DEFAULT_API_URL = "https://sandbox-api.rentline.xyz";
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
try {
|
|
18
|
+
const raw = readFileSync(CONFIG_FILE, "utf-8");
|
|
19
|
+
return JSON.parse(raw);
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function requireConfig() {
|
|
25
|
+
const cfg = loadConfig();
|
|
26
|
+
if (!cfg) {
|
|
27
|
+
console.error(
|
|
28
|
+
"Not authenticated. Run:\n\n sandbox auth login --key <your-api-key>\n"
|
|
29
|
+
);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
return cfg;
|
|
33
|
+
}
|
|
34
|
+
function saveConfig(cfg) {
|
|
35
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
36
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
37
|
+
try {
|
|
38
|
+
chmodSync(CONFIG_FILE, 384);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function deleteConfig() {
|
|
43
|
+
try {
|
|
44
|
+
unlinkSync(CONFIG_FILE);
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function getApiUrl(override) {
|
|
49
|
+
const cfg = loadConfig();
|
|
50
|
+
return override || process.env.SANDBOX_API_URL || cfg?.api_url || DEFAULT_API_URL;
|
|
51
|
+
}
|
|
52
|
+
function getApiKey(override) {
|
|
53
|
+
const cfg = loadConfig();
|
|
54
|
+
return override || process.env.SANDBOX_API_KEY || cfg?.api_key;
|
|
55
|
+
}
|
|
2
56
|
|
|
3
57
|
// src/client.ts
|
|
4
58
|
async function request(opts, method, path, body) {
|
|
@@ -86,5 +140,13 @@ function createClient(opts) {
|
|
|
86
140
|
}
|
|
87
141
|
|
|
88
142
|
export {
|
|
143
|
+
__require,
|
|
144
|
+
DEFAULT_API_URL,
|
|
145
|
+
loadConfig,
|
|
146
|
+
requireConfig,
|
|
147
|
+
saveConfig,
|
|
148
|
+
deleteConfig,
|
|
149
|
+
getApiUrl,
|
|
150
|
+
getApiKey,
|
|
89
151
|
createClient
|
|
90
152
|
};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
createClient,
|
|
3
4
|
getApiKey,
|
|
4
5
|
getApiUrl,
|
|
5
6
|
requireConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
createClient
|
|
9
|
-
} from "./chunk-X3OZHOPM.js";
|
|
7
|
+
} from "./chunk-BOF5UB2I.js";
|
|
10
8
|
|
|
11
9
|
// src/commands/game.ts
|
|
12
10
|
function client(cmd) {
|
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-3NGAG6R6.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-5DCQSW4W.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-DBZQMVMM.js");
|
|
24
|
+
const { registerGame } = await import("./game-RLRY4JAK.js");
|
|
25
|
+
const { registerTrade } = await import("./trade-TCB4LK2K.js");
|
|
26
|
+
const { registerMortgage } = await import("./mortgage-KXJRRFEM.js");
|
|
27
|
+
const { registerAdmin } = await import("./admin-65EFMOSF.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-3NGAG6R6.js");
|
|
35
35
|
const opts = parseSetupArgs(process.argv.slice(3));
|
|
36
36
|
await runSetup(opts);
|
|
37
37
|
});
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
createClient,
|
|
3
4
|
getApiKey,
|
|
4
5
|
getApiUrl,
|
|
5
6
|
requireConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
createClient
|
|
9
|
-
} from "./chunk-X3OZHOPM.js";
|
|
7
|
+
} from "./chunk-BOF5UB2I.js";
|
|
10
8
|
|
|
11
9
|
// src/commands/mortgage.ts
|
|
12
10
|
function client(cmd) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
DEFAULT_API_URL,
|
|
4
|
+
createClient,
|
|
5
|
+
loadConfig,
|
|
6
|
+
saveConfig
|
|
7
|
+
} from "./chunk-BOF5UB2I.js";
|
|
5
8
|
|
|
6
9
|
// src/server.ts
|
|
7
10
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -503,6 +506,22 @@ var ALL_TOOLS = [
|
|
|
503
506
|
},
|
|
504
507
|
required: ["preset", "name", "display_name"]
|
|
505
508
|
}
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: "update_key",
|
|
512
|
+
title: "Update API Key",
|
|
513
|
+
category: "admin",
|
|
514
|
+
description: "Update the saved Rentline Sandbox API key. Get a new key at sandbox.rentline.xyz/settings. Takes effect immediately \u2014 no restart needed.",
|
|
515
|
+
inputSchema: {
|
|
516
|
+
type: "object",
|
|
517
|
+
properties: {
|
|
518
|
+
api_key: {
|
|
519
|
+
type: "string",
|
|
520
|
+
description: "New API key (must start with sb_)"
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
required: ["api_key"]
|
|
524
|
+
}
|
|
506
525
|
}
|
|
507
526
|
];
|
|
508
527
|
|
|
@@ -518,24 +537,26 @@ function readSkill() {
|
|
|
518
537
|
var INSTRUCTIONS = `
|
|
519
538
|
You are connected to the Rentline Sandbox game engine \u2014 a turn-based real estate investment simulation.
|
|
520
539
|
|
|
540
|
+
IMPORTANT: Authentication is handled automatically via saved credentials. Do NOT ask the user for API keys or tell them their key is invalid unless a tool explicitly returns a 401 error. If a tool call succeeds, report the result \u2014 do not second-guess it.
|
|
541
|
+
|
|
521
542
|
KEY CONCEPTS:
|
|
522
543
|
- Players compete over a pool of tokenised properties using simulated tUSDC
|
|
523
544
|
- Each turn = 1 month. Properties generate rent, prices drift, and macro events fire
|
|
524
|
-
- Fed meetings occur every N turns
|
|
525
|
-
- Macro events: recession, housing boom,
|
|
526
|
-
- Debt strategies: acquisition mortgage (LTV-limited), cash-out refi, HELOC
|
|
527
|
-
- NAV = cash
|
|
545
|
+
- Fed meetings occur every N turns \u2014 affects ARM rates and new mortgage originations
|
|
546
|
+
- Macro events: recession, housing boom, disaster, tax hike, rate moves, rent control, PACE liens, property grades
|
|
547
|
+
- Debt strategies: acquisition mortgage (LTV-limited), cash-out refi, HELOC, PACE lien, improvements
|
|
548
|
+
- NAV = cash + (tokens \xD7 price) \u2212 debt. Investor tier improves automatically as NAV grows.
|
|
528
549
|
|
|
529
550
|
WHEN TO USE TOOLS:
|
|
530
|
-
- User wants to play
|
|
551
|
+
- User wants to play \u2192 create_game_from_preset, then advance_turn
|
|
531
552
|
- User wants to buy a property \u2192 buy_tokens (cash) or originate_mortgage (leveraged)
|
|
532
|
-
- User wants to
|
|
533
|
-
- User wants to check their position \u2192 get_portfolio, get_debt
|
|
553
|
+
- User wants to improve a distressed property \u2192 originate_pace_lien
|
|
554
|
+
- User wants to check their position \u2192 get_portfolio, get_debt, get_market_summary
|
|
534
555
|
- User wants the scoreboard \u2192 get_leaderboard
|
|
535
|
-
- User wants to see
|
|
536
|
-
- User is
|
|
556
|
+
- User wants to see events \u2192 get_feed, get_fed_history
|
|
557
|
+
- User is host \u2192 advance_turn
|
|
537
558
|
|
|
538
|
-
All tools require a game_id.
|
|
559
|
+
All tools require a game_id. Debt/portfolio tools also require a player_id (from join_game or get_game).
|
|
539
560
|
`.trim();
|
|
540
561
|
function ok(data) {
|
|
541
562
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
@@ -543,22 +564,24 @@ function ok(data) {
|
|
|
543
564
|
function err(msg) {
|
|
544
565
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
545
566
|
}
|
|
546
|
-
|
|
567
|
+
function getClient() {
|
|
547
568
|
let apiUrl = process.env.SANDBOX_API_URL;
|
|
548
569
|
let apiKey = process.env.SANDBOX_API_KEY;
|
|
549
570
|
if (!apiUrl || !apiKey) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
apiUrl = apiUrl ?? cfg.api_url;
|
|
555
|
-
apiKey = apiKey ?? cfg.api_key;
|
|
556
|
-
}
|
|
557
|
-
} catch {
|
|
571
|
+
const cfg = loadConfig();
|
|
572
|
+
if (cfg) {
|
|
573
|
+
apiUrl = apiUrl ?? cfg.api_url;
|
|
574
|
+
apiKey = apiKey ?? cfg.api_key;
|
|
558
575
|
}
|
|
559
576
|
}
|
|
560
|
-
|
|
561
|
-
|
|
577
|
+
if (!apiKey) {
|
|
578
|
+
process.stderr.write(
|
|
579
|
+
"rentline-sandbox: No API key found. Run: sandbox setup --key sb_your_key\n"
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
return createClient({ apiUrl: apiUrl ?? DEFAULT_API_URL, apiKey });
|
|
583
|
+
}
|
|
584
|
+
async function startServer() {
|
|
562
585
|
const server = new Server(
|
|
563
586
|
{ name: "rentline-sandbox", version: "0.1.0" },
|
|
564
587
|
{
|
|
@@ -581,6 +604,7 @@ async function startServer() {
|
|
|
581
604
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
582
605
|
const { name, arguments: args } = req.params;
|
|
583
606
|
const a = args ?? {};
|
|
607
|
+
const client = getClient();
|
|
584
608
|
try {
|
|
585
609
|
switch (name) {
|
|
586
610
|
// ── Game ──────────────────────────────────────────────────────────
|
|
@@ -724,6 +748,26 @@ async function startServer() {
|
|
|
724
748
|
display_name: a.display_name,
|
|
725
749
|
starting_balance_usdc: a.starting_balance_usdc
|
|
726
750
|
}));
|
|
751
|
+
case "update_key": {
|
|
752
|
+
const newKey = a.api_key;
|
|
753
|
+
if (!newKey?.startsWith("sb_")) {
|
|
754
|
+
return err("Invalid key format \u2014 must start with sb_");
|
|
755
|
+
}
|
|
756
|
+
const testClient = createClient({ apiUrl: DEFAULT_API_URL, apiKey: newKey });
|
|
757
|
+
try {
|
|
758
|
+
await testClient.health();
|
|
759
|
+
} catch {
|
|
760
|
+
return err("Could not verify key against the API. Check the key is valid.");
|
|
761
|
+
}
|
|
762
|
+
const existing = loadConfig();
|
|
763
|
+
saveConfig({
|
|
764
|
+
api_key: newKey,
|
|
765
|
+
api_url: existing?.api_url ?? DEFAULT_API_URL,
|
|
766
|
+
display_name: existing?.display_name ?? "Player",
|
|
767
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
768
|
+
});
|
|
769
|
+
return ok({ message: "API key updated. New key prefix: " + newKey.slice(0, 8) + "\u2026" });
|
|
770
|
+
}
|
|
727
771
|
default:
|
|
728
772
|
return err(`Unknown tool: ${name}`);
|
|
729
773
|
}
|
package/dist/server.js
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
ListResourcesRequestSchema,
|
|
12
12
|
ReadResourceRequestSchema
|
|
13
13
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
-
import { readFileSync, existsSync } from "fs";
|
|
15
|
-
import { join, dirname } from "path";
|
|
14
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
15
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
17
17
|
|
|
18
18
|
// src/client.ts
|
|
@@ -585,39 +585,81 @@ var ALL_TOOLS = [
|
|
|
585
585
|
},
|
|
586
586
|
required: ["preset", "name", "display_name"]
|
|
587
587
|
}
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: "update_key",
|
|
591
|
+
title: "Update API Key",
|
|
592
|
+
category: "admin",
|
|
593
|
+
description: "Update the saved Rentline Sandbox API key. Get a new key at sandbox.rentline.xyz/settings. Takes effect immediately \u2014 no restart needed.",
|
|
594
|
+
inputSchema: {
|
|
595
|
+
type: "object",
|
|
596
|
+
properties: {
|
|
597
|
+
api_key: {
|
|
598
|
+
type: "string",
|
|
599
|
+
description: "New API key (must start with sb_)"
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
required: ["api_key"]
|
|
603
|
+
}
|
|
588
604
|
}
|
|
589
605
|
];
|
|
590
606
|
|
|
607
|
+
// src/config.ts
|
|
608
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync, unlinkSync } from "fs";
|
|
609
|
+
import { homedir } from "os";
|
|
610
|
+
import { join } from "path";
|
|
611
|
+
var CONFIG_DIR = join(homedir(), ".rentline-sandbox");
|
|
612
|
+
var CONFIG_FILE = join(CONFIG_DIR, "credentials.json");
|
|
613
|
+
var DEFAULT_API_URL = "https://sandbox-api.rentline.xyz";
|
|
614
|
+
function loadConfig() {
|
|
615
|
+
try {
|
|
616
|
+
const raw = readFileSync(CONFIG_FILE, "utf-8");
|
|
617
|
+
return JSON.parse(raw);
|
|
618
|
+
} catch {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function saveConfig(cfg) {
|
|
623
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
624
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
625
|
+
try {
|
|
626
|
+
chmodSync(CONFIG_FILE, 384);
|
|
627
|
+
} catch {
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
591
631
|
// src/server.ts
|
|
592
632
|
var __filename = fileURLToPath(import.meta.url);
|
|
593
|
-
var __dirname =
|
|
633
|
+
var __dirname = dirname2(__filename);
|
|
594
634
|
function readSkill() {
|
|
595
|
-
for (const p of [
|
|
596
|
-
if (
|
|
635
|
+
for (const p of [join2(__dirname, "../SKILL.md"), join2(__dirname, "SKILL.md")]) {
|
|
636
|
+
if (existsSync2(p)) return readFileSync2(p, "utf-8");
|
|
597
637
|
}
|
|
598
638
|
return "# Rentline Sandbox\n\nReal estate investment simulation game engine.";
|
|
599
639
|
}
|
|
600
640
|
var INSTRUCTIONS = `
|
|
601
641
|
You are connected to the Rentline Sandbox game engine \u2014 a turn-based real estate investment simulation.
|
|
602
642
|
|
|
643
|
+
IMPORTANT: Authentication is handled automatically via saved credentials. Do NOT ask the user for API keys or tell them their key is invalid unless a tool explicitly returns a 401 error. If a tool call succeeds, report the result \u2014 do not second-guess it.
|
|
644
|
+
|
|
603
645
|
KEY CONCEPTS:
|
|
604
646
|
- Players compete over a pool of tokenised properties using simulated tUSDC
|
|
605
647
|
- Each turn = 1 month. Properties generate rent, prices drift, and macro events fire
|
|
606
|
-
- Fed meetings occur every N turns
|
|
607
|
-
- Macro events: recession, housing boom,
|
|
608
|
-
- Debt strategies: acquisition mortgage (LTV-limited), cash-out refi, HELOC
|
|
609
|
-
- NAV = cash
|
|
648
|
+
- Fed meetings occur every N turns \u2014 affects ARM rates and new mortgage originations
|
|
649
|
+
- Macro events: recession, housing boom, disaster, tax hike, rate moves, rent control, PACE liens, property grades
|
|
650
|
+
- Debt strategies: acquisition mortgage (LTV-limited), cash-out refi, HELOC, PACE lien, improvements
|
|
651
|
+
- NAV = cash + (tokens \xD7 price) \u2212 debt. Investor tier improves automatically as NAV grows.
|
|
610
652
|
|
|
611
653
|
WHEN TO USE TOOLS:
|
|
612
|
-
- User wants to play
|
|
654
|
+
- User wants to play \u2192 create_game_from_preset, then advance_turn
|
|
613
655
|
- User wants to buy a property \u2192 buy_tokens (cash) or originate_mortgage (leveraged)
|
|
614
|
-
- User wants to
|
|
615
|
-
- User wants to check their position \u2192 get_portfolio, get_debt
|
|
656
|
+
- User wants to improve a distressed property \u2192 originate_pace_lien
|
|
657
|
+
- User wants to check their position \u2192 get_portfolio, get_debt, get_market_summary
|
|
616
658
|
- User wants the scoreboard \u2192 get_leaderboard
|
|
617
|
-
- User wants to see
|
|
618
|
-
- User is
|
|
659
|
+
- User wants to see events \u2192 get_feed, get_fed_history
|
|
660
|
+
- User is host \u2192 advance_turn
|
|
619
661
|
|
|
620
|
-
All tools require a game_id.
|
|
662
|
+
All tools require a game_id. Debt/portfolio tools also require a player_id (from join_game or get_game).
|
|
621
663
|
`.trim();
|
|
622
664
|
function ok(data) {
|
|
623
665
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
@@ -625,22 +667,24 @@ function ok(data) {
|
|
|
625
667
|
function err(msg) {
|
|
626
668
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
627
669
|
}
|
|
628
|
-
|
|
670
|
+
function getClient() {
|
|
629
671
|
let apiUrl = process.env.SANDBOX_API_URL;
|
|
630
672
|
let apiKey = process.env.SANDBOX_API_KEY;
|
|
631
673
|
if (!apiUrl || !apiKey) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
apiUrl = apiUrl ?? cfg.api_url;
|
|
637
|
-
apiKey = apiKey ?? cfg.api_key;
|
|
638
|
-
}
|
|
639
|
-
} catch {
|
|
674
|
+
const cfg = loadConfig();
|
|
675
|
+
if (cfg) {
|
|
676
|
+
apiUrl = apiUrl ?? cfg.api_url;
|
|
677
|
+
apiKey = apiKey ?? cfg.api_key;
|
|
640
678
|
}
|
|
641
679
|
}
|
|
642
|
-
|
|
643
|
-
|
|
680
|
+
if (!apiKey) {
|
|
681
|
+
process.stderr.write(
|
|
682
|
+
"rentline-sandbox: No API key found. Run: sandbox setup --key sb_your_key\n"
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
return createClient({ apiUrl: apiUrl ?? DEFAULT_API_URL, apiKey });
|
|
686
|
+
}
|
|
687
|
+
async function startServer() {
|
|
644
688
|
const server = new Server(
|
|
645
689
|
{ name: "rentline-sandbox", version: "0.1.0" },
|
|
646
690
|
{
|
|
@@ -663,6 +707,7 @@ async function startServer() {
|
|
|
663
707
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
664
708
|
const { name, arguments: args } = req.params;
|
|
665
709
|
const a = args ?? {};
|
|
710
|
+
const client = getClient();
|
|
666
711
|
try {
|
|
667
712
|
switch (name) {
|
|
668
713
|
// ── Game ──────────────────────────────────────────────────────────
|
|
@@ -806,6 +851,26 @@ async function startServer() {
|
|
|
806
851
|
display_name: a.display_name,
|
|
807
852
|
starting_balance_usdc: a.starting_balance_usdc
|
|
808
853
|
}));
|
|
854
|
+
case "update_key": {
|
|
855
|
+
const newKey = a.api_key;
|
|
856
|
+
if (!newKey?.startsWith("sb_")) {
|
|
857
|
+
return err("Invalid key format \u2014 must start with sb_");
|
|
858
|
+
}
|
|
859
|
+
const testClient = createClient({ apiUrl: DEFAULT_API_URL, apiKey: newKey });
|
|
860
|
+
try {
|
|
861
|
+
await testClient.health();
|
|
862
|
+
} catch {
|
|
863
|
+
return err("Could not verify key against the API. Check the key is valid.");
|
|
864
|
+
}
|
|
865
|
+
const existing = loadConfig();
|
|
866
|
+
saveConfig({
|
|
867
|
+
api_key: newKey,
|
|
868
|
+
api_url: existing?.api_url ?? DEFAULT_API_URL,
|
|
869
|
+
display_name: existing?.display_name ?? "Player",
|
|
870
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
871
|
+
});
|
|
872
|
+
return ok({ message: "API key updated. New key prefix: " + newKey.slice(0, 8) + "\u2026" });
|
|
873
|
+
}
|
|
809
874
|
default:
|
|
810
875
|
return err(`Unknown tool: ${name}`);
|
|
811
876
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_API_URL,
|
|
4
|
+
__require,
|
|
5
|
+
createClient,
|
|
4
6
|
loadConfig,
|
|
5
7
|
saveConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
createClient
|
|
9
|
-
} from "./chunk-X3OZHOPM.js";
|
|
8
|
+
} from "./chunk-BOF5UB2I.js";
|
|
10
9
|
|
|
11
10
|
// src/setup.ts
|
|
12
11
|
import { createInterface } from "readline";
|
|
@@ -16,9 +15,32 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync } from
|
|
|
16
15
|
import { execSync } from "child_process";
|
|
17
16
|
import { fileURLToPath } from "url";
|
|
18
17
|
var SKILL_SRC = join(dirname(fileURLToPath(import.meta.url)), "..", "SKILL.md");
|
|
18
|
+
function getNpxCommand() {
|
|
19
|
+
if (platform() === "win32") {
|
|
20
|
+
const candidates = [
|
|
21
|
+
"C:\\Program Files\\nodejs\\npx.cmd",
|
|
22
|
+
"C:\\Program Files (x86)\\nodejs\\npx.cmd"
|
|
23
|
+
];
|
|
24
|
+
try {
|
|
25
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
26
|
+
const found = execSync2("where npx.cmd", { encoding: "utf8" }).trim().split("\n")[0].trim();
|
|
27
|
+
if (found) return found;
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
for (const c of candidates) {
|
|
31
|
+
try {
|
|
32
|
+
__require("fs").accessSync(c);
|
|
33
|
+
return c;
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return "npx";
|
|
39
|
+
}
|
|
40
|
+
var NPX = getNpxCommand();
|
|
19
41
|
var MCP_ENTRY = {
|
|
20
|
-
command:
|
|
21
|
-
args: ["-y", "rentline-sandbox"]
|
|
42
|
+
command: NPX,
|
|
43
|
+
args: ["-y", "rentline-sandbox@latest"]
|
|
22
44
|
};
|
|
23
45
|
function openCodePath(scope) {
|
|
24
46
|
if (scope === "project") return join(process.cwd(), "opencode.json");
|
|
@@ -26,7 +48,7 @@ function openCodePath(scope) {
|
|
|
26
48
|
const b = join(homedir(), ".config", "opencode", "config.json");
|
|
27
49
|
return existsSync(b) && !existsSync(a) ? b : a;
|
|
28
50
|
}
|
|
29
|
-
function patchMcpJson(filePath, serverName, entry, key) {
|
|
51
|
+
function patchMcpJson(filePath, serverName, entry, key, environment) {
|
|
30
52
|
let config = {};
|
|
31
53
|
if (existsSync(filePath)) {
|
|
32
54
|
try {
|
|
@@ -38,7 +60,15 @@ function patchMcpJson(filePath, serverName, entry, key) {
|
|
|
38
60
|
if (key === "mcp") {
|
|
39
61
|
if (!config["$schema"]) config["$schema"] = "https://opencode.ai/config.json";
|
|
40
62
|
const mcp = config.mcp ?? {};
|
|
41
|
-
|
|
63
|
+
const mcpEntry = {
|
|
64
|
+
type: "local",
|
|
65
|
+
command: entry.args ? [entry.command, ...entry.args] : [entry.command],
|
|
66
|
+
enabled: true
|
|
67
|
+
};
|
|
68
|
+
if (environment && Object.keys(environment).length > 0) {
|
|
69
|
+
mcpEntry.environment = environment;
|
|
70
|
+
}
|
|
71
|
+
mcp[serverName] = mcpEntry;
|
|
42
72
|
config.mcp = mcp;
|
|
43
73
|
} else {
|
|
44
74
|
const servers = config[key] ?? {};
|
|
@@ -55,7 +85,7 @@ function installSkill(dirs) {
|
|
|
55
85
|
console.log(`SKILL.md \u2192 ${dir}`);
|
|
56
86
|
}
|
|
57
87
|
}
|
|
58
|
-
function installForClient(client, scope) {
|
|
88
|
+
function installForClient(client, scope, apiKey) {
|
|
59
89
|
switch (client) {
|
|
60
90
|
case "claude-code": {
|
|
61
91
|
try {
|
|
@@ -92,7 +122,7 @@ function installForClient(client, scope) {
|
|
|
92
122
|
}
|
|
93
123
|
case "opencode": {
|
|
94
124
|
const file = openCodePath(scope);
|
|
95
|
-
patchMcpJson(file, "rentline-sandbox", MCP_ENTRY, "mcp");
|
|
125
|
+
patchMcpJson(file, "rentline-sandbox", MCP_ENTRY, "mcp", apiKey ? { SANDBOX_API_KEY: apiKey } : void 0);
|
|
96
126
|
console.log(`Patched ${file}`);
|
|
97
127
|
installSkill([
|
|
98
128
|
join(homedir(), ".config", "opencode", "skills", "rentline-sandbox"),
|
|
@@ -189,7 +219,7 @@ async function runSetup(opts = {}) {
|
|
|
189
219
|
const idx = parseInt(choice);
|
|
190
220
|
client = isNaN(idx) ? choice : clients[idx - 1] ?? "other";
|
|
191
221
|
}
|
|
192
|
-
installForClient(client, opts.scope ?? "user");
|
|
222
|
+
installForClient(client, opts.scope ?? "user", apiKey);
|
|
193
223
|
rl?.close();
|
|
194
224
|
console.log("\nSetup complete. Restart your AI client to load the Rentline Sandbox MCP server.\n");
|
|
195
225
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
createClient,
|
|
3
4
|
getApiKey,
|
|
4
5
|
getApiUrl,
|
|
5
6
|
requireConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
createClient
|
|
9
|
-
} from "./chunk-X3OZHOPM.js";
|
|
7
|
+
} from "./chunk-BOF5UB2I.js";
|
|
10
8
|
|
|
11
9
|
// src/commands/trade.ts
|
|
12
10
|
function client(cmd) {
|
package/package.json
CHANGED
package/dist/chunk-JQL6X6FZ.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
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
|
-
|
|
51
|
-
export {
|
|
52
|
-
DEFAULT_API_URL,
|
|
53
|
-
loadConfig,
|
|
54
|
-
requireConfig,
|
|
55
|
-
saveConfig,
|
|
56
|
-
deleteConfig,
|
|
57
|
-
getApiUrl,
|
|
58
|
-
getApiKey
|
|
59
|
-
};
|
package/dist/config-7QGCVVHS.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
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
|
-
};
|
package/dist/config-YHSAFZLC.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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
|
-
};
|