@retasc/cli 1.46.0 → 1.48.0
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/CHANGELOG.md +44 -0
- package/dist/commands/bind.js +3 -0
- package/dist/commands/mcp.js +53 -0
- package/dist/index.js +28 -2
- package/dist/lib/harness.js +30 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,50 @@ release commits and the issues they reference.
|
|
|
6
6
|
|
|
7
7
|
Dates are the npm publish date. Each entry names the RTSC issue behind it.
|
|
8
8
|
|
|
9
|
+
## 1.48.0 (2026-09-08)
|
|
10
|
+
|
|
11
|
+
- **RTSC-859** — `retasc key mint --hosted`: a key now says at mint what it is for.
|
|
12
|
+
`workspace` (the default, and what every bind door says explicitly) expects the local
|
|
13
|
+
watchdog; `hosted` is for a cloud agent or CI that will never have one.
|
|
14
|
+
|
|
15
|
+
Why: a workspace key sent raw to the server with no proxy is two things at once and the
|
|
16
|
+
server cannot tell which, a hosted agent working as documented or a laptop whose config
|
|
17
|
+
went stale. Both got the same advice on every claim, run `bind`, which a hosted agent
|
|
18
|
+
cannot do. With `kind` on the row, a hosted key is told the one true thing (renew your
|
|
19
|
+
own leases) and shows "no folder (hosted)" on the Agents page as a fact rather than a
|
|
20
|
+
gap; a workspace key with no runner gets the firm remedy, because for that row it is
|
|
21
|
+
true. A key that said nothing, which includes every key minted before this and any bare
|
|
22
|
+
`key mint` since, keeps the old hedged text, since nothing on the row can say which it
|
|
23
|
+
is. `--hosted` and `--install` are refused together: one wires a local watchdog, the
|
|
24
|
+
other says there will never be one.
|
|
25
|
+
|
|
26
|
+
`retasc mcp install --no-watchdog` is not deprecated (it writes exactly a hosted
|
|
27
|
+
deployment's config) but now says, once, that a key used that way should be minted
|
|
28
|
+
`--hosted`.
|
|
29
|
+
|
|
30
|
+
## 1.47.0 (2026-09-08)
|
|
31
|
+
|
|
32
|
+
- **RTSC-864** — `retasc key mint` now prints every remote-key config block a hosted
|
|
33
|
+
agent can paste, including the TOML one that did not exist.
|
|
34
|
+
|
|
35
|
+
A key is shown once, and until now that was all `key mint` printed. Anyone wiring a
|
|
36
|
+
host that cannot run our proxy (a claude.ai connector, Codex Cloud, CI) had to invent
|
|
37
|
+
the config, and for two of the six harnesses it was not inventable: `tomlBlock` emitted
|
|
38
|
+
only the stdio proxy shape, so Codex and Grok users had nothing correct to paste at all.
|
|
39
|
+
The new `tomlHttpBlock` fills that in, and all three blocks now print next to the key
|
|
40
|
+
while it is still on the screen.
|
|
41
|
+
|
|
42
|
+
The two TOML dialects are separate on purpose, and the reason is the dangerous part.
|
|
43
|
+
Verified against both real binaries: Codex reads `[mcp_servers.retasc.http_headers]`
|
|
44
|
+
and Grok reads `[mcp_servers.retasc.headers]`, and each loads the other's file without
|
|
45
|
+
a warning while ignoring the header. The result is a server that reports itself
|
|
46
|
+
enabled and configured, and returns UNAUTHORIZED on the first tool call.
|
|
47
|
+
|
|
48
|
+
The blocks also say, next to themselves, that a key belongs in a user-scope config or
|
|
49
|
+
a platform secret store and never in a tracked `.mcp.json`, which the repo's pre-commit
|
|
50
|
+
hook refuses anyway. Where a container DOES have an environment, the proxy with
|
|
51
|
+
`RETASC_MCP_KEY` set is still the better door: it renews leases for you.
|
|
52
|
+
|
|
9
53
|
## 1.46.0 (2026-09-08)
|
|
10
54
|
|
|
11
55
|
- **RTSC-855** — `retasc doctor` and `retasc setup` now say whether the **Retasc Agent
|
package/dist/commands/bind.js
CHANGED
|
@@ -515,6 +515,9 @@ export async function completeWorkspaceSetup(args) {
|
|
|
515
515
|
projectId: projectId,
|
|
516
516
|
agentName: opts.agent,
|
|
517
517
|
runtime: opts.runtime ?? "claude-code",
|
|
518
|
+
// RTSC-859 — bind is the folder door, so this is a workspace key, and the row
|
|
519
|
+
// says so rather than leaving it to the server's default.
|
|
520
|
+
kind: "workspace",
|
|
518
521
|
// RTSC-810 — named after the FOLDER, the leaf only, exactly as the setup-token
|
|
519
522
|
// door has done since RTSC-532: the Keys list is the folder map (`client-a →
|
|
520
523
|
// ENG`), and the old prefix-plus-"key" name told nobody which folder held it.
|
package/dist/commands/mcp.js
CHANGED
|
@@ -4,6 +4,9 @@ import { join } from "node:path";
|
|
|
4
4
|
import { resolveLauncher, launcherNote, portableLauncher, } from "../lib/launcher.js";
|
|
5
5
|
import { VERSION } from "../version.js";
|
|
6
6
|
import { hasClaudeLocalPlaceholder } from "../lib/binding.js";
|
|
7
|
+
// The TOML dialects live with the rest of the TOML knowledge (harness.ts), not here:
|
|
8
|
+
// the same file has to keep Codex's and Grok's config shapes straight for the proxy form.
|
|
9
|
+
import { tomlHttpBlock } from "../lib/harness.js";
|
|
7
10
|
export const SERVER_NAME = "retasc";
|
|
8
11
|
/** Normalize a user-supplied scope string. `user` (global) is refused and
|
|
9
12
|
* downgraded to `local`, loudly — per-folder binding is the only right way. */
|
|
@@ -29,6 +32,47 @@ export function mcpServerEntry(url, key) {
|
|
|
29
32
|
export function mcpConfigBlock(url, key) {
|
|
30
33
|
return JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpServerEntry(url, key) } }, null, 2);
|
|
31
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Every direct-form block, labelled with the harnesses that read it (RTSC-864).
|
|
37
|
+
*
|
|
38
|
+
* The direct form is for a host that cannot spawn our proxy at all, such as a claude.ai
|
|
39
|
+
* connector, Codex Cloud or a hosted OpenCode, where an HTTP URL and a bearer key is the
|
|
40
|
+
* only door. Two config languages and three dialects, because `tomlHttpBlock` documents
|
|
41
|
+
* that Codex and Grok silently ignore each other's header key.
|
|
42
|
+
*
|
|
43
|
+
* Printed by `key mint` in the same breath as the key itself, and NOT only when some
|
|
44
|
+
* install failed: the key is shown once, so every form a person might have to paste it
|
|
45
|
+
* into has to be on the screen while it is still readable. Sending them to a doc after
|
|
46
|
+
* the one credential they were given has scrolled away is how a key gets re-minted.
|
|
47
|
+
*/
|
|
48
|
+
export function directFormBlocks(url, key) {
|
|
49
|
+
return [
|
|
50
|
+
"Remote-key config, for a host that cannot run a local process",
|
|
51
|
+
"(claude.ai connectors, Codex Cloud, CI, any hosted agent):",
|
|
52
|
+
"",
|
|
53
|
+
" JSON, for Claude Code, Cursor, OpenCode and Gemini CLI:",
|
|
54
|
+
"",
|
|
55
|
+
mcpConfigBlock(url, key),
|
|
56
|
+
"",
|
|
57
|
+
" TOML, for Codex (~/.codex/config.toml):",
|
|
58
|
+
"",
|
|
59
|
+
tomlHttpBlock(url, key, "codex"),
|
|
60
|
+
"",
|
|
61
|
+
" TOML, for Grok (~/.grok/config.toml):",
|
|
62
|
+
"",
|
|
63
|
+
tomlHttpBlock(url, key, "grok"),
|
|
64
|
+
"",
|
|
65
|
+
// The hook is not a style preference and the reason belongs next to the block that
|
|
66
|
+
// would trip it: a committed key is readable by everyone who ever clones the repo,
|
|
67
|
+
// and stays readable in git history after it is deleted.
|
|
68
|
+
"These blocks carry a live credential. Put them in the harness's own user-scope",
|
|
69
|
+
"config or the platform's secret store, never in a tracked `.mcp.json`. Retasc's own",
|
|
70
|
+
"repo refuses one at pre-commit, because a key committed once stays readable in git",
|
|
71
|
+
"history long after it is deleted.",
|
|
72
|
+
"Where the host DOES give you an environment, prefer the proxy with `RETASC_MCP_KEY`",
|
|
73
|
+
"set as a secret: it keeps your leases alive for you.",
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
32
76
|
/**
|
|
33
77
|
* The stdio server entry that runs the liveness watchdog proxy (RTSC-44). The
|
|
34
78
|
* harness spawns `retasc mcp-proxy`, which forwards to the remote MCP and keeps
|
|
@@ -223,6 +267,15 @@ export function installMcp(opts) {
|
|
|
223
267
|
console.log("Two things it does not cover, both safe (they fall back to the normal 30-minute lease timeout): if the proxy stops, renewal stops; and a claim made elsewhere — another terminal, direct HTTP MCP, or a previous proxy before a restart — is renewed by nobody. Heartbeat or checkpoint those yourself.");
|
|
224
268
|
return;
|
|
225
269
|
}
|
|
270
|
+
// RTSC-859 — this is the direct HTTP form, no watchdog, which is exactly a HOSTED
|
|
271
|
+
// deployment's shape. It is the correct config for one (RTSC-864 documents it), and it
|
|
272
|
+
// is NOT deprecated. But the key it wires does not know what it is for unless it was
|
|
273
|
+
// minted `--hosted`, and a workspace key used this way is told on every claim to go
|
|
274
|
+
// and wire the watchdog it is deliberately running without. Say so once, here, where
|
|
275
|
+
// the choice is being made, rather than warning on every claim afterwards.
|
|
276
|
+
console.log("\nNo watchdog: nothing on a machine will renew this key's claims. If this key runs " +
|
|
277
|
+
"from a cloud agent or CI, mint it with `retasc key mint --hosted` so Retasc knows " +
|
|
278
|
+
"and stops advising it to run `bind`; a hosted key is expected to heartbeat itself.");
|
|
226
279
|
const res = tryClaudeCli(opts.url, opts.key, scope);
|
|
227
280
|
if (res.ok) {
|
|
228
281
|
console.log(`✓ Registered MCP server "${SERVER_NAME}" with Claude Code (scope: ${scope}).`);
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
import { VERSION } from "./version.js";
|
|
6
6
|
import { selfCommand, versionStamp } from "./lib/launcher.js";
|
|
7
7
|
import { loadConfig, patchConfig, saveConfig, configPath, isLoggedIn } from "./config.js";
|
|
8
|
-
import { installMcp, noteRuntimeIsALabel, normalizeScope } from "./commands/mcp.js";
|
|
8
|
+
import { installMcp, noteRuntimeIsALabel, normalizeScope, directFormBlocks } from "./commands/mcp.js";
|
|
9
9
|
import { runSetup, printSetup } from "./commands/setup.js";
|
|
10
10
|
import { installGate, resolveGatePrefix } from "./commands/gate.js";
|
|
11
11
|
import { claimAction, releaseAction } from "./commands/claim.js";
|
|
@@ -365,10 +365,22 @@ key
|
|
|
365
365
|
.option("--agent <name>", "Agent member name (default: auto, \"{you}'s {runtime}\")")
|
|
366
366
|
.option("--runtime <runtime>", "Label for this agent in the Dash (claude-code | codex | grok | …). Does NOT choose where MCP config is written — `retasc setup` wires every harness on the machine.", "claude-code")
|
|
367
367
|
.option("--name <label>", "Key label")
|
|
368
|
+
// RTSC-859 — say at mint that this key will never have a local runner. Without it
|
|
369
|
+
// the server cannot tell a cloud agent working as documented from a laptop whose
|
|
370
|
+
// config went stale, and hands both the same advice: run `bind`, which a hosted
|
|
371
|
+
// agent cannot.
|
|
372
|
+
.option("--hosted", "This key runs somewhere with no local install (a cloud agent, CI): never told to run bind, never mistaken for a stale setup")
|
|
368
373
|
.option("--install", "Also wire the key into your agent via MCP")
|
|
369
374
|
.option("--scope <scope>", "MCP install scope if --install: local | project", "local")
|
|
370
375
|
.action(async (opts) => {
|
|
371
376
|
requireLogin();
|
|
377
|
+
// RTSC-859 — the two flags contradict each other. `--install` wires a local watchdog;
|
|
378
|
+
// `--hosted` declares there will never be one. Accepting both produced a row the Dash
|
|
379
|
+
// labelled "no local watchdog" for a laptop that was running one, silenced its
|
|
380
|
+
// stale-CLI nudge, and had the proxy's `name_workspace` refused on every start.
|
|
381
|
+
if (opts.hosted && opts.install) {
|
|
382
|
+
fail(new Error("--hosted and --install contradict each other: --install wires a local watchdog, --hosted says this key will never have one. Pick one."));
|
|
383
|
+
}
|
|
372
384
|
try {
|
|
373
385
|
const res = (await api.mintKey({
|
|
374
386
|
orgId: opts.orgId,
|
|
@@ -376,11 +388,25 @@ key
|
|
|
376
388
|
agentName: opts.agent,
|
|
377
389
|
runtime: opts.runtime,
|
|
378
390
|
keyName: opts.name,
|
|
391
|
+
// RTSC-859 — said only when the caller actually said something. `--hosted` is
|
|
392
|
+
// the word for a cloud agent or CI. `--install` wires the watchdog right here, so
|
|
393
|
+
// that key is a workspace key by construction. A BARE `key mint` says nothing:
|
|
394
|
+
// it is the documented hosted path (RTSC-864, llms.txt) AND the manual-wiring
|
|
395
|
+
// path, and stamping `workspace` on it sent every cloud agent minted that way a
|
|
396
|
+
// firm "re-run bind in that folder" for a folder that never existed. Absent keeps
|
|
397
|
+
// the honest hedge, which is exactly right for a key we know nothing about.
|
|
398
|
+
...(opts.hosted ? { kind: "hosted" } : opts.install ? { kind: "workspace" } : {}),
|
|
379
399
|
}));
|
|
380
400
|
console.log(`✓ Minted key: ${res.key}`);
|
|
381
401
|
console.log(" (Shown once — store it now.)");
|
|
402
|
+
// RTSC-864: every paste form, now, while the key is still on the screen. A hosted
|
|
403
|
+
// agent or a container is the reason most people run this command at all, and
|
|
404
|
+
// until now it printed a bare key and left them to invent the config, which for
|
|
405
|
+
// Codex and Grok was not inventable, since no direct-form TOML block existed.
|
|
406
|
+
const cfg = loadConfig();
|
|
407
|
+
console.log("");
|
|
408
|
+
console.log(directFormBlocks(cfg.mcpUrl, res.key));
|
|
382
409
|
if (opts.install) {
|
|
383
|
-
const cfg = loadConfig();
|
|
384
410
|
console.log("");
|
|
385
411
|
noteRuntimeIsALabel(opts.runtime);
|
|
386
412
|
installMcp({ url: cfg.mcpUrl, key: res.key, scope: normalizeScope(opts.scope), watchdog: true });
|
package/dist/lib/harness.js
CHANGED
|
@@ -166,6 +166,36 @@ export function tomlBlock(entry) {
|
|
|
166
166
|
env,
|
|
167
167
|
].join("\n");
|
|
168
168
|
}
|
|
169
|
+
/** The header table key each dialect actually reads. */
|
|
170
|
+
const HTTP_HEADER_TABLE = {
|
|
171
|
+
codex: "http_headers",
|
|
172
|
+
grok: "headers",
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* The DIRECT-form `[mcp_servers.retasc]` block: talk to the remote MCP over HTTP with a
|
|
176
|
+
* bearer key, no local process (RTSC-864).
|
|
177
|
+
*
|
|
178
|
+
* The TOML counterpart of `mcpConfigBlock`, and the block that did not exist: `tomlBlock`
|
|
179
|
+
* above emits only the stdio proxy shape, so a Codex or Grok user on a host that cannot
|
|
180
|
+
* spawn a process (claude.ai connectors, Codex Cloud, a hosted OpenCode) had nothing
|
|
181
|
+
* correct to paste and no way to tell that from the JSON form.
|
|
182
|
+
*
|
|
183
|
+
* The key is written literally, exactly as the JSON block writes it, because the host
|
|
184
|
+
* this is pasted into is the one that has no environment for us to read. Both tools can
|
|
185
|
+
* take the key from the environment instead where there IS one (Codex:
|
|
186
|
+
* `bearer_token_env_var = "RETASC_MCP_KEY"`; Grok expands `${RETASC_MCP_KEY}` inside a
|
|
187
|
+
* header value), so prefer that, and never paste either form into a file the repo tracks.
|
|
188
|
+
*/
|
|
189
|
+
export function tomlHttpBlock(url, key, dialect) {
|
|
190
|
+
return [
|
|
191
|
+
`[mcp_servers.${SERVER_NAME}]`,
|
|
192
|
+
`url = ${JSON.stringify(url)}`,
|
|
193
|
+
`enabled = true`,
|
|
194
|
+
``,
|
|
195
|
+
`[mcp_servers.${SERVER_NAME}.${HTTP_HEADER_TABLE[dialect]}]`,
|
|
196
|
+
`Authorization = ${JSON.stringify(`Bearer ${key}`)}`,
|
|
197
|
+
].join("\n");
|
|
198
|
+
}
|
|
169
199
|
function tomlHarness(id, label, path, bin, skills) {
|
|
170
200
|
return {
|
|
171
201
|
id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retasc/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "Retasc CLI — the issue tracker AI agents pull work from. Sign in with GitHub or Google, create projects, mint agent API keys, and wire your agent to the Retasc MCP server in one command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|