@viraatdas/rudder 1.0.73 → 1.0.74
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 +169 -510
- package/dist/brain.js +4 -4
- package/dist/brain.js.map +1 -1
- package/dist/cloud.js +80 -350
- package/dist/cloud.js.map +1 -1
- package/dist/git.d.ts +19 -2
- package/dist/git.js +260 -5
- package/dist/git.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/main.js +21 -12
- package/dist/main.js.map +1 -1
- package/dist/migration.js +8 -1
- package/dist/migration.js.map +1 -1
- package/dist/models.js +21 -36
- package/dist/models.js.map +1 -1
- package/dist/native/rudder-native +0 -0
- package/dist/repl.js +6 -2
- package/dist/repl.js.map +1 -1
- package/dist/run-manager.d.ts +3 -0
- package/dist/run-manager.js +140 -29
- package/dist/run-manager.js.map +1 -1
- package/dist/state.d.ts +3 -1
- package/dist/state.js +38 -3
- package/dist/state.js.map +1 -1
- package/dist/tmux-dashboard.js +58 -7
- package/dist/tmux-dashboard.js.map +1 -1
- package/dist/tui.js +112 -17
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +17 -1
- package/dist/util.d.ts +6 -1
- package/dist/util.js +36 -7
- package/dist/util.js.map +1 -1
- package/package.json +2 -1
package/dist/brain.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fsp from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { currentBranch, currentCommit,
|
|
3
|
+
import { currentBranch, currentCommit, runHasChanges, workspaceDiff, workspaceStatus } from "./git.js";
|
|
4
4
|
import { pathExists, readJson, runCommand, writeJson } from "./util.js";
|
|
5
5
|
import { agentContextPath, specPath, verifierPath } from "./state.js";
|
|
6
6
|
const INSTRUCTION_FILES = ["AGENTS.md", "CLAUDE.md", "README.md"];
|
|
@@ -35,7 +35,7 @@ export async function createSpec(run) {
|
|
|
35
35
|
root: workspace,
|
|
36
36
|
branch: await currentBranch(workspace),
|
|
37
37
|
baseCommit: (await currentCommit(workspace)) || run.baseCommit,
|
|
38
|
-
status: await
|
|
38
|
+
status: await workspaceStatus(run),
|
|
39
39
|
},
|
|
40
40
|
instructionsFiles,
|
|
41
41
|
acceptanceCriteria: buildAcceptanceCriteria(task),
|
|
@@ -74,8 +74,8 @@ export function renderContract(spec) {
|
|
|
74
74
|
.join("\n");
|
|
75
75
|
}
|
|
76
76
|
export async function verifyRun(run) {
|
|
77
|
-
const diff = await
|
|
78
|
-
const changed = await
|
|
77
|
+
const diff = await workspaceDiff(run);
|
|
78
|
+
const changed = await runHasChanges(run);
|
|
79
79
|
const outputPath = path.join(run.repoRoot, ".rudder", "runs", run.id, "output.txt");
|
|
80
80
|
const transcript = await fsp.readFile(outputPath, "utf8").catch(() => "");
|
|
81
81
|
const spec = await readJson(specPath(run.repoRoot, run.id));
|
package/dist/brain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brain.js","sourceRoot":"","sources":["../src/brain.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,kBAAkB,CAAC;AACnC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"brain.js","sourceRoot":"","sources":["../src/brain.ts"],"names":[],"mappings":"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"}
|
package/dist/cloud.js
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
import { spawn
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import fsp from "node:fs/promises";
|
|
4
|
-
import net from "node:net";
|
|
5
4
|
import os from "node:os";
|
|
6
5
|
import path from "node:path";
|
|
7
6
|
import { WebSocket } from "ws";
|
|
8
7
|
import { currentBranch, currentCommit, findRepoRoot } from "./git.js";
|
|
9
|
-
import { applyDefaultDecisions, buildFreshHandoffPrompt, cloudWorktreeAbsolutePath, findMigrationCandidates, migrationSummary, } from "./migration.js";
|
|
8
|
+
import { applyDefaultDecisions, buildFreshHandoffPrompt, cloudWorktreeAbsolutePath, findMigrationCandidates, migrationSummary, summaryAsJson, } from "./migration.js";
|
|
10
9
|
import { cloudAuthPath } from "./state.js";
|
|
11
10
|
import { ensureDir, commandExists, expandHome, isTty, newRunId, nowIso, pathExists, promptConfirm, promptText, promptSelect, promptSecret, readJson, runCommand, shortenHome, shellQuote, writeJson, } from "./util.js";
|
|
12
11
|
const DEFAULT_LOGIN_INTERVAL_MS = 2000;
|
|
13
12
|
const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
14
13
|
const DEFAULT_CLOUD_URL = "https://rudder-cloud-control.fly.dev";
|
|
15
|
-
const DEFAULT_CLOUD_REQUEST_TIMEOUT_MS = 5000;
|
|
16
|
-
const DEFAULT_CLOUD_REGION_TIMEOUT_MS = 2000;
|
|
17
|
-
const FLY_CONTROL_APP = "rudder-cloud-control";
|
|
18
14
|
const GITHUB_CLI_CLIENT_ID = "178c6fc778ccc68e1d6a";
|
|
19
15
|
const MAX_HOME_SECRET_SCAN_BYTES = 1024 * 1024;
|
|
20
16
|
const DEFAULT_HOME_PATHS = [
|
|
@@ -111,9 +107,9 @@ export async function runCloudCommand(command, args, options = {}) {
|
|
|
111
107
|
printCloudHelp();
|
|
112
108
|
return;
|
|
113
109
|
}
|
|
114
|
-
// `rudder cloud` with no further args means "open
|
|
115
|
-
//
|
|
116
|
-
//
|
|
110
|
+
// `rudder cloud` with no further args means "open the cloud workspace for
|
|
111
|
+
// this repo". Explicit subcommands (sail, launch, etc.) keep their old
|
|
112
|
+
// behavior. `rudder sail` and `rudder cloud sail` still launch a sail.
|
|
117
113
|
if (command === "cloud" && subcommand === "") {
|
|
118
114
|
await workspaceCommand([], options);
|
|
119
115
|
return;
|
|
@@ -331,7 +327,7 @@ async function githubOAuthRequest(url, body) {
|
|
|
331
327
|
const text = await response.text();
|
|
332
328
|
const parsed = text ? parseJson(text) : null;
|
|
333
329
|
if (!response.ok) {
|
|
334
|
-
throw new Error(responseErrorMessage(parsed)
|
|
330
|
+
throw new Error(responseErrorMessage(parsed) || text.trim() || `${response.status} ${response.statusText}`);
|
|
335
331
|
}
|
|
336
332
|
return parsed;
|
|
337
333
|
}
|
|
@@ -824,13 +820,12 @@ async function cloudClient(options) {
|
|
|
824
820
|
if (options.requireToken && !token) {
|
|
825
821
|
throw new Error("Not logged in to Rudder Cloud. Run `rudder login` first.");
|
|
826
822
|
}
|
|
827
|
-
let activeBaseUrl = baseUrl;
|
|
828
|
-
let proxyFallbackTried = false;
|
|
829
823
|
return {
|
|
830
|
-
|
|
831
|
-
return activeBaseUrl;
|
|
832
|
-
},
|
|
824
|
+
baseUrl,
|
|
833
825
|
async request(pathOrUrl, init) {
|
|
826
|
+
const url = pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")
|
|
827
|
+
? pathOrUrl
|
|
828
|
+
: new URL(pathOrUrl, `${baseUrl}/`).toString();
|
|
834
829
|
const headers = {
|
|
835
830
|
Accept: "application/json",
|
|
836
831
|
};
|
|
@@ -842,219 +837,21 @@ async function cloudClient(options) {
|
|
|
842
837
|
if (token) {
|
|
843
838
|
headers.Authorization = `Bearer ${token}`;
|
|
844
839
|
}
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const requestWithRetries = async (retryCount) => {
|
|
851
|
-
const url = pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")
|
|
852
|
-
? pathOrUrl
|
|
853
|
-
: new URL(pathOrUrl, `${activeBaseUrl}/`).toString();
|
|
854
|
-
for (let attempt = 0;; attempt++) {
|
|
855
|
-
try {
|
|
856
|
-
return await fetchWithTimeout(url, {
|
|
857
|
-
method: init.method,
|
|
858
|
-
headers,
|
|
859
|
-
body,
|
|
860
|
-
}, timeoutMs);
|
|
861
|
-
}
|
|
862
|
-
catch (error) {
|
|
863
|
-
if (attempt >= retryCount) {
|
|
864
|
-
throw error;
|
|
865
|
-
}
|
|
866
|
-
await sleep(Math.min(2500, 300 * 2 ** attempt));
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
};
|
|
870
|
-
let response;
|
|
871
|
-
try {
|
|
872
|
-
response = await requestWithRetries(canUseProxyFallback ? 0 : requestedRetries);
|
|
873
|
-
}
|
|
874
|
-
catch (error) {
|
|
875
|
-
if (!proxyFallbackTried
|
|
876
|
-
&& canUseProxyFallback) {
|
|
877
|
-
proxyFallbackTried = true;
|
|
878
|
-
const proxyUrl = await startFlyControlProxy().catch(() => null);
|
|
879
|
-
if (proxyUrl) {
|
|
880
|
-
activeBaseUrl = proxyUrl;
|
|
881
|
-
response = await requestWithRetries(requestedRetries);
|
|
882
|
-
}
|
|
883
|
-
else {
|
|
884
|
-
const detail = networkErrorDetail(error);
|
|
885
|
-
throw new Error(detail ? `Rudder Cloud network request failed: ${detail}` : "Rudder Cloud network request failed");
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
else {
|
|
889
|
-
const detail = networkErrorDetail(error);
|
|
890
|
-
throw new Error(detail ? `Rudder Cloud network request failed: ${detail}` : "Rudder Cloud network request failed");
|
|
891
|
-
}
|
|
892
|
-
}
|
|
840
|
+
const response = await fetch(url, {
|
|
841
|
+
method: init.method,
|
|
842
|
+
headers,
|
|
843
|
+
body,
|
|
844
|
+
});
|
|
893
845
|
const text = await response.text();
|
|
894
846
|
const parsed = text ? parseJson(text) : null;
|
|
895
847
|
if (!response.ok) {
|
|
896
|
-
const message = responseErrorMessage(parsed)
|
|
848
|
+
const message = responseErrorMessage(parsed) || text.trim() || `${response.status} ${response.statusText}`;
|
|
897
849
|
throw new Error(`Rudder Cloud request failed: ${message}`);
|
|
898
850
|
}
|
|
899
851
|
return parsed;
|
|
900
852
|
},
|
|
901
853
|
};
|
|
902
854
|
}
|
|
903
|
-
function shouldTryFlyProxyFallback(configuredBaseUrl, activeBaseUrl, pathOrUrl, hasToken) {
|
|
904
|
-
return Boolean(hasToken
|
|
905
|
-
&& !process.env.RUDDER_CLOUD_URL
|
|
906
|
-
&& process.env.RUDDER_CLOUD_DISABLE_FLY_PROXY !== "1"
|
|
907
|
-
&& configuredBaseUrl === DEFAULT_CLOUD_URL
|
|
908
|
-
&& activeBaseUrl === configuredBaseUrl
|
|
909
|
-
&& !pathOrUrl.startsWith("http://")
|
|
910
|
-
&& !pathOrUrl.startsWith("https://"));
|
|
911
|
-
}
|
|
912
|
-
let flyProxyStart = null;
|
|
913
|
-
let flyProxyProcess = null;
|
|
914
|
-
async function startFlyControlProxy() {
|
|
915
|
-
if (flyProxyProcess && !flyProxyProcess.killed) {
|
|
916
|
-
return flyProxyStart;
|
|
917
|
-
}
|
|
918
|
-
flyProxyStart = startFlyControlProxyOnce();
|
|
919
|
-
return flyProxyStart;
|
|
920
|
-
}
|
|
921
|
-
async function startFlyControlProxyOnce() {
|
|
922
|
-
if (!(await commandExists("flyctl"))) {
|
|
923
|
-
return null;
|
|
924
|
-
}
|
|
925
|
-
const port = await openLocalPort();
|
|
926
|
-
const url = `http://127.0.0.1:${port}`;
|
|
927
|
-
const child = spawn("flyctl", [
|
|
928
|
-
"proxy",
|
|
929
|
-
`${port}:3000`,
|
|
930
|
-
`${FLY_CONTROL_APP}.internal`,
|
|
931
|
-
"--app",
|
|
932
|
-
FLY_CONTROL_APP,
|
|
933
|
-
"--quiet",
|
|
934
|
-
], {
|
|
935
|
-
stdio: "ignore",
|
|
936
|
-
});
|
|
937
|
-
flyProxyProcess = child;
|
|
938
|
-
child.unref();
|
|
939
|
-
child.once("exit", () => {
|
|
940
|
-
if (flyProxyProcess === child) {
|
|
941
|
-
flyProxyProcess = null;
|
|
942
|
-
flyProxyStart = null;
|
|
943
|
-
}
|
|
944
|
-
});
|
|
945
|
-
registerFlyProxyCleanup();
|
|
946
|
-
try {
|
|
947
|
-
await waitForFlyProxy(url);
|
|
948
|
-
process.stderr.write("Rudder Cloud public endpoint is unreachable; using local Fly proxy.\n");
|
|
949
|
-
return url;
|
|
950
|
-
}
|
|
951
|
-
catch {
|
|
952
|
-
try {
|
|
953
|
-
child.kill("SIGTERM");
|
|
954
|
-
}
|
|
955
|
-
catch {
|
|
956
|
-
// ignore
|
|
957
|
-
}
|
|
958
|
-
if (flyProxyProcess === child) {
|
|
959
|
-
flyProxyProcess = null;
|
|
960
|
-
flyProxyStart = null;
|
|
961
|
-
}
|
|
962
|
-
return null;
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
let flyProxyCleanupRegistered = false;
|
|
966
|
-
function registerFlyProxyCleanup() {
|
|
967
|
-
if (flyProxyCleanupRegistered) {
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
flyProxyCleanupRegistered = true;
|
|
971
|
-
process.once("exit", () => {
|
|
972
|
-
try {
|
|
973
|
-
flyProxyProcess?.kill("SIGTERM");
|
|
974
|
-
}
|
|
975
|
-
catch {
|
|
976
|
-
// ignore
|
|
977
|
-
}
|
|
978
|
-
});
|
|
979
|
-
}
|
|
980
|
-
async function openLocalPort() {
|
|
981
|
-
return await new Promise((resolve, reject) => {
|
|
982
|
-
const server = net.createServer();
|
|
983
|
-
server.once("error", reject);
|
|
984
|
-
server.listen(0, "127.0.0.1", () => {
|
|
985
|
-
const address = server.address();
|
|
986
|
-
server.close(() => {
|
|
987
|
-
if (address && typeof address === "object") {
|
|
988
|
-
resolve(address.port);
|
|
989
|
-
}
|
|
990
|
-
else {
|
|
991
|
-
reject(new Error("could not allocate local proxy port"));
|
|
992
|
-
}
|
|
993
|
-
});
|
|
994
|
-
});
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
async function waitForFlyProxy(baseUrl) {
|
|
998
|
-
const deadline = Date.now() + 10_000;
|
|
999
|
-
let lastError;
|
|
1000
|
-
while (Date.now() < deadline) {
|
|
1001
|
-
try {
|
|
1002
|
-
const response = await fetchWithTimeout(`${baseUrl}/health`, { method: "GET" }, 1000);
|
|
1003
|
-
if (response.ok) {
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
lastError = new Error(`HTTP ${response.status}`);
|
|
1007
|
-
}
|
|
1008
|
-
catch (error) {
|
|
1009
|
-
lastError = error;
|
|
1010
|
-
}
|
|
1011
|
-
await sleep(300);
|
|
1012
|
-
}
|
|
1013
|
-
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
1014
|
-
}
|
|
1015
|
-
function networkErrorDetail(error) {
|
|
1016
|
-
if (!(error instanceof Error)) {
|
|
1017
|
-
return String(error);
|
|
1018
|
-
}
|
|
1019
|
-
const cause = error.cause;
|
|
1020
|
-
if (cause && typeof cause === "object") {
|
|
1021
|
-
const code = cause.code;
|
|
1022
|
-
const message = cause.message;
|
|
1023
|
-
if (typeof code === "string" && typeof message === "string") {
|
|
1024
|
-
return `${code}: ${message}`;
|
|
1025
|
-
}
|
|
1026
|
-
if (typeof code === "string") {
|
|
1027
|
-
return code;
|
|
1028
|
-
}
|
|
1029
|
-
if (typeof message === "string") {
|
|
1030
|
-
return message;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
return error.message;
|
|
1034
|
-
}
|
|
1035
|
-
async function fetchWithTimeout(url, init, timeoutMs) {
|
|
1036
|
-
const controller = new AbortController();
|
|
1037
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1038
|
-
try {
|
|
1039
|
-
return await fetch(url, { ...init, signal: controller.signal });
|
|
1040
|
-
}
|
|
1041
|
-
catch (error) {
|
|
1042
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
1043
|
-
throw new Error(`request timed out after ${timeoutMs}ms`);
|
|
1044
|
-
}
|
|
1045
|
-
throw error;
|
|
1046
|
-
}
|
|
1047
|
-
finally {
|
|
1048
|
-
clearTimeout(timer);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
function cloudRequestTimeoutMs() {
|
|
1052
|
-
const parsed = Number(process.env.RUDDER_CLOUD_REQUEST_TIMEOUT_MS);
|
|
1053
|
-
if (Number.isFinite(parsed) && parsed > 0) {
|
|
1054
|
-
return Math.max(1000, Math.floor(parsed));
|
|
1055
|
-
}
|
|
1056
|
-
return DEFAULT_CLOUD_REQUEST_TIMEOUT_MS;
|
|
1057
|
-
}
|
|
1058
855
|
function normalizeCloudUrl(raw) {
|
|
1059
856
|
const value = raw?.trim() || DEFAULT_CLOUD_URL;
|
|
1060
857
|
if (!value) {
|
|
@@ -1680,7 +1477,7 @@ async function detectFlyRegion(baseUrl) {
|
|
|
1680
1477
|
return cachedFlyRegion;
|
|
1681
1478
|
}
|
|
1682
1479
|
try {
|
|
1683
|
-
const response = await
|
|
1480
|
+
const response = await fetch(`${baseUrl.replace(/\/$/, "")}/health`, { method: "GET" });
|
|
1684
1481
|
const requestId = response.headers.get("fly-request-id");
|
|
1685
1482
|
if (requestId) {
|
|
1686
1483
|
// Fly request-id format: <ulid>-<region>
|
|
@@ -1744,21 +1541,70 @@ async function workspaceAttach(args, options) {
|
|
|
1744
1541
|
if (!options.json) {
|
|
1745
1542
|
process.stderr.write(`Resolving cloud workspace for ${repoName}...\n`);
|
|
1746
1543
|
}
|
|
1747
|
-
|
|
1544
|
+
// Kick off the non-interactive work in parallel. planAgentMigration can
|
|
1545
|
+
// call promptConfirm for a TTY prompt, so we serialize it AFTER the
|
|
1546
|
+
// parallel work resolves to avoid garbled stdout during the prompt.
|
|
1547
|
+
const [region, fingerprint] = await Promise.all([
|
|
1548
|
+
detectFlyRegion(client.baseUrl).catch(() => undefined),
|
|
1549
|
+
computeSnapshotFingerprint(repoRoot, options.homePaths ?? []),
|
|
1550
|
+
]);
|
|
1551
|
+
const migrationPlan = await planAgentMigration(repoRoot, options);
|
|
1552
|
+
const mustUploadSnapshot = Boolean(migrationPlan && migrationPlan.migrated.length > 0);
|
|
1748
1553
|
const baseBody = {
|
|
1749
1554
|
workspaceKey,
|
|
1750
1555
|
repoName,
|
|
1751
|
-
|
|
1752
|
-
snapshotFingerprint: `fresh:${workspaceKey}`,
|
|
1556
|
+
snapshotFingerprint: fingerprint,
|
|
1753
1557
|
...(region ? { region } : {}),
|
|
1754
1558
|
};
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1559
|
+
let result = null;
|
|
1560
|
+
if (!mustUploadSnapshot) {
|
|
1561
|
+
try {
|
|
1562
|
+
result = await client.request("/api/rudder/workspace/attach", {
|
|
1563
|
+
method: "POST",
|
|
1564
|
+
body: baseBody,
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
catch (error) {
|
|
1568
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1569
|
+
if (!/snapshot/i.test(message)) {
|
|
1570
|
+
throw error;
|
|
1571
|
+
}
|
|
1572
|
+
result = null;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
if (!result) {
|
|
1576
|
+
if (!options.json) {
|
|
1577
|
+
process.stderr.write(`Uploading workspace snapshot...\n`);
|
|
1578
|
+
}
|
|
1579
|
+
const snapshot = await createSnapshot(repoRoot, options.homePaths ?? [], {
|
|
1580
|
+
includeRudderState: true,
|
|
1581
|
+
migration: migrationPlan ? { repoName, plan: migrationPlan } : undefined,
|
|
1582
|
+
});
|
|
1583
|
+
try {
|
|
1584
|
+
const body = {
|
|
1585
|
+
...baseBody,
|
|
1586
|
+
snapshot: {
|
|
1587
|
+
name: path.basename(snapshot.archivePath),
|
|
1588
|
+
contentType: "application/gzip",
|
|
1589
|
+
base64: await fsp.readFile(snapshot.archivePath, "base64"),
|
|
1590
|
+
manifest: snapshot.manifest,
|
|
1591
|
+
},
|
|
1592
|
+
};
|
|
1593
|
+
if (migrationPlan && migrationPlan.migrated.length > 0) {
|
|
1594
|
+
body.migratedAgents = summaryAsJson(migrationPlan);
|
|
1595
|
+
}
|
|
1596
|
+
result = await client.request("/api/rudder/workspace/attach", {
|
|
1597
|
+
method: "POST",
|
|
1598
|
+
body,
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
finally {
|
|
1602
|
+
await fsp.rm(snapshot.tempDir, { recursive: true, force: true });
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
if (migrationPlan && !options.json) {
|
|
1606
|
+
process.stderr.write(`${migrationSummary(migrationPlan)}\n`);
|
|
1607
|
+
}
|
|
1762
1608
|
if (!result) {
|
|
1763
1609
|
throw new Error("Workspace attach returned no result");
|
|
1764
1610
|
}
|
|
@@ -1965,15 +1811,10 @@ async function attach(args, options) {
|
|
|
1965
1811
|
}
|
|
1966
1812
|
async function runAttach(target, options) {
|
|
1967
1813
|
const client = await cloudClient({ requireToken: true });
|
|
1968
|
-
const
|
|
1814
|
+
const baseUrl = client.baseUrl;
|
|
1969
1815
|
const state = await loadCloudAuth();
|
|
1970
1816
|
const envToken = process.env.RUDDER_CLOUD_TOKEN?.trim();
|
|
1971
|
-
const token = envToken || (state?.cloudUrl ===
|
|
1972
|
-
// Attach uses a WebSocket, so it does not naturally pass through the
|
|
1973
|
-
// JSON request path that activates the local Fly proxy fallback. Probe the
|
|
1974
|
-
// control plane first so `client.baseUrl` reflects the reachable endpoint.
|
|
1975
|
-
await client.request("/health", { method: "GET", timeoutMs: 1500 });
|
|
1976
|
-
const baseUrl = client.baseUrl;
|
|
1817
|
+
const token = envToken || (state?.cloudUrl === baseUrl ? state.token : undefined);
|
|
1977
1818
|
if (!token) {
|
|
1978
1819
|
throw new Error("Not logged in to Rudder Cloud. Run `rudder login` first.");
|
|
1979
1820
|
}
|
|
@@ -1991,7 +1832,6 @@ async function runAttach(target, options) {
|
|
|
1991
1832
|
let cleaned = false;
|
|
1992
1833
|
let firstFrameRendered = false;
|
|
1993
1834
|
let result = "exited";
|
|
1994
|
-
const clipboardBridge = { pending: "", warned: false };
|
|
1995
1835
|
const splashAllowed = isInteractive && !options.json && !options.quietBanner;
|
|
1996
1836
|
const splash = splashAllowed ? new AttachSplash(stdout, target.label) : null;
|
|
1997
1837
|
const sendResize = () => {
|
|
@@ -2177,7 +2017,7 @@ async function runAttach(target, options) {
|
|
|
2177
2017
|
sendResize();
|
|
2178
2018
|
setTimeout(sendResize, 150);
|
|
2179
2019
|
}
|
|
2180
|
-
stdout.write(
|
|
2020
|
+
stdout.write(data);
|
|
2181
2021
|
return;
|
|
2182
2022
|
}
|
|
2183
2023
|
const text = Buffer.isBuffer(data)
|
|
@@ -2269,117 +2109,8 @@ async function runAttach(target, options) {
|
|
|
2269
2109
|
}
|
|
2270
2110
|
}
|
|
2271
2111
|
}
|
|
2272
|
-
function stderrWarning(message) {
|
|
2273
|
-
if (options.json) {
|
|
2274
|
-
return;
|
|
2275
|
-
}
|
|
2276
|
-
try {
|
|
2277
|
-
process.stderr.write(`\n${message}\n`);
|
|
2278
|
-
}
|
|
2279
|
-
catch {
|
|
2280
|
-
// ignore
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
2112
|
});
|
|
2284
2113
|
}
|
|
2285
|
-
function handleRemoteClipboardSequences(data, state, warn) {
|
|
2286
|
-
const text = state.pending + data.toString("binary");
|
|
2287
|
-
state.pending = "";
|
|
2288
|
-
let output = "";
|
|
2289
|
-
let cursor = 0;
|
|
2290
|
-
while (cursor < text.length) {
|
|
2291
|
-
const start = text.indexOf("\x1b]52;", cursor);
|
|
2292
|
-
if (start === -1) {
|
|
2293
|
-
output += text.slice(cursor);
|
|
2294
|
-
break;
|
|
2295
|
-
}
|
|
2296
|
-
output += text.slice(cursor, start);
|
|
2297
|
-
const bel = text.indexOf("\x07", start);
|
|
2298
|
-
const st = text.indexOf("\x1b\\", start);
|
|
2299
|
-
let end = -1;
|
|
2300
|
-
let terminator = "";
|
|
2301
|
-
if (bel !== -1 && (st === -1 || bel < st)) {
|
|
2302
|
-
end = bel;
|
|
2303
|
-
terminator = "\x07";
|
|
2304
|
-
}
|
|
2305
|
-
else if (st !== -1) {
|
|
2306
|
-
end = st;
|
|
2307
|
-
terminator = "\x1b\\";
|
|
2308
|
-
}
|
|
2309
|
-
if (end === -1) {
|
|
2310
|
-
state.pending = text.slice(start);
|
|
2311
|
-
break;
|
|
2312
|
-
}
|
|
2313
|
-
const sequence = text.slice(start, end);
|
|
2314
|
-
const handled = handleOsc52Clipboard(sequence, state, warn);
|
|
2315
|
-
if (!handled) {
|
|
2316
|
-
output += sequence + terminator;
|
|
2317
|
-
}
|
|
2318
|
-
cursor = end + terminator.length;
|
|
2319
|
-
}
|
|
2320
|
-
return Buffer.from(output, "binary");
|
|
2321
|
-
}
|
|
2322
|
-
function handleOsc52Clipboard(sequence, state, warn) {
|
|
2323
|
-
const body = sequence.slice("\x1b]52;".length);
|
|
2324
|
-
const separator = body.indexOf(";");
|
|
2325
|
-
if (separator === -1) {
|
|
2326
|
-
return false;
|
|
2327
|
-
}
|
|
2328
|
-
const encoded = body.slice(separator + 1);
|
|
2329
|
-
if (!encoded || encoded === "?") {
|
|
2330
|
-
return false;
|
|
2331
|
-
}
|
|
2332
|
-
let text;
|
|
2333
|
-
try {
|
|
2334
|
-
text = Buffer.from(encoded, "base64").toString("utf8");
|
|
2335
|
-
}
|
|
2336
|
-
catch {
|
|
2337
|
-
return false;
|
|
2338
|
-
}
|
|
2339
|
-
const error = copyTextToLocalClipboard(text);
|
|
2340
|
-
if (!error) {
|
|
2341
|
-
return true;
|
|
2342
|
-
}
|
|
2343
|
-
if (!state.warned) {
|
|
2344
|
-
state.warned = true;
|
|
2345
|
-
warn(`Rudder cloud clipboard copy did not reach the local clipboard: ${error}`);
|
|
2346
|
-
}
|
|
2347
|
-
return false;
|
|
2348
|
-
}
|
|
2349
|
-
function copyTextToLocalClipboard(text) {
|
|
2350
|
-
if (process.platform === "darwin") {
|
|
2351
|
-
return runLocalClipboardCommand("pbcopy", [], text);
|
|
2352
|
-
}
|
|
2353
|
-
if (process.platform === "win32") {
|
|
2354
|
-
return runLocalClipboardCommand("clip", [], text);
|
|
2355
|
-
}
|
|
2356
|
-
for (const [command, args] of [
|
|
2357
|
-
["wl-copy", []],
|
|
2358
|
-
["xclip", ["-selection", "clipboard"]],
|
|
2359
|
-
["xsel", ["--clipboard", "--input"]],
|
|
2360
|
-
]) {
|
|
2361
|
-
const error = runLocalClipboardCommand(command, args, text);
|
|
2362
|
-
if (!error) {
|
|
2363
|
-
return null;
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
return "no local clipboard command found";
|
|
2367
|
-
}
|
|
2368
|
-
function runLocalClipboardCommand(command, args, text) {
|
|
2369
|
-
const result = spawnSync(command, args, {
|
|
2370
|
-
input: text,
|
|
2371
|
-
stdio: ["pipe", "ignore", "pipe"],
|
|
2372
|
-
encoding: "utf8",
|
|
2373
|
-
});
|
|
2374
|
-
if (result.error) {
|
|
2375
|
-
return result.error.message;
|
|
2376
|
-
}
|
|
2377
|
-
if (result.status !== 0) {
|
|
2378
|
-
const detail = typeof result.stderr === "string" ? result.stderr.trim() : "";
|
|
2379
|
-
return detail || `${command} exited with ${result.status ?? "unknown status"}`;
|
|
2380
|
-
}
|
|
2381
|
-
return null;
|
|
2382
|
-
}
|
|
2383
2114
|
class AttachSplash {
|
|
2384
2115
|
stdout;
|
|
2385
2116
|
label;
|
|
@@ -2506,8 +2237,7 @@ function printCloudHelp() {
|
|
|
2506
2237
|
Usage:
|
|
2507
2238
|
rudder cloud login
|
|
2508
2239
|
rudder cloud help
|
|
2509
|
-
rudder cloud
|
|
2510
|
-
rudder cloud <task>
|
|
2240
|
+
rudder cloud [name or task]
|
|
2511
2241
|
rudder cloud launch [--home-path <path>] ["task"]
|
|
2512
2242
|
rudder cloud byoc [ssh-host]
|
|
2513
2243
|
rudder cloud vm ["task"]
|