@retasc/cli 1.18.0 → 1.19.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 +31 -0
- package/dist/api.js +19 -0
- package/dist/commands/bind.js +48 -0
- package/dist/index.js +16 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,37 @@ 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.19.0 (2026-08-03)
|
|
10
|
+
|
|
11
|
+
- **RTSC-495** — `retasc bind --setup <code>` sets a folder up with no sign-in and nothing
|
|
12
|
+
to answer, so an agent can do it on behalf of someone who does not use a terminal.
|
|
13
|
+
|
|
14
|
+
The Dash's connect step now hands over a block you paste to your agent, with this command
|
|
15
|
+
inside it. Everything the interactive `bind` would ask was already answered in the
|
|
16
|
+
browser, and the code carries those answers across. An agent's shell is not a TTY, so the
|
|
17
|
+
ordinary path refuses it outright at the first prompt.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
npx @retasc/cli bind --setup rtscsetup_…
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The code is single-use and lives ten minutes. The alternative — pasting a real API key
|
|
24
|
+
into the prompt — needed no backend at all, but would have left a long-lived credential in
|
|
25
|
+
an agent's conversation history forever.
|
|
26
|
+
|
|
27
|
+
A folder that is already connected is refused rather than silently replaced, and the
|
|
28
|
+
refusal happens BEFORE the code is spent, so a mix-up costs nothing.
|
|
29
|
+
|
|
30
|
+
- **RTSC-532** — the agent says which folder it is in, and the key remembers.
|
|
31
|
+
|
|
32
|
+
Binding the wrong folder used to look exactly like success: the agent still called in, so
|
|
33
|
+
the Dash went green, while the folder you actually work in had no Retasc in it. The prompt
|
|
34
|
+
now asks the agent to report its full path and wait before it runs anything, and the
|
|
35
|
+
confirmation names the real path instead of "This folder".
|
|
36
|
+
|
|
37
|
+
The key it creates is named after that folder, so the Keys page shows which folder each
|
|
38
|
+
key belongs to (`client-a`) instead of naming them all after the project (`ENG key`).
|
|
39
|
+
|
|
9
40
|
## 1.18.0 (2026-08-02)
|
|
10
41
|
|
|
11
42
|
- **RTSC-530** — setting up from scratch now asks where your work comes from, and imports it
|
package/dist/api.js
CHANGED
|
@@ -43,6 +43,10 @@ const fns = {
|
|
|
43
43
|
claimableGhosts: makeFunctionReference("ghosts:claimableGhosts"),
|
|
44
44
|
claimGhost: makeFunctionReference("ghosts:claimGhost"),
|
|
45
45
|
dismissGhostPrompt: makeFunctionReference("ghosts:dismissGhostPrompt"),
|
|
46
|
+
// RTSC-495 — the ONLY unauthenticated call the CLI makes. Redeemed by `bind --setup`
|
|
47
|
+
// on a machine that has never signed in, because the human answered everything in the
|
|
48
|
+
// Dash and it is her agent, not her, running this.
|
|
49
|
+
redeemSetupToken: makeFunctionReference("setupToken:redeemSetupToken"),
|
|
46
50
|
};
|
|
47
51
|
function client() {
|
|
48
52
|
const cfg = loadConfig();
|
|
@@ -51,6 +55,18 @@ function client() {
|
|
|
51
55
|
c.setAuth(cfg.token);
|
|
52
56
|
return c;
|
|
53
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* A client that NEVER attaches a session (RTSC-495).
|
|
60
|
+
*
|
|
61
|
+
* `client()` attaches `cfg.token` whenever one exists, and a setup-token redeem must not
|
|
62
|
+
* depend on it either way: the machine usually has no session at all, and where it has a
|
|
63
|
+
* STALE one, sending it risks failing an auth check on a call that never needed identity.
|
|
64
|
+
* Separate function rather than a flag, so "this call is unauthenticated" is visible at
|
|
65
|
+
* the call site instead of buried in an argument.
|
|
66
|
+
*/
|
|
67
|
+
function anonClient() {
|
|
68
|
+
return new ConvexHttpClient(loadConfig().deploymentUrl);
|
|
69
|
+
}
|
|
54
70
|
/**
|
|
55
71
|
* Read a caught backend error into the parts a command wants to show (RTSC-261).
|
|
56
72
|
*
|
|
@@ -178,6 +194,9 @@ export const api = {
|
|
|
178
194
|
runImport: (args) => withAuth(() => client().action(fns.runImport, args)),
|
|
179
195
|
latestImport: (args) => withAuth(() => client().query(fns.latestImport, args)),
|
|
180
196
|
importHistory: (args) => withAuth(() => client().query(fns.importHistory, args)),
|
|
197
|
+
// NOT wrapped in `withAuth`: there is no session to refresh, and its retry path would
|
|
198
|
+
// start a device flow — the exact interactive prompt this whole flow exists to avoid.
|
|
199
|
+
redeemSetupToken: (args) => anonClient().action(fns.redeemSetupToken, args),
|
|
181
200
|
claimableGhosts: (args) => withAuth(() => client().query(fns.claimableGhosts, args)),
|
|
182
201
|
claimGhost: (args) => withAuth(() => client().mutation(fns.claimGhost, args)),
|
|
183
202
|
dismissGhostPrompt: (args) => withAuth(() => client().mutation(fns.dismissGhostPrompt, args)),
|
package/dist/commands/bind.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
1
2
|
import { api, cliError } from "../api.js";
|
|
2
3
|
import { deviceLogin } from "../auth.js";
|
|
3
4
|
import { loadConfig, patchConfig } from "../config.js";
|
|
@@ -416,6 +417,53 @@ export async function completeWorkspaceSetup(args) {
|
|
|
416
417
|
if (isInteractive())
|
|
417
418
|
console.log(`\n${NEXT_STEP}`);
|
|
418
419
|
}
|
|
420
|
+
export async function setupFromToken(token, opts, deps = {}) {
|
|
421
|
+
const cfg = loadConfig();
|
|
422
|
+
const cwd = process.cwd();
|
|
423
|
+
const guard = deps.guard ?? rebindGuard;
|
|
424
|
+
const redeem = deps.redeem ??
|
|
425
|
+
((t, label) => api.redeemSetupToken({ token: t, label }));
|
|
426
|
+
// BEFORE the redeem. `rebindGuard` prints its own explanation and, with no TTY,
|
|
427
|
+
// refuses with exit 1 unless `--yes` — which is exactly the required behaviour, so it
|
|
428
|
+
// is reused rather than restated. No org/project passed: its idempotent-converge
|
|
429
|
+
// shortcut needs ids we do not have yet, and cannot apply to a single-use token anyway.
|
|
430
|
+
const { proceed, existing } = await guard({ cwd, mcpUrl: cfg.mcpUrl, yes: opts.yes });
|
|
431
|
+
if (!proceed)
|
|
432
|
+
return;
|
|
433
|
+
// Resolve (and if needed install) `retasc` BEFORE the token is spent, so a machine that
|
|
434
|
+
// cannot get a working launcher fails while the code is still redeemable.
|
|
435
|
+
const install = await chooseInstall(opts.install);
|
|
436
|
+
const launcher = resolveLauncher({ version: VERSION, install });
|
|
437
|
+
const note = launcherNote(launcher);
|
|
438
|
+
if (note)
|
|
439
|
+
console.log(note);
|
|
440
|
+
// RTSC-532 — the LEAF name, never the full path. It names the key in the Dash, so it
|
|
441
|
+
// has to be recognisable ("client-a → ENG") without publishing where on her disk it
|
|
442
|
+
// sits. `basename` of the cwd is exactly that.
|
|
443
|
+
const redeemed = await redeem(token.trim(), basename(cwd));
|
|
444
|
+
const workspaceId = existing?.workspaceId ?? newWorkspaceId();
|
|
445
|
+
(deps.bind ?? setBinding)(workspaceId, {
|
|
446
|
+
orgId: redeemed.orgId,
|
|
447
|
+
projectId: redeemed.projectId,
|
|
448
|
+
key: redeemed.key,
|
|
449
|
+
url: cfg.mcpUrl,
|
|
450
|
+
prefix: redeemed.prefix,
|
|
451
|
+
orgName: redeemed.orgName,
|
|
452
|
+
boundPath: cwd,
|
|
453
|
+
createdAt: Date.now(),
|
|
454
|
+
});
|
|
455
|
+
(deps.marker ?? installMarker)({ workspaceId, scope: "local", launcher });
|
|
456
|
+
// RTSC-532 — name the PATH, not "this folder". A binding is folder → project, and
|
|
457
|
+
// until this line said which folder, a wrong one was undetectable: the agent still
|
|
458
|
+
// calls in, so the Dash shows success while the folder she actually works in has no
|
|
459
|
+
// Retasc tools. Her agent relays this sentence to her, so it is also the last chance
|
|
460
|
+
// to notice.
|
|
461
|
+
console.log(`✓ ${clean(cwd)} is connected to ${clean(redeemed.orgName)} / ${clean(redeemed.prefix)}.`);
|
|
462
|
+
// NOT gated on isInteractive(), unlike the interactive path's copy of this line. There
|
|
463
|
+
// is never a TTY here, and this is the one instruction the agent has to pass on — the
|
|
464
|
+
// Dash waits on a tool call that cannot happen until the restart does (RTSC-496).
|
|
465
|
+
console.log(`\n${NEXT_STEP}`);
|
|
466
|
+
}
|
|
419
467
|
/**
|
|
420
468
|
* What to do with a workspace that is now set up.
|
|
421
469
|
*
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { loadConfig, patchConfig, saveConfig, configPath, isLoggedIn } from "./c
|
|
|
5
5
|
import { installMcp, normalizeScope } from "./commands/mcp.js";
|
|
6
6
|
import { installGate } from "./commands/gate.js";
|
|
7
7
|
import { claimAction } from "./commands/claim.js";
|
|
8
|
-
import { bindAction } from "./commands/bind.js";
|
|
8
|
+
import { bindAction, setupFromToken } from "./commands/bind.js";
|
|
9
9
|
import { joinAction } from "./commands/join.js";
|
|
10
10
|
import { identityAction } from "./commands/identity.js";
|
|
11
11
|
import { importAction } from "./commands/import.js";
|
|
@@ -183,7 +183,22 @@ program
|
|
|
183
183
|
// is present; this is how a scripted run, or a developer whose own agent runs setup,
|
|
184
184
|
// says no up front instead of being installed onto.
|
|
185
185
|
.option("--no-install", "Don't install `retasc` on this machine; wire the pinned npx launcher instead")
|
|
186
|
+
// RTSC-495 — the agent's door. Everything this command normally asks was already
|
|
187
|
+
// answered in the Dash, so the token stands in for all of it and nothing is prompted.
|
|
188
|
+
.option("--setup <token>", "Complete setup from a Dash setup code — no sign-in, no prompts")
|
|
186
189
|
.action(async (opts) => {
|
|
190
|
+
// BEFORE requireLogin: the whole point is a machine that has never signed in. The
|
|
191
|
+
// token is the authorization, and asking for a session here would refuse every
|
|
192
|
+
// caller this flag exists for.
|
|
193
|
+
//
|
|
194
|
+
// `!== undefined`, not truthiness: `--setup ""` is a stated intent to use a setup
|
|
195
|
+
// code, and it must fail as a bad code. Falling through to the interactive path
|
|
196
|
+
// would answer it with "Not signed in", which is the one diagnosis that sends the
|
|
197
|
+
// reader looking in exactly the wrong place.
|
|
198
|
+
if (opts.setup !== undefined) {
|
|
199
|
+
await setupFromToken(opts.setup, opts).catch(fail);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
187
202
|
requireLogin();
|
|
188
203
|
await bindAction(opts).catch(fail);
|
|
189
204
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retasc/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Retasc CLI \u2014 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": {
|