@retasc/cli 1.27.0 → 1.28.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 +12 -0
- package/dist/auth.js +55 -4
- package/dist/commands/bind.js +9 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ 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.28.0 (2026-08-19)
|
|
10
|
+
|
|
11
|
+
- **RTSC-676** — signing in opens your browser. It used to print a URL and an
|
|
12
|
+
eight-character code and leave you to switch windows, navigate, and retype it. Now the
|
|
13
|
+
page opens for you, pre-filled when the provider supplies RFC 8628's complete URL. The
|
|
14
|
+
URL and code are still printed first and always: there is no browser inside a container
|
|
15
|
+
or over SSH, which is where `bind` runs most, so the browser is a convenience layered
|
|
16
|
+
on top and never the only way through. A missing `xdg-open` changes nothing.
|
|
17
|
+
`RETASC_NO_BROWSER=1` turns it off.
|
|
18
|
+
- **RTSC-676** — the finish screen's closing lines are flush left, and it ends with a
|
|
19
|
+
blank line instead of welding the shell prompt to the last thing you read.
|
|
20
|
+
|
|
9
21
|
## 1.27.0 (2026-08-19)
|
|
10
22
|
|
|
11
23
|
- **RTSC-673** — `bind` ends by telling you what to do, not by dumping config. It used to
|
package/dist/auth.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
1
2
|
import { ConvexHttpClient } from "convex/browser";
|
|
2
3
|
import { makeFunctionReference } from "convex/server";
|
|
3
4
|
import { loadConfig, patchConfig } from "./config.js";
|
|
@@ -28,11 +29,61 @@ async function postForm(url, body) {
|
|
|
28
29
|
return res.json();
|
|
29
30
|
}
|
|
30
31
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
31
|
-
/**
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Open a URL in the machine's browser, best effort (RTSC-676).
|
|
34
|
+
*
|
|
35
|
+
* BEST EFFORT is the whole contract. There is no browser inside a container or over
|
|
36
|
+
* SSH, which is where `bind` most often runs, so this must never be the only way
|
|
37
|
+
* through and must never turn a failure of its own into a failure of sign-in. It is
|
|
38
|
+
* spawned detached and every outcome is swallowed: the printed URL and code below are
|
|
39
|
+
* the real path, and this only saves the person who does have a browser a trip through
|
|
40
|
+
* their address bar.
|
|
41
|
+
*
|
|
42
|
+
* `xdg-open` on Linux is frequently absent even under a desktop; that is a miss, not an
|
|
43
|
+
* error. `start` needs a shell on Windows, and its first quoted argument is the window
|
|
44
|
+
* TITLE, hence the empty string.
|
|
45
|
+
*/
|
|
46
|
+
function openBrowser(url) {
|
|
47
|
+
if (process.env.RETASC_NO_BROWSER)
|
|
48
|
+
return;
|
|
49
|
+
const [cmd, args] = process.platform === "darwin"
|
|
50
|
+
? ["open", [url]]
|
|
51
|
+
: process.platform === "win32"
|
|
52
|
+
? ["start", ['""', url]]
|
|
53
|
+
: ["xdg-open", [url]];
|
|
54
|
+
try {
|
|
55
|
+
const child = spawn(cmd, args, {
|
|
56
|
+
stdio: "ignore",
|
|
57
|
+
detached: true,
|
|
58
|
+
shell: process.platform === "win32",
|
|
59
|
+
});
|
|
60
|
+
// Without this the CLI would wait on a browser that outlives it by hours.
|
|
61
|
+
child.unref();
|
|
62
|
+
// A missing `xdg-open` surfaces here rather than as a throw.
|
|
63
|
+
child.on("error", () => { });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* no browser, no shell, no matter — the URL is printed below */
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Print the two lines a human has to act on, identically for both doors, and open the
|
|
71
|
+
* browser for them if this machine has one.
|
|
72
|
+
*
|
|
73
|
+
* The lines are printed FIRST and unconditionally. Someone reading a terminal in a
|
|
74
|
+
* container has to see the URL whatever happened, and an auto-open that silently
|
|
75
|
+
* failed must leave the screen exactly as useful as it was before (RTSC-676).
|
|
76
|
+
*
|
|
77
|
+
* `complete` is RFC 8628's pre-filled URL when the provider sent one. It is opened in
|
|
78
|
+
* preference to the plain one, but never PRINTED in its place: the code stays on its own
|
|
79
|
+
* line so it can be read aloud, retyped on a phone, or pasted into a browser on another
|
|
80
|
+
* machine — none of which a link helps with.
|
|
81
|
+
*/
|
|
82
|
+
function announceCode(where, url, code, complete) {
|
|
33
83
|
console.log(`\n Open: ${url}`);
|
|
34
84
|
console.log(` Enter code: ${code}\n`);
|
|
35
85
|
console.log(` Waiting for ${where} authorization…`);
|
|
86
|
+
openBrowser(complete || url);
|
|
36
87
|
}
|
|
37
88
|
/** Run GitHub's device flow (talking to GitHub directly) and return its access token. */
|
|
38
89
|
async function githubDeviceToken() {
|
|
@@ -46,7 +97,7 @@ async function githubDeviceToken() {
|
|
|
46
97
|
if (!start.device_code) {
|
|
47
98
|
throw new Error(`GitHub device-flow start failed: ${JSON.stringify(start)}`);
|
|
48
99
|
}
|
|
49
|
-
announceCode("GitHub", start.verification_uri, start.user_code);
|
|
100
|
+
announceCode("GitHub", start.verification_uri, start.user_code, start.verification_uri_complete);
|
|
50
101
|
// Poll GitHub for the access token.
|
|
51
102
|
let intervalMs = (start.interval || 5) * 1000;
|
|
52
103
|
const deadline = Date.now() + start.expires_in * 1000;
|
|
@@ -80,7 +131,7 @@ async function githubDeviceToken() {
|
|
|
80
131
|
*/
|
|
81
132
|
async function googleDeviceToken(convex) {
|
|
82
133
|
const start = (await convex.action(googleDeviceStart, {}));
|
|
83
|
-
announceCode("Google", start.verificationUrl, start.userCode);
|
|
134
|
+
announceCode("Google", start.verificationUrl, start.userCode, start.verificationUrlComplete);
|
|
84
135
|
let intervalMs = (start.intervalSeconds || 5) * 1000;
|
|
85
136
|
const deadline = Date.now() + start.expiresInSeconds * 1000;
|
|
86
137
|
while (Date.now() < deadline) {
|
package/dist/commands/bind.js
CHANGED
|
@@ -446,7 +446,7 @@ export async function completeWorkspaceSetup(args) {
|
|
|
446
446
|
// AFTER the confirmation, and after everything that can throw, so it is only ever
|
|
447
447
|
// printed by a run that actually finished. Nothing below it can fail.
|
|
448
448
|
if (isInteractive())
|
|
449
|
-
console.log(`\n${NEXT_STEP}\n\n${whatNow(pfx, emptyProject)}`);
|
|
449
|
+
console.log(`\n${NEXT_STEP}\n\n${whatNow(pfx, emptyProject)}\n`);
|
|
450
450
|
}
|
|
451
451
|
/**
|
|
452
452
|
* The two sentences under `NEXT_STEP`, chosen by whether there is anything to pull yet
|
|
@@ -477,12 +477,15 @@ export function projectIsEmpty(p) {
|
|
|
477
477
|
return p.counter === 0;
|
|
478
478
|
}
|
|
479
479
|
export function whatNow(prefix, empty) {
|
|
480
|
+
// FLUSH LEFT (RTSC-676). Indented, these read as subordinate to the line above them
|
|
481
|
+
// when they are in fact continuing it — the eye takes the block for a nested detail
|
|
482
|
+
// rather than the instruction it is.
|
|
480
483
|
return empty
|
|
481
|
-
?
|
|
482
|
-
`
|
|
483
|
-
`
|
|
484
|
-
: `
|
|
485
|
-
`
|
|
484
|
+
? `${prefix} is empty, so start by talking with the agent. Tell it what you're\n` +
|
|
485
|
+
`building and it will file the work as it goes.\n\n` +
|
|
486
|
+
`Once there's a backlog, saying "next issue" is how you pull from it.`
|
|
487
|
+
: `There's already work in ${prefix}. Say "next issue" and your agent takes\n` +
|
|
488
|
+
`the top unblocked one and starts.`;
|
|
486
489
|
}
|
|
487
490
|
export async function setupFromToken(token, opts, deps = {}) {
|
|
488
491
|
const cfg = loadConfig();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retasc/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.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": {
|