@supacloud/admin 0.10.0 → 0.10.2
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 +13 -3
- package/dist/index.js +111 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,9 +137,19 @@ With `--artifact_transport remote` (the default), omitting
|
|
|
137
137
|
`--edge_runtime_version` retains the Management and Web Console-only upgrade
|
|
138
138
|
behavior and reports that Edge Runtime was not upgraded. Local transport
|
|
139
139
|
requires exact Management and Edge Runtime versions. Caddy and GoTrue are
|
|
140
|
-
outside this transaction and are not replaced.
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
outside this transaction and are not replaced.
|
|
141
|
+
|
|
142
|
+
The remote transport allows the same 30-minute transaction window plus bounded
|
|
143
|
+
verifier/bootstrap downloads: 42 minutes for direct GitHub access, or 52
|
|
144
|
+
minutes when each download may try direct GitHub before an explicit proxy. If
|
|
145
|
+
the SSH command times out or its stream fails after dispatch, Admin reports
|
|
146
|
+
`OUTCOME_UNKNOWN` and does not issue client-side helper cleanup. The remote
|
|
147
|
+
command may finish later and then run its own cleanup. Reconcile the reported
|
|
148
|
+
helper and trusted-root paths and read back deployed versions before deciding
|
|
149
|
+
whether to retry.
|
|
150
|
+
|
|
151
|
+
After a capable Management release is active, an exact rollback can use that
|
|
152
|
+
active upgrader with explicit older targets, for example:
|
|
143
153
|
|
|
144
154
|
```bash
|
|
145
155
|
npx @supacloud/admin ssh upgrade \
|
package/dist/index.js
CHANGED
|
@@ -24931,6 +24931,14 @@ function schemaProperties(schema) {
|
|
|
24931
24931
|
var import_ssh2 = __toESM(require_lib3(), 1);
|
|
24932
24932
|
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
24933
24933
|
import { readFileSync } from "node:fs";
|
|
24934
|
+
|
|
24935
|
+
class SshCommandOutcomeUnknownError extends Error {
|
|
24936
|
+
code = "OUTCOME_UNKNOWN";
|
|
24937
|
+
constructor(message) {
|
|
24938
|
+
super(message);
|
|
24939
|
+
this.name = "SshCommandOutcomeUnknownError";
|
|
24940
|
+
}
|
|
24941
|
+
}
|
|
24934
24942
|
var DEFAULT_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
24935
24943
|
var MAX_CONFIGURABLE_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
24936
24944
|
var DEFAULT_UPLOAD_TIMEOUT_MS = 10 * 60000;
|
|
@@ -25148,34 +25156,45 @@ class SshTransport {
|
|
|
25148
25156
|
const stderr = new BoundedOutputCollector(outputLimit);
|
|
25149
25157
|
const timer = setTimeout(() => {
|
|
25150
25158
|
connectionReusable = false;
|
|
25151
|
-
reject(new
|
|
25159
|
+
reject(new SshCommandOutcomeUnknownError(`SSH command timed out after ${timeoutMs}ms; remote outcome is unknown`));
|
|
25152
25160
|
}, timeoutMs);
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25156
|
-
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25161
|
+
try {
|
|
25162
|
+
conn.exec(command, (err, stream) => {
|
|
25163
|
+
if (err) {
|
|
25164
|
+
clearTimeout(timer);
|
|
25165
|
+
connectionReusable = false;
|
|
25166
|
+
return reject(err);
|
|
25167
|
+
}
|
|
25168
|
+
stream.on("close", (code) => {
|
|
25169
|
+
clearTimeout(timer);
|
|
25170
|
+
if (code === undefined) {
|
|
25171
|
+
connectionReusable = false;
|
|
25172
|
+
reject(new SshCommandOutcomeUnknownError("SSH command stream closed without a terminal status; remote outcome is unknown"));
|
|
25173
|
+
return;
|
|
25174
|
+
}
|
|
25175
|
+
resolve({
|
|
25176
|
+
success: code === 0,
|
|
25177
|
+
stdout: stdout.finalize(),
|
|
25178
|
+
stderr: stderr.finalize(),
|
|
25179
|
+
code: code ?? 128,
|
|
25180
|
+
stdoutTruncated: stdout.truncated,
|
|
25181
|
+
stderrTruncated: stderr.truncated
|
|
25182
|
+
});
|
|
25183
|
+
}).on("error", () => {
|
|
25184
|
+
clearTimeout(timer);
|
|
25185
|
+
connectionReusable = false;
|
|
25186
|
+
reject(new SshCommandOutcomeUnknownError("SSH command stream failed after dispatch; remote outcome is unknown"));
|
|
25187
|
+
}).on("data", (data) => {
|
|
25188
|
+
stdout.append(data);
|
|
25189
|
+
}).stderr.on("data", (data) => {
|
|
25190
|
+
stderr.append(data);
|
|
25168
25191
|
});
|
|
25169
|
-
}).on("error", (streamError) => {
|
|
25170
|
-
clearTimeout(timer);
|
|
25171
|
-
connectionReusable = false;
|
|
25172
|
-
reject(streamError);
|
|
25173
|
-
}).on("data", (data) => {
|
|
25174
|
-
stdout.append(data);
|
|
25175
|
-
}).stderr.on("data", (data) => {
|
|
25176
|
-
stderr.append(data);
|
|
25177
25192
|
});
|
|
25178
|
-
})
|
|
25193
|
+
} catch (error) {
|
|
25194
|
+
clearTimeout(timer);
|
|
25195
|
+
connectionReusable = false;
|
|
25196
|
+
reject(error);
|
|
25197
|
+
}
|
|
25179
25198
|
});
|
|
25180
25199
|
} finally {
|
|
25181
25200
|
if (connectionReusable)
|
|
@@ -27073,7 +27092,7 @@ function buildRemotePreflightScript() {
|
|
|
27073
27092
|
"test -d /run/lock || { echo '/run/lock is unavailable' >&2; exit 1; }",
|
|
27074
27093
|
"test -d /var/lib/supacloud || { echo '/var/lib/supacloud is unavailable' >&2; exit 1; }",
|
|
27075
27094
|
"test -f /etc/supabase/management-api.env || { echo 'EDGE_RUNTIME_MODE is unavailable' >&2; exit 1; }",
|
|
27076
|
-
`EDGE_MODE=$(awk -F= '$1 == "EDGE_RUNTIME_MODE" { value
|
|
27095
|
+
`EDGE_MODE=$(awk -F= '$1 == "EDGE_RUNTIME_MODE" { value=substr($0, index($0, "=") + 1) } END { gsub(/^[[:space:]\\042\\047]+|[[:space:]\\042\\047]+$/, "", value); print value }' /etc/supabase/management-api.env)`,
|
|
27077
27096
|
`test "$EDGE_MODE" = external || { echo 'Local component upgrade requires persisted external Edge Runtime mode' >&2; exit 1; }`,
|
|
27078
27097
|
`case "$(uname -m)" in x86_64|amd64) echo ARCH=amd64 ;; aarch64|arm64) echo ARCH=arm64 ;; *) echo 'Unsupported remote architecture' >&2; exit 1 ;; esac`,
|
|
27079
27098
|
"VERIFIER=bundled",
|
|
@@ -27722,8 +27741,10 @@ var SAFE_HOSTNAME = /^(?=.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9
|
|
|
27722
27741
|
var SAFE_SYSTEMD_UNIT = /^[a-zA-Z0-9][a-zA-Z0-9_.@:-]{0,127}$/;
|
|
27723
27742
|
var SAFE_DB_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_-]{0,62}$/;
|
|
27724
27743
|
var MINIMUM_COMPONENT_UPGRADE_VERSION = "0.50.27";
|
|
27725
|
-
var
|
|
27726
|
-
var
|
|
27744
|
+
var DIRECT_BOOTSTRAP_TRANSFER_BUDGET_MS = 12 * 60000;
|
|
27745
|
+
var PROXIED_BOOTSTRAP_TRANSFER_BUDGET_MS = 22 * 60000;
|
|
27746
|
+
var DIRECT_UPGRADE_SSH_TIMEOUT_MS = UPGRADE_OBSERVATION_TIMEOUT_MS + DIRECT_BOOTSTRAP_TRANSFER_BUDGET_MS;
|
|
27747
|
+
var PROXIED_UPGRADE_SSH_TIMEOUT_MS = UPGRADE_OBSERVATION_TIMEOUT_MS + PROXIED_BOOTSTRAP_TRANSFER_BUDGET_MS;
|
|
27727
27748
|
var PLATFORM_PROBE_TIMEOUT_MS = 1e4;
|
|
27728
27749
|
var PLATFORM_HASH_TIMEOUT_MS = 30000;
|
|
27729
27750
|
var WEB_CONSOLE_PROBE_TIMEOUT_MS = 60000;
|
|
@@ -27902,7 +27923,7 @@ function componentBootstrapCommands(request) {
|
|
|
27902
27923
|
function componentPreflightCommands(request) {
|
|
27903
27924
|
return request.edgeRuntimeVersion ? [
|
|
27904
27925
|
"test -f /etc/supabase/management-api.env || { echo 'EDGE_RUNTIME_MODE is unavailable; component upgrade requires external mode' >&2; exit 1; }",
|
|
27905
|
-
`EDGE_RUNTIME_MODE_VALUE=$(awk -F= '$1 == "EDGE_RUNTIME_MODE" { value
|
|
27926
|
+
`EDGE_RUNTIME_MODE_VALUE=$(awk -F= '$1 == "EDGE_RUNTIME_MODE" { value=substr($0, index($0, "=") + 1) } END { gsub(/^[[:space:]\\042\\047]+|[[:space:]\\042\\047]+$/, "", value); print value }' /etc/supabase/management-api.env)`,
|
|
27906
27927
|
`test "$EDGE_RUNTIME_MODE_VALUE" = external || { echo 'Edge Runtime component upgrade supports persisted external mode only' >&2; exit 1; }`
|
|
27907
27928
|
] : [];
|
|
27908
27929
|
}
|
|
@@ -27996,11 +28017,58 @@ async function removeRemoteUpgradeHelper(ssh, helperPath) {
|
|
|
27996
28017
|
throw new Error(`Failed to remove remote upgrade helper (exit ${cleanup.code}): ${cleanup.stderr.slice(-300)}`);
|
|
27997
28018
|
}
|
|
27998
28019
|
}
|
|
28020
|
+
|
|
28021
|
+
class RemoteUpgradeOutcomeUnknownError extends AggregateError {
|
|
28022
|
+
code = "OUTCOME_UNKNOWN";
|
|
28023
|
+
}
|
|
28024
|
+
function boundedUpgradeError(error) {
|
|
28025
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28026
|
+
const diagnostic = redactSshOutput(message).replace(/[\r\n\t]+/g, " ").trim().slice(0, 500);
|
|
28027
|
+
return new Error(diagnostic || "Remote operation ended without a diagnostic");
|
|
28028
|
+
}
|
|
28029
|
+
function remoteUpgradeEvidence(helperPath) {
|
|
28030
|
+
const trustedRootPath = join2(dirname(helperPath), SIGSTORE_PUBLIC_GOOD_TRUSTED_ROOT_FILENAME);
|
|
28031
|
+
return `helper=${helperPath} trusted_root=${trustedRootPath}`;
|
|
28032
|
+
}
|
|
28033
|
+
function remoteUpgradeReconciliationFailure(message, failures, helperPath) {
|
|
28034
|
+
return new RemoteUpgradeOutcomeUnknownError(failures.map(boundedUpgradeError), `${message} Reconcile ${remoteUpgradeEvidence(helperPath)}; do not retry blindly`);
|
|
28035
|
+
}
|
|
28036
|
+
function remoteUpgradeOutcomeUnknown(transportError, helperPath) {
|
|
28037
|
+
return remoteUpgradeReconciliationFailure("OUTCOME_UNKNOWN: Remote upgrade transport ended after dispatch; client cleanup was suppressed " + "because the remote command may still be running. Verify deployed versions before retrying.", [transportError], helperPath);
|
|
28038
|
+
}
|
|
27999
28039
|
function remoteUpgradeFailure(execution) {
|
|
28000
28040
|
const diagnostic = execution.stderr.trim() || execution.stdout.trim() || "no remote diagnostic";
|
|
28001
28041
|
return new Error(`Remote upgrade failed (exit ${execution.code}): ${diagnostic.slice(-500)}`);
|
|
28002
28042
|
}
|
|
28043
|
+
function remoteHelperSetupOutcomeUnknown(outcome) {
|
|
28044
|
+
const cleanupStatus = outcome.cleanupFailed ? "the helper cleanup did not complete" : "the cleanup command completed, but setup may finish later";
|
|
28045
|
+
const failures = outcome.cleanupFailed ? [outcome.executionError, outcome.cleanupError] : [outcome.executionError];
|
|
28046
|
+
return remoteUpgradeReconciliationFailure(`OUTCOME_UNKNOWN: Remote helper setup ended without terminal status; ${cleanupStatus}. ` + "The upgrade command was not dispatched.", failures, outcome.helperPath);
|
|
28047
|
+
}
|
|
28048
|
+
function completedUpgradeCleanupOutcomeUnknown(outcome) {
|
|
28049
|
+
if (!outcome.execution) {
|
|
28050
|
+
return remoteUpgradeReconciliationFailure("OUTCOME_UNKNOWN: Upgrade execution returned no result, and helper cleanup outcome is unknown.", [outcome.cleanupError], outcome.helperPath);
|
|
28051
|
+
}
|
|
28052
|
+
const message = outcome.execution.success ? "OUTCOME_UNKNOWN: Remote upgrade succeeded, but helper cleanup outcome is unknown." : "OUTCOME_UNKNOWN: Remote upgrade failed with a terminal result, and helper cleanup outcome is unknown.";
|
|
28053
|
+
const failures = outcome.execution.success ? [outcome.cleanupError] : [remoteUpgradeFailure(outcome.execution), outcome.cleanupError];
|
|
28054
|
+
return remoteUpgradeReconciliationFailure(message, failures, outcome.helperPath);
|
|
28055
|
+
}
|
|
28056
|
+
function remoteHelperCleanupOutcomeUnknown(outcome) {
|
|
28057
|
+
if (!outcome.upgradeExecutionRequested) {
|
|
28058
|
+
return remoteUpgradeReconciliationFailure("OUTCOME_UNKNOWN: Remote helper setup failed, and helper cleanup outcome is unknown. " + "The upgrade command was not dispatched.", [outcome.executionError, outcome.cleanupError], outcome.helperPath);
|
|
28059
|
+
}
|
|
28060
|
+
if (outcome.executionFailed) {
|
|
28061
|
+
return remoteUpgradeReconciliationFailure("OUTCOME_UNKNOWN: Upgrade execution request failed, and helper cleanup outcome is unknown.", [outcome.executionError, outcome.cleanupError], outcome.helperPath);
|
|
28062
|
+
}
|
|
28063
|
+
return completedUpgradeCleanupOutcomeUnknown(outcome);
|
|
28064
|
+
}
|
|
28003
28065
|
function officialUpgradeOutcome(outcome) {
|
|
28066
|
+
if (!outcome.upgradeExecutionRequested && outcome.executionError instanceof SshCommandOutcomeUnknownError) {
|
|
28067
|
+
throw remoteHelperSetupOutcomeUnknown(outcome);
|
|
28068
|
+
}
|
|
28069
|
+
if (outcome.cleanupFailed && outcome.cleanupError instanceof SshCommandOutcomeUnknownError) {
|
|
28070
|
+
throw remoteHelperCleanupOutcomeUnknown(outcome);
|
|
28071
|
+
}
|
|
28004
28072
|
if (outcome.executionFailed && outcome.cleanupFailed) {
|
|
28005
28073
|
throw new AggregateError([outcome.executionError, outcome.cleanupError], "Upgrade execution failed and helper cleanup did not complete");
|
|
28006
28074
|
}
|
|
@@ -28021,34 +28089,37 @@ async function executeOfficialUpgrade(ssh, helperPath, command, timeoutMs) {
|
|
|
28021
28089
|
let execution;
|
|
28022
28090
|
let executionFailed = false;
|
|
28023
28091
|
let executionError;
|
|
28024
|
-
let
|
|
28092
|
+
let upgradeExecutionRequested = false;
|
|
28025
28093
|
try {
|
|
28026
28094
|
await prepareRemoteUpgradeHelperDirectory(ssh, helperPath);
|
|
28027
|
-
helperPrepared = true;
|
|
28028
28095
|
await ssh.uploadText(helperPath, release_assets_default, 384);
|
|
28029
28096
|
const trustedRootPath = join2(dirname(helperPath), SIGSTORE_PUBLIC_GOOD_TRUSTED_ROOT_FILENAME);
|
|
28030
28097
|
await ssh.uploadText(trustedRootPath, SIGSTORE_PUBLIC_GOOD_TRUSTED_ROOT_JSONL, 384);
|
|
28098
|
+
upgradeExecutionRequested = true;
|
|
28031
28099
|
execution = await ssh.exec(command, timeoutMs);
|
|
28032
28100
|
} catch (error) {
|
|
28101
|
+
if (upgradeExecutionRequested && error instanceof SshCommandOutcomeUnknownError) {
|
|
28102
|
+
throw remoteUpgradeOutcomeUnknown(error, helperPath);
|
|
28103
|
+
}
|
|
28033
28104
|
executionFailed = true;
|
|
28034
28105
|
executionError = error;
|
|
28035
28106
|
}
|
|
28036
28107
|
let cleanupFailed = false;
|
|
28037
28108
|
let cleanupError;
|
|
28038
|
-
|
|
28039
|
-
|
|
28040
|
-
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
cleanupError = error;
|
|
28044
|
-
}
|
|
28109
|
+
try {
|
|
28110
|
+
await removeRemoteUpgradeHelper(ssh, helperPath);
|
|
28111
|
+
} catch (error) {
|
|
28112
|
+
cleanupFailed = true;
|
|
28113
|
+
cleanupError = error;
|
|
28045
28114
|
}
|
|
28046
28115
|
return officialUpgradeOutcome({
|
|
28047
28116
|
execution,
|
|
28048
28117
|
executionFailed,
|
|
28049
28118
|
executionError,
|
|
28050
28119
|
cleanupFailed,
|
|
28051
|
-
cleanupError
|
|
28120
|
+
cleanupError,
|
|
28121
|
+
helperPath,
|
|
28122
|
+
upgradeExecutionRequested
|
|
28052
28123
|
});
|
|
28053
28124
|
}
|
|
28054
28125
|
function assertSafeGithubProxy(value) {
|
|
@@ -30472,7 +30543,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
|
|
|
30472
30543
|
// package.json
|
|
30473
30544
|
var package_default = {
|
|
30474
30545
|
name: "@supacloud/admin",
|
|
30475
|
-
version: "0.10.
|
|
30546
|
+
version: "0.10.2",
|
|
30476
30547
|
description: "Platform administration CLI for SupaCloud operators",
|
|
30477
30548
|
type: "module",
|
|
30478
30549
|
main: "./dist/index.js",
|