@relayfile/sdk 0.10.63 → 0.10.65
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/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/relay-cli/cloud-preflight.d.ts +99 -0
- package/dist/relay-cli/cloud-preflight.js +352 -0
- package/dist/relay-cli/command-spec.json +1682 -0
- package/dist/relay-cli/index.d.ts +119 -0
- package/dist/relay-cli/index.js +207 -0
- package/dist/relay-cli/resolve-binary.d.ts +220 -0
- package/dist/relay-cli/resolve-binary.js +565 -0
- package/package.json +21 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const RELAYFILE_VERSION = "0.10.
|
|
1
|
+
export declare const RELAYFILE_VERSION = "0.10.65";
|
package/dist/package-version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/sync-package-version.mjs during every SDK build.
|
|
2
|
-
export const RELAYFILE_VERSION = "0.10.
|
|
2
|
+
export const RELAYFILE_VERSION = "0.10.65";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one implementation of relayfile's Cloud sign-in preflight.
|
|
3
|
+
*
|
|
4
|
+
* Agent Relay's Cloud SDK owns interactive login, token refresh, locking, and
|
|
5
|
+
* the canonical session store. The native runtime reads that same store
|
|
6
|
+
* directly instead of receiving copied tokens, so this preflight only has to
|
|
7
|
+
* ensure a session exists before `relayfile setup` starts.
|
|
8
|
+
*
|
|
9
|
+
* Both entry points into the Go binary run it: the `relayfile` bin shim
|
|
10
|
+
* (`packages/cli/scripts/run.js`) and the `agent-relay file` CLI surface, so
|
|
11
|
+
* `agent-relay file setup` behaves identically to `relayfile setup`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_CLOUD_API_URL = "https://agentrelay.com/cloud";
|
|
14
|
+
export declare const SETUP_INTENT = "Relayfile setup. This signs you in, connects an integration, and prepares a local VFS mount.";
|
|
15
|
+
export declare const SETUP_INTENT_PRINTED_ENV = "RELAYFILE_NPM_SETUP_INTENT_PRINTED";
|
|
16
|
+
export type SetupFlagValue = string | boolean;
|
|
17
|
+
export type ParsedSetupArguments = {
|
|
18
|
+
valid: true;
|
|
19
|
+
values: Map<string, SetupFlagValue>;
|
|
20
|
+
durations: Map<string, number>;
|
|
21
|
+
} | {
|
|
22
|
+
valid: false;
|
|
23
|
+
error: string;
|
|
24
|
+
};
|
|
25
|
+
export interface EnsureCloudSessionOptions {
|
|
26
|
+
apiUrl: string;
|
|
27
|
+
client: string;
|
|
28
|
+
interactive: boolean;
|
|
29
|
+
device: boolean;
|
|
30
|
+
loginTimeoutMs: number;
|
|
31
|
+
refreshTimeoutMs: number;
|
|
32
|
+
signal: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
export type EnsureCloudSession = (options: EnsureCloudSessionOptions) => Promise<unknown>;
|
|
35
|
+
export interface CloudPreflightOptions {
|
|
36
|
+
/** Defaults to `process.env`. Mutated to record the printed intent. */
|
|
37
|
+
env?: NodeJS.ProcessEnv;
|
|
38
|
+
/** Where the setup intent line is written. Defaults to `console.log`. */
|
|
39
|
+
writeLine?: (line: string) => void;
|
|
40
|
+
/** Injected Cloud SDK entry point; loaded from the vendored bundle by default. */
|
|
41
|
+
ensureCloudSession?: EnsureCloudSession;
|
|
42
|
+
/** Explicit path to the vendored `cloud-auth.cjs` bundle. */
|
|
43
|
+
cloudAuthBundlePath?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse a Go `time.Duration` string the way the Go CLI does.
|
|
47
|
+
*
|
|
48
|
+
* @param value - A duration such as `5m` or `1h30m`.
|
|
49
|
+
* @returns Milliseconds, or null when the string is not a valid duration.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseGoDurationMilliseconds(value: string): number | null;
|
|
52
|
+
/**
|
|
53
|
+
* Parse `relayfile setup`'s arguments without running it.
|
|
54
|
+
*
|
|
55
|
+
* @param args - Argv, with or without a leading `setup`.
|
|
56
|
+
* @returns The parsed flags, or the first validation error.
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseSetupArguments(args: readonly string[]): ParsedSetupArguments;
|
|
59
|
+
/**
|
|
60
|
+
* Report whether the given argv would parse as a valid `setup` invocation.
|
|
61
|
+
*
|
|
62
|
+
* @param args - Argv, with or without a leading `setup`.
|
|
63
|
+
* @returns True when the arguments are valid.
|
|
64
|
+
*/
|
|
65
|
+
export declare function hasValidSetupArguments(args: readonly string[]): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Decide whether a Cloud session must be prepared before the binary runs.
|
|
68
|
+
*
|
|
69
|
+
* @param args - Argv passed to relayfile.
|
|
70
|
+
* @param env - Environment to read caller-owned credentials from.
|
|
71
|
+
* @returns True only for a real interactive `setup` with no explicit credentials.
|
|
72
|
+
*/
|
|
73
|
+
export declare function shouldPrepareCloudSession(args: readonly string[], env: NodeJS.ProcessEnv): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Print the one-time setup intent line, if this invocation warrants it.
|
|
76
|
+
*
|
|
77
|
+
* @param args - Argv passed to relayfile.
|
|
78
|
+
* @param env - Environment; the printed marker is recorded here.
|
|
79
|
+
* @param writeLine - Sink for the line. Defaults to `console.log`.
|
|
80
|
+
* @returns True when this invocation prepares a Cloud session.
|
|
81
|
+
*/
|
|
82
|
+
export declare function announceSetupIntent(args: readonly string[], env: NodeJS.ProcessEnv, writeLine?: (line: string) => void): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Load `ensureCloudSession` from the vendored Agent Relay Cloud SDK bundle.
|
|
85
|
+
*
|
|
86
|
+
* @param explicitPath - A known bundle path, preferred when supplied.
|
|
87
|
+
* @returns The Cloud SDK's session entry point.
|
|
88
|
+
* @throws When the bundle cannot be found or loaded.
|
|
89
|
+
*/
|
|
90
|
+
export declare function loadCloudSessionSDK(explicitPath?: string): EnsureCloudSession;
|
|
91
|
+
/**
|
|
92
|
+
* Ensure a Cloud session exists before `relayfile setup` starts.
|
|
93
|
+
*
|
|
94
|
+
* @param args - Argv passed to relayfile.
|
|
95
|
+
* @param options - Environment, output sink, and injectable Cloud SDK.
|
|
96
|
+
* @returns True when a session was prepared, false when the invocation needs none.
|
|
97
|
+
* @throws When setup's arguments are invalid, or sign-in fails or times out.
|
|
98
|
+
*/
|
|
99
|
+
export declare function prepareCloudSession(args: readonly string[], options?: CloudPreflightOptions): Promise<boolean>;
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one implementation of relayfile's Cloud sign-in preflight.
|
|
3
|
+
*
|
|
4
|
+
* Agent Relay's Cloud SDK owns interactive login, token refresh, locking, and
|
|
5
|
+
* the canonical session store. The native runtime reads that same store
|
|
6
|
+
* directly instead of receiving copied tokens, so this preflight only has to
|
|
7
|
+
* ensure a session exists before `relayfile setup` starts.
|
|
8
|
+
*
|
|
9
|
+
* Both entry points into the Go binary run it: the `relayfile` bin shim
|
|
10
|
+
* (`packages/cli/scripts/run.js`) and the `agent-relay file` CLI surface, so
|
|
11
|
+
* `agent-relay file setup` behaves identically to `relayfile setup`.
|
|
12
|
+
*/
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
export const DEFAULT_CLOUD_API_URL = "https://agentrelay.com/cloud";
|
|
18
|
+
export const SETUP_INTENT = "Relayfile setup. This signs you in, connects an integration, and prepares a local VFS mount.";
|
|
19
|
+
export const SETUP_INTENT_PRINTED_ENV = "RELAYFILE_NPM_SETUP_INTENT_PRINTED";
|
|
20
|
+
const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
21
|
+
const DEFAULT_REFRESH_TIMEOUT_MS = 10 * 1000;
|
|
22
|
+
const MAX_NODE_TIMER_DELAY_MS = 2_147_483_647;
|
|
23
|
+
const MAX_GO_DURATION_NANOSECONDS = 9_223_372_036_854_775_807;
|
|
24
|
+
/** Flags `relayfile setup` accepts, mapped to whether they take a value. */
|
|
25
|
+
const SETUP_FLAGS = new Map([
|
|
26
|
+
["cloud-api-url", true],
|
|
27
|
+
["cloud-token", true],
|
|
28
|
+
["workspace", true],
|
|
29
|
+
["provider", true],
|
|
30
|
+
["backend", true],
|
|
31
|
+
["local-dir", true],
|
|
32
|
+
["no-open", false],
|
|
33
|
+
["skip-mount", false],
|
|
34
|
+
["once", false],
|
|
35
|
+
["login-timeout", true],
|
|
36
|
+
["connect-timeout", true],
|
|
37
|
+
["help", false],
|
|
38
|
+
["h", false]
|
|
39
|
+
]);
|
|
40
|
+
const GO_BOOLEAN_VALUES = new Set([
|
|
41
|
+
"1",
|
|
42
|
+
"t",
|
|
43
|
+
"T",
|
|
44
|
+
"true",
|
|
45
|
+
"TRUE",
|
|
46
|
+
"True",
|
|
47
|
+
"0",
|
|
48
|
+
"f",
|
|
49
|
+
"F",
|
|
50
|
+
"false",
|
|
51
|
+
"FALSE",
|
|
52
|
+
"False"
|
|
53
|
+
]);
|
|
54
|
+
const GO_TRUE_VALUES = new Set(["1", "t", "T", "true", "TRUE", "True"]);
|
|
55
|
+
const GO_DURATION_UNITS_IN_NANOSECONDS = new Map([
|
|
56
|
+
["ns", 1],
|
|
57
|
+
["us", 1_000],
|
|
58
|
+
["µs", 1_000],
|
|
59
|
+
["μs", 1_000],
|
|
60
|
+
["ms", 1_000_000],
|
|
61
|
+
["s", 1_000_000_000],
|
|
62
|
+
["m", 60 * 1_000_000_000],
|
|
63
|
+
["h", 60 * 60 * 1_000_000_000]
|
|
64
|
+
]);
|
|
65
|
+
const VALID_INTEGRATION_BACKENDS = new Set(["", "default", "nango", "composio"]);
|
|
66
|
+
/**
|
|
67
|
+
* Parse a Go `time.Duration` string the way the Go CLI does.
|
|
68
|
+
*
|
|
69
|
+
* @param value - A duration such as `5m` or `1h30m`.
|
|
70
|
+
* @returns Milliseconds, or null when the string is not a valid duration.
|
|
71
|
+
*/
|
|
72
|
+
export function parseGoDurationMilliseconds(value) {
|
|
73
|
+
let remaining = String(value);
|
|
74
|
+
let sign = 1;
|
|
75
|
+
if (remaining.startsWith("+") || remaining.startsWith("-")) {
|
|
76
|
+
sign = remaining[0] === "-" ? -1 : 1;
|
|
77
|
+
remaining = remaining.slice(1);
|
|
78
|
+
}
|
|
79
|
+
if (remaining === "0") {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
if (!remaining) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
let nanoseconds = 0;
|
|
86
|
+
let parts = 0;
|
|
87
|
+
while (remaining) {
|
|
88
|
+
const match = /^(\d+(?:\.\d*)?|\.\d+)(ns|us|µs|μs|ms|s|m|h)/.exec(remaining);
|
|
89
|
+
if (!match) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const amount = Number(match[1]);
|
|
93
|
+
const unitNanoseconds = GO_DURATION_UNITS_IN_NANOSECONDS.get(match[2]);
|
|
94
|
+
nanoseconds += amount * unitNanoseconds;
|
|
95
|
+
if (!Number.isFinite(nanoseconds) || nanoseconds > MAX_GO_DURATION_NANOSECONDS) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
remaining = remaining.slice(match[0].length);
|
|
99
|
+
parts += 1;
|
|
100
|
+
}
|
|
101
|
+
return parts > 0 ? (sign * nanoseconds) / 1_000_000 : null;
|
|
102
|
+
}
|
|
103
|
+
function wantsNativeVersion(args) {
|
|
104
|
+
return args.length === 1 && (args[0] === "--version" || args[0] === "version");
|
|
105
|
+
}
|
|
106
|
+
function wantsNativeHelp(args) {
|
|
107
|
+
return args.some((arg) => arg === "--help" || arg === "-h");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parse `relayfile setup`'s arguments without running it.
|
|
111
|
+
*
|
|
112
|
+
* @param args - Argv, with or without a leading `setup`.
|
|
113
|
+
* @returns The parsed flags, or the first validation error.
|
|
114
|
+
*/
|
|
115
|
+
export function parseSetupArguments(args) {
|
|
116
|
+
const setupArgs = args[0] === "setup" ? args.slice(1) : args;
|
|
117
|
+
const values = new Map();
|
|
118
|
+
const durations = new Map();
|
|
119
|
+
for (let index = 0; index < setupArgs.length; index += 1) {
|
|
120
|
+
const arg = setupArgs[index];
|
|
121
|
+
if (arg === "--") {
|
|
122
|
+
if (index !== setupArgs.length - 1) {
|
|
123
|
+
return { valid: false, error: "setup does not accept positional arguments" };
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
const match = /^--?([^=]+)(?:=(.*))?$/.exec(arg);
|
|
128
|
+
if (!match) {
|
|
129
|
+
return { valid: false, error: `unexpected setup argument ${JSON.stringify(arg)}` };
|
|
130
|
+
}
|
|
131
|
+
const name = match[1];
|
|
132
|
+
const inlineValue = match[2];
|
|
133
|
+
const takesValue = SETUP_FLAGS.get(name);
|
|
134
|
+
if (takesValue === undefined) {
|
|
135
|
+
return { valid: false, error: `unknown setup flag --${name}` };
|
|
136
|
+
}
|
|
137
|
+
if (takesValue) {
|
|
138
|
+
let value = inlineValue;
|
|
139
|
+
if (inlineValue === undefined) {
|
|
140
|
+
const next = setupArgs[index + 1];
|
|
141
|
+
if (next === undefined) {
|
|
142
|
+
return { valid: false, error: `--${name} requires a value` };
|
|
143
|
+
}
|
|
144
|
+
value = next;
|
|
145
|
+
index += 1;
|
|
146
|
+
}
|
|
147
|
+
values.set(name, value);
|
|
148
|
+
if (name === "login-timeout" || name === "connect-timeout") {
|
|
149
|
+
const duration = parseGoDurationMilliseconds(value);
|
|
150
|
+
if (duration === null) {
|
|
151
|
+
return {
|
|
152
|
+
valid: false,
|
|
153
|
+
error: `--${name} has invalid duration ${JSON.stringify(value)}`
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (name === "login-timeout" && duration <= 0) {
|
|
157
|
+
return { valid: false, error: "--login-timeout must be greater than zero" };
|
|
158
|
+
}
|
|
159
|
+
durations.set(name, duration);
|
|
160
|
+
}
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (inlineValue !== undefined && !GO_BOOLEAN_VALUES.has(inlineValue)) {
|
|
164
|
+
return {
|
|
165
|
+
valid: false,
|
|
166
|
+
error: `--${name} has invalid boolean value ${JSON.stringify(inlineValue)}`
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
values.set(name, inlineValue === undefined || GO_TRUE_VALUES.has(inlineValue));
|
|
170
|
+
}
|
|
171
|
+
const backend = String(values.get("backend") || "")
|
|
172
|
+
.trim()
|
|
173
|
+
.toLowerCase();
|
|
174
|
+
if (!VALID_INTEGRATION_BACKENDS.has(backend)) {
|
|
175
|
+
return {
|
|
176
|
+
valid: false,
|
|
177
|
+
error: `unsupported integration backend ${JSON.stringify(backend)} (expected nango or composio)`
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return { valid: true, values, durations };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Report whether the given argv would parse as a valid `setup` invocation.
|
|
184
|
+
*
|
|
185
|
+
* @param args - Argv, with or without a leading `setup`.
|
|
186
|
+
* @returns True when the arguments are valid.
|
|
187
|
+
*/
|
|
188
|
+
export function hasValidSetupArguments(args) {
|
|
189
|
+
return parseSetupArguments(args).valid;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Decide whether a Cloud session must be prepared before the binary runs.
|
|
193
|
+
*
|
|
194
|
+
* @param args - Argv passed to relayfile.
|
|
195
|
+
* @param env - Environment to read caller-owned credentials from.
|
|
196
|
+
* @returns True only for a real interactive `setup` with no explicit credentials.
|
|
197
|
+
*/
|
|
198
|
+
export function shouldPrepareCloudSession(args, env) {
|
|
199
|
+
const setupCommand = args.length === 0 || args[0] === "setup";
|
|
200
|
+
if (!setupCommand) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
if (wantsNativeVersion(args) || wantsNativeHelp(args)) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const parsed = parseSetupArguments(args);
|
|
207
|
+
if (!parsed.valid) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
if (parsed.values.has("help") || parsed.values.has("h")) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
// Explicit credentials are caller-owned. Let the Go CLI validate and use
|
|
214
|
+
// them without replacing them with an interactive session.
|
|
215
|
+
const relayfileCloudToken = parsed.values.has("cloud-token")
|
|
216
|
+
? String(parsed.values.get("cloud-token") || "").trim()
|
|
217
|
+
: String(env.RELAYFILE_CLOUD_TOKEN || "").trim();
|
|
218
|
+
if (relayfileCloudToken || String(env.CLOUD_API_ACCESS_TOKEN || "").trim()) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
// Let the native CLI report malformed flags without first opening a login
|
|
222
|
+
// flow or mutating the caller's canonical Cloud session.
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Print the one-time setup intent line, if this invocation warrants it.
|
|
227
|
+
*
|
|
228
|
+
* @param args - Argv passed to relayfile.
|
|
229
|
+
* @param env - Environment; the printed marker is recorded here.
|
|
230
|
+
* @param writeLine - Sink for the line. Defaults to `console.log`.
|
|
231
|
+
* @returns True when this invocation prepares a Cloud session.
|
|
232
|
+
*/
|
|
233
|
+
export function announceSetupIntent(args, env, writeLine = console.log) {
|
|
234
|
+
if (!shouldPrepareCloudSession(args, env)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
if (String(env[SETUP_INTENT_PRINTED_ENV] || "").trim() !== "1") {
|
|
238
|
+
writeLine(SETUP_INTENT);
|
|
239
|
+
env[SETUP_INTENT_PRINTED_ENV] = "1";
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
function bundleCandidates(explicitPath) {
|
|
244
|
+
const candidates = [];
|
|
245
|
+
if (explicitPath) {
|
|
246
|
+
candidates.push(explicitPath);
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const require = createRequire(import.meta.url);
|
|
250
|
+
candidates.push(require.resolve("relayfile/scripts/cloud-auth.cjs"));
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// The CLI package is not installed alongside the SDK; fall through to the
|
|
254
|
+
// workspace layout below.
|
|
255
|
+
}
|
|
256
|
+
let current = path.dirname(fileURLToPath(import.meta.url));
|
|
257
|
+
for (;;) {
|
|
258
|
+
candidates.push(path.join(current, "packages", "cli", "scripts", "cloud-auth.cjs"));
|
|
259
|
+
const parent = path.dirname(current);
|
|
260
|
+
if (parent === current) {
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
current = parent;
|
|
264
|
+
}
|
|
265
|
+
return candidates;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Load `ensureCloudSession` from the vendored Agent Relay Cloud SDK bundle.
|
|
269
|
+
*
|
|
270
|
+
* @param explicitPath - A known bundle path, preferred when supplied.
|
|
271
|
+
* @returns The Cloud SDK's session entry point.
|
|
272
|
+
* @throws When the bundle cannot be found or loaded.
|
|
273
|
+
*/
|
|
274
|
+
export function loadCloudSessionSDK(explicitPath) {
|
|
275
|
+
const require = createRequire(import.meta.url);
|
|
276
|
+
const candidates = bundleCandidates(explicitPath);
|
|
277
|
+
for (const candidate of candidates) {
|
|
278
|
+
if (!existsSync(candidate)) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
try {
|
|
282
|
+
return require(candidate).ensureCloudSession;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
throw new Error("Relayfile's Agent Relay Cloud SDK bundle failed to load. Reinstall relayfile or run its package build.", { cause: error });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
throw new Error("Relayfile's Agent Relay Cloud SDK bundle is missing. Reinstall relayfile or run its package build.");
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Ensure a Cloud session exists before `relayfile setup` starts.
|
|
292
|
+
*
|
|
293
|
+
* @param args - Argv passed to relayfile.
|
|
294
|
+
* @param options - Environment, output sink, and injectable Cloud SDK.
|
|
295
|
+
* @returns True when a session was prepared, false when the invocation needs none.
|
|
296
|
+
* @throws When setup's arguments are invalid, or sign-in fails or times out.
|
|
297
|
+
*/
|
|
298
|
+
export async function prepareCloudSession(args, options = {}) {
|
|
299
|
+
const env = options.env ?? process.env;
|
|
300
|
+
const setupCommand = args.length === 0 || args[0] === "setup";
|
|
301
|
+
if (wantsNativeVersion(args) || wantsNativeHelp(args)) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
const parsed = setupCommand ? parseSetupArguments(args) : null;
|
|
305
|
+
if (parsed !== null && parsed.valid === false) {
|
|
306
|
+
throw new Error(parsed.error);
|
|
307
|
+
}
|
|
308
|
+
if (!shouldPrepareCloudSession(args, env)) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
// shouldPrepareCloudSession only returns true for a setup invocation, so a
|
|
312
|
+
// valid parse is guaranteed here.
|
|
313
|
+
const setup = parsed;
|
|
314
|
+
const ensureCloudSession = options.ensureCloudSession ?? loadCloudSessionSDK(options.cloudAuthBundlePath);
|
|
315
|
+
const apiUrl = String(setup.values.get("cloud-api-url") || "").trim() ||
|
|
316
|
+
String(env.RELAYFILE_CLOUD_API_URL || "").trim() ||
|
|
317
|
+
String(env.CLOUD_API_URL || "").trim() ||
|
|
318
|
+
DEFAULT_CLOUD_API_URL;
|
|
319
|
+
const loginTimeoutMs = setup.durations.get("login-timeout") || DEFAULT_LOGIN_TIMEOUT_MS;
|
|
320
|
+
const loginAbort = new AbortController();
|
|
321
|
+
let timer;
|
|
322
|
+
try {
|
|
323
|
+
await Promise.race([
|
|
324
|
+
ensureCloudSession({
|
|
325
|
+
apiUrl,
|
|
326
|
+
client: "relayfile",
|
|
327
|
+
interactive: true,
|
|
328
|
+
device: setup.values.get("no-open") === true,
|
|
329
|
+
loginTimeoutMs,
|
|
330
|
+
refreshTimeoutMs: Math.max(1, Math.min(loginTimeoutMs, DEFAULT_REFRESH_TIMEOUT_MS)),
|
|
331
|
+
signal: loginAbort.signal
|
|
332
|
+
}),
|
|
333
|
+
new Promise((_, reject) => {
|
|
334
|
+
timer = setTimeout(() => {
|
|
335
|
+
const error = new Error(`Cloud sign-in timed out after ${setup.values.get("login-timeout") || "5m"}`);
|
|
336
|
+
loginAbort.abort(error);
|
|
337
|
+
reject(error);
|
|
338
|
+
}, Math.min(loginTimeoutMs, MAX_NODE_TIMER_DELAY_MS));
|
|
339
|
+
})
|
|
340
|
+
]);
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
clearTimeout(timer);
|
|
344
|
+
}
|
|
345
|
+
// The SDK owns and refreshes its canonical on-disk session. Do not promote
|
|
346
|
+
// that session into CLOUD_API_* for the child: Relayfile would correctly
|
|
347
|
+
// treat those variables as caller-owned and would not persist rotated
|
|
348
|
+
// refresh tokens back to the shared file. The native runtime reads the same
|
|
349
|
+
// canonical file directly. Genuine caller-provided environment credentials
|
|
350
|
+
// bypass this preflight above and remain untouched.
|
|
351
|
+
return true;
|
|
352
|
+
}
|