@retasc/cli 1.46.0 → 1.47.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 CHANGED
@@ -6,6 +6,29 @@ 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.47.0 (2026-09-08)
10
+
11
+ - **RTSC-864** — `retasc key mint` now prints every remote-key config block a hosted
12
+ agent can paste, including the TOML one that did not exist.
13
+
14
+ A key is shown once, and until now that was all `key mint` printed. Anyone wiring a
15
+ host that cannot run our proxy (a claude.ai connector, Codex Cloud, CI) had to invent
16
+ the config, and for two of the six harnesses it was not inventable: `tomlBlock` emitted
17
+ only the stdio proxy shape, so Codex and Grok users had nothing correct to paste at all.
18
+ The new `tomlHttpBlock` fills that in, and all three blocks now print next to the key
19
+ while it is still on the screen.
20
+
21
+ The two TOML dialects are separate on purpose, and the reason is the dangerous part.
22
+ Verified against both real binaries: Codex reads `[mcp_servers.retasc.http_headers]`
23
+ and Grok reads `[mcp_servers.retasc.headers]`, and each loads the other's file without
24
+ a warning while ignoring the header. The result is a server that reports itself
25
+ enabled and configured, and returns UNAUTHORIZED on the first tool call.
26
+
27
+ The blocks also say, next to themselves, that a key belongs in a user-scope config or
28
+ a platform secret store and never in a tracked `.mcp.json`, which the repo's pre-commit
29
+ hook refuses anyway. Where a container DOES have an environment, the proxy with
30
+ `RETASC_MCP_KEY` set is still the better door: it renews leases for you.
31
+
9
32
  ## 1.46.0 (2026-09-08)
10
33
 
11
34
  - **RTSC-855** — `retasc doctor` and `retasc setup` now say whether the **Retasc Agent
@@ -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
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";
@@ -379,8 +379,14 @@ key
379
379
  }));
380
380
  console.log(`✓ Minted key: ${res.key}`);
381
381
  console.log(" (Shown once — store it now.)");
382
+ // RTSC-864: every paste form, now, while the key is still on the screen. A hosted
383
+ // agent or a container is the reason most people run this command at all, and
384
+ // until now it printed a bare key and left them to invent the config, which for
385
+ // Codex and Grok was not inventable, since no direct-form TOML block existed.
386
+ const cfg = loadConfig();
387
+ console.log("");
388
+ console.log(directFormBlocks(cfg.mcpUrl, res.key));
382
389
  if (opts.install) {
383
- const cfg = loadConfig();
384
390
  console.log("");
385
391
  noteRuntimeIsALabel(opts.runtime);
386
392
  installMcp({ url: cfg.mcpUrl, key: res.key, scope: normalizeScope(opts.scope), watchdog: true });
@@ -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.46.0",
3
+ "version": "1.47.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": {