relay-companion 0.1.477 → 0.1.478
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.
|
@@ -6,6 +6,7 @@ const path = require("node:path");
|
|
|
6
6
|
const { spawn } = require("node:child_process");
|
|
7
7
|
|
|
8
8
|
const STATUS_VERSION = 1;
|
|
9
|
+
const RUNNING_STATUSES = ["starting", "installing", "waiting_authorization"];
|
|
9
10
|
|
|
10
11
|
function statusPath({ homeDir = os.homedir(), env = process.env } = {}) {
|
|
11
12
|
return env.RELAY_BACKGROUND_INSTALL_STATUS || path.join(env.RELAY_CONFIG_DIR || path.join(homeDir, ".relay"), "companion-install.json");
|
|
@@ -42,7 +43,7 @@ function processAlive(pid) {
|
|
|
42
43
|
|
|
43
44
|
function installationStatus(options = {}) {
|
|
44
45
|
const state = readStatus(options);
|
|
45
|
-
if (
|
|
46
|
+
if (RUNNING_STATUSES.includes(state.status) && !processAlive(state.pid)) {
|
|
46
47
|
return { ...state, status: "failed", reason: "installer_stopped", finishedAt: new Date().toISOString() };
|
|
47
48
|
}
|
|
48
49
|
return state;
|
|
@@ -57,7 +58,7 @@ function startBackgroundInstall({
|
|
|
57
58
|
} = {}) {
|
|
58
59
|
const paths = { homeDir, env };
|
|
59
60
|
const existing = installationStatus(paths);
|
|
60
|
-
if (
|
|
61
|
+
if (RUNNING_STATUSES.includes(existing.status)) {
|
|
61
62
|
return { ok: true, started: false, alreadyRunning: true, status: existing };
|
|
62
63
|
}
|
|
63
64
|
fs.mkdirSync(path.dirname(statusPath(paths)), { recursive: true, mode: 0o700 });
|
|
@@ -86,6 +87,37 @@ function option(argv, name) {
|
|
|
86
87
|
return index >= 0 ? argv[index + 1] : "";
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
// Download and verify first, but never activate or enroll Companion until the
|
|
91
|
+
// helper has verified the browser grant and finished its atomic handoff. Only
|
|
92
|
+
// inspect file existence here; account/consent checks remain in adoption.
|
|
93
|
+
async function waitForAgentAuthorization({
|
|
94
|
+
env = process.env,
|
|
95
|
+
homeDir = os.homedir(),
|
|
96
|
+
exists = fs.existsSync,
|
|
97
|
+
now = Date.now,
|
|
98
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
99
|
+
timeoutMs = 30 * 60 * 1000,
|
|
100
|
+
intervalMs = 1000,
|
|
101
|
+
} = {}) {
|
|
102
|
+
const root = env.RELAY_CONFIG_DIR || path.join(homeDir, ".relay");
|
|
103
|
+
const credential = env.RELAY_AGENT_CONFIG || path.join(root, "agent-protocol.json");
|
|
104
|
+
const pending = env.RELAY_AGENT_AUTHORIZATION || path.join(root, "agent-authorization.json");
|
|
105
|
+
const deadline = now() + timeoutMs;
|
|
106
|
+
const options = { env, homeDir };
|
|
107
|
+
let waiting = false;
|
|
108
|
+
while (exists(pending) || !exists(credential)) {
|
|
109
|
+
if (!waiting) {
|
|
110
|
+
atomicWriteJson(statusPath(options), { ...readStatus(options), status: "waiting_authorization" });
|
|
111
|
+
waiting = true;
|
|
112
|
+
}
|
|
113
|
+
if (now() >= deadline) {
|
|
114
|
+
throw new Error("Companion is downloaded but browser approval is not complete. Finish the Relay connection, then retry background-install.");
|
|
115
|
+
}
|
|
116
|
+
await sleep(Math.min(intervalMs, deadline - now()));
|
|
117
|
+
}
|
|
118
|
+
if (waiting) atomicWriteJson(statusPath(options), { ...readStatus(options), status: "installing" });
|
|
119
|
+
}
|
|
120
|
+
|
|
89
121
|
async function runWorker(argv = process.argv.slice(2), { spawnImpl = spawn, env = process.env } = {}) {
|
|
90
122
|
const entry = path.resolve(option(argv, "--entry") || path.resolve(__dirname, "relay-setup.cjs"));
|
|
91
123
|
if (path.basename(entry) !== "relay-setup.cjs" || !fs.existsSync(entry)) throw new Error("Relay's background installer entry is invalid.");
|
|
@@ -146,4 +178,5 @@ module.exports = {
|
|
|
146
178
|
runWorker,
|
|
147
179
|
startBackgroundInstall,
|
|
148
180
|
statusPath,
|
|
181
|
+
waitForAgentAuthorization,
|
|
149
182
|
};
|
|
@@ -1377,6 +1377,9 @@ async function setup(argv = []) {
|
|
|
1377
1377
|
// archives instead of leaking hundreds of megabytes per interrupted attempt.
|
|
1378
1378
|
removeAbandonedRuntimeDownloads(layout.releasesDir);
|
|
1379
1379
|
const runtime = await stageVerifiedRuntime({ version, platformKey, destination: layout.releaseRoot });
|
|
1380
|
+
if (setupCompatibilityArgs.includes("--agent-protocol") && process.env.RELAY_BACKGROUND_INSTALL_WORKER === "1") {
|
|
1381
|
+
await require("./relay-background-install.cjs").waitForAgentAuthorization();
|
|
1382
|
+
}
|
|
1380
1383
|
const activated = await activateRuntime(layout, runtime, version, { setupCompatibilityArgs });
|
|
1381
1384
|
if (setupCompatibilityArgs.includes("--code")) {
|
|
1382
1385
|
console.log(`Relay ${version} is installed and paired. The Relay pill is open.`);
|
package/package.json
CHANGED
package/skill/manifest.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"name": "relay",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.8",
|
|
5
5
|
"consentVersion": 2,
|
|
6
|
-
"baseUrl": "https://sendrelays.com/skills/relay/v1.1.
|
|
6
|
+
"baseUrl": "https://sendrelays.com/skills/relay/v1.1.8",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
9
9
|
"path": "SKILL.md",
|
|
10
|
-
"sha256": "
|
|
10
|
+
"sha256": "ea97b5ec82fd7545a6c851829658e41ab02525cfb06d03d5f1ad25c1aaafcf5b"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"path": "agents/openai.yaml",
|
package/skill/relay/SKILL.md
CHANGED
|
@@ -50,14 +50,22 @@ from the pasted `/i/:token/agent` Relay invite. Never ask the person to paste a
|
|
|
50
50
|
password, Google credential, one-time code, or Relay access token into chat. Use
|
|
51
51
|
the browser approval URL for identity and permission. Start with the pinned
|
|
52
52
|
protocol helper in this conversation; no skill discovery or agent restart is
|
|
53
|
-
needed.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
needed. Run connect-start and open its approval URL in the person's usual
|
|
54
|
+
browser. With setup permission already given, start the same pinned package's
|
|
55
|
+
background-install on a supported desktop before yielding to wait for browser
|
|
56
|
+
approval. It downloads and verifies Companion while the person approves; do not
|
|
57
|
+
postpone it until authorization finishes. The installer waits for the verified
|
|
58
|
+
connection before adopting that account and activating Companion, without
|
|
59
|
+
another login. Skip desktop installation on a hosted/headless machine.
|
|
60
|
+
|
|
61
|
+
After browser approval, run connect-finish, then follow the invitation's host
|
|
62
|
+
activation procedure: install the skill, attempt supported discovery refresh in
|
|
63
|
+
the running Claude Code or Codex host, and verify the scope of activation. Keep
|
|
64
|
+
Companion installation non-blocking; continue as soon as the HTTPS protocol is
|
|
65
|
+
ready. If the approval link expires, renew browser approval without restarting
|
|
66
|
+
a running installer. Check background-status; retry installation only if it
|
|
67
|
+
failed or stopped, and report installation failures separately from the working
|
|
68
|
+
connection.
|
|
61
69
|
|
|
62
70
|
Create the authorization with `POST /v1/agent/authorizations` and consume it
|
|
63
71
|
after browser approval with `POST /v1/agent/authorizations/:id/consume`. Treat
|