@trusty-squire/mcp 0.9.19-rc.2 → 0.9.19-rc.21
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/README.md +15 -33
- package/dist/api-client.d.ts +6 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js.map +1 -1
- package/dist/bin.js +4 -10
- package/dist/bin.js.map +1 -1
- package/dist/bot/agent.d.ts +24 -2
- package/dist/bot/agent.d.ts.map +1 -1
- package/dist/bot/agent.js +634 -55
- package/dist/bot/agent.js.map +1 -1
- package/dist/bot/browser.d.ts +7 -0
- package/dist/bot/browser.d.ts.map +1 -1
- package/dist/bot/browser.js +182 -3
- package/dist/bot/browser.js.map +1 -1
- package/dist/bot/credential-extraction-flow.d.ts +2 -0
- package/dist/bot/credential-extraction-flow.d.ts.map +1 -1
- package/dist/bot/credential-extraction-flow.js +71 -1
- package/dist/bot/credential-extraction-flow.js.map +1 -1
- package/dist/bot/form-fill.d.ts.map +1 -1
- package/dist/bot/form-fill.js +11 -0
- package/dist/bot/form-fill.js.map +1 -1
- package/dist/bot/google-login.d.ts.map +1 -1
- package/dist/bot/google-login.js +37 -1
- package/dist/bot/google-login.js.map +1 -1
- package/dist/bot/index.d.ts +1 -0
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js +1 -0
- package/dist/bot/index.js.map +1 -1
- package/dist/bot/login-state.d.ts +2 -1
- package/dist/bot/login-state.d.ts.map +1 -1
- package/dist/bot/login-state.js +22 -5
- package/dist/bot/login-state.js.map +1 -1
- package/dist/bot/nav-search.d.ts.map +1 -1
- package/dist/bot/nav-search.js +9 -0
- package/dist/bot/nav-search.js.map +1 -1
- package/dist/bot/post-signup-flow.d.ts.map +1 -1
- package/dist/bot/post-signup-flow.js +21 -0
- package/dist/bot/post-signup-flow.js.map +1 -1
- package/dist/bot/post-signup-recovery-state.d.ts +3 -0
- package/dist/bot/post-signup-recovery-state.d.ts.map +1 -1
- package/dist/bot/post-signup-recovery-state.js +3 -0
- package/dist/bot/post-signup-recovery-state.js.map +1 -1
- package/dist/bot/provision-session.d.ts +116 -1
- package/dist/bot/provision-session.d.ts.map +1 -1
- package/dist/bot/provision-session.js +885 -41
- package/dist/bot/provision-session.js.map +1 -1
- package/dist/bot/redact.d.ts.map +1 -1
- package/dist/bot/redact.js +25 -2
- package/dist/bot/redact.js.map +1 -1
- package/dist/bot/replay-skill.d.ts +6 -0
- package/dist/bot/replay-skill.d.ts.map +1 -1
- package/dist/bot/replay-skill.js +39 -5
- package/dist/bot/replay-skill.js.map +1 -1
- package/dist/bot/skill-hint.d.ts +7 -0
- package/dist/bot/skill-hint.d.ts.map +1 -0
- package/dist/bot/skill-hint.js +105 -0
- package/dist/bot/skill-hint.js.map +1 -0
- package/dist/bot/terminal-gate.d.ts +3 -1
- package/dist/bot/terminal-gate.d.ts.map +1 -1
- package/dist/bot/terminal-gate.js +19 -0
- package/dist/bot/terminal-gate.js.map +1 -1
- package/dist/install/agents.d.ts.map +1 -1
- package/dist/install/agents.js +12 -2
- package/dist/install/agents.js.map +1 -1
- package/dist/install/cli.d.ts +14 -2
- package/dist/install/cli.d.ts.map +1 -1
- package/dist/install/cli.js +346 -150
- package/dist/install/cli.js.map +1 -1
- package/dist/install/interactive.d.ts +9 -3
- package/dist/install/interactive.d.ts.map +1 -1
- package/dist/install/interactive.js +80 -140
- package/dist/install/interactive.js.map +1 -1
- package/dist/install/proxy-url.d.ts +2 -0
- package/dist/install/proxy-url.d.ts.map +1 -0
- package/dist/install/proxy-url.js +20 -0
- package/dist/install/proxy-url.js.map +1 -0
- package/dist/install/ui.js +1 -1
- package/dist/install/ui.js.map +1 -1
- package/dist/session.d.ts +3 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js.map +1 -1
- package/dist/skill-registry-client.d.ts +8 -0
- package/dist/skill-registry-client.d.ts.map +1 -1
- package/dist/skill-registry-client.js +70 -53
- package/dist/skill-registry-client.js.map +1 -1
- package/dist/tools/index.d.ts +1 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +10 -19
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/provision-any.d.ts +3 -0
- package/dist/tools/provision-any.d.ts.map +1 -1
- package/dist/tools/provision-any.js +162 -32
- package/dist/tools/provision-any.js.map +1 -1
- package/dist/tools/provision-drive.d.ts +121 -5
- package/dist/tools/provision-drive.d.ts.map +1 -1
- package/dist/tools/provision-drive.js +339 -48
- package/dist/tools/provision-drive.js.map +1 -1
- package/dist/tools/store-credential.d.ts +5 -0
- package/dist/tools/store-credential.d.ts.map +1 -1
- package/dist/tools/store-credential.js +5 -0
- package/dist/tools/store-credential.js.map +1 -1
- package/package.json +1 -3
- package/dist/bot/telegram-notify.d.ts +0 -8
- package/dist/bot/telegram-notify.d.ts.map +0 -1
- package/dist/bot/telegram-notify.js +0 -134
- package/dist/bot/telegram-notify.js.map +0 -1
package/dist/install/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// Setup CLI —
|
|
1
|
+
// Setup CLI — connect / settings / logout / login subcommands.
|
|
2
2
|
//
|
|
3
|
-
// npx @trusty-squire/mcp
|
|
3
|
+
// npx @trusty-squire/mcp connect --target=claude-code
|
|
4
4
|
// Issues a machine token, then opens the trustysquire install-
|
|
5
5
|
// confirm page in the bot's own Chrome. The user signs in there
|
|
6
|
-
// once
|
|
6
|
+
// once with Google — that single sign-in does TWO things:
|
|
7
7
|
// (a) trustysquire claims the install and binds the machine to
|
|
8
8
|
// the user's account, and
|
|
9
9
|
// (b) the bot's Chrome profile gains a provider session it can
|
|
@@ -26,14 +26,7 @@
|
|
|
26
26
|
// their own browser (CI / scripted installs)
|
|
27
27
|
// --proxy-url=<url> bake a residential proxy into the MCP config's
|
|
28
28
|
// env (UNIVERSAL_BOT_PROXY_URL)
|
|
29
|
-
// --registry
|
|
30
|
-
// (default: https://registry.trustysquire.ai).
|
|
31
|
-
// Baked into the MCP config's env as
|
|
32
|
-
// TRUSTY_SQUIRE_REGISTRY_URL so the Tier-2
|
|
33
|
-
// router is on out of the box.
|
|
34
|
-
// --no-registry omit TRUSTY_SQUIRE_REGISTRY_URL from the
|
|
35
|
-
// config → mcp skips the router entirely and
|
|
36
|
-
// every signup goes through the universal bot
|
|
29
|
+
// --no-registry disable managed registry participation
|
|
37
30
|
//
|
|
38
31
|
// Pure module — `runCli()` is invoked by bin.ts. No shebang, no
|
|
39
32
|
// entrypoint guard, no top-level execution.
|
|
@@ -41,7 +34,6 @@ import process from "node:process";
|
|
|
41
34
|
import { cpSync, rmSync } from "node:fs";
|
|
42
35
|
import { homedir } from "node:os";
|
|
43
36
|
import { dirname, join } from "node:path";
|
|
44
|
-
import { createInterface } from "node:readline";
|
|
45
37
|
import { loadHarvesterEnvFile } from "../operator-env.js";
|
|
46
38
|
import { fileURLToPath } from "node:url";
|
|
47
39
|
import { installInitiate, installPoll, issueMachineToken } from "../api-client.js";
|
|
@@ -50,42 +42,35 @@ import { AGENTS, detectInstalledAgents, writeClaudeCodePermissions, } from "./ag
|
|
|
50
42
|
import { detectAsn } from "../bot/index.js";
|
|
51
43
|
import { detectActiveProviderSessions, ensureOAuthSession, openInstallConfirmInBotChrome, } from "../bot/google-login.js";
|
|
52
44
|
import {} from "../bot/oauth-providers.js";
|
|
53
|
-
import { clearAllProviderMarkers, clearProviderCookies, clearProviderLoggedIn, loggedInProviders, markProviderLoggedIn, } from "../bot/login-state.js";
|
|
45
|
+
import { clearAllProviderMarkers, clearBrowserProfile, clearProviderCookies, clearProviderLoggedIn, loggedInProviders, markProviderLoggedIn, } from "../bot/login-state.js";
|
|
46
|
+
import { waitForProfileFree } from "../bot/profile.js";
|
|
54
47
|
import { VERSION } from "../version.js";
|
|
55
48
|
import * as ui from "./ui.js";
|
|
56
|
-
import { runInteractiveSetup, shouldRunInteractive, showOutro } from "./interactive.js";
|
|
49
|
+
import { runInteractiveSetup, runSettingsSetup, shouldRunInteractive, showOutro, } from "./interactive.js";
|
|
57
50
|
import chalk from "chalk";
|
|
51
|
+
import { normalizeProxyUrl } from "./proxy-url.js";
|
|
58
52
|
const DEFAULT_API_BASE = process.env.TRUSTY_SQUIRE_API_BASE ?? "https://trusty-squire-api.fly.dev";
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
// (fail-open by design, but a worse experience than just using the
|
|
63
|
-
// closed loop).
|
|
64
|
-
const DEFAULT_REGISTRY_URL = process.env.TRUSTY_SQUIRE_REGISTRY_URL ?? "https://registry.trustysquire.ai";
|
|
53
|
+
// Managed skill-registry URL. Advanced setup decides whether this is written
|
|
54
|
+
// into the MCP config; the URL itself is product-owned and not user-editable.
|
|
55
|
+
const DEFAULT_REGISTRY_URL = "https://registry.trustysquire.ai";
|
|
65
56
|
function parseArgs(argv) {
|
|
66
57
|
const positional = argv.filter((a) => !a.startsWith("--"));
|
|
67
|
-
// `connect`
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
// otherwise behaves identically. Default (no positional) → `connect`
|
|
71
|
-
// because the most common invocation is `npx @trusty-squire/mcp` with
|
|
72
|
-
// no args, and that should still kick off the setup flow.
|
|
73
|
-
let command = positional[0] ?? "connect";
|
|
58
|
+
// Default (no positional) → `connect` because the most common invocation is
|
|
59
|
+
// `npx @trusty-squire/mcp` with no args, and that should kick off setup.
|
|
60
|
+
const command = positional[0] ?? "connect";
|
|
74
61
|
if (command === "install") {
|
|
75
|
-
|
|
76
|
-
"This alias still works but will be removed in a future major. " +
|
|
77
|
-
"Update your docs/scripts to: `npx @trusty-squire/mcp connect`.");
|
|
78
|
-
command = "connect";
|
|
62
|
+
rejectDeprecatedCli("`install` has been removed. Use `npx @trusty-squire/mcp connect`.");
|
|
79
63
|
}
|
|
80
64
|
let target;
|
|
81
65
|
let apiBase = DEFAULT_API_BASE;
|
|
82
66
|
let proxyUrl;
|
|
83
|
-
let registryUrl;
|
|
84
67
|
let noRegistry = false;
|
|
68
|
+
let registryConfigured = false;
|
|
85
69
|
let providerArg;
|
|
86
70
|
let profileDir;
|
|
87
71
|
let skipBrowser = false;
|
|
88
72
|
let forceRelogin = false;
|
|
73
|
+
let forceReloginProvider;
|
|
89
74
|
let noInteractive = false;
|
|
90
75
|
for (const arg of argv) {
|
|
91
76
|
if (arg.startsWith("--target=")) {
|
|
@@ -103,13 +88,23 @@ function parseArgs(argv) {
|
|
|
103
88
|
apiBase = arg.slice("--api-base=".length);
|
|
104
89
|
}
|
|
105
90
|
else if (arg.startsWith("--proxy-url=")) {
|
|
106
|
-
|
|
91
|
+
const rawProxyUrl = arg.slice("--proxy-url=".length);
|
|
92
|
+
const normalized = normalizeProxyUrl(rawProxyUrl);
|
|
93
|
+
if (rawProxyUrl.length > 0 && normalized === undefined) {
|
|
94
|
+
console.error("invalid --proxy-url. Use http://user:pass@host:port or socks5://host:port.");
|
|
95
|
+
process.exit(64);
|
|
96
|
+
}
|
|
97
|
+
proxyUrl = normalized;
|
|
107
98
|
}
|
|
108
99
|
else if (arg.startsWith("--registry-url=")) {
|
|
109
|
-
|
|
100
|
+
rejectDeprecatedCli("`--registry-url` has been removed. Trusty Squire uses the managed skill registry.");
|
|
110
101
|
}
|
|
111
102
|
else if (arg === "--no-registry") {
|
|
112
103
|
noRegistry = true;
|
|
104
|
+
registryConfigured = true;
|
|
105
|
+
}
|
|
106
|
+
else if (arg === "--registry") {
|
|
107
|
+
rejectDeprecatedCli("`--registry` has been removed because the managed registry is enabled by default.");
|
|
113
108
|
}
|
|
114
109
|
else if (arg.startsWith("--provider=")) {
|
|
115
110
|
const p = arg.slice("--provider=".length);
|
|
@@ -119,18 +114,23 @@ function parseArgs(argv) {
|
|
|
119
114
|
else if (arg.startsWith("--profile-dir=")) {
|
|
120
115
|
profileDir = arg.slice("--profile-dir=".length);
|
|
121
116
|
}
|
|
122
|
-
else if (arg === "--skip-browser"
|
|
123
|
-
// --skip-login kept as an alias for the 0.5.0 spelling so any
|
|
124
|
-
// scripted callers still work.
|
|
117
|
+
else if (arg === "--skip-browser") {
|
|
125
118
|
skipBrowser = true;
|
|
126
119
|
}
|
|
120
|
+
else if (arg === "--skip-login") {
|
|
121
|
+
rejectDeprecatedCli("`--skip-login` has been removed. Use `--skip-browser`.");
|
|
122
|
+
}
|
|
127
123
|
else if (arg === "--force-relogin") {
|
|
128
124
|
forceRelogin = true;
|
|
129
125
|
}
|
|
126
|
+
else if (arg.startsWith("--force-relogin=")) {
|
|
127
|
+
forceRelogin = true;
|
|
128
|
+
const p = arg.slice("--force-relogin=".length);
|
|
129
|
+
if (p === "google" || p === "github")
|
|
130
|
+
forceReloginProvider = p;
|
|
131
|
+
}
|
|
130
132
|
else if (arg === "--skip-secondary") {
|
|
131
|
-
|
|
132
|
-
// collapsed step 1 + step 2 into one browser session, so there
|
|
133
|
-
// is no "secondary" stage left to skip.
|
|
133
|
+
rejectDeprecatedCli("`--skip-secondary` has been removed; connect is single-stage.");
|
|
134
134
|
}
|
|
135
135
|
else if (arg === "--no-interactive") {
|
|
136
136
|
noInteractive = true;
|
|
@@ -141,21 +141,25 @@ function parseArgs(argv) {
|
|
|
141
141
|
apiBase,
|
|
142
142
|
skipBrowser,
|
|
143
143
|
forceRelogin,
|
|
144
|
+
...(forceReloginProvider !== undefined ? { forceReloginProvider } : {}),
|
|
144
145
|
noRegistry,
|
|
146
|
+
...(registryConfigured ? { registryConfigured } : {}),
|
|
145
147
|
noInteractive,
|
|
146
148
|
};
|
|
147
149
|
if (target !== undefined)
|
|
148
150
|
args.target = target;
|
|
149
151
|
if (proxyUrl !== undefined && proxyUrl.length > 0)
|
|
150
152
|
args.proxyUrl = proxyUrl;
|
|
151
|
-
if (registryUrl !== undefined && registryUrl.length > 0)
|
|
152
|
-
args.registryUrl = registryUrl;
|
|
153
153
|
if (providerArg !== undefined)
|
|
154
154
|
args.providerArg = providerArg;
|
|
155
155
|
if (profileDir !== undefined && profileDir.length > 0)
|
|
156
156
|
args.profileDir = profileDir;
|
|
157
157
|
return args;
|
|
158
158
|
}
|
|
159
|
+
function rejectDeprecatedCli(message) {
|
|
160
|
+
console.error(`[trusty-squire] ${message}`);
|
|
161
|
+
process.exit(64);
|
|
162
|
+
}
|
|
159
163
|
function isAgentTarget(s) {
|
|
160
164
|
// Source of truth is AGENTS — adding/removing a target there auto-
|
|
161
165
|
// propagates here, so a new agent (or a removed one) can't drift the
|
|
@@ -168,23 +172,14 @@ function isAgentTarget(s) {
|
|
|
168
172
|
// that points at a permanent path. Use the absolute bin.js path
|
|
169
173
|
// directly. Deterministic, offline, fast.
|
|
170
174
|
//
|
|
171
|
-
// 2. Ephemeral
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
// `npx <tarball-url>` (the GitHub-Release test pattern). The
|
|
180
|
-
// version isn't on npm, so case 2 would fail with `ETARGET`.
|
|
181
|
-
// We instead copy the package out of the ephemeral cache into
|
|
182
|
-
// `~/.trusty-squire/lib/mcp` and write a `node <stable>/dist/bin.js`
|
|
183
|
-
// launch. The stable copy survives npx-cache cleanup; if the user
|
|
184
|
-
// re-installs they overwrite it.
|
|
185
|
-
//
|
|
186
|
-
// Prerelease detection: semver prerelease versions carry a `-` (e.g.
|
|
187
|
-
// `0.6.0-rc.1`). Stable versions don't. Cheap, reliable.
|
|
175
|
+
// 2. Ephemeral — the CLI was invoked via npx, which copies the package into
|
|
176
|
+
// npx's throwaway cache. The cache CAN get swept, so never pin the cache
|
|
177
|
+
// path into a host agent config. Instead write
|
|
178
|
+
// `npx -y @trusty-squire/mcp@<version> server`, which re-resolves the exact
|
|
179
|
+
// published version on each agent launch. This matters for RCs: prerelease
|
|
180
|
+
// versions are published to npm on the `next` tag, so treating every
|
|
181
|
+
// prerelease as a non-registry tarball leaves Goose pointing at dead npx
|
|
182
|
+
// cache paths and stale tool schemas.
|
|
188
183
|
/**
|
|
189
184
|
* Copy an npx-style node_modules tree to a stable location.
|
|
190
185
|
*
|
|
@@ -221,11 +216,14 @@ function resolveServerLaunch() {
|
|
|
221
216
|
if (!ephemeral) {
|
|
222
217
|
return { command: process.execPath, args: [binPath, "server"] };
|
|
223
218
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
219
|
+
return { command: "npx", args: ["-y", `@trusty-squire/mcp@${VERSION}`, "server"] };
|
|
220
|
+
}
|
|
221
|
+
// Historical fallback for GitHub-release tarball installs. The normal install
|
|
222
|
+
// path no longer calls this because RCs are published to npm and host configs
|
|
223
|
+
// must not pin npx cache paths. Kept exported for the regression tests around
|
|
224
|
+
// copying broken npx symlink trees.
|
|
225
|
+
function resolveCopiedNpxServerLaunch(binPath) {
|
|
226
|
+
// Copy the package PLUS the
|
|
229
227
|
// ephemeral cache's entire `node_modules` to a stable location.
|
|
230
228
|
//
|
|
231
229
|
// We need both because the package imports @modelcontextprotocol/sdk,
|
|
@@ -286,6 +284,9 @@ export async function runCli(argv) {
|
|
|
286
284
|
case "login":
|
|
287
285
|
await login(args);
|
|
288
286
|
return;
|
|
287
|
+
case "settings":
|
|
288
|
+
await settings(args);
|
|
289
|
+
return;
|
|
289
290
|
case "help":
|
|
290
291
|
printHelp();
|
|
291
292
|
return;
|
|
@@ -295,11 +296,58 @@ export async function runCli(argv) {
|
|
|
295
296
|
process.exit(64);
|
|
296
297
|
}
|
|
297
298
|
}
|
|
299
|
+
async function settings(args) {
|
|
300
|
+
const storage = await openSessionStorage();
|
|
301
|
+
const session = await storage.read();
|
|
302
|
+
if (session === null) {
|
|
303
|
+
ui.fail(`No local Trusty Squire session found. Run ${ui.code("npx @trusty-squire/mcp connect")} first.`);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
if (!args.noInteractive && process.stdin.isTTY === true) {
|
|
307
|
+
const picker = await runSettingsSetup({
|
|
308
|
+
...(args.target !== undefined ? { initialTarget: args.target } : {}),
|
|
309
|
+
...(session.proxy_url !== undefined ? { initialProxyUrl: session.proxy_url } : {}),
|
|
310
|
+
initialRegistryEnabled: session.consent_skillify_telemetry === true,
|
|
311
|
+
initialConsentOperatorInboxOtp: session.consent_operator_inbox_otp === true,
|
|
312
|
+
});
|
|
313
|
+
args.target = picker.target;
|
|
314
|
+
if (picker.proxyUrl !== undefined)
|
|
315
|
+
args.proxyUrl = picker.proxyUrl;
|
|
316
|
+
args.noRegistry = !picker.registryEnabled;
|
|
317
|
+
args.advancedConfigured = true;
|
|
318
|
+
args.consentOperatorInboxOtp = picker.consentOperatorInboxOtp === true;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
if (args.target === undefined) {
|
|
322
|
+
ui.fail(`Pass ${ui.code("--target=<agent>")} when running settings outside an interactive terminal.`);
|
|
323
|
+
process.exit(64);
|
|
324
|
+
}
|
|
325
|
+
if (args.registryConfigured !== true) {
|
|
326
|
+
args.noRegistry = session.consent_skillify_telemetry !== true;
|
|
327
|
+
}
|
|
328
|
+
if (args.proxyUrl === undefined && session.proxy_url !== undefined) {
|
|
329
|
+
args.proxyUrl = session.proxy_url;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const target = await resolveTarget(args.target);
|
|
333
|
+
const agent = AGENTS[target];
|
|
334
|
+
const updated = {
|
|
335
|
+
...session,
|
|
336
|
+
saved_at: new Date().toISOString(),
|
|
337
|
+
consent_skillify_telemetry: !args.noRegistry,
|
|
338
|
+
consent_operator_inbox_otp: args.consentOperatorInboxOtp === true,
|
|
339
|
+
...(args.proxyUrl !== undefined && args.proxyUrl.trim().length > 0
|
|
340
|
+
? { proxy_url: args.proxyUrl.trim() }
|
|
341
|
+
: {}),
|
|
342
|
+
};
|
|
343
|
+
await storage.write(updated);
|
|
344
|
+
await writeAgentConfig(target, agent, args);
|
|
345
|
+
ui.success(`${agent.display_name} settings saved.`);
|
|
346
|
+
}
|
|
298
347
|
async function connect(args) {
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
// the rest of this function is unchanged.
|
|
348
|
+
// Interactive picker (clack). Walks the user through agent + advanced setup
|
|
349
|
+
// before the browser install ceremony fires. The picker fills in args so the
|
|
350
|
+
// rest of this function is unchanged.
|
|
303
351
|
const wantInteractive = !args.noInteractive &&
|
|
304
352
|
shouldRunInteractive({
|
|
305
353
|
hasTty: process.stdin.isTTY === true,
|
|
@@ -311,12 +359,11 @@ async function connect(args) {
|
|
|
311
359
|
// 0.8.2 — picker no longer asks about OAuth providers. The
|
|
312
360
|
// install wizard rendered in the bot's Chrome handles the
|
|
313
361
|
// Google + (optional) GitHub flow directly; the CLI just
|
|
314
|
-
// surfaces agent +
|
|
362
|
+
// surfaces agent + advanced.
|
|
315
363
|
const picker = await runInteractiveSetup({
|
|
316
364
|
...(args.target !== undefined ? { initialTarget: args.target } : {}),
|
|
317
365
|
...(args.proxyUrl !== undefined ? { initialProxyUrl: args.proxyUrl } : {}),
|
|
318
|
-
|
|
319
|
-
registryEnabled: !args.noRegistry,
|
|
366
|
+
initialRegistryEnabled: !args.noRegistry,
|
|
320
367
|
});
|
|
321
368
|
args.target = picker.target;
|
|
322
369
|
args.llmChoice = picker.llmChoice;
|
|
@@ -324,11 +371,10 @@ async function connect(args) {
|
|
|
324
371
|
args.byokKey = picker.byokKey;
|
|
325
372
|
if (picker.proxyUrl !== undefined)
|
|
326
373
|
args.proxyUrl = picker.proxyUrl;
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
args.registryUrl = picker.registryUrl;
|
|
374
|
+
args.noRegistry = !picker.registryEnabled;
|
|
375
|
+
args.advancedConfigured = picker.advancedConfigured;
|
|
376
|
+
if (picker.consentOperatorInboxOtp !== undefined) {
|
|
377
|
+
args.consentOperatorInboxOtp = picker.consentOperatorInboxOtp;
|
|
332
378
|
}
|
|
333
379
|
}
|
|
334
380
|
else {
|
|
@@ -337,26 +383,33 @@ async function connect(args) {
|
|
|
337
383
|
}
|
|
338
384
|
const target = await resolveTarget(args.target);
|
|
339
385
|
const agent = AGENTS[target];
|
|
340
|
-
// Preflight: an existing install
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
386
|
+
// Preflight: an existing install is "connected" only when BOTH the
|
|
387
|
+
// account-bound plumbing still works and the bot profile has a confirmed
|
|
388
|
+
// Google session. A bare machine/agent token can talk to Trusty Squire, but
|
|
389
|
+
// it cannot act as the user at third-party sites, so it must not skip the
|
|
390
|
+
// browser confirm. Pass --force-relogin to bypass (e.g. to switch Google).
|
|
345
391
|
if (!args.forceRelogin) {
|
|
346
392
|
const preflight = await checkAlreadyProvisioned();
|
|
347
393
|
if (preflight !== null) {
|
|
348
394
|
ui.divider();
|
|
395
|
+
await hydrateArgsFromStoredPreferences(args);
|
|
396
|
+
await ensureConsentRecorded(consentFromArgs(args), args.advancedConfigured === true);
|
|
349
397
|
await writeAgentConfig(target, agent, args);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
`(run ${ui.code("npx @trusty-squire/mcp login")} to enable ` +
|
|
354
|
-
`OAuth-preferring signups).`;
|
|
355
|
-
ui.success(`${provNote} ${agent.display_name} config refreshed.`);
|
|
398
|
+
ui.success(`Already connected (${preflight.providers.join(" + ")}). ` +
|
|
399
|
+
`${agent.display_name} config refreshed.`);
|
|
400
|
+
printProviderState(preflight.providers);
|
|
356
401
|
// Backfill connected_providers from the bot-side marker on
|
|
357
402
|
// pre-rc.5 sessions, so the preflight cache is current.
|
|
358
403
|
for (const p of preflight.providers)
|
|
359
404
|
await recordConnectedProvider(p);
|
|
405
|
+
// "Skipped but dead" notice (DESIGN-connect-session-validation): we
|
|
406
|
+
// short-circuited because Google is valid, but if the bot's GitHub session
|
|
407
|
+
// validated as dead, say so — a dead session must never be silently
|
|
408
|
+
// hidden. GitHub is optional, so we don't raise the ceremony for it; the
|
|
409
|
+
// user refreshes it explicitly when a GitHub-only service needs it.
|
|
410
|
+
if (!preflight.providers.includes("github")) {
|
|
411
|
+
ui.hint(`GitHub session is not active — run ${ui.code("npx @trusty-squire/mcp connect --force-relogin=github")} if a service needs GitHub.`);
|
|
412
|
+
}
|
|
360
413
|
ui.hint(`Pass ${ui.code("--force-relogin")} to switch accounts.`);
|
|
361
414
|
return;
|
|
362
415
|
}
|
|
@@ -364,13 +417,64 @@ async function connect(args) {
|
|
|
364
417
|
console.warn("");
|
|
365
418
|
console.warn("Opening the Trusty Squire install page in a browser. " +
|
|
366
419
|
"The page walks you through signing in with Google and (optionally) GitHub.");
|
|
367
|
-
// --force-relogin means "redo the OAuth dance from scratch"
|
|
368
|
-
//
|
|
369
|
-
//
|
|
420
|
+
// --force-relogin means "redo the OAuth dance from scratch". The scoped
|
|
421
|
+
// form clears only one provider; bare --force-relogin is the full-profile
|
|
422
|
+
// account-switch escape hatch.
|
|
370
423
|
if (args.forceRelogin) {
|
|
371
|
-
|
|
372
|
-
|
|
424
|
+
if (args.forceReloginProvider !== undefined) {
|
|
425
|
+
clearProviderLoggedIn(args.forceReloginProvider);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
clearAllProviderMarkers();
|
|
429
|
+
}
|
|
430
|
+
const free = await waitForProfileFree(undefined, {
|
|
431
|
+
deadlineMs: 120_000,
|
|
432
|
+
onWait: () => ui.hint("Waiting for the bot browser to finish before clearing the old session…"),
|
|
433
|
+
});
|
|
434
|
+
if (!free) {
|
|
435
|
+
ui.fail("The bot browser is still using the profile, so I can't safely switch accounts yet. " +
|
|
436
|
+
"Close the running signup/login browser and retry with --force-relogin.");
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
if (args.forceReloginProvider !== undefined) {
|
|
440
|
+
await clearProviderCookies(undefined, args.forceReloginProvider);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
clearBrowserProfile();
|
|
444
|
+
await clearProviderCookies();
|
|
445
|
+
}
|
|
373
446
|
}
|
|
447
|
+
// DESIGN-connect-session-validation: a SCOPED force-relogin=github on an
|
|
448
|
+
// already-bound account is a GitHub-only login — Google's gate is already
|
|
449
|
+
// satisfied (the account is bound + its session is what we'd re-bind), so we
|
|
450
|
+
// must NOT drag the Google-first account-binding confirm page into it. Route
|
|
451
|
+
// straight to the provider-scoped login (the `login --provider=github` path):
|
|
452
|
+
// it opens a GitHub-only login (account chooser, since cookies were cleared
|
|
453
|
+
// above), never touching Google. Falls through to the full confirm flow only
|
|
454
|
+
// when the account isn't bound yet (nothing to skip) or the scope is google
|
|
455
|
+
// (which can re-bind the account and so needs the claim).
|
|
456
|
+
if (args.forceReloginProvider === "github") {
|
|
457
|
+
const bound = await checkAlreadyBound();
|
|
458
|
+
if (bound) {
|
|
459
|
+
ui.heading("Sign in to GitHub");
|
|
460
|
+
const result = await ensureOAuthSession({
|
|
461
|
+
provider: "github",
|
|
462
|
+
apiBaseUrl: args.apiBase,
|
|
463
|
+
forceOpen: true,
|
|
464
|
+
});
|
|
465
|
+
if (result.status === "logged_in" || result.status === "already_valid") {
|
|
466
|
+
await recordConnectedProvider("github");
|
|
467
|
+
await writeAgentConfig(target, agent, args);
|
|
468
|
+
ui.success("Signed in to GitHub. The bot is ready; config refreshed.");
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
ui.fail(result.status === "timeout"
|
|
472
|
+
? `GitHub sign-in timed out. Retry: ${ui.code("npx @trusty-squire/mcp connect --force-relogin=github")}`
|
|
473
|
+
: `GitHub sign-in failed: ${result.detail ?? "unknown error"}`);
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const consent = consentFromArgs(args);
|
|
374
478
|
// Detect egress class so the asn rides along in the install payload
|
|
375
479
|
// (API uses it to correlate captcha failures with network class).
|
|
376
480
|
// Best-effort: a failure returns null and the install continues.
|
|
@@ -407,6 +511,8 @@ async function connect(args) {
|
|
|
407
511
|
api_base_url: args.apiBase,
|
|
408
512
|
saved_at: new Date().toISOString(),
|
|
409
513
|
machine_token: machine.machine_token,
|
|
514
|
+
consent_skillify_telemetry: consent.skillifyTelemetry,
|
|
515
|
+
consent_operator_inbox_otp: consent.operatorInboxOtp,
|
|
410
516
|
};
|
|
411
517
|
const session = await runInstallClaim(args.apiBase, target, baseSession, args.skipBrowser);
|
|
412
518
|
if (session === null) {
|
|
@@ -417,6 +523,10 @@ async function connect(args) {
|
|
|
417
523
|
const storage = await openSessionStorage();
|
|
418
524
|
await storage.write(session);
|
|
419
525
|
ui.success(`Session saved (${storage.backendName()})`);
|
|
526
|
+
args.noRegistry = session.consent_skillify_telemetry !== true;
|
|
527
|
+
args.consentOperatorInboxOtp = session.consent_operator_inbox_otp === true;
|
|
528
|
+
if (session.proxy_url !== undefined)
|
|
529
|
+
args.proxyUrl = session.proxy_url;
|
|
420
530
|
// 0.8.1 — the bot's persistent profile may have a stale provider
|
|
421
531
|
// marker from a previous install (the marker is sticky on disk).
|
|
422
532
|
// The install confirm above only seeded one provider (whichever
|
|
@@ -429,6 +539,8 @@ async function connect(args) {
|
|
|
429
539
|
start: "Checking provider sessions",
|
|
430
540
|
done: "Provider sessions checked",
|
|
431
541
|
fail: () => "Provider session check failed (continuing)",
|
|
542
|
+
// validate=true: confirm each session is LIVE (not just cookie-present),
|
|
543
|
+
// so a dead-but-present GitHub session isn't shown as connected.
|
|
432
544
|
task: () => detectActiveProviderSessions(),
|
|
433
545
|
});
|
|
434
546
|
if (actual !== null) {
|
|
@@ -446,8 +558,10 @@ async function connect(args) {
|
|
|
446
558
|
// reach GitHub even if we mis-identified the live state.
|
|
447
559
|
}
|
|
448
560
|
// Backfill connected_providers from the (now-fresh) bot-side marker.
|
|
449
|
-
|
|
561
|
+
const providers = loggedInProviders();
|
|
562
|
+
for (const p of providers)
|
|
450
563
|
await recordConnectedProvider(p);
|
|
564
|
+
printProviderState(providers);
|
|
451
565
|
await writeAgentConfig(target, agent, args);
|
|
452
566
|
if (args.skipBrowser) {
|
|
453
567
|
ui.panel(`--skip-browser was set, so the bot's Chrome didn't observe your sign-in.\n` +
|
|
@@ -468,6 +582,27 @@ async function connect(args) {
|
|
|
468
582
|
ui.panel(closingLine, { color: "wine" });
|
|
469
583
|
}
|
|
470
584
|
}
|
|
585
|
+
async function hydrateArgsFromStoredPreferences(args) {
|
|
586
|
+
if (args.advancedConfigured === true)
|
|
587
|
+
return;
|
|
588
|
+
try {
|
|
589
|
+
const session = await (await openSessionStorage()).read();
|
|
590
|
+
if (session === null)
|
|
591
|
+
return;
|
|
592
|
+
args.noRegistry = session.consent_skillify_telemetry !== true;
|
|
593
|
+
args.consentOperatorInboxOtp = session.consent_operator_inbox_otp === true;
|
|
594
|
+
if (session.proxy_url !== undefined)
|
|
595
|
+
args.proxyUrl = session.proxy_url;
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
// Best-effort. Missing preferences fall back to the privacy-safe defaults.
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function printProviderState(providers) {
|
|
602
|
+
const have = new Set(providers);
|
|
603
|
+
ui.hint(` Provider sessions: Google ${have.has("google") ? "connected" : "not connected"}; ` +
|
|
604
|
+
`GitHub ${have.has("github") ? "connected" : "not connected"}`);
|
|
605
|
+
}
|
|
471
606
|
// Runs the browser-based install confirm flow.
|
|
472
607
|
//
|
|
473
608
|
// Default path (`skipBrowser=false`): opens the trustysquire confirm
|
|
@@ -483,9 +618,9 @@ async function connect(args) {
|
|
|
483
618
|
// provider session afterwards — the user must run `mcp login` before
|
|
484
619
|
// their first OAuth signup. This path is for CI / scripted installs.
|
|
485
620
|
// True when the local session + bot profile already carry everything
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
621
|
+
// connect would establish. Returns the list of confirmed provider sessions, or
|
|
622
|
+
// null when anything's missing. Best-effort: any read/probe error returns null
|
|
623
|
+
// and the caller proceeds with the normal browser flow.
|
|
489
624
|
// Probe whether the stored agent token still authenticates. Agent
|
|
490
625
|
// sessions have a 24h absolute cap, so a token can be PRESENT in the
|
|
491
626
|
// session file but already dead on the server. Treating present as
|
|
@@ -507,19 +642,15 @@ export async function agentTokenStillValid(apiBaseUrl, token, fetchImpl = fetch)
|
|
|
507
642
|
return true;
|
|
508
643
|
}
|
|
509
644
|
}
|
|
510
|
-
// Pure gate for the `connect` fast path: given the read session, whether
|
|
511
|
-
//
|
|
512
|
-
// whether connect can (re)write the MCP config WITHOUT a browser
|
|
645
|
+
// Pure gate for the `connect` fast path: given the read session, whether its
|
|
646
|
+
// agent token still validated, and the bot's confirmed provider sessions,
|
|
647
|
+
// decide whether connect can (re)write the MCP config WITHOUT a browser
|
|
648
|
+
// re-claim.
|
|
513
649
|
//
|
|
514
|
-
//
|
|
515
|
-
//
|
|
516
|
-
//
|
|
517
|
-
// is
|
|
518
|
-
// browser re-claim on any box whose bot profile had no login yet (a fresh
|
|
519
|
-
// machine that restored session.json, or a headless box where the claim
|
|
520
|
-
// browser can't run) — leaving the user with NO config written and no
|
|
521
|
-
// clear reason. So an empty `providers` is fine; we still return
|
|
522
|
-
// provisioned. Exported for unit tests.
|
|
650
|
+
// Account-bound plumbing is not enough. The product-level connection is the
|
|
651
|
+
// bot-profile Google session: without it the host agent may be able to call the
|
|
652
|
+
// Trusty Squire API, but cannot act as the user at third-party services.
|
|
653
|
+
// GitHub is optional headroom; Google is the required primary identity.
|
|
523
654
|
export function decideProvisioned(session, tokenValid, providers) {
|
|
524
655
|
if (session === null ||
|
|
525
656
|
session.machine_token === undefined ||
|
|
@@ -529,6 +660,8 @@ export function decideProvisioned(session, tokenValid, providers) {
|
|
|
529
660
|
}
|
|
530
661
|
if (!tokenValid)
|
|
531
662
|
return null;
|
|
663
|
+
if (!providers.includes("google"))
|
|
664
|
+
return null;
|
|
532
665
|
return { providers };
|
|
533
666
|
}
|
|
534
667
|
async function checkAlreadyProvisioned() {
|
|
@@ -545,12 +678,60 @@ async function checkAlreadyProvisioned() {
|
|
|
545
678
|
}
|
|
546
679
|
// Don't short-circuit on a present-but-expired token — re-pair.
|
|
547
680
|
const stillValid = await agentTokenStillValid(session.api_base_url, session.agent_session_token);
|
|
548
|
-
|
|
681
|
+
// Probe the profile cookies instead of trusting the marker. The marker is a
|
|
682
|
+
// cache that can lie after logout/expiry; connect is rare enough to pay this
|
|
683
|
+
// cost, and this keeps "Already connected" aligned with the bot's real
|
|
684
|
+
// ability to wear the user's Google identity. validate=true so a dead-but-
|
|
685
|
+
// present GitHub session isn't persisted into connected_providers.
|
|
686
|
+
const providers = await detectActiveProviderSessions();
|
|
687
|
+
await syncConnectedProviders(providers);
|
|
688
|
+
return decideProvisioned(session, stillValid, providers);
|
|
549
689
|
}
|
|
550
690
|
catch {
|
|
551
691
|
return null;
|
|
552
692
|
}
|
|
553
693
|
}
|
|
694
|
+
// Is the account already BOUND? — a valid session (machine + agent token +
|
|
695
|
+
// account id) whose agent token still validates. Unlike checkAlreadyProvisioned
|
|
696
|
+
// this does NOT require a live Google session: it answers "is the install
|
|
697
|
+
// claimed to an account", which is what a scoped GitHub force-relogin needs to
|
|
698
|
+
// know it can skip the Google-binding confirm page.
|
|
699
|
+
async function checkAlreadyBound() {
|
|
700
|
+
try {
|
|
701
|
+
const storage = await openSessionStorage();
|
|
702
|
+
const session = await storage.read();
|
|
703
|
+
if (session === null ||
|
|
704
|
+
session.machine_token === undefined ||
|
|
705
|
+
session.agent_session_token === undefined ||
|
|
706
|
+
session.account_id === undefined) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
return await agentTokenStillValid(session.api_base_url, session.agent_session_token);
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
async function syncConnectedProviders(providers) {
|
|
716
|
+
clearAllProviderMarkers();
|
|
717
|
+
for (const p of providers)
|
|
718
|
+
markProviderLoggedIn(p);
|
|
719
|
+
try {
|
|
720
|
+
const storage = await openSessionStorage();
|
|
721
|
+
const session = await storage.read();
|
|
722
|
+
if (session === null)
|
|
723
|
+
return;
|
|
724
|
+
await storage.write({
|
|
725
|
+
...session,
|
|
726
|
+
connected_providers: [...providers],
|
|
727
|
+
saved_at: new Date().toISOString(),
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
catch {
|
|
731
|
+
// Best-effort — marker/session drift only affects fast-path UX. The next
|
|
732
|
+
// connect/login/provision probe can repair it.
|
|
733
|
+
}
|
|
734
|
+
}
|
|
554
735
|
// Persist `provider` into session.connected_providers (idempotent).
|
|
555
736
|
// Called after a successful ensureOAuthSession so the install
|
|
556
737
|
// preflight on the next run can read both providers off the session
|
|
@@ -579,26 +760,33 @@ async function recordConnectedProvider(provider) {
|
|
|
579
760
|
// which is recoverable.
|
|
580
761
|
}
|
|
581
762
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const suffix = defaultYes ? "[Y/n]" : "[y/N]";
|
|
590
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
763
|
+
function consentFromArgs(args) {
|
|
764
|
+
return {
|
|
765
|
+
skillifyTelemetry: !args.noRegistry,
|
|
766
|
+
operatorInboxOtp: args.consentOperatorInboxOtp === true,
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
async function ensureConsentRecorded(consent, overwrite) {
|
|
591
770
|
try {
|
|
592
|
-
const
|
|
593
|
-
|
|
771
|
+
const storage = await openSessionStorage();
|
|
772
|
+
const session = await storage.read();
|
|
773
|
+
if (session === null)
|
|
774
|
+
return;
|
|
775
|
+
if (!overwrite &&
|
|
776
|
+
session.consent_skillify_telemetry !== undefined &&
|
|
777
|
+
session.consent_operator_inbox_otp !== undefined) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
await storage.write({
|
|
781
|
+
...session,
|
|
782
|
+
saved_at: new Date().toISOString(),
|
|
783
|
+
consent_skillify_telemetry: consent.skillifyTelemetry,
|
|
784
|
+
consent_operator_inbox_otp: consent.operatorInboxOtp,
|
|
594
785
|
});
|
|
595
|
-
const trimmed = answer.trim().toLowerCase();
|
|
596
|
-
if (trimmed.length === 0)
|
|
597
|
-
return defaultYes;
|
|
598
|
-
return trimmed === "y" || trimmed === "yes";
|
|
599
786
|
}
|
|
600
|
-
|
|
601
|
-
|
|
787
|
+
catch {
|
|
788
|
+
// Best-effort. If we can't persist consent, runtime treats missing
|
|
789
|
+
// fields as not approved.
|
|
602
790
|
}
|
|
603
791
|
}
|
|
604
792
|
// Writes the host agent's MCP config — extracted so both the normal
|
|
@@ -616,17 +804,14 @@ async function writeAgentConfig(target, agent, args) {
|
|
|
616
804
|
if (args.proxyUrl !== undefined) {
|
|
617
805
|
env.UNIVERSAL_BOT_PROXY_URL = args.proxyUrl;
|
|
618
806
|
}
|
|
619
|
-
// Skill registry URL
|
|
620
|
-
//
|
|
621
|
-
//
|
|
807
|
+
// Skill registry URL. The endpoint is not user-configurable; Advanced setup
|
|
808
|
+
// controls whether it is written at all. Registry participation is also the
|
|
809
|
+
// user's consent to contribute successful non-personal signup recipes.
|
|
622
810
|
if (!args.noRegistry) {
|
|
623
|
-
env.TRUSTY_SQUIRE_REGISTRY_URL =
|
|
811
|
+
env.TRUSTY_SQUIRE_REGISTRY_URL = DEFAULT_REGISTRY_URL;
|
|
624
812
|
}
|
|
625
|
-
//
|
|
626
|
-
//
|
|
627
|
-
// env; the server's LLM client picks the matching path. The
|
|
628
|
-
// managed-free path omits keys entirely so the server routes
|
|
629
|
-
// through our proxy (the rate-limited /v1/llm/chat endpoint).
|
|
813
|
+
// Legacy LLM env support. Signup navigation is session-agent driven now, so
|
|
814
|
+
// normal installs never set these fields; keep the switch for older callers.
|
|
630
815
|
switch (args.llmChoice) {
|
|
631
816
|
case "byok_openrouter":
|
|
632
817
|
if (args.byokKey !== undefined)
|
|
@@ -665,10 +850,7 @@ async function writeAgentConfig(target, agent, args) {
|
|
|
665
850
|
ui.hint(` Residential proxy baked in: ${args.proxyUrl}`);
|
|
666
851
|
}
|
|
667
852
|
if (args.noRegistry) {
|
|
668
|
-
ui.hint(" Skill registry disabled
|
|
669
|
-
}
|
|
670
|
-
else if (args.registryUrl !== undefined) {
|
|
671
|
-
ui.hint(` Skill registry: ${args.registryUrl}`);
|
|
853
|
+
ui.hint(" Skill registry disabled — every signup goes through the universal bot");
|
|
672
854
|
}
|
|
673
855
|
}
|
|
674
856
|
// A confirm-flow page URL that means the claim is finished and the
|
|
@@ -716,6 +898,9 @@ async function runInstallClaim(apiBase, target, baseSession, skipBrowser) {
|
|
|
716
898
|
state.value = {
|
|
717
899
|
token: status.agent_session_token,
|
|
718
900
|
account_id: status.account_id ?? "",
|
|
901
|
+
...(status.install_preferences !== undefined
|
|
902
|
+
? { preferences: status.install_preferences }
|
|
903
|
+
: {}),
|
|
719
904
|
};
|
|
720
905
|
}
|
|
721
906
|
else if (status.status === "expired") {
|
|
@@ -761,7 +946,7 @@ async function runInstallClaim(apiBase, target, baseSession, skipBrowser) {
|
|
|
761
946
|
if (ok === null)
|
|
762
947
|
return null;
|
|
763
948
|
return {
|
|
764
|
-
...baseSession,
|
|
949
|
+
...applyInstallPreferences(baseSession, ok.preferences),
|
|
765
950
|
api_base_url: apiBase,
|
|
766
951
|
saved_at: new Date().toISOString(),
|
|
767
952
|
agent_session_token: ok.token,
|
|
@@ -792,13 +977,24 @@ async function runInstallClaim(apiBase, target, baseSession, skipBrowser) {
|
|
|
792
977
|
return null;
|
|
793
978
|
}
|
|
794
979
|
return {
|
|
795
|
-
...baseSession,
|
|
980
|
+
...applyInstallPreferences(baseSession, state.value.preferences),
|
|
796
981
|
api_base_url: apiBase,
|
|
797
982
|
saved_at: new Date().toISOString(),
|
|
798
983
|
agent_session_token: state.value.token,
|
|
799
984
|
account_id: state.value.account_id,
|
|
800
985
|
};
|
|
801
986
|
}
|
|
987
|
+
function applyInstallPreferences(baseSession, preferences) {
|
|
988
|
+
if (preferences === undefined)
|
|
989
|
+
return baseSession;
|
|
990
|
+
const proxy = preferences.proxy_url?.trim();
|
|
991
|
+
return {
|
|
992
|
+
...baseSession,
|
|
993
|
+
consent_skillify_telemetry: preferences.registry_enabled === true,
|
|
994
|
+
consent_operator_inbox_otp: preferences.consent_operator_inbox_otp === true,
|
|
995
|
+
...(proxy !== undefined && proxy.length > 0 ? { proxy_url: proxy } : {}),
|
|
996
|
+
};
|
|
997
|
+
}
|
|
802
998
|
async function resolveTarget(explicit) {
|
|
803
999
|
if (explicit !== undefined)
|
|
804
1000
|
return explicit;
|
|
@@ -832,7 +1028,7 @@ async function logout() {
|
|
|
832
1028
|
// Unlike the login stage inside `install`, this command fails loud on
|
|
833
1029
|
// timeout/error — it's the explicit retry path.
|
|
834
1030
|
async function login(args) {
|
|
835
|
-
const provider = args.providerArg
|
|
1031
|
+
const provider = args.providerArg ?? args.forceReloginProvider ?? "google";
|
|
836
1032
|
const label = provider === "github" ? "GitHub" : "Google";
|
|
837
1033
|
ui.heading(`Sign in to ${label}`);
|
|
838
1034
|
// --force-relogin wipes this provider's cookies (via forceOpen below),
|
|
@@ -883,16 +1079,15 @@ function printHelp() {
|
|
|
883
1079
|
console.warn(`${chalk.bold("Commands")}`);
|
|
884
1080
|
console.warn(` ${ui.code("connect")} set up this machine (default)`);
|
|
885
1081
|
console.warn(` ${ui.code("login --provider=<p>")} add a Google or GitHub session`);
|
|
1082
|
+
console.warn(` ${ui.code("settings")} edit registry, OTP, and proxy choices`);
|
|
886
1083
|
console.warn(` ${ui.code("logout")} clear the local session`);
|
|
887
1084
|
console.warn("");
|
|
888
1085
|
console.warn(`${chalk.bold("Flags for connect")}`);
|
|
889
1086
|
console.warn(` --target=<${Object.keys(AGENTS).join("|")}>`);
|
|
890
|
-
console.warn(` --skip-
|
|
891
|
-
console.warn(` --
|
|
892
|
-
console.warn(` --force-relogin switch the bound account`);
|
|
1087
|
+
console.warn(` --skip-browser don't launch a browser (CI mode)`);
|
|
1088
|
+
console.warn(` --force-relogin[=google|github] switch the bound account or one provider`);
|
|
893
1089
|
console.warn(` --proxy-url=<url> bake a residential proxy into the bot env`);
|
|
894
|
-
console.warn(` --registry
|
|
895
|
-
console.warn(` --no-registry disable the Tier-2 router entirely`);
|
|
1090
|
+
console.warn(` --no-registry disable managed registry participation`);
|
|
896
1091
|
console.warn(` --no-interactive skip the TUI picker (use flag defaults only)`);
|
|
897
1092
|
console.warn("");
|
|
898
1093
|
console.warn(`${chalk.bold("Example")}`);
|
|
@@ -912,6 +1107,9 @@ async function pollForClaim(apiBase, setupCode, intervalMsOrOpts = {}, timeoutMs
|
|
|
912
1107
|
return {
|
|
913
1108
|
token: status.agent_session_token,
|
|
914
1109
|
account_id: status.account_id ?? "",
|
|
1110
|
+
...(status.install_preferences !== undefined
|
|
1111
|
+
? { preferences: status.install_preferences }
|
|
1112
|
+
: {}),
|
|
915
1113
|
};
|
|
916
1114
|
}
|
|
917
1115
|
if (status.status === "expired")
|
|
@@ -945,7 +1143,5 @@ function printAsnWarning(asn) {
|
|
|
945
1143
|
return;
|
|
946
1144
|
}
|
|
947
1145
|
}
|
|
948
|
-
|
|
949
|
-
// level. The CLI command got renamed; the function is the same.
|
|
950
|
-
export { connect, connect as install, logout, login, parseArgs, pollForClaim, printAsnWarning, resolveServerLaunch, };
|
|
1146
|
+
export { connect, logout, login, parseArgs, pollForClaim, printAsnWarning, resolveCopiedNpxServerLaunch, resolveServerLaunch, };
|
|
951
1147
|
//# sourceMappingURL=cli.js.map
|