@retasc/cli 1.1.1 → 1.1.3
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/commands/mcp.js +54 -31
- package/dist/index.js +9 -1
- package/package.json +1 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -58,52 +58,75 @@ export function mcpMarkerEntry(workspaceId) {
|
|
|
58
58
|
export function mcpMarkerConfigBlock(workspaceId) {
|
|
59
59
|
return JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpMarkerEntry(workspaceId) } }, null, 2);
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
// --- `claude mcp add` argv builders (pure, exported for tests) -------------
|
|
62
|
+
// RTSC-99: the stdio builders put SERVER_NAME *before* the variadic `--env`
|
|
63
|
+
// flags. `--env` consumes every following non-`--` token, so a server name after
|
|
64
|
+
// it was eaten as a bogus env var ("Invalid environment variable format: retasc").
|
|
65
|
+
// Name-first + a closing `--` keeps env values bounded and the command intact.
|
|
66
|
+
/** Build `claude mcp add` argv for the HTTP transport. (`url` is a positional
|
|
67
|
+
* BEFORE the variadic `--header`, so the header flag can't swallow it.) */
|
|
68
|
+
export function buildHttpAddArgs(url, key, scope) {
|
|
69
|
+
return [
|
|
64
70
|
"mcp", "add",
|
|
65
71
|
"--transport", "http",
|
|
66
72
|
"--scope", scope,
|
|
67
73
|
SERVER_NAME, url,
|
|
68
74
|
"--header", `Authorization: Bearer ${key}`,
|
|
69
75
|
];
|
|
70
|
-
return runClaudeAdd(args);
|
|
71
76
|
}
|
|
72
|
-
/**
|
|
73
|
-
function
|
|
74
|
-
|
|
77
|
+
/** Build argv for the stdio watchdog proxy (RTSC-44). */
|
|
78
|
+
export function buildWatchdogAddArgs(url, key, scope) {
|
|
79
|
+
return [
|
|
75
80
|
"mcp", "add",
|
|
81
|
+
SERVER_NAME,
|
|
76
82
|
"--transport", "stdio",
|
|
77
83
|
"--scope", scope,
|
|
78
84
|
"--env", `RETASC_MCP_URL=${url}`,
|
|
79
85
|
"--env", `RETASC_MCP_KEY=${key}`,
|
|
80
|
-
|
|
86
|
+
"--", "retasc", "mcp-proxy",
|
|
81
87
|
];
|
|
82
|
-
return runClaudeAdd(args);
|
|
83
88
|
}
|
|
84
|
-
/**
|
|
85
|
-
function
|
|
86
|
-
|
|
89
|
+
/** Build argv for the secret-free stdio marker (RTSC-92). */
|
|
90
|
+
export function buildMarkerAddArgs(workspaceId, scope) {
|
|
91
|
+
return [
|
|
87
92
|
"mcp", "add",
|
|
93
|
+
SERVER_NAME,
|
|
88
94
|
"--transport", "stdio",
|
|
89
95
|
"--scope", scope,
|
|
90
96
|
"--env", `RETASC_WORKSPACE=${workspaceId}`,
|
|
91
|
-
|
|
97
|
+
"--", "retasc", "mcp-proxy",
|
|
92
98
|
];
|
|
93
|
-
|
|
99
|
+
}
|
|
100
|
+
function tryClaudeCli(url, key, scope) {
|
|
101
|
+
return runClaudeAdd(buildHttpAddArgs(url, key, scope));
|
|
102
|
+
}
|
|
103
|
+
function tryClaudeCliWatchdog(url, key, scope) {
|
|
104
|
+
return runClaudeAdd(buildWatchdogAddArgs(url, key, scope));
|
|
105
|
+
}
|
|
106
|
+
function tryClaudeCliMarker(workspaceId, scope) {
|
|
107
|
+
return runClaudeAdd(buildMarkerAddArgs(workspaceId, scope));
|
|
94
108
|
}
|
|
95
109
|
function runClaudeAdd(args) {
|
|
96
110
|
const r = spawnSync("claude", args, { encoding: "utf8" });
|
|
97
|
-
if (r.error)
|
|
98
|
-
|
|
111
|
+
if (r.error) {
|
|
112
|
+
// ENOENT = `claude` not on PATH (genuinely absent). Any other spawn error is
|
|
113
|
+
// a real failure of a present CLI — report it as such, not as "not detected".
|
|
114
|
+
const absent = r.error.code === "ENOENT";
|
|
115
|
+
return { ok: false, absent, msg: absent ? undefined : r.error.message };
|
|
116
|
+
}
|
|
99
117
|
if (r.status !== 0) {
|
|
100
|
-
//
|
|
101
|
-
const msg = (r.stderr || r.stdout || "").trim()
|
|
102
|
-
|
|
103
|
-
console.error(` (claude mcp add: ${msg.split("\n")[0]})`);
|
|
104
|
-
return false;
|
|
118
|
+
// CLI ran but rejected the command — present, not absent. Surface the real reason.
|
|
119
|
+
const msg = (r.stderr || r.stdout || "").trim().split("\n")[0] || `exited ${r.status}`;
|
|
120
|
+
return { ok: false, absent: false, msg };
|
|
105
121
|
}
|
|
106
|
-
return true;
|
|
122
|
+
return { ok: true };
|
|
123
|
+
}
|
|
124
|
+
/** The fallback note printed after writing .mcp.json — names the REAL reason the
|
|
125
|
+
* CLI path didn't take, never falsely claiming the CLI is missing (RTSC-99). */
|
|
126
|
+
export function fallbackNote(res) {
|
|
127
|
+
return res.absent
|
|
128
|
+
? "(Claude Code CLI not detected — wrote .mcp.json instead.)"
|
|
129
|
+
: `(claude mcp add failed${res.msg ? `: ${res.msg}` : ""} — wrote .mcp.json instead.)`;
|
|
107
130
|
}
|
|
108
131
|
/** Merge a given server entry into ./.mcp.json (project scope on disk). */
|
|
109
132
|
function writeProjectMcpJson(entry) {
|
|
@@ -131,28 +154,28 @@ export function installMcp(opts) {
|
|
|
131
154
|
const scope = opts.scope ?? "local";
|
|
132
155
|
// Watchdog mode (RTSC-44): wire the stdio proxy so claims stay alive automatically.
|
|
133
156
|
if (opts.watchdog) {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
157
|
+
const res = tryClaudeCliWatchdog(opts.url, opts.key, scope);
|
|
158
|
+
if (res.ok) {
|
|
136
159
|
console.log(`✓ Registered Retasc with the liveness watchdog (stdio proxy, scope: ${scope}).`);
|
|
137
160
|
}
|
|
138
161
|
else {
|
|
139
162
|
const path = writeProjectMcpJson(mcpProxyEntry(opts.url, opts.key));
|
|
140
163
|
console.log(`✓ Wrote watchdog MCP config to ${path}`);
|
|
141
|
-
console.log(
|
|
164
|
+
console.log(` ${fallbackNote(res)}`);
|
|
142
165
|
}
|
|
143
166
|
console.log("\nWatchdog MCP config (Codex / OpenCode / any stdio MCP client):\n");
|
|
144
167
|
console.log(mcpProxyConfigBlock(opts.url, opts.key));
|
|
145
168
|
console.log("\nThe watchdog keeps your claims alive automatically — no per-claim heartbeats. If it ever isn't running you just fall back to the normal lease timeout (safe).");
|
|
146
169
|
return;
|
|
147
170
|
}
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
171
|
+
const res = tryClaudeCli(opts.url, opts.key, scope);
|
|
172
|
+
if (res.ok) {
|
|
150
173
|
console.log(`✓ Registered MCP server "${SERVER_NAME}" with Claude Code (scope: ${scope}).`);
|
|
151
174
|
}
|
|
152
175
|
else {
|
|
153
176
|
const path = writeProjectMcpJson(mcpServerEntry(opts.url, opts.key));
|
|
154
177
|
console.log(`✓ Wrote MCP config to ${path}`);
|
|
155
|
-
console.log(
|
|
178
|
+
console.log(` ${fallbackNote(res)}`);
|
|
156
179
|
}
|
|
157
180
|
console.log("\nMCP config block (for any other agent/client):\n");
|
|
158
181
|
console.log(mcpConfigBlock(opts.url, opts.key));
|
|
@@ -165,14 +188,14 @@ export function installMcp(opts) {
|
|
|
165
188
|
*/
|
|
166
189
|
export function installMarker(opts) {
|
|
167
190
|
const scope = opts.scope ?? "local";
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
191
|
+
const res = tryClaudeCliMarker(opts.workspaceId, scope);
|
|
192
|
+
if (res.ok) {
|
|
170
193
|
console.log(`✓ Registered Retasc watchdog (secret-free marker, scope: ${scope}).`);
|
|
171
194
|
}
|
|
172
195
|
else {
|
|
173
196
|
const path = writeProjectMcpJson(mcpMarkerEntry(opts.workspaceId));
|
|
174
197
|
console.log(`✓ Wrote secret-free watchdog marker to ${path}`);
|
|
175
|
-
console.log(
|
|
198
|
+
console.log(` ${fallbackNote(res)}`);
|
|
176
199
|
}
|
|
177
200
|
console.log("\nMarker block (any stdio MCP client) — no secret, safe to commit:\n");
|
|
178
201
|
console.log(mcpMarkerConfigBlock(opts.workspaceId));
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
3
6
|
import { loadConfig, patchConfig, saveConfig, configPath, isLoggedIn } from "./config.js";
|
|
4
7
|
import { installMcp, normalizeScope } from "./commands/mcp.js";
|
|
5
8
|
import { installGate } from "./commands/gate.js";
|
|
@@ -11,11 +14,16 @@ import { tidyAction, doneAction } from "./commands/tidy.js";
|
|
|
11
14
|
import { runProxy } from "./proxy.js";
|
|
12
15
|
import { deviceLogin } from "./auth.js";
|
|
13
16
|
import { api } from "./api.js";
|
|
17
|
+
// Single source of truth for the version: read package.json at runtime from the
|
|
18
|
+
// compiled file's location (dist/index.js -> ../package.json). A JSON import won't
|
|
19
|
+
// work here — tsconfig has rootDir "src", so importing ../package.json is outside
|
|
20
|
+
// rootDir and fails tsc.
|
|
21
|
+
const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
|
|
14
22
|
const program = new Command();
|
|
15
23
|
program
|
|
16
24
|
.name("retasc")
|
|
17
25
|
.description("Retasc — sign in, create projects, mint agent API keys, and wire your agent to the MCP server.")
|
|
18
|
-
.version(
|
|
26
|
+
.version(pkg.version);
|
|
19
27
|
function requireLogin() {
|
|
20
28
|
if (!isLoggedIn()) {
|
|
21
29
|
console.error("Not signed in. Run `retasc login` first.");
|
package/package.json
CHANGED