@supacloud/admin 0.24.1 → 0.25.0
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 +10 -0
- package/dist/index.js +313 -132
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -70,6 +70,7 @@ production requires the exact project ref confirmation:
|
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
72
|
supacloud-admin frontend list_releases --ref abc123 --id web
|
|
73
|
+
supacloud-admin frontend get_active_release --ref abc123 --id web
|
|
73
74
|
supacloud-admin frontend get_release --ref abc123 --id web --release_id <sha256>
|
|
74
75
|
supacloud-admin frontend upload_release --ref abc123 --id web \
|
|
75
76
|
--zip_path /secure/site.zip --confirm-production abc123
|
|
@@ -77,6 +78,8 @@ supacloud-admin frontend activate_release --ref abc123 --id web \
|
|
|
77
78
|
--release_id <sha256> --expected_active_release_id absent \
|
|
78
79
|
--expected_activation_id absent --mutation_id <uuid-v4> \
|
|
79
80
|
--confirm-production abc123
|
|
81
|
+
supacloud-admin frontend rollback --ref abc123 --id web \
|
|
82
|
+
--release_id <retained-sha256> --confirm-production abc123
|
|
80
83
|
```
|
|
81
84
|
|
|
82
85
|
The CLI reads the archive through a no-follow file descriptor, verifies its
|
|
@@ -88,6 +91,13 @@ activation mutations require the Linux held-directory-FD implementation; on
|
|
|
88
91
|
other platforms, Management API returns HTTP 503 before it reads the upload
|
|
89
92
|
body, creates release directories, or writes a mutation journal.
|
|
90
93
|
|
|
94
|
+
The active snapshot automatically supports older HTTP 404 endpoints through
|
|
95
|
+
bounded history and exact artifact readback, never authorization or integrity
|
|
96
|
+
failures. `rollback` handles current CAS and mutation IDs automatically while
|
|
97
|
+
requiring an explicit retained target. It never retries a concurrent conflict,
|
|
98
|
+
restores a database or infers the previous version from sorted history.
|
|
99
|
+
Unknown results include the mutation ID for read-only reconciliation.
|
|
100
|
+
|
|
91
101
|
SSH host keys are fail-closed: setting `SUPACLOUD_HOST` and credentials is not
|
|
92
102
|
enough to enable SSH actions. Obtain the fingerprint through a trusted channel,
|
|
93
103
|
compare it out of band, then set it explicitly. For example, the discovery
|
package/dist/index.js
CHANGED
|
@@ -2855,23 +2855,23 @@ var require_nacl_fast = __commonJS(function(exports, module) {
|
|
|
2855
2855
|
randombytes = fn;
|
|
2856
2856
|
};
|
|
2857
2857
|
(function() {
|
|
2858
|
-
var
|
|
2859
|
-
if (
|
|
2858
|
+
var crypto2 = typeof self !== "undefined" ? self.crypto || self.msCrypto : null;
|
|
2859
|
+
if (crypto2 && crypto2.getRandomValues) {
|
|
2860
2860
|
var QUOTA = 65536;
|
|
2861
2861
|
nacl.setPRNG(function(x, n) {
|
|
2862
2862
|
var i, v = new Uint8Array(n);
|
|
2863
2863
|
for (i = 0;i < n; i += QUOTA) {
|
|
2864
|
-
|
|
2864
|
+
crypto2.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
|
|
2865
2865
|
}
|
|
2866
2866
|
for (i = 0;i < n; i++)
|
|
2867
2867
|
x[i] = v[i];
|
|
2868
2868
|
cleanup(v);
|
|
2869
2869
|
});
|
|
2870
2870
|
} else if (true) {
|
|
2871
|
-
|
|
2872
|
-
if (
|
|
2871
|
+
crypto2 = __require("crypto");
|
|
2872
|
+
if (crypto2 && crypto2.randomBytes) {
|
|
2873
2873
|
nacl.setPRNG(function(x, n) {
|
|
2874
|
-
var i, v =
|
|
2874
|
+
var i, v = crypto2.randomBytes(n);
|
|
2875
2875
|
for (i = 0;i < n; i++)
|
|
2876
2876
|
x[i] = v[i];
|
|
2877
2877
|
cleanup(v);
|
|
@@ -4135,14 +4135,14 @@ var require_bcrypt_pbkdf = __commonJS(function(exports, module) {
|
|
|
4135
4135
|
|
|
4136
4136
|
// node_modules/ssh2/lib/protocol/constants.js
|
|
4137
4137
|
var require_constants = __commonJS(function(exports, module) {
|
|
4138
|
-
var
|
|
4138
|
+
var crypto2 = __require("crypto");
|
|
4139
4139
|
var cpuInfo;
|
|
4140
4140
|
try {
|
|
4141
4141
|
cpuInfo = __require("cpu-features")();
|
|
4142
4142
|
} catch {}
|
|
4143
4143
|
var { bindingAvailable, CIPHER_INFO, MAC_INFO } = require_crypto();
|
|
4144
4144
|
var eddsaSupported = (() => {
|
|
4145
|
-
if (typeof
|
|
4145
|
+
if (typeof crypto2.sign === "function" && typeof crypto2.verify === "function") {
|
|
4146
4146
|
const key = `-----BEGIN PRIVATE KEY-----\r
|
|
4147
4147
|
MC4CAQAwBQYDK2VwBCIEIHKj+sVa9WcD` + `/q2DJUJaf43Kptc8xYuUQA4bOFj9vC8T\r
|
|
4148
4148
|
-----END PRIVATE KEY-----`;
|
|
@@ -4150,14 +4150,14 @@ MC4CAQAwBQYDK2VwBCIEIHKj+sVa9WcD` + `/q2DJUJaf43Kptc8xYuUQA4bOFj9vC8T\r
|
|
|
4150
4150
|
let sig;
|
|
4151
4151
|
let verified;
|
|
4152
4152
|
try {
|
|
4153
|
-
sig =
|
|
4154
|
-
verified =
|
|
4153
|
+
sig = crypto2.sign(null, data, key);
|
|
4154
|
+
verified = crypto2.verify(null, data, key, sig);
|
|
4155
4155
|
} catch {}
|
|
4156
4156
|
return Buffer.isBuffer(sig) && sig.length === 64 && verified === true;
|
|
4157
4157
|
}
|
|
4158
4158
|
return false;
|
|
4159
4159
|
})();
|
|
4160
|
-
var curve25519Supported = typeof
|
|
4160
|
+
var curve25519Supported = typeof crypto2.diffieHellman === "function" && typeof crypto2.generateKeyPairSync === "function" && typeof crypto2.createPublicKey === "function";
|
|
4161
4161
|
var DEFAULT_KEX = [
|
|
4162
4162
|
"ecdh-sha2-nistp256",
|
|
4163
4163
|
"ecdh-sha2-nistp384",
|
|
@@ -4192,7 +4192,7 @@ MC4CAQAwBQYDK2VwBCIEIHKj+sVa9WcD` + `/q2DJUJaf43Kptc8xYuUQA4bOFj9vC8T\r
|
|
|
4192
4192
|
"ssh-dss"
|
|
4193
4193
|
]);
|
|
4194
4194
|
var canUseCipher = (() => {
|
|
4195
|
-
const ciphers =
|
|
4195
|
+
const ciphers = crypto2.getCiphers();
|
|
4196
4196
|
return (name) => ciphers.includes(CIPHER_INFO[name].sslName);
|
|
4197
4197
|
})();
|
|
4198
4198
|
var DEFAULT_CIPHER = [
|
|
@@ -4227,7 +4227,7 @@ MC4CAQAwBQYDK2VwBCIEIHKj+sVa9WcD` + `/q2DJUJaf43Kptc8xYuUQA4bOFj9vC8T\r
|
|
|
4227
4227
|
"arcfour"
|
|
4228
4228
|
].filter(canUseCipher));
|
|
4229
4229
|
var canUseMAC = (() => {
|
|
4230
|
-
const hashes =
|
|
4230
|
+
const hashes = crypto2.getHashes();
|
|
4231
4231
|
return (name) => hashes.includes(MAC_INFO[name].sslName);
|
|
4232
4232
|
})();
|
|
4233
4233
|
var DEFAULT_MAC = [
|
|
@@ -19618,6 +19618,11 @@ class DecodeBuilder {
|
|
|
19618
19618
|
function Codec(type) {
|
|
19619
19619
|
return new DecodeBuilder(type);
|
|
19620
19620
|
}
|
|
19621
|
+
function Decode(type, callback) {
|
|
19622
|
+
return Codec(type).Decode(callback).Encode(() => {
|
|
19623
|
+
throw Error("Encode not implemented");
|
|
19624
|
+
});
|
|
19625
|
+
}
|
|
19621
19626
|
function IsCodec(value) {
|
|
19622
19627
|
return IsSchema(value) && HasPropertyKey(value, "~codec") && IsObject(value["~codec"]) && HasPropertyKey(value["~codec"], "encode") && HasPropertyKey(value["~codec"], "decode");
|
|
19623
19628
|
}
|
|
@@ -24838,7 +24843,22 @@ var Delete = _Object_({
|
|
|
24838
24843
|
});
|
|
24839
24844
|
var Edit = Union([Insert2, Update2, Delete]);
|
|
24840
24845
|
// ../cli/src/shared/schema.ts
|
|
24841
|
-
var
|
|
24846
|
+
var DECODE_ID = "~supacloudDecodeId";
|
|
24847
|
+
|
|
24848
|
+
class OutputDecodeError extends Error {
|
|
24849
|
+
decodeId;
|
|
24850
|
+
issues;
|
|
24851
|
+
constructor(decodeId, issues) {
|
|
24852
|
+
super(issues.map((issue) => `- ${issue.path}: ${issue.message}`).join(`
|
|
24853
|
+
`));
|
|
24854
|
+
this.decodeId = decodeId;
|
|
24855
|
+
this.issues = issues;
|
|
24856
|
+
}
|
|
24857
|
+
}
|
|
24858
|
+
function decodeIdOf(schema) {
|
|
24859
|
+
const value = Object.getOwnPropertyDescriptor(schema, DECODE_ID)?.value;
|
|
24860
|
+
return typeof value === "symbol" ? value : undefined;
|
|
24861
|
+
}
|
|
24842
24862
|
function schemaErrorPath(path) {
|
|
24843
24863
|
return path.replace(/^\//, "").replaceAll("/", ".") || "args";
|
|
24844
24864
|
}
|
|
@@ -24850,42 +24870,46 @@ function transformErrorLine(error) {
|
|
|
24850
24870
|
const path = typeof error === "object" && error !== null && "instancePath" in error && typeof error.instancePath === "string" ? schemaErrorPath(error.instancePath) : "args";
|
|
24851
24871
|
return `- ${path}: ${message}`;
|
|
24852
24872
|
}
|
|
24853
|
-
function
|
|
24854
|
-
const
|
|
24855
|
-
const issues = schemaErrorLines(objectSchema, input);
|
|
24873
|
+
function validateToolArguments(schema, input) {
|
|
24874
|
+
const issues = schemaErrorLines(_Object_(schema, { additionalProperties: false }), input);
|
|
24856
24875
|
if (issues.length > 0)
|
|
24857
24876
|
throw new Error(`Invalid arguments:
|
|
24858
24877
|
${issues.join(`
|
|
24859
24878
|
`)}`);
|
|
24879
|
+
}
|
|
24880
|
+
function isRecord(value) {
|
|
24881
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
24882
|
+
}
|
|
24883
|
+
function parseToolArguments(schema, input) {
|
|
24884
|
+
const objectSchema = _Object_(schema, { additionalProperties: false });
|
|
24885
|
+
validateToolArguments(schema, input);
|
|
24860
24886
|
let decoded;
|
|
24861
24887
|
try {
|
|
24862
24888
|
decoded = Decode10(objectSchema, input);
|
|
24863
24889
|
} catch (error) {
|
|
24890
|
+
if (error instanceof OutputDecodeError) {
|
|
24891
|
+
const entry = Object.entries(schema).find(([, property]) => decodeIdOf(property) === error.decodeId);
|
|
24892
|
+
const path = entry?.[0] ?? "args";
|
|
24893
|
+
const issues = error.issues.map((issue) => `- ${issue.path === "args" ? path : `${path}.${issue.path}`}: ${issue.message}`);
|
|
24894
|
+
throw new Error(`Invalid arguments:
|
|
24895
|
+
${issues.join(`
|
|
24896
|
+
`)}`);
|
|
24897
|
+
}
|
|
24864
24898
|
throw new Error(`Invalid arguments:
|
|
24865
24899
|
${transformErrorLine(error)}`);
|
|
24866
24900
|
}
|
|
24867
|
-
const
|
|
24868
|
-
|
|
24869
|
-
|
|
24870
|
-
if (outputSchema === undefined || decoded[name] === undefined)
|
|
24871
|
-
continue;
|
|
24872
|
-
for (const issue of Errors2(outputSchema, decoded[name])) {
|
|
24873
|
-
const path = schemaErrorPath(issue.instancePath);
|
|
24874
|
-
decodedIssues.push(`- ${path === "args" ? name : `${name}.${path}`}: ${issue.message}`);
|
|
24875
|
-
}
|
|
24901
|
+
const decodedFields = decoded;
|
|
24902
|
+
if (!isRecord(decodedFields)) {
|
|
24903
|
+
throw new Error("Invalid arguments: decoded value must be an object");
|
|
24876
24904
|
}
|
|
24877
|
-
if (decodedIssues.length > 0)
|
|
24878
|
-
throw new Error(`Invalid arguments:
|
|
24879
|
-
${decodedIssues.join(`
|
|
24880
|
-
`)}`);
|
|
24881
24905
|
return decoded;
|
|
24882
24906
|
}
|
|
24883
24907
|
function stringEnum(values, options) {
|
|
24884
|
-
|
|
24885
|
-
return Enum(enumRecord, options);
|
|
24908
|
+
return Enum([...values], options);
|
|
24886
24909
|
}
|
|
24887
24910
|
function mergeSchemaOptions(schema, options) {
|
|
24888
|
-
const result =
|
|
24911
|
+
const result = { ...schema };
|
|
24912
|
+
Object.setPrototypeOf(result, Object.getPrototypeOf(schema));
|
|
24889
24913
|
Object.defineProperties(result, Object.getOwnPropertyDescriptors(schema));
|
|
24890
24914
|
for (const [key, value] of Object.entries(options)) {
|
|
24891
24915
|
Object.defineProperty(result, key, { value, enumerable: true, writable: true, configurable: true });
|
|
@@ -24899,9 +24923,19 @@ function optional(schema, description) {
|
|
|
24899
24923
|
return Optional(description ? withDescription(schema, description) : schema);
|
|
24900
24924
|
}
|
|
24901
24925
|
function decodedSchema(inputSchema, outputSchema, decode, options) {
|
|
24902
|
-
const
|
|
24926
|
+
const decodeId = Symbol("decodedSchema");
|
|
24927
|
+
const transform = Decode(inputSchema, (input) => {
|
|
24928
|
+
const value = decode(input);
|
|
24929
|
+
const issues = [...Errors2(outputSchema, value)].map((issue) => ({
|
|
24930
|
+
path: schemaErrorPath(issue.instancePath),
|
|
24931
|
+
message: issue.message
|
|
24932
|
+
}));
|
|
24933
|
+
if (issues.length > 0)
|
|
24934
|
+
throw new OutputDecodeError(decodeId, issues);
|
|
24935
|
+
return Decode10(outputSchema, value);
|
|
24936
|
+
});
|
|
24903
24937
|
const result = options ? mergeSchemaOptions(transform, options) : transform;
|
|
24904
|
-
Object.defineProperty(result,
|
|
24938
|
+
Object.defineProperty(result, DECODE_ID, { value: decodeId, enumerable: false, configurable: true });
|
|
24905
24939
|
return result;
|
|
24906
24940
|
}
|
|
24907
24941
|
function schemaEnumValues(schema) {
|
|
@@ -25894,8 +25928,8 @@ var ACTION_POLICY = {
|
|
|
25894
25928
|
]
|
|
25895
25929
|
},
|
|
25896
25930
|
frontend: {
|
|
25897
|
-
read: ["list_releases", "get_release"],
|
|
25898
|
-
write: ["upload_release", "activate_release"]
|
|
25931
|
+
read: ["list_releases", "get_active_release", "get_release"],
|
|
25932
|
+
write: ["upload_release", "activate_release", "rollback"]
|
|
25899
25933
|
},
|
|
25900
25934
|
ssh: {
|
|
25901
25935
|
read: [
|
|
@@ -29364,14 +29398,13 @@ function quoteShell(shellText) {
|
|
|
29364
29398
|
}
|
|
29365
29399
|
|
|
29366
29400
|
// src/shared/releases/local-upgrade-bundle.ts
|
|
29367
|
-
import { spawn } from "node:child_process";
|
|
29368
29401
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
29369
29402
|
import { accessSync, chmodSync as chmodSync2, constants as fsConstants, copyFileSync, createWriteStream, lstatSync, mkdirSync, mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, readdirSync, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
29370
29403
|
import { get } from "node:https";
|
|
29371
29404
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
29372
29405
|
import { basename, delimiter, isAbsolute, join } from "node:path";
|
|
29373
29406
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
29374
|
-
import { Transform } from "node:stream";
|
|
29407
|
+
import { Readable, Transform } from "node:stream";
|
|
29375
29408
|
|
|
29376
29409
|
// ../management-api/src/release-manifest.ts
|
|
29377
29410
|
var RELEASE_MANIFEST_NAME = "SUPACLOUD-RELEASE.json";
|
|
@@ -30015,70 +30048,81 @@ function spawnGithubCli(arguments_, download = false) {
|
|
|
30015
30048
|
environment[key] = process.env[key];
|
|
30016
30049
|
}
|
|
30017
30050
|
}
|
|
30018
|
-
return spawn(githubCliExecutable(environment), arguments_, {
|
|
30051
|
+
return Bun.spawn([githubCliExecutable(environment), ...arguments_], {
|
|
30019
30052
|
env: environment,
|
|
30020
30053
|
stdio: ["ignore", "pipe", "pipe"]
|
|
30021
30054
|
});
|
|
30022
30055
|
}
|
|
30023
|
-
function githubCliExitCode(child, timeoutMs) {
|
|
30024
|
-
return new Promise((resolve, reject) => {
|
|
30025
|
-
let timedOut = false;
|
|
30026
|
-
let settled = false;
|
|
30027
|
-
let forceKillTimer;
|
|
30028
|
-
const timeout = setTimeout(() => {
|
|
30029
|
-
timedOut = true;
|
|
30030
|
-
child.kill("SIGTERM");
|
|
30031
|
-
forceKillTimer = setTimeout(() => child.kill("SIGKILL"), GH_TERMINATION_GRACE_MS);
|
|
30032
|
-
}, timeoutMs);
|
|
30033
|
-
const settleExecution = (error, exitCode) => {
|
|
30034
|
-
if (settled)
|
|
30035
|
-
return;
|
|
30036
|
-
settled = true;
|
|
30037
|
-
clearTimeout(timeout);
|
|
30038
|
-
if (forceKillTimer)
|
|
30039
|
-
clearTimeout(forceKillTimer);
|
|
30040
|
-
if (error)
|
|
30041
|
-
reject(error);
|
|
30042
|
-
else
|
|
30043
|
-
resolve(timedOut ? 124 : exitCode);
|
|
30044
|
-
};
|
|
30045
|
-
child.once("error", (error) => settleExecution(error, 127));
|
|
30046
|
-
child.once("close", (code) => settleExecution(undefined, code ?? 1));
|
|
30047
|
-
});
|
|
30048
|
-
}
|
|
30049
30056
|
async function runGithubCli(arguments_, timeoutMs) {
|
|
30050
30057
|
const child = spawnGithubCli(arguments_);
|
|
30051
|
-
let
|
|
30052
|
-
let
|
|
30053
|
-
|
|
30054
|
-
|
|
30055
|
-
|
|
30056
|
-
|
|
30057
|
-
|
|
30058
|
-
|
|
30059
|
-
|
|
30060
|
-
|
|
30058
|
+
let timedOut = false;
|
|
30059
|
+
let forceKillTimer;
|
|
30060
|
+
const timeout = setTimeout(() => {
|
|
30061
|
+
timedOut = true;
|
|
30062
|
+
child.kill("SIGTERM");
|
|
30063
|
+
forceKillTimer = setTimeout(() => child.kill("SIGKILL"), GH_TERMINATION_GRACE_MS);
|
|
30064
|
+
}, timeoutMs);
|
|
30065
|
+
try {
|
|
30066
|
+
const [rawStdout, rawStderr, exitCode] = await Promise.all([
|
|
30067
|
+
child.stdout ? new Response(child.stdout).text() : Promise.resolve(""),
|
|
30068
|
+
child.stderr ? new Response(child.stderr).text() : Promise.resolve(""),
|
|
30069
|
+
child.exited
|
|
30070
|
+
]);
|
|
30071
|
+
clearTimeout(timeout);
|
|
30072
|
+
if (forceKillTimer)
|
|
30073
|
+
clearTimeout(forceKillTimer);
|
|
30074
|
+
const stdout = rawStdout.slice(-8000);
|
|
30075
|
+
const stderr = rawStderr.slice(-8000);
|
|
30076
|
+
return { exitCode: timedOut ? 124 : exitCode ?? 1, stdout, stderr };
|
|
30077
|
+
} catch (error) {
|
|
30078
|
+
clearTimeout(timeout);
|
|
30079
|
+
if (forceKillTimer)
|
|
30080
|
+
clearTimeout(forceKillTimer);
|
|
30081
|
+
child.kill("SIGKILL");
|
|
30082
|
+
throw error;
|
|
30083
|
+
}
|
|
30061
30084
|
}
|
|
30062
30085
|
async function runGithubCliDownload(arguments_, destination, maxBytes, timeoutMs) {
|
|
30063
30086
|
const child = spawnGithubCli(arguments_, true);
|
|
30064
|
-
let
|
|
30065
|
-
|
|
30066
|
-
|
|
30067
|
-
|
|
30068
|
-
|
|
30087
|
+
let timedOut = false;
|
|
30088
|
+
let forceKillTimer;
|
|
30089
|
+
const timeout = setTimeout(() => {
|
|
30090
|
+
timedOut = true;
|
|
30091
|
+
child.kill("SIGTERM");
|
|
30092
|
+
forceKillTimer = setTimeout(() => child.kill("SIGKILL"), GH_TERMINATION_GRACE_MS);
|
|
30093
|
+
}, timeoutMs);
|
|
30094
|
+
const stderrPromise = (async () => {
|
|
30095
|
+
try {
|
|
30096
|
+
return child.stderr ? (await new Response(child.stderr).text()).slice(-8000) : "";
|
|
30097
|
+
} catch {
|
|
30098
|
+
return "";
|
|
30099
|
+
}
|
|
30100
|
+
})();
|
|
30101
|
+
const write = pipeline3(Readable.fromWeb(child.stdout), boundedWriter(maxBytes), createWriteStream(destination, { flags: "wx", mode: 384 })).catch((error) => {
|
|
30069
30102
|
child.kill("SIGKILL");
|
|
30070
30103
|
throw error;
|
|
30071
30104
|
});
|
|
30072
|
-
|
|
30073
|
-
|
|
30074
|
-
|
|
30075
|
-
|
|
30076
|
-
|
|
30077
|
-
|
|
30105
|
+
try {
|
|
30106
|
+
const [writeState, exitState, stderrState] = await Promise.allSettled([
|
|
30107
|
+
write,
|
|
30108
|
+
child.exited,
|
|
30109
|
+
stderrPromise
|
|
30110
|
+
]);
|
|
30111
|
+
if (writeState.status === "rejected")
|
|
30112
|
+
throw writeState.reason;
|
|
30113
|
+
if (exitState.status === "rejected")
|
|
30114
|
+
throw exitState.reason;
|
|
30115
|
+
if (stderrState.status === "rejected")
|
|
30116
|
+
throw stderrState.reason;
|
|
30117
|
+
return { exitCode: timedOut ? 124 : exitState.value, stdout: "", stderr: stderrState.value };
|
|
30118
|
+
} catch (error) {
|
|
30078
30119
|
rmSync2(destination, { force: true });
|
|
30079
|
-
throw
|
|
30120
|
+
throw error;
|
|
30121
|
+
} finally {
|
|
30122
|
+
clearTimeout(timeout);
|
|
30123
|
+
if (forceKillTimer)
|
|
30124
|
+
clearTimeout(forceKillTimer);
|
|
30080
30125
|
}
|
|
30081
|
-
return { exitCode: exitState.value, stdout: "", stderr };
|
|
30082
30126
|
}
|
|
30083
30127
|
function supportsStrictGithubVerification(execution) {
|
|
30084
30128
|
const tokens = `${execution.stdout}
|
|
@@ -33137,18 +33181,25 @@ Actions: list, deploy, deploy_bundle, source, delete, check`, {
|
|
|
33137
33181
|
};
|
|
33138
33182
|
let text;
|
|
33139
33183
|
const checkSyntax = async (sourceCode) => {
|
|
33140
|
-
const fs = __require("fs");
|
|
33141
33184
|
const os = __require("os");
|
|
33142
|
-
const
|
|
33143
|
-
const execAsync = promisify(__require("child_process").exec);
|
|
33185
|
+
const fs = __require("fs");
|
|
33144
33186
|
const tmpFile = `${os.tmpdir()}/supacloud_edge_${Date.now()}.ts`;
|
|
33145
|
-
|
|
33187
|
+
await Bun.write(tmpFile, sourceCode);
|
|
33146
33188
|
try {
|
|
33147
|
-
|
|
33148
|
-
|
|
33189
|
+
const proc = Bun.spawn(["bun", "build", tmpFile, "--external=*"], {
|
|
33190
|
+
stdout: "pipe",
|
|
33191
|
+
stderr: "pipe"
|
|
33192
|
+
});
|
|
33193
|
+
const exitCode = await proc.exited;
|
|
33194
|
+
if (exitCode === 0) {
|
|
33195
|
+
return { ok: true };
|
|
33196
|
+
}
|
|
33197
|
+
const stdout = await new Response(proc.stdout).text();
|
|
33198
|
+
const stderr = await new Response(proc.stderr).text();
|
|
33199
|
+
return { ok: false, err: stdout + `
|
|
33200
|
+
` + (stderr || `exit code ${exitCode}`) };
|
|
33149
33201
|
} catch (e) {
|
|
33150
|
-
return { ok: false, err: e.
|
|
33151
|
-
` + (e.stderr || e.message) };
|
|
33202
|
+
return { ok: false, err: e.message || String(e) };
|
|
33152
33203
|
} finally {
|
|
33153
33204
|
try {
|
|
33154
33205
|
fs.unlinkSync(tmpFile);
|
|
@@ -33159,8 +33210,6 @@ Actions: list, deploy, deploy_bundle, source, delete, check`, {
|
|
|
33159
33210
|
try {
|
|
33160
33211
|
const fs = __require("fs");
|
|
33161
33212
|
const os = __require("os");
|
|
33162
|
-
const { promisify } = __require("util");
|
|
33163
|
-
const execAsync = promisify(__require("child_process").exec);
|
|
33164
33213
|
const stat = fs.statSync(pathArg);
|
|
33165
33214
|
let entrypoint = pathArg;
|
|
33166
33215
|
if (stat.isDirectory()) {
|
|
@@ -33171,10 +33220,17 @@ Actions: list, deploy, deploy_bundle, source, delete, check`, {
|
|
|
33171
33220
|
}
|
|
33172
33221
|
const tmpOut = `${os.tmpdir()}/supacloud_bundled_${Date.now()}.js`;
|
|
33173
33222
|
try {
|
|
33174
|
-
const
|
|
33175
|
-
|
|
33176
|
-
|
|
33177
|
-
|
|
33223
|
+
const proc = Bun.spawn(["bun", "build", entrypoint, "--target", "bun", "--outfile", tmpOut], {
|
|
33224
|
+
stdout: "pipe",
|
|
33225
|
+
stderr: "pipe"
|
|
33226
|
+
});
|
|
33227
|
+
const exitCode = await proc.exited;
|
|
33228
|
+
const stderr = await new Response(proc.stderr).text();
|
|
33229
|
+
const fileHandle = Bun.file(tmpOut);
|
|
33230
|
+
if (exitCode !== 0 || !await fileHandle.exists()) {
|
|
33231
|
+
throw new Error(`Bundle failed: ${stderr || `exit code ${exitCode}`}`);
|
|
33232
|
+
}
|
|
33233
|
+
code = await fileHandle.text();
|
|
33178
33234
|
} finally {
|
|
33179
33235
|
try {
|
|
33180
33236
|
fs.unlinkSync(tmpOut);
|
|
@@ -33687,13 +33743,13 @@ async function writeProjectEnvFile(prepared, credentials, operations = nodeProje
|
|
|
33687
33743
|
throw new ProjectCreateEnvError("ENV_FILE_WRITE_FAILED");
|
|
33688
33744
|
}
|
|
33689
33745
|
}
|
|
33690
|
-
function
|
|
33746
|
+
function isRecord2(candidate) {
|
|
33691
33747
|
return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
|
|
33692
33748
|
}
|
|
33693
33749
|
function decodedJwtPart(encodedPart) {
|
|
33694
33750
|
try {
|
|
33695
33751
|
const decodedPart = JSON.parse(Buffer.from(encodedPart, "base64url").toString("utf8"));
|
|
33696
|
-
return
|
|
33752
|
+
return isRecord2(decodedPart) ? decodedPart : null;
|
|
33697
33753
|
} catch {
|
|
33698
33754
|
return null;
|
|
33699
33755
|
}
|
|
@@ -33734,28 +33790,28 @@ function projectApiUrl(candidate, projectRef, expectedApiOrigin) {
|
|
|
33734
33790
|
return parsedUrl.origin;
|
|
33735
33791
|
}
|
|
33736
33792
|
function parseProjectCreateIdentity(responsePayload, expectedApiOrigin) {
|
|
33737
|
-
if (!
|
|
33793
|
+
if (!isRecord2(responsePayload) || typeof responsePayload.ref !== "string")
|
|
33738
33794
|
return null;
|
|
33739
|
-
if (!PROJECT_REF.test(responsePayload.ref) || !
|
|
33795
|
+
if (!PROJECT_REF.test(responsePayload.ref) || !isRecord2(responsePayload.api))
|
|
33740
33796
|
return null;
|
|
33741
33797
|
const apiUrl = projectApiUrl(responsePayload.api.url, responsePayload.ref, expectedApiOrigin);
|
|
33742
33798
|
return apiUrl ? { projectRef: responsePayload.ref, apiUrl } : null;
|
|
33743
33799
|
}
|
|
33744
33800
|
function parseProjectCreateWithoutCredentials(responsePayload, expectedApiOrigin) {
|
|
33745
33801
|
const identity = parseProjectCreateIdentity(responsePayload, expectedApiOrigin);
|
|
33746
|
-
if (!identity || !
|
|
33802
|
+
if (!identity || !isRecord2(responsePayload))
|
|
33747
33803
|
return null;
|
|
33748
33804
|
return Object.hasOwn(responsePayload, "credentials") ? null : identity;
|
|
33749
33805
|
}
|
|
33750
33806
|
function parseProjectCreateCredentials(responsePayload, expectedApiOrigin, expectedProjectName) {
|
|
33751
|
-
if (!expectedApiOrigin || !expectedProjectName || !
|
|
33807
|
+
if (!expectedApiOrigin || !expectedProjectName || !isRecord2(responsePayload))
|
|
33752
33808
|
return null;
|
|
33753
33809
|
if (responsePayload.name !== expectedProjectName)
|
|
33754
33810
|
return null;
|
|
33755
33811
|
const identity = parseProjectCreateIdentity(responsePayload, expectedApiOrigin);
|
|
33756
33812
|
if (!identity)
|
|
33757
33813
|
return null;
|
|
33758
|
-
if (!
|
|
33814
|
+
if (!isRecord2(responsePayload.credentials))
|
|
33759
33815
|
return null;
|
|
33760
33816
|
const serviceRoleKey = responsePayload.credentials.service_role_key;
|
|
33761
33817
|
return isServiceRoleKey(serviceRoleKey) ? { ...identity, serviceRoleKey } : null;
|
|
@@ -34149,7 +34205,7 @@ var POSTGREST_ATTESTATION_STATES = [
|
|
|
34149
34205
|
var POSTGREST_DESIRED_STATES = ["running", "stopped"];
|
|
34150
34206
|
var POSTGREST_ACTUAL_STATES = ["running", "stopped", "starting", "error"];
|
|
34151
34207
|
var POSTGREST_HEALTH_STATES = ["healthy", "unhealthy", "unknown"];
|
|
34152
|
-
function
|
|
34208
|
+
function isRecord3(candidate) {
|
|
34153
34209
|
return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
|
|
34154
34210
|
}
|
|
34155
34211
|
function hasExactKeys(candidate, expectedKeys) {
|
|
@@ -34198,7 +34254,7 @@ function hasValidSecretsState(candidate) {
|
|
|
34198
34254
|
return isLoadedSecretsState(candidate) || isUnverifiedSecretsState(candidate);
|
|
34199
34255
|
}
|
|
34200
34256
|
function isRuntimeSecretsSnapshot(payload) {
|
|
34201
|
-
if (!
|
|
34257
|
+
if (!isRecord3(payload) || !hasExactKeys(payload, SECRETS_KEYS))
|
|
34202
34258
|
return false;
|
|
34203
34259
|
if (!isRevisionOrNull(payload.loaded_revision) || typeof payload.desired_revision !== "string" || !ATTESTED_REVISION_PATTERN.test(payload.desired_revision) || !isEnumMember(payload.load_state, SECRET_LOAD_STATES) || !(payload.load_source === null || isEnumMember(payload.load_source, SECRET_LOAD_SOURCES)) || !isBooleanOrNull(payload.matches_desired) || !isIsoTimestampOrNull(payload.loaded_at))
|
|
34204
34260
|
return false;
|
|
@@ -34231,7 +34287,7 @@ function hasValidPostgrestState(candidate) {
|
|
|
34231
34287
|
return candidate.attestation_state === "drifted";
|
|
34232
34288
|
}
|
|
34233
34289
|
function isPostgrestRuntimeSnapshot(payload, projectRef) {
|
|
34234
|
-
if (!
|
|
34290
|
+
if (!isRecord3(payload) || !hasExactKeys(payload, POSTGREST_KEYS))
|
|
34235
34291
|
return false;
|
|
34236
34292
|
if (typeof payload.desired_revision !== "string" || !ATTESTED_REVISION_PATTERN.test(payload.desired_revision) || !isRevisionOrNull(payload.loaded_revision) || !isEnumMember(payload.attestation_state, POSTGREST_ATTESTATION_STATES) || !isBooleanOrNull(payload.matches_desired) || !isEnumMember(payload.desired, POSTGREST_DESIRED_STATES) || !isEnumMember(payload.actual, POSTGREST_ACTUAL_STATES) || !isEnumMember(payload.health, POSTGREST_HEALTH_STATES) || !Number.isSafeInteger(payload.port) || Number(payload.port) < 1 || Number(payload.port) > 65535 || payload.unit !== `supacloud-pgrst@${projectRef}` || !isIsoTimestampOrNull(payload.loaded_at))
|
|
34237
34293
|
return false;
|
|
@@ -34251,7 +34307,7 @@ function hasCausalLoadTimestamps(snapshot) {
|
|
|
34251
34307
|
return [snapshot.secrets.loaded_at, snapshot.postgrest.loaded_at].every((loadedAt) => loadedAt === null || Date.parse(loadedAt) <= capturedAt);
|
|
34252
34308
|
}
|
|
34253
34309
|
function parseProjectRuntimeSnapshot(payload, requestedProjectRef) {
|
|
34254
|
-
if (!SAFE_PROJECT_REF3.test(requestedProjectRef) || !
|
|
34310
|
+
if (!SAFE_PROJECT_REF3.test(requestedProjectRef) || !isRecord3(payload) || !hasExactKeys(payload, SNAPSHOT_KEYS) || payload.schema !== RUNTIME_SNAPSHOT_SCHEMA || payload.project_ref !== requestedProjectRef || !isIsoTimestampOrNull(payload.captured_at) || payload.captured_at === null || !isRuntimeSecretsSnapshot(payload.secrets) || !isPostgrestRuntimeSnapshot(payload.postgrest, requestedProjectRef))
|
|
34255
34311
|
return null;
|
|
34256
34312
|
const snapshot = payload;
|
|
34257
34313
|
return hasCausalLoadTimestamps(snapshot) ? sanitizedSnapshot(snapshot) : null;
|
|
@@ -34790,6 +34846,21 @@ function registerAdminProjectCliTools(server, http, options = {}) {
|
|
|
34790
34846
|
});
|
|
34791
34847
|
}
|
|
34792
34848
|
|
|
34849
|
+
// ../cli/src/shared/tool-server.ts
|
|
34850
|
+
var resultSchema = _Object_({
|
|
34851
|
+
content: _Array_(_Object_({ type: Literal("text"), text: String2() })),
|
|
34852
|
+
isError: Optional(Boolean2())
|
|
34853
|
+
});
|
|
34854
|
+
function registerTool(server, name, description, schema, callback) {
|
|
34855
|
+
server.tool(name, description, schema, async (input) => {
|
|
34856
|
+
const args = parseToolArguments(schema, input);
|
|
34857
|
+
const result = await callback(args);
|
|
34858
|
+
if (!Check2(resultSchema, result))
|
|
34859
|
+
throw new Error(`Invalid result from tool '${name}'`);
|
|
34860
|
+
return result;
|
|
34861
|
+
});
|
|
34862
|
+
}
|
|
34863
|
+
|
|
34793
34864
|
// ../cli/src/shared/tools/gateway-tools.ts
|
|
34794
34865
|
function parseCliStringArray(value) {
|
|
34795
34866
|
if (Array.isArray(value))
|
|
@@ -34844,7 +34915,7 @@ var ok2 = (res) => res.ok ? JSON.stringify(res.data, null, 2) : `❌ Failed (${r
|
|
|
34844
34915
|
var simple2 = (res, msg) => res.ok ? `✅ ${msg}` : `❌ Failed (${res.status}): ${JSON.stringify(res.data)}`;
|
|
34845
34916
|
function registerGatewayTools(server, http, options = {}) {
|
|
34846
34917
|
const { projectRef } = options;
|
|
34847
|
-
server
|
|
34918
|
+
registerTool(server, "gateway", `Gateway / Caddy 配置(通过 JSON Admin API 注入)。要求 admin 权限。
|
|
34848
34919
|
Actions: routes, upsert_route, update_route, delete_route, config, get_certificate, update_certificate, issue_certificate, deploy_certificate, rebuild, custom_hostname, set_custom_hostname, delete_custom_hostname, verify_custom_hostname`, {
|
|
34849
34920
|
action: withDescription(stringEnum([
|
|
34850
34921
|
"routes",
|
|
@@ -35245,12 +35316,15 @@ function releaseInventory(candidate, expected) {
|
|
|
35245
35316
|
next_cursor: nextCursor
|
|
35246
35317
|
};
|
|
35247
35318
|
}
|
|
35248
|
-
function
|
|
35319
|
+
function deploymentEndpoint(projectRef, deploymentId) {
|
|
35249
35320
|
if (!PROJECT_REF_PATTERN3.test(projectRef))
|
|
35250
35321
|
throw new Error("'ref' is invalid for frontend releases");
|
|
35251
35322
|
if (!DEPLOYMENT_ID_PATTERN.test(deploymentId))
|
|
35252
35323
|
throw new Error("'id' is invalid for frontend releases");
|
|
35253
|
-
return `/v1/projects/${encodeURIComponent(projectRef)}/frontend/deployments/${encodeURIComponent(deploymentId)}
|
|
35324
|
+
return `/v1/projects/${encodeURIComponent(projectRef)}/frontend/deployments/${encodeURIComponent(deploymentId)}`;
|
|
35325
|
+
}
|
|
35326
|
+
function releaseEndpoint(projectRef, deploymentId) {
|
|
35327
|
+
return `${deploymentEndpoint(projectRef, deploymentId)}/releases`;
|
|
35254
35328
|
}
|
|
35255
35329
|
function releasePath(projectRef, deploymentId, releaseId) {
|
|
35256
35330
|
if (!RELEASE_ID_PATTERN.test(releaseId))
|
|
@@ -35455,16 +35529,15 @@ function activationReceipt(candidate, input) {
|
|
|
35455
35529
|
return release;
|
|
35456
35530
|
}
|
|
35457
35531
|
async function activeReleaseReadback(http, identity) {
|
|
35458
|
-
const
|
|
35459
|
-
|
|
35460
|
-
|
|
35461
|
-
|
|
35462
|
-
|
|
35463
|
-
|
|
35464
|
-
|
|
35465
|
-
|
|
35466
|
-
|
|
35467
|
-
return { release: null, status: inventoryRead.status };
|
|
35532
|
+
const snapshot = await readActiveFrontendRelease(http, identity.projectRef, identity.deploymentId);
|
|
35533
|
+
if ("failure" in snapshot)
|
|
35534
|
+
return { release: null, status: snapshot.status };
|
|
35535
|
+
const inventory = snapshot.inventory;
|
|
35536
|
+
if (inventory.active_release_id !== identity.releaseId || inventory.active_activation_id !== identity.mutationId) {
|
|
35537
|
+
return { release: null, status: 200 };
|
|
35538
|
+
}
|
|
35539
|
+
if (snapshot.exactReadback) {
|
|
35540
|
+
return { release: inventory.releases[0] ?? null, status: 200 };
|
|
35468
35541
|
}
|
|
35469
35542
|
const releaseRead = await http.get(releasePath(identity.projectRef, identity.deploymentId, identity.releaseId), { maxJsonBytes: RESPONSE_MAX_BYTES });
|
|
35470
35543
|
const release = releaseRead.ok ? releaseEnvelope(releaseRead.data, {
|
|
@@ -35474,6 +35547,100 @@ async function activeReleaseReadback(http, identity) {
|
|
|
35474
35547
|
}) : null;
|
|
35475
35548
|
return { release, status: releaseRead.status };
|
|
35476
35549
|
}
|
|
35550
|
+
async function readActiveFrontendRelease(http, projectRef, deploymentId) {
|
|
35551
|
+
const response = await http.get(`${deploymentEndpoint(projectRef, deploymentId)}/active-release`, {
|
|
35552
|
+
maxJsonBytes: RESPONSE_MAX_BYTES
|
|
35553
|
+
});
|
|
35554
|
+
const inventory = response.ok && response.status === 200 ? releaseInventory(response.data, { projectRef, deploymentId }) : null;
|
|
35555
|
+
if (inventory) {
|
|
35556
|
+
if (inventory.next_cursor !== null || inventory.releases.length !== (inventory.active_release_id === null ? 0 : 1) || inventory.releases[0] && inventory.releases[0].release_id !== inventory.active_release_id) {
|
|
35557
|
+
return { failure: releaseReadFailure("frontend.get_active_release", response), status: response.status };
|
|
35558
|
+
}
|
|
35559
|
+
return { inventory, exactReadback: false };
|
|
35560
|
+
}
|
|
35561
|
+
if (response.ok || response.status !== 404) {
|
|
35562
|
+
return { failure: releaseReadFailure("frontend.get_active_release", response), status: response.status };
|
|
35563
|
+
}
|
|
35564
|
+
const historyResponse = await http.get(`${releaseEndpoint(projectRef, deploymentId)}?limit=1`, {
|
|
35565
|
+
maxJsonBytes: RESPONSE_MAX_BYTES
|
|
35566
|
+
});
|
|
35567
|
+
const history = historyResponse.ok && historyResponse.status === 200 ? releaseInventory(historyResponse.data, { projectRef, deploymentId }) : null;
|
|
35568
|
+
if (!history || history.releases.length > 1) {
|
|
35569
|
+
return {
|
|
35570
|
+
failure: releaseReadFailure("frontend.get_active_release", historyResponse),
|
|
35571
|
+
status: historyResponse.status
|
|
35572
|
+
};
|
|
35573
|
+
}
|
|
35574
|
+
if (history.active_release_id === null) {
|
|
35575
|
+
return { inventory: { ...history, releases: [], next_cursor: null }, exactReadback: false };
|
|
35576
|
+
}
|
|
35577
|
+
const exactResponse = await http.get(releasePath(projectRef, deploymentId, history.active_release_id), {
|
|
35578
|
+
maxJsonBytes: RESPONSE_MAX_BYTES
|
|
35579
|
+
});
|
|
35580
|
+
const release = exactResponse.ok && exactResponse.status === 200 ? releaseEnvelope(exactResponse.data, { projectRef, deploymentId, releaseId: history.active_release_id }) : null;
|
|
35581
|
+
if (!release) {
|
|
35582
|
+
return {
|
|
35583
|
+
failure: releaseReadFailure("frontend.get_active_release", exactResponse),
|
|
35584
|
+
status: exactResponse.status
|
|
35585
|
+
};
|
|
35586
|
+
}
|
|
35587
|
+
return { inventory: { ...history, releases: [release], next_cursor: null }, exactReadback: true };
|
|
35588
|
+
}
|
|
35589
|
+
async function getActiveFrontendRelease(http, projectRef, deploymentId) {
|
|
35590
|
+
const snapshot = await readActiveFrontendRelease(http, projectRef, deploymentId);
|
|
35591
|
+
return "failure" in snapshot ? snapshot.failure : toolResponse(snapshot.inventory);
|
|
35592
|
+
}
|
|
35593
|
+
async function rollbackFrontendRelease(http, projectRef, deploymentId, releaseId) {
|
|
35594
|
+
if (!RELEASE_ID_PATTERN.test(releaseId))
|
|
35595
|
+
throw new Error("'release_id' must be a SHA-256 digest");
|
|
35596
|
+
const authority = await readActiveFrontendRelease(http, projectRef, deploymentId);
|
|
35597
|
+
if ("failure" in authority)
|
|
35598
|
+
return authority.failure;
|
|
35599
|
+
const inventory = authority.inventory;
|
|
35600
|
+
if (inventory.active_release_id === releaseId) {
|
|
35601
|
+
return toolResponse({
|
|
35602
|
+
project_ref: projectRef,
|
|
35603
|
+
deployment_id: deploymentId,
|
|
35604
|
+
active_release_id: releaseId,
|
|
35605
|
+
activation_id: inventory.active_activation_id,
|
|
35606
|
+
unchanged: true
|
|
35607
|
+
});
|
|
35608
|
+
}
|
|
35609
|
+
if (inventory.active_release_id === null || inventory.active_activation_id === null) {
|
|
35610
|
+
return releaseFailure("frontend.rollback", "NO_ACTIVE_RELEASE", 409);
|
|
35611
|
+
}
|
|
35612
|
+
const target = await getFrontendRelease(http, projectRef, deploymentId, releaseId);
|
|
35613
|
+
if (target.isError)
|
|
35614
|
+
return target;
|
|
35615
|
+
const mutationId = crypto.randomUUID();
|
|
35616
|
+
const activation = await activateFrontendRelease(http, {
|
|
35617
|
+
projectRef,
|
|
35618
|
+
deploymentId,
|
|
35619
|
+
releaseId,
|
|
35620
|
+
expectedActiveReleaseId: inventory.active_release_id,
|
|
35621
|
+
expectedActivationId: inventory.active_activation_id,
|
|
35622
|
+
mutationId
|
|
35623
|
+
});
|
|
35624
|
+
if (activation.isError) {
|
|
35625
|
+
return {
|
|
35626
|
+
...toolResponse({
|
|
35627
|
+
...JSON.parse(activation.content[0].text),
|
|
35628
|
+
operation: "frontend.rollback",
|
|
35629
|
+
mutation_id: mutationId,
|
|
35630
|
+
target_release_id: releaseId,
|
|
35631
|
+
expected_active_release_id: inventory.active_release_id,
|
|
35632
|
+
expected_activation_id: inventory.active_activation_id
|
|
35633
|
+
}),
|
|
35634
|
+
isError: true
|
|
35635
|
+
};
|
|
35636
|
+
}
|
|
35637
|
+
return toolResponse({
|
|
35638
|
+
...JSON.parse(activation.content[0].text),
|
|
35639
|
+
rollback: true,
|
|
35640
|
+
previous_release_id: inventory.active_release_id,
|
|
35641
|
+
previous_activation_id: inventory.active_activation_id
|
|
35642
|
+
});
|
|
35643
|
+
}
|
|
35477
35644
|
async function activateFrontendRelease(http, input) {
|
|
35478
35645
|
const mutationId = input.mutationId;
|
|
35479
35646
|
if (!MUTATION_ID_PATTERN.test(mutationId))
|
|
@@ -35539,17 +35706,19 @@ async function activationReadback(http, input, mutationId, activationStatus) {
|
|
|
35539
35706
|
// src/shared/tools/frontend-tools.ts
|
|
35540
35707
|
function registerFrontendTools(server, http) {
|
|
35541
35708
|
server.tool("frontend", `Immutable prebuilt frontend release control.
|
|
35542
|
-
Actions: list_releases, get_release, upload_release, activate_release`, {
|
|
35709
|
+
Actions: list_releases, get_active_release, get_release, upload_release, activate_release, rollback`, {
|
|
35543
35710
|
action: withDescription(stringEnum([
|
|
35544
35711
|
"list_releases",
|
|
35712
|
+
"get_active_release",
|
|
35545
35713
|
"get_release",
|
|
35546
35714
|
"upload_release",
|
|
35547
|
-
"activate_release"
|
|
35715
|
+
"activate_release",
|
|
35716
|
+
"rollback"
|
|
35548
35717
|
]), "Action"),
|
|
35549
35718
|
ref: optional(String4(), "Project ref"),
|
|
35550
35719
|
id: optional(String4(), "Deployment ID"),
|
|
35551
35720
|
zip_path: optional(String4(), "[upload_release] Local ZIP file path"),
|
|
35552
|
-
release_id: optional(String4(), "[get_release/activate_release] SHA-256 release ID"),
|
|
35721
|
+
release_id: optional(String4(), "[get_release/activate_release/rollback] SHA-256 release ID"),
|
|
35553
35722
|
expected_active_release_id: optional(String4(), "[activate_release] Current release SHA-256 or absent"),
|
|
35554
35723
|
expected_activation_id: optional(String4(), "[activate_release] Current activation UUIDv4 or absent"),
|
|
35555
35724
|
mutation_id: optional(String4(), "[activate_release] Required retry-stable UUIDv4"),
|
|
@@ -35582,6 +35751,10 @@ Actions: list_releases, get_release, upload_release, activate_release`, {
|
|
|
35582
35751
|
need("id", id);
|
|
35583
35752
|
need("release_id", release_id);
|
|
35584
35753
|
return getFrontendRelease(http, ref, id, release_id);
|
|
35754
|
+
case "get_active_release":
|
|
35755
|
+
need("ref", ref);
|
|
35756
|
+
need("id", id);
|
|
35757
|
+
return getActiveFrontendRelease(http, ref, id);
|
|
35585
35758
|
case "upload_release":
|
|
35586
35759
|
need("ref", ref);
|
|
35587
35760
|
need("id", id);
|
|
@@ -35602,6 +35775,11 @@ Actions: list_releases, get_release, upload_release, activate_release`, {
|
|
|
35602
35775
|
expectedActivationId: expected_activation_id,
|
|
35603
35776
|
mutationId: mutation_id
|
|
35604
35777
|
});
|
|
35778
|
+
case "rollback":
|
|
35779
|
+
need("ref", ref);
|
|
35780
|
+
need("id", id);
|
|
35781
|
+
need("release_id", release_id);
|
|
35782
|
+
return rollbackFrontendRelease(http, ref, id, release_id);
|
|
35605
35783
|
default:
|
|
35606
35784
|
throw new Error("Unknown immutable frontend release action");
|
|
35607
35785
|
}
|
|
@@ -35610,7 +35788,7 @@ Actions: list_releases, get_release, upload_release, activate_release`, {
|
|
|
35610
35788
|
// package.json
|
|
35611
35789
|
var package_default = {
|
|
35612
35790
|
name: "@supacloud/admin",
|
|
35613
|
-
version: "0.
|
|
35791
|
+
version: "0.25.0",
|
|
35614
35792
|
description: "Platform administration CLI for SupaCloud operators",
|
|
35615
35793
|
type: "module",
|
|
35616
35794
|
main: "./dist/index.js",
|
|
@@ -35643,13 +35821,16 @@ var package_default = {
|
|
|
35643
35821
|
},
|
|
35644
35822
|
dependencies: {
|
|
35645
35823
|
typebox: "1.3.34",
|
|
35646
|
-
"@supacloud/cli": "0.
|
|
35824
|
+
"@supacloud/cli": "0.76.0",
|
|
35647
35825
|
ssh2: "^1.17.0"
|
|
35648
35826
|
},
|
|
35649
35827
|
devDependencies: {
|
|
35650
35828
|
"@types/bun": "^1.4.2",
|
|
35651
35829
|
"@types/ssh2": "^1.15.6",
|
|
35652
35830
|
typescript: "^7.0.2"
|
|
35831
|
+
},
|
|
35832
|
+
overrides: {
|
|
35833
|
+
"@graphql-tools/utils": "^12.0.3"
|
|
35653
35834
|
}
|
|
35654
35835
|
};
|
|
35655
35836
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/admin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Platform administration CLI for SupaCloud operators",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,12 +33,15 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"typebox": "1.3.34",
|
|
36
|
-
"@supacloud/cli": "0.
|
|
36
|
+
"@supacloud/cli": "0.76.0",
|
|
37
37
|
"ssh2": "^1.17.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/bun": "^1.4.2",
|
|
41
41
|
"@types/ssh2": "^1.15.6",
|
|
42
42
|
"typescript": "^7.0.2"
|
|
43
|
+
},
|
|
44
|
+
"overrides": {
|
|
45
|
+
"@graphql-tools/utils": "^12.0.3"
|
|
43
46
|
}
|
|
44
47
|
}
|