@supacloud/cli 0.15.0 → 0.16.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 +16 -0
- package/dist/index.js +525 -105
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,6 +146,7 @@ supacloud-cli queue dlq --queue emails --limit 20
|
|
|
146
146
|
supacloud-cli task_events inspect_webhook --ref abc123
|
|
147
147
|
supacloud-cli database query --sql "select now()"
|
|
148
148
|
supacloud-cli database query --ref abc123 --file ./queries/vector-search.sql
|
|
149
|
+
supacloud-cli database migration_inventory --ref abc123
|
|
149
150
|
supacloud-cli database push_migrations --ref abc123 --dir supabase/migrations --dry_run
|
|
150
151
|
supacloud-cli supabase migration_new --name add_accounts
|
|
151
152
|
supacloud-cli supabase db_diff --schema public --name add_accounts
|
|
@@ -160,8 +161,23 @@ supacloud-cli edge_functions source --ref abc123 --slug hello --output ./hello.t
|
|
|
160
161
|
supacloud-cli edge_functions activate --ref abc123 --slug hello --version 3
|
|
161
162
|
supacloud-cli scheduled_functions list --ref abc123
|
|
162
163
|
supacloud-cli secrets upsert --ref abc123 --from-env API_KEY,WEBHOOK_SECRET
|
|
164
|
+
supacloud-cli storage list_buckets --ref abc123
|
|
165
|
+
supacloud-cli storage get_bucket --ref abc123 --bucket reports
|
|
166
|
+
supacloud-cli storage create_bucket --ref abc123 --bucket reports --public false \
|
|
167
|
+
--file_size_limit 10485760 --allowed_mime_types "application/pdf,image/png"
|
|
168
|
+
supacloud-cli storage update_bucket --ref abc123 --bucket reports \
|
|
169
|
+
--allowed_mime_types '["application/pdf"]'
|
|
170
|
+
supacloud-cli storage delete_bucket --ref abc123 --bucket reports
|
|
163
171
|
```
|
|
164
172
|
|
|
173
|
+
`database migration_inventory` reads the canonical migration ledger through the
|
|
174
|
+
project-scoped Management API and prints only a validated JSON array. It rejects
|
|
175
|
+
non-2xx responses, malformed entries, unsafe project refs, duplicate canonical
|
|
176
|
+
migration versions, checksum
|
|
177
|
+
drift, and statement-count mismatches instead of treating them as an empty
|
|
178
|
+
ledger. `database list_migrations` remains available with its legacy SQL-backed,
|
|
179
|
+
human-readable behavior.
|
|
180
|
+
|
|
165
181
|
`edge_functions deploy --path` bundles local TypeScript and dependencies with
|
|
166
182
|
Bun and runs a local syntax check before upload. The Management API validates and
|
|
167
183
|
normalizes the final server-side artifact against the multi-tenant Edge Runtime
|
package/dist/index.js
CHANGED
|
@@ -6471,7 +6471,7 @@ var ACTION_POLICY = {
|
|
|
6471
6471
|
write: ["task_cancel", "task_retry", "update_background_settings"]
|
|
6472
6472
|
},
|
|
6473
6473
|
database: {
|
|
6474
|
-
read: ["list_tables", "describe_columns", "list_indexes", "list_constraints", "list_extensions", "rls_status", "rls_policies", "list_auth_users", "get_auth_user", "connections", "stats", "slow_queries", "list_migrations", "project_url", "generate_types"],
|
|
6474
|
+
read: ["list_tables", "describe_columns", "list_indexes", "list_constraints", "list_extensions", "rls_status", "rls_policies", "list_auth_users", "get_auth_user", "connections", "stats", "slow_queries", "list_migrations", "migration_inventory", "project_url", "generate_types"],
|
|
6475
6475
|
write: ["query", "apply_migration", "push_migrations", "baseline_migrations", "create_table_rls"]
|
|
6476
6476
|
},
|
|
6477
6477
|
supabase: {
|
|
@@ -6483,8 +6483,8 @@ var ACTION_POLICY = {
|
|
|
6483
6483
|
write: ["configure_provider", "update_provider", "disable_provider", "wechat_mini", "wechat_open", "update_settings", "update_config"]
|
|
6484
6484
|
},
|
|
6485
6485
|
storage: {
|
|
6486
|
-
read: ["status", "list_buckets", "list_files"],
|
|
6487
|
-
write: ["upload_base64", "delete_file"]
|
|
6486
|
+
read: ["status", "list_buckets", "get_bucket", "list_files"],
|
|
6487
|
+
write: ["create_bucket", "update_bucket", "delete_bucket", "upload_base64", "delete_file"]
|
|
6488
6488
|
},
|
|
6489
6489
|
edge_functions: {
|
|
6490
6490
|
read: ["list", "source"],
|
|
@@ -6784,10 +6784,120 @@ class HttpTransport {
|
|
|
6784
6784
|
import { createHash } from "node:crypto";
|
|
6785
6785
|
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
6786
6786
|
import { basename, join } from "node:path";
|
|
6787
|
+
|
|
6788
|
+
// src/shared/project-ref.ts
|
|
6789
|
+
var PROJECT_REF_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
6790
|
+
function projectRefPathSegment(ref, operation) {
|
|
6791
|
+
if (typeof ref !== "string" || !PROJECT_REF_PATTERN.test(ref)) {
|
|
6792
|
+
throw new Error(`'ref' is invalid for ${operation}`);
|
|
6793
|
+
}
|
|
6794
|
+
return encodeURIComponent(ref);
|
|
6795
|
+
}
|
|
6796
|
+
|
|
6797
|
+
// src/shared/tools/database-tools.ts
|
|
6787
6798
|
var MAX_MIGRATION_VERSION = 9223372036854775807n;
|
|
6788
6799
|
var FALLBACK_MIGRATION_VERSION_BASE = 8000000000000000000n;
|
|
6789
6800
|
var FALLBACK_MIGRATION_VERSION_RANGE = 1000000000000000000n;
|
|
6790
6801
|
var FALLBACK_MIGRATION_VERSION_LIMIT = FALLBACK_MIGRATION_VERSION_BASE + FALLBACK_MIGRATION_VERSION_RANGE;
|
|
6802
|
+
var MAX_MIGRATION_INVENTORY_BYTES = 64 * 1024 * 1024;
|
|
6803
|
+
function isMigrationInventoryVersion(version) {
|
|
6804
|
+
if (typeof version !== "string" || !/^\d{1,19}$/.test(version))
|
|
6805
|
+
return false;
|
|
6806
|
+
const numericVersion = BigInt(version);
|
|
6807
|
+
return numericVersion >= 1n && numericVersion <= MAX_MIGRATION_VERSION && numericVersion.toString() === version;
|
|
6808
|
+
}
|
|
6809
|
+
function isMigrationInventoryName(name) {
|
|
6810
|
+
return name === null || typeof name === "string" && name.length <= 255 && name.length > 0 && name.trim() === name;
|
|
6811
|
+
}
|
|
6812
|
+
function isMigrationInventoryStatements(statements) {
|
|
6813
|
+
return Array.isArray(statements) && statements.every((statement) => typeof statement === "string" && statement.length > 0 && statement.trim() === statement && !statement.includes("\r"));
|
|
6814
|
+
}
|
|
6815
|
+
function isMigrationInventoryAppliedAt(appliedAt) {
|
|
6816
|
+
return appliedAt === null || typeof appliedAt === "string" && appliedAt.trim() === appliedAt && appliedAt.length > 0 && !Number.isNaN(Date.parse(appliedAt));
|
|
6817
|
+
}
|
|
6818
|
+
function migrationInventoryChecksum(entry) {
|
|
6819
|
+
return createHash("sha256").update(JSON.stringify({
|
|
6820
|
+
version: entry.version,
|
|
6821
|
+
name: entry.name,
|
|
6822
|
+
statements: entry.statements
|
|
6823
|
+
})).digest("hex");
|
|
6824
|
+
}
|
|
6825
|
+
function migrationInventoryEntry(rawEntry) {
|
|
6826
|
+
if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry))
|
|
6827
|
+
return null;
|
|
6828
|
+
const entry = rawEntry;
|
|
6829
|
+
if (!isMigrationInventoryVersion(entry.version))
|
|
6830
|
+
return null;
|
|
6831
|
+
if (!isMigrationInventoryName(entry.name))
|
|
6832
|
+
return null;
|
|
6833
|
+
if (!isMigrationInventoryStatements(entry.statements))
|
|
6834
|
+
return null;
|
|
6835
|
+
if (typeof entry.statement_count !== "number" || !Number.isInteger(entry.statement_count))
|
|
6836
|
+
return null;
|
|
6837
|
+
if (entry.statement_count !== entry.statements.length)
|
|
6838
|
+
return null;
|
|
6839
|
+
if (typeof entry.checksum !== "string" || !/^[0-9a-f]{64}$/.test(entry.checksum))
|
|
6840
|
+
return null;
|
|
6841
|
+
if (!isMigrationInventoryAppliedAt(entry.applied_at))
|
|
6842
|
+
return null;
|
|
6843
|
+
const migration = {
|
|
6844
|
+
version: entry.version,
|
|
6845
|
+
name: entry.name,
|
|
6846
|
+
statements: entry.statements,
|
|
6847
|
+
statement_count: entry.statement_count,
|
|
6848
|
+
checksum: entry.checksum,
|
|
6849
|
+
applied_at: entry.applied_at
|
|
6850
|
+
};
|
|
6851
|
+
return migration.checksum === migrationInventoryChecksum(migration) ? migration : null;
|
|
6852
|
+
}
|
|
6853
|
+
function compareMigrationInventoryEntries(left, right) {
|
|
6854
|
+
const leftVersion = BigInt(left.version);
|
|
6855
|
+
const rightVersion = BigInt(right.version);
|
|
6856
|
+
if (leftVersion === rightVersion)
|
|
6857
|
+
return 0;
|
|
6858
|
+
return leftVersion < rightVersion ? -1 : 1;
|
|
6859
|
+
}
|
|
6860
|
+
function migrationInventory(payload) {
|
|
6861
|
+
if (!Array.isArray(payload))
|
|
6862
|
+
return null;
|
|
6863
|
+
const inventory = [];
|
|
6864
|
+
const versions = new Set;
|
|
6865
|
+
for (const rawEntry of payload) {
|
|
6866
|
+
const entry = migrationInventoryEntry(rawEntry);
|
|
6867
|
+
if (!entry)
|
|
6868
|
+
return null;
|
|
6869
|
+
if (versions.has(entry.version))
|
|
6870
|
+
return null;
|
|
6871
|
+
versions.add(entry.version);
|
|
6872
|
+
inventory.push(entry);
|
|
6873
|
+
}
|
|
6874
|
+
return inventory.sort(compareMigrationInventoryEntries);
|
|
6875
|
+
}
|
|
6876
|
+
function migrationInventoryPath(ref) {
|
|
6877
|
+
return `/v1/projects/${projectRefPathSegment(ref, "migration_inventory")}/database/migrations`;
|
|
6878
|
+
}
|
|
6879
|
+
function migrationInventoryFailure(code, httpStatus) {
|
|
6880
|
+
return {
|
|
6881
|
+
isError: true,
|
|
6882
|
+
content: [{
|
|
6883
|
+
type: "text",
|
|
6884
|
+
text: JSON.stringify({
|
|
6885
|
+
ok: false,
|
|
6886
|
+
operation: "database.migration_inventory",
|
|
6887
|
+
error: { code, http_status: httpStatus }
|
|
6888
|
+
}, null, 2)
|
|
6889
|
+
}]
|
|
6890
|
+
};
|
|
6891
|
+
}
|
|
6892
|
+
function migrationInventoryResponse(response) {
|
|
6893
|
+
if (!response.ok) {
|
|
6894
|
+
return migrationInventoryFailure("HTTP_ERROR", response.transportError ? null : response.status);
|
|
6895
|
+
}
|
|
6896
|
+
const inventory = migrationInventory(response.data);
|
|
6897
|
+
if (!inventory)
|
|
6898
|
+
return migrationInventoryFailure("INVALID_RESPONSE", response.status);
|
|
6899
|
+
return { content: [{ type: "text", text: JSON.stringify(inventory, null, 2) }] };
|
|
6900
|
+
}
|
|
6791
6901
|
function readMigrationFile(dir, file) {
|
|
6792
6902
|
const rawBytes = readFileSync2(join(dir, file));
|
|
6793
6903
|
return {
|
|
@@ -6939,6 +7049,7 @@ function registerDatabaseTools(server, http, config = {}) {
|
|
|
6939
7049
|
"stats",
|
|
6940
7050
|
"slow_queries",
|
|
6941
7051
|
"list_migrations",
|
|
7052
|
+
"migration_inventory",
|
|
6942
7053
|
"project_url",
|
|
6943
7054
|
"generate_types"
|
|
6944
7055
|
];
|
|
@@ -7078,6 +7189,10 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
|
|
|
7078
7189
|
text = r.ok ? formatMigrations(r.data) : `❌ Failed (${r.status})`;
|
|
7079
7190
|
break;
|
|
7080
7191
|
}
|
|
7192
|
+
case "migration_inventory": {
|
|
7193
|
+
const response = await http.get(migrationInventoryPath(ref), { maxResponseBytes: MAX_MIGRATION_INVENTORY_BYTES });
|
|
7194
|
+
return migrationInventoryResponse(response);
|
|
7195
|
+
}
|
|
7081
7196
|
case "project_url": {
|
|
7082
7197
|
const r = await http.get(`/v1/projects/${ref}`);
|
|
7083
7198
|
text = r.ok ? JSON.stringify({ url: r.data.api?.url || `https://${ref}.supabase.co` }, null, 2) : `❌ Failed (${r.status})`;
|
|
@@ -7729,90 +7844,424 @@ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status}): ${JSON.stringif
|
|
|
7729
7844
|
});
|
|
7730
7845
|
}
|
|
7731
7846
|
|
|
7847
|
+
// src/shared/tools/release-control-response.ts
|
|
7848
|
+
var RELEASE_CONTROL_RESPONSE_SCHEMA = "supacloud.cli.release-control.v1";
|
|
7849
|
+
function releaseControlSuccess(operation, payload) {
|
|
7850
|
+
return releaseControlResponse({
|
|
7851
|
+
schema: RELEASE_CONTROL_RESPONSE_SCHEMA,
|
|
7852
|
+
ok: true,
|
|
7853
|
+
operation,
|
|
7854
|
+
...payload
|
|
7855
|
+
});
|
|
7856
|
+
}
|
|
7857
|
+
function releaseControlFailure(operation, code, httpStatus) {
|
|
7858
|
+
return releaseControlErrorResponse({
|
|
7859
|
+
schema: RELEASE_CONTROL_RESPONSE_SCHEMA,
|
|
7860
|
+
ok: false,
|
|
7861
|
+
operation,
|
|
7862
|
+
error: { code, http_status: httpStatus }
|
|
7863
|
+
});
|
|
7864
|
+
}
|
|
7865
|
+
function releaseControlMutationFailure(operation, response) {
|
|
7866
|
+
const outcomeUnknown = response.transportError || response.status === 408 || response.status >= 500;
|
|
7867
|
+
return outcomeUnknown ? releaseControlFailure(operation, "OUTCOME_UNKNOWN", response.transportError ? null : response.status) : releaseControlFailure(operation, "HTTP_ERROR", response.status);
|
|
7868
|
+
}
|
|
7869
|
+
function releaseControlResponse(payload) {
|
|
7870
|
+
return {
|
|
7871
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
|
|
7872
|
+
};
|
|
7873
|
+
}
|
|
7874
|
+
function releaseControlErrorResponse(payload) {
|
|
7875
|
+
return { ...releaseControlResponse(payload), isError: true };
|
|
7876
|
+
}
|
|
7877
|
+
|
|
7732
7878
|
// src/shared/tools/storage-tools.ts
|
|
7879
|
+
var MAX_BUCKET_ID_LENGTH = 100;
|
|
7880
|
+
var MAX_MIME_TYPE_COUNT = 100;
|
|
7881
|
+
var MAX_MIME_TYPE_LENGTH = 255;
|
|
7882
|
+
var PROJECT_REF_PATTERN2 = /^[A-Za-z0-9_-]{1,64}$/;
|
|
7883
|
+
var BUCKET_ID_PATTERN = new RegExp(`^(?!\\.+$)[A-Za-z0-9._-]{1,${MAX_BUCKET_ID_LENGTH}}$`);
|
|
7884
|
+
var MIME_TYPE_PATTERN = /^(?=\S)(?=.*\S$)[^\u0000-\u001f\u007f]+$/;
|
|
7885
|
+
var ACTION_ARGUMENTS = {
|
|
7886
|
+
status: new Set(["action"]),
|
|
7887
|
+
list_buckets: new Set(["action", "ref"]),
|
|
7888
|
+
get_bucket: new Set(["action", "ref", "bucket"]),
|
|
7889
|
+
create_bucket: new Set(["action", "ref", "bucket", "public", "file_size_limit", "allowed_mime_types"]),
|
|
7890
|
+
update_bucket: new Set(["action", "ref", "bucket", "public", "file_size_limit", "allowed_mime_types"]),
|
|
7891
|
+
delete_bucket: new Set(["action", "ref", "bucket"]),
|
|
7892
|
+
list_files: new Set(["action", "ref", "bucket"]),
|
|
7893
|
+
upload_base64: new Set(["action", "ref", "bucket", "filename", "base64_content", "mime_type"]),
|
|
7894
|
+
delete_file: new Set(["action", "ref", "bucket", "filename"])
|
|
7895
|
+
};
|
|
7896
|
+
function normalizedMimeTypes(candidate) {
|
|
7897
|
+
return Array.isArray(candidate) ? candidate.map((mimeType) => typeof mimeType === "string" ? mimeType.trim() : mimeType) : candidate;
|
|
7898
|
+
}
|
|
7899
|
+
function parseAllowedMimeTypes(input) {
|
|
7900
|
+
if (Array.isArray(input))
|
|
7901
|
+
return normalizedMimeTypes(input);
|
|
7902
|
+
const trimmed = input.trim();
|
|
7903
|
+
if (!trimmed)
|
|
7904
|
+
return [];
|
|
7905
|
+
if (!trimmed.startsWith("[")) {
|
|
7906
|
+
return normalizedMimeTypes(trimmed.split(","));
|
|
7907
|
+
}
|
|
7908
|
+
try {
|
|
7909
|
+
return normalizedMimeTypes(JSON.parse(trimmed));
|
|
7910
|
+
} catch (error) {
|
|
7911
|
+
if (!(error instanceof SyntaxError))
|
|
7912
|
+
throw error;
|
|
7913
|
+
throw new Error("Invalid allowed_mime_types JSON array");
|
|
7914
|
+
}
|
|
7915
|
+
}
|
|
7916
|
+
var allowedMimeTypesSchema = Type.Optional(decodedSchema(Type.Union([Type.String(), Type.Array(Type.String())]), Type.Array(Type.String({
|
|
7917
|
+
minLength: 1,
|
|
7918
|
+
maxLength: MAX_MIME_TYPE_LENGTH,
|
|
7919
|
+
pattern: MIME_TYPE_PATTERN.source
|
|
7920
|
+
}), { maxItems: MAX_MIME_TYPE_COUNT }), parseAllowedMimeTypes));
|
|
7921
|
+
var fileSizeLimitSchema = Type.Optional(Type.Integer({
|
|
7922
|
+
minimum: 1,
|
|
7923
|
+
maximum: Number.MAX_SAFE_INTEGER
|
|
7924
|
+
}));
|
|
7925
|
+
function requiredText(args, field) {
|
|
7926
|
+
const text = args[field];
|
|
7927
|
+
if (typeof text !== "string" || !text.trim()) {
|
|
7928
|
+
throw new Error(`'${field}' required for '${String(args.action)}'`);
|
|
7929
|
+
}
|
|
7930
|
+
return text.trim();
|
|
7931
|
+
}
|
|
7932
|
+
function validBucketId(bucket) {
|
|
7933
|
+
return BUCKET_ID_PATTERN.test(bucket);
|
|
7934
|
+
}
|
|
7935
|
+
function requiredProjectRef(args) {
|
|
7936
|
+
const ref = requiredText(args, "ref");
|
|
7937
|
+
if (!PROJECT_REF_PATTERN2.test(ref))
|
|
7938
|
+
throw new Error("'ref' is invalid for Storage buckets");
|
|
7939
|
+
return ref;
|
|
7940
|
+
}
|
|
7941
|
+
function requiredBucketId(args) {
|
|
7942
|
+
const bucket = requiredText(args, "bucket");
|
|
7943
|
+
if (!validBucketId(bucket))
|
|
7944
|
+
throw new Error("'bucket' is invalid for Storage buckets");
|
|
7945
|
+
return bucket;
|
|
7946
|
+
}
|
|
7947
|
+
function assertActionArguments(action, args) {
|
|
7948
|
+
const allowedArguments = ACTION_ARGUMENTS[action];
|
|
7949
|
+
if (!allowedArguments)
|
|
7950
|
+
throw new Error(`Unsupported Storage action '${action}'`);
|
|
7951
|
+
const unsupported = Object.keys(args).filter((field) => !allowedArguments.has(field));
|
|
7952
|
+
if (unsupported.length > 0)
|
|
7953
|
+
throw new Error(`'${unsupported[0]}' is not supported for '${action}'`);
|
|
7954
|
+
}
|
|
7955
|
+
function storageBucketPath(ref, bucket) {
|
|
7956
|
+
if (!PROJECT_REF_PATTERN2.test(ref))
|
|
7957
|
+
throw new Error("'ref' is invalid for Storage buckets");
|
|
7958
|
+
if (bucket !== undefined && !validBucketId(bucket)) {
|
|
7959
|
+
throw new Error("'bucket' is invalid for Storage buckets");
|
|
7960
|
+
}
|
|
7961
|
+
const root = `/v1/projects/${encodeURIComponent(ref)}/storage/buckets`;
|
|
7962
|
+
return bucket === undefined ? root : `${root}/${encodeURIComponent(bucket)}`;
|
|
7963
|
+
}
|
|
7964
|
+
function bucketRecord(candidate) {
|
|
7965
|
+
return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
|
|
7966
|
+
}
|
|
7967
|
+
function isFileSizeLimit(candidate) {
|
|
7968
|
+
return candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate > 0;
|
|
7969
|
+
}
|
|
7970
|
+
function assertBucketSettings(args) {
|
|
7971
|
+
if (args.file_size_limit !== undefined && (args.file_size_limit === null || !isFileSizeLimit(args.file_size_limit))) {
|
|
7972
|
+
throw new Error("'file_size_limit' must be a positive safe integer");
|
|
7973
|
+
}
|
|
7974
|
+
if (args.allowed_mime_types !== undefined && (args.allowed_mime_types === null || !isAllowedMimeTypes(args.allowed_mime_types))) {
|
|
7975
|
+
throw new Error("'allowed_mime_types' is invalid");
|
|
7976
|
+
}
|
|
7977
|
+
}
|
|
7978
|
+
function isAllowedMimeTypes(candidate) {
|
|
7979
|
+
return candidate === null || Array.isArray(candidate) && candidate.length <= MAX_MIME_TYPE_COUNT && candidate.every((mimeType) => typeof mimeType === "string" && mimeType.length <= MAX_MIME_TYPE_LENGTH && MIME_TYPE_PATTERN.test(mimeType));
|
|
7980
|
+
}
|
|
7981
|
+
function safeBucket(candidate, expectedBucket) {
|
|
7982
|
+
const bucket = bucketRecord(candidate);
|
|
7983
|
+
if (bucket === null || typeof bucket.id !== "string" || !validBucketId(bucket.id) || typeof bucket.name !== "string" || !validBucketId(bucket.name) || typeof bucket.public !== "boolean" || !isFileSizeLimit(bucket.file_size_limit) || !isAllowedMimeTypes(bucket.allowed_mime_types) || expectedBucket !== undefined && bucket.id !== expectedBucket && bucket.name !== expectedBucket) {
|
|
7984
|
+
return null;
|
|
7985
|
+
}
|
|
7986
|
+
return {
|
|
7987
|
+
id: bucket.id,
|
|
7988
|
+
name: bucket.name,
|
|
7989
|
+
public: bucket.public,
|
|
7990
|
+
file_size_limit: bucket.file_size_limit,
|
|
7991
|
+
allowed_mime_types: bucket.allowed_mime_types === null ? null : [...bucket.allowed_mime_types]
|
|
7992
|
+
};
|
|
7993
|
+
}
|
|
7994
|
+
function safeExactBucket(candidate, expectedBucket) {
|
|
7995
|
+
const bucket = safeBucket(candidate);
|
|
7996
|
+
return bucket?.id === expectedBucket ? bucket : null;
|
|
7997
|
+
}
|
|
7998
|
+
function safeBucketList(candidate) {
|
|
7999
|
+
if (!Array.isArray(candidate))
|
|
8000
|
+
return null;
|
|
8001
|
+
const buckets = candidate.map((bucket) => safeBucket(bucket));
|
|
8002
|
+
if (buckets.some((bucket) => bucket === null))
|
|
8003
|
+
return null;
|
|
8004
|
+
const safeBuckets = buckets;
|
|
8005
|
+
const ids = safeBuckets.map((bucket) => bucket.id);
|
|
8006
|
+
const names = safeBuckets.map((bucket) => bucket.name);
|
|
8007
|
+
return new Set(ids).size === ids.length && new Set(names).size === names.length ? safeBuckets : null;
|
|
8008
|
+
}
|
|
8009
|
+
function safeCreatedBucketReceipt(candidate, expectedBucket, request) {
|
|
8010
|
+
const bucket = bucketRecord(candidate);
|
|
8011
|
+
if (bucket?.id !== expectedBucket || bucket.name !== expectedBucket || bucket.public !== (request.public === true))
|
|
8012
|
+
return null;
|
|
8013
|
+
return { bucket: { id: expectedBucket, name: expectedBucket, public: request.public === true } };
|
|
8014
|
+
}
|
|
8015
|
+
function safeDeletedBucket(candidate, expectedBucket) {
|
|
8016
|
+
const receipt = bucketRecord(candidate);
|
|
8017
|
+
return receipt?.id === expectedBucket && receipt.deleted === true ? { bucket_id: expectedBucket, deleted: true } : null;
|
|
8018
|
+
}
|
|
8019
|
+
function mutationReadbackResponse(expectation) {
|
|
8020
|
+
const { operation, ref, response, expectedBucket, request } = expectation;
|
|
8021
|
+
const readback = safeExactBucket(response.data, expectedBucket);
|
|
8022
|
+
const validReadback = readback?.name === expectedBucket && bucketMatchesRequest(readback, request);
|
|
8023
|
+
if (!response.ok || !validReadback || !readback) {
|
|
8024
|
+
return releaseControlFailure(operation, "OUTCOME_UNKNOWN", response.transportError ? null : response.status);
|
|
8025
|
+
}
|
|
8026
|
+
return releaseControlSuccess(operation, { project_ref: ref, bucket: readback });
|
|
8027
|
+
}
|
|
8028
|
+
function bucketResponse(expectation) {
|
|
8029
|
+
const { operation, ref, response, operationKind, safePayload } = expectation;
|
|
8030
|
+
if (!response.ok) {
|
|
8031
|
+
return operationKind === "mutation" ? releaseControlMutationFailure(operation, response) : releaseControlFailure(operation, "HTTP_ERROR", response.transportError ? null : response.status);
|
|
8032
|
+
}
|
|
8033
|
+
const payload = safePayload(response.data);
|
|
8034
|
+
if (!payload) {
|
|
8035
|
+
return operationKind === "mutation" ? releaseControlFailure(operation, "OUTCOME_UNKNOWN", response.status) : releaseControlFailure(operation, "INVALID_RESPONSE", null);
|
|
8036
|
+
}
|
|
8037
|
+
return releaseControlSuccess(operation, { project_ref: ref, ...payload });
|
|
8038
|
+
}
|
|
8039
|
+
function createBucketRequest(args) {
|
|
8040
|
+
assertBucketSettings(args);
|
|
8041
|
+
const request = { name: requiredText(args, "bucket") };
|
|
8042
|
+
if (args.public !== undefined)
|
|
8043
|
+
request.public = args.public;
|
|
8044
|
+
if (args.file_size_limit !== undefined)
|
|
8045
|
+
request.file_size_limit = args.file_size_limit;
|
|
8046
|
+
if (args.allowed_mime_types !== undefined)
|
|
8047
|
+
request.allowed_mime_types = args.allowed_mime_types;
|
|
8048
|
+
return request;
|
|
8049
|
+
}
|
|
8050
|
+
function updateBucketRequest(args) {
|
|
8051
|
+
assertBucketSettings(args);
|
|
8052
|
+
const request = Object.fromEntries(["public", "file_size_limit", "allowed_mime_types"].filter((field) => args[field] !== undefined).map((field) => [field, args[field]]));
|
|
8053
|
+
if (Object.keys(request).length === 0)
|
|
8054
|
+
throw new Error("Bucket update requires at least one field");
|
|
8055
|
+
return request;
|
|
8056
|
+
}
|
|
8057
|
+
function equalAllowedMimeTypes(candidate, expected) {
|
|
8058
|
+
if (!Array.isArray(expected))
|
|
8059
|
+
return false;
|
|
8060
|
+
if (expected.length === 0 && candidate === null)
|
|
8061
|
+
return true;
|
|
8062
|
+
return Array.isArray(candidate) && candidate.length === expected.length && candidate.every((mimeType, index) => mimeType === expected[index]);
|
|
8063
|
+
}
|
|
8064
|
+
function bucketMatchesRequest(candidate, request) {
|
|
8065
|
+
const bucket = bucketRecord(candidate);
|
|
8066
|
+
if (!bucket)
|
|
8067
|
+
return false;
|
|
8068
|
+
if (request.public !== undefined && bucket.public !== request.public)
|
|
8069
|
+
return false;
|
|
8070
|
+
if (request.file_size_limit !== undefined && bucket.file_size_limit !== request.file_size_limit)
|
|
8071
|
+
return false;
|
|
8072
|
+
return request.allowed_mime_types === undefined || equalAllowedMimeTypes(bucket.allowed_mime_types, request.allowed_mime_types);
|
|
8073
|
+
}
|
|
8074
|
+
async function createBucketMutationReceipt(http, ref, bucket, request) {
|
|
8075
|
+
return bucketResponse({
|
|
8076
|
+
operation: "storage.create_bucket",
|
|
8077
|
+
ref,
|
|
8078
|
+
response: await http.post(storageBucketPath(ref), request),
|
|
8079
|
+
operationKind: "mutation",
|
|
8080
|
+
safePayload: (candidate) => safeCreatedBucketReceipt(candidate, bucket, request)
|
|
8081
|
+
});
|
|
8082
|
+
}
|
|
8083
|
+
async function updateBucketMutationReceipt(expectation) {
|
|
8084
|
+
const { http, ref, bucket, bucketPath, request } = expectation;
|
|
8085
|
+
return bucketResponse({
|
|
8086
|
+
operation: "storage.update_bucket",
|
|
8087
|
+
ref,
|
|
8088
|
+
response: await http.put(bucketPath, request),
|
|
8089
|
+
operationKind: "mutation",
|
|
8090
|
+
safePayload: (apiPayload) => {
|
|
8091
|
+
const updated = safeExactBucket(apiPayload, bucket);
|
|
8092
|
+
return updated && bucketMatchesRequest(updated, request) ? { bucket: updated } : null;
|
|
8093
|
+
}
|
|
8094
|
+
});
|
|
8095
|
+
}
|
|
8096
|
+
async function listBuckets(http, args) {
|
|
8097
|
+
const ref = requiredProjectRef(args);
|
|
8098
|
+
return bucketResponse({
|
|
8099
|
+
operation: "storage.list_buckets",
|
|
8100
|
+
ref,
|
|
8101
|
+
response: await http.get(storageBucketPath(ref)),
|
|
8102
|
+
operationKind: "read",
|
|
8103
|
+
safePayload: (candidate) => {
|
|
8104
|
+
const buckets = safeBucketList(candidate);
|
|
8105
|
+
return buckets ? { buckets } : null;
|
|
8106
|
+
}
|
|
8107
|
+
});
|
|
8108
|
+
}
|
|
8109
|
+
async function getBucket(http, args) {
|
|
8110
|
+
const ref = requiredProjectRef(args);
|
|
8111
|
+
const bucket = requiredBucketId(args);
|
|
8112
|
+
return bucketResponse({
|
|
8113
|
+
operation: "storage.get_bucket",
|
|
8114
|
+
ref,
|
|
8115
|
+
response: await http.get(storageBucketPath(ref, bucket)),
|
|
8116
|
+
operationKind: "read",
|
|
8117
|
+
safePayload: (candidate) => {
|
|
8118
|
+
const safeReadback = safeBucket(candidate, bucket);
|
|
8119
|
+
return safeReadback ? { bucket: safeReadback } : null;
|
|
8120
|
+
}
|
|
8121
|
+
});
|
|
8122
|
+
}
|
|
8123
|
+
async function createBucket(http, args) {
|
|
8124
|
+
const ref = requiredProjectRef(args);
|
|
8125
|
+
const request = createBucketRequest(args);
|
|
8126
|
+
const bucket = request.name;
|
|
8127
|
+
const bucketPath = storageBucketPath(ref, bucket);
|
|
8128
|
+
const receipt = await createBucketMutationReceipt(http, ref, bucket, request);
|
|
8129
|
+
if (receipt.isError)
|
|
8130
|
+
return receipt;
|
|
8131
|
+
const expectedReadback = { ...request, public: request.public === true };
|
|
8132
|
+
return mutationReadbackResponse({
|
|
8133
|
+
operation: "storage.create_bucket",
|
|
8134
|
+
ref,
|
|
8135
|
+
response: await http.get(bucketPath),
|
|
8136
|
+
expectedBucket: bucket,
|
|
8137
|
+
request: expectedReadback
|
|
8138
|
+
});
|
|
8139
|
+
}
|
|
8140
|
+
async function updateBucket(http, args) {
|
|
8141
|
+
const ref = requiredProjectRef(args);
|
|
8142
|
+
const bucket = requiredBucketId(args);
|
|
8143
|
+
const request = updateBucketRequest(args);
|
|
8144
|
+
const bucketPath = storageBucketPath(ref, bucket);
|
|
8145
|
+
const receipt = await updateBucketMutationReceipt({ http, ref, bucket, bucketPath, request });
|
|
8146
|
+
if (receipt.isError)
|
|
8147
|
+
return receipt;
|
|
8148
|
+
return mutationReadbackResponse({
|
|
8149
|
+
operation: "storage.update_bucket",
|
|
8150
|
+
ref,
|
|
8151
|
+
response: await http.get(bucketPath),
|
|
8152
|
+
expectedBucket: bucket,
|
|
8153
|
+
request
|
|
8154
|
+
});
|
|
8155
|
+
}
|
|
8156
|
+
async function deleteBucket(http, args) {
|
|
8157
|
+
const ref = requiredProjectRef(args);
|
|
8158
|
+
const bucket = requiredBucketId(args);
|
|
8159
|
+
const bucketPath = storageBucketPath(ref, bucket);
|
|
8160
|
+
const receipt = bucketResponse({
|
|
8161
|
+
operation: "storage.delete_bucket",
|
|
8162
|
+
ref,
|
|
8163
|
+
response: await http.delete(bucketPath),
|
|
8164
|
+
operationKind: "mutation",
|
|
8165
|
+
safePayload: (candidate) => safeDeletedBucket(candidate, bucket)
|
|
8166
|
+
});
|
|
8167
|
+
if (receipt.isError)
|
|
8168
|
+
return receipt;
|
|
8169
|
+
const readback = await http.get(bucketPath);
|
|
8170
|
+
if (readback.ok || readback.transportError || readback.status !== 404) {
|
|
8171
|
+
return releaseControlFailure("storage.delete_bucket", "OUTCOME_UNKNOWN", readback.transportError ? null : readback.status);
|
|
8172
|
+
}
|
|
8173
|
+
return receipt;
|
|
8174
|
+
}
|
|
8175
|
+
var BUCKET_ACTION_HANDLERS = {
|
|
8176
|
+
list_buckets: listBuckets,
|
|
8177
|
+
get_bucket: getBucket,
|
|
8178
|
+
create_bucket: createBucket,
|
|
8179
|
+
update_bucket: updateBucket,
|
|
8180
|
+
delete_bucket: deleteBucket
|
|
8181
|
+
};
|
|
8182
|
+
function executeBucketAction(action, http, args) {
|
|
8183
|
+
if (!Object.hasOwn(BUCKET_ACTION_HANDLERS, action))
|
|
8184
|
+
return null;
|
|
8185
|
+
return BUCKET_ACTION_HANDLERS[action](http, args);
|
|
8186
|
+
}
|
|
7733
8187
|
function registerStorageTools(server, http) {
|
|
7734
8188
|
server.tool("storage", `S3/MinIO storage management.
|
|
7735
|
-
Actions: status, list_buckets, list_files, upload_base64, delete_file`, {
|
|
7736
|
-
action: withDescription(stringEnum([
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
8189
|
+
Actions: status, list_buckets, get_bucket, create_bucket, update_bucket, delete_bucket, list_files, upload_base64, delete_file`, {
|
|
8190
|
+
action: withDescription(stringEnum([
|
|
8191
|
+
"status",
|
|
8192
|
+
"list_buckets",
|
|
8193
|
+
"get_bucket",
|
|
8194
|
+
"create_bucket",
|
|
8195
|
+
"update_bucket",
|
|
8196
|
+
"delete_bucket",
|
|
8197
|
+
"list_files",
|
|
8198
|
+
"upload_base64",
|
|
8199
|
+
"delete_file"
|
|
8200
|
+
]), "Action"),
|
|
8201
|
+
ref: optional(Type.String({ pattern: PROJECT_REF_PATTERN2.source }), "[list_buckets/get_bucket/create_bucket/update_bucket/delete_bucket/list_files/upload_base64/delete_file] Project ref"),
|
|
8202
|
+
bucket: optional(Type.String({ pattern: BUCKET_ID_PATTERN.source }), "[get_bucket/create_bucket/update_bucket/delete_bucket/list_files/upload_base64/delete_file] Bucket name or ID"),
|
|
8203
|
+
public: optional(Type.Boolean(), "[create_bucket/update_bucket] Public bucket access"),
|
|
8204
|
+
file_size_limit: withDescription(fileSizeLimitSchema, "[create_bucket/update_bucket] Positive safe-integer per-file size limit in bytes"),
|
|
8205
|
+
allowed_mime_types: withDescription(allowedMimeTypesSchema, "[create_bucket/update_bucket] MIME types as a comma-separated or JSON array"),
|
|
8206
|
+
filename: optional(Type.String(), "[upload_base64/delete_file] File name/path"),
|
|
7740
8207
|
base64_content: optional(Type.String(), "[upload_base64] Base64 encoded content"),
|
|
7741
8208
|
mime_type: optional(Type.String(), "[upload_base64] MIME type (default: application/octet-stream)")
|
|
7742
8209
|
}, async (args) => {
|
|
7743
|
-
const
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
return Array.isArray(data) ? fmtFn(data) : JSON.stringify(data, null, 2);
|
|
7750
|
-
};
|
|
8210
|
+
const action = String(args.action);
|
|
8211
|
+
assertActionArguments(action, args);
|
|
8212
|
+
const bucketAction = executeBucketAction(action, http, args);
|
|
8213
|
+
const bucketActionResponse = bucketAction ? await bucketAction : null;
|
|
8214
|
+
if (bucketActionResponse)
|
|
8215
|
+
return bucketActionResponse;
|
|
7751
8216
|
let text;
|
|
7752
8217
|
switch (action) {
|
|
7753
8218
|
case "status":
|
|
7754
8219
|
text = JSON.stringify((await http.get("/v1/storage/status")).data, null, 2);
|
|
7755
8220
|
break;
|
|
7756
|
-
case "list_buckets": {
|
|
7757
|
-
need("ref", ref);
|
|
7758
|
-
const res = await http.get(`/v1/storage/${ref}/buckets`);
|
|
7759
|
-
if (!res.ok) {
|
|
7760
|
-
text = `❌ Failed (${res.status})`;
|
|
7761
|
-
break;
|
|
7762
|
-
}
|
|
7763
|
-
const buckets = res.data;
|
|
7764
|
-
if (!Array.isArray(buckets) || !buckets.length) {
|
|
7765
|
-
text = "No buckets found.";
|
|
7766
|
-
break;
|
|
7767
|
-
}
|
|
7768
|
-
text = `\uD83D\uDCE6 Buckets (${buckets.length}):
|
|
7769
|
-
` + buckets.map((b) => ` - ${b.name} (${b.public ? "\uD83D\uDD13 public" : "\uD83D\uDD12 private"})`).join(`
|
|
7770
|
-
`);
|
|
7771
|
-
break;
|
|
7772
|
-
}
|
|
7773
8221
|
case "list_files": {
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
const
|
|
7777
|
-
if (!
|
|
7778
|
-
text = `❌ Failed (${
|
|
8222
|
+
const ref = requiredProjectRef(args);
|
|
8223
|
+
const bucket = requiredBucketId(args);
|
|
8224
|
+
const response = await http.get(`/v1/storage/${ref}/buckets/${bucket}/files`);
|
|
8225
|
+
if (!response.ok) {
|
|
8226
|
+
text = `❌ Failed (${response.status})`;
|
|
7779
8227
|
break;
|
|
7780
8228
|
}
|
|
7781
|
-
const files =
|
|
8229
|
+
const files = response.data;
|
|
7782
8230
|
if (!Array.isArray(files) || !files.length) {
|
|
7783
8231
|
text = "No files.";
|
|
7784
8232
|
break;
|
|
7785
8233
|
}
|
|
7786
8234
|
text = `\uD83D\uDCC1 Files (${files.length}):
|
|
7787
|
-
` + files.map((
|
|
8235
|
+
` + files.map((file) => ` - ${file.name} (${file.size ? (file.size / 1024).toFixed(1) + "KB" : "?"})`).join(`
|
|
7788
8236
|
`);
|
|
7789
8237
|
break;
|
|
7790
8238
|
}
|
|
7791
8239
|
case "upload_base64": {
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
8240
|
+
const ref = requiredProjectRef(args);
|
|
8241
|
+
const bucket = requiredBucketId(args);
|
|
8242
|
+
const filename = requiredText(args, "filename");
|
|
8243
|
+
const base64Content = requiredText(args, "base64_content");
|
|
7796
8244
|
try {
|
|
7797
|
-
const buffer = Buffer.from(
|
|
7798
|
-
const blob = new Blob([buffer], { type: mime_type
|
|
8245
|
+
const buffer = Buffer.from(base64Content, "base64");
|
|
8246
|
+
const blob = new Blob([buffer], { type: typeof args.mime_type === "string" ? args.mime_type : "application/octet-stream" });
|
|
7799
8247
|
const formData = new FormData;
|
|
7800
8248
|
formData.append("file", blob, filename);
|
|
7801
|
-
const
|
|
7802
|
-
text =
|
|
7803
|
-
} catch (
|
|
7804
|
-
text = `❌ Error: ${
|
|
8249
|
+
const response = await http.postMultipart(`/v1/storage/${ref}/buckets/${bucket}/upload`, formData);
|
|
8250
|
+
text = response.ok ? `✅ File ${filename} uploaded to ${bucket}` : `❌ Upload failed (${response.status})`;
|
|
8251
|
+
} catch (error) {
|
|
8252
|
+
text = `❌ Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
7805
8253
|
}
|
|
7806
8254
|
break;
|
|
7807
8255
|
}
|
|
7808
|
-
case "delete_file":
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
text = (await http.delete(`/v1/storage/${ref}/buckets/${bucket}/files/${filename}`)).ok ? `✅ File ${filename} deleted` :
|
|
8256
|
+
case "delete_file": {
|
|
8257
|
+
const ref = requiredProjectRef(args);
|
|
8258
|
+
const bucket = requiredBucketId(args);
|
|
8259
|
+
const filename = requiredText(args, "filename");
|
|
8260
|
+
text = (await http.delete(`/v1/storage/${ref}/buckets/${bucket}/files/${filename}`)).ok ? `✅ File ${filename} deleted` : "❌ Failed";
|
|
7813
8261
|
break;
|
|
8262
|
+
}
|
|
7814
8263
|
default:
|
|
7815
|
-
|
|
8264
|
+
return releaseControlFailure(`storage.${action}`, "INVALID_RESPONSE", null);
|
|
7816
8265
|
}
|
|
7817
8266
|
return { content: [{ type: "text", text }] };
|
|
7818
8267
|
});
|
|
@@ -7824,39 +8273,6 @@ import { tmpdir } from "node:os";
|
|
|
7824
8273
|
import { basename as basename2, join as join2, resolve as resolve2 } from "node:path";
|
|
7825
8274
|
import { promisify } from "node:util";
|
|
7826
8275
|
import { execFile } from "node:child_process";
|
|
7827
|
-
|
|
7828
|
-
// src/shared/tools/release-control-response.ts
|
|
7829
|
-
var RELEASE_CONTROL_RESPONSE_SCHEMA = "supacloud.cli.release-control.v1";
|
|
7830
|
-
function releaseControlSuccess(operation, payload) {
|
|
7831
|
-
return releaseControlResponse({
|
|
7832
|
-
schema: RELEASE_CONTROL_RESPONSE_SCHEMA,
|
|
7833
|
-
ok: true,
|
|
7834
|
-
operation,
|
|
7835
|
-
...payload
|
|
7836
|
-
});
|
|
7837
|
-
}
|
|
7838
|
-
function releaseControlFailure(operation, code, httpStatus) {
|
|
7839
|
-
return releaseControlErrorResponse({
|
|
7840
|
-
schema: RELEASE_CONTROL_RESPONSE_SCHEMA,
|
|
7841
|
-
ok: false,
|
|
7842
|
-
operation,
|
|
7843
|
-
error: { code, http_status: httpStatus }
|
|
7844
|
-
});
|
|
7845
|
-
}
|
|
7846
|
-
function releaseControlMutationFailure(operation, response) {
|
|
7847
|
-
const outcomeUnknown = response.transportError || response.status === 408 || response.status >= 500;
|
|
7848
|
-
return outcomeUnknown ? releaseControlFailure(operation, "OUTCOME_UNKNOWN", response.transportError ? null : response.status) : releaseControlFailure(operation, "HTTP_ERROR", response.status);
|
|
7849
|
-
}
|
|
7850
|
-
function releaseControlResponse(payload) {
|
|
7851
|
-
return {
|
|
7852
|
-
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
|
|
7853
|
-
};
|
|
7854
|
-
}
|
|
7855
|
-
function releaseControlErrorResponse(payload) {
|
|
7856
|
-
return { ...releaseControlResponse(payload), isError: true };
|
|
7857
|
-
}
|
|
7858
|
-
|
|
7859
|
-
// src/shared/tools/advanced-tools.ts
|
|
7860
8276
|
var execFileAsync = promisify(execFile);
|
|
7861
8277
|
async function runBunBuild(args) {
|
|
7862
8278
|
try {
|
|
@@ -10180,7 +10596,6 @@ var FORBIDDEN_HEADER_NAMES = new Set([
|
|
|
10180
10596
|
"via",
|
|
10181
10597
|
"x-project-ref"
|
|
10182
10598
|
]);
|
|
10183
|
-
var PROJECT_REF_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
10184
10599
|
var SCHEDULE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
10185
10600
|
var SAFE_SLUG_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
10186
10601
|
var CRON_PART_PATTERN = /^(\*|([0-9]+)(?:-([0-9]+))?)(?:\/([0-9]+))?$/;
|
|
@@ -10191,7 +10606,7 @@ var MAX_HEADER_COUNT = 64;
|
|
|
10191
10606
|
var MAX_HEADER_VALUE_LENGTH = 8192;
|
|
10192
10607
|
var MAX_SCHEDULE_NAME_LENGTH = 120;
|
|
10193
10608
|
var headerEnvironmentRecord = Type.Record(Type.String(), Type.String());
|
|
10194
|
-
var
|
|
10609
|
+
var ACTION_ARGUMENTS2 = {
|
|
10195
10610
|
list: new Set(["action", "ref"]),
|
|
10196
10611
|
create: new Set(["action", "ref", "name", "slug", "cron", "method", "body_file", "header_env"]),
|
|
10197
10612
|
update: new Set(["action", "ref", "schedule_id", "name", "cron", "method", "enabled", "body_file", "header_env"]),
|
|
@@ -10349,26 +10764,25 @@ function safeSchedule(candidate) {
|
|
|
10349
10764
|
updated_at: schedule.updated_at
|
|
10350
10765
|
};
|
|
10351
10766
|
}
|
|
10352
|
-
function
|
|
10767
|
+
function requiredText2(args, name, action) {
|
|
10353
10768
|
const candidate = args[name];
|
|
10354
10769
|
if (typeof candidate !== "string" || !candidate.trim()) {
|
|
10355
10770
|
throw new Error(`'${name}' is required for '${action}'`);
|
|
10356
10771
|
}
|
|
10357
10772
|
return candidate.trim();
|
|
10358
10773
|
}
|
|
10359
|
-
function
|
|
10360
|
-
const unsupported = Object.keys(args).filter((name) => !
|
|
10774
|
+
function assertActionArguments2(action, args) {
|
|
10775
|
+
const unsupported = Object.keys(args).filter((name) => !ACTION_ARGUMENTS2[action].has(name));
|
|
10361
10776
|
if (unsupported.length > 0) {
|
|
10362
10777
|
throw new Error(`'${unsupported[0]}' is not supported for '${action}'`);
|
|
10363
10778
|
}
|
|
10364
10779
|
}
|
|
10365
10780
|
function schedulePath(ref, scheduleId) {
|
|
10366
|
-
|
|
10367
|
-
throw new Error("'ref' is invalid for Scheduled Functions");
|
|
10781
|
+
const projectRefSegment = projectRefPathSegment(ref, "Scheduled Functions");
|
|
10368
10782
|
if (scheduleId !== undefined && !SCHEDULE_ID_PATTERN.test(scheduleId)) {
|
|
10369
10783
|
throw new Error("'schedule_id' is invalid");
|
|
10370
10784
|
}
|
|
10371
|
-
const root = `/v1/projects/${
|
|
10785
|
+
const root = `/v1/projects/${projectRefSegment}/scheduled-functions`;
|
|
10372
10786
|
return scheduleId ? `${root}/${encodeURIComponent(scheduleId)}` : root;
|
|
10373
10787
|
}
|
|
10374
10788
|
function scheduleFailure(operation, response) {
|
|
@@ -10429,7 +10843,7 @@ function createRequest(args, environment) {
|
|
|
10429
10843
|
name: requiredName(args, "create"),
|
|
10430
10844
|
slug: requiredSlug(args, "create"),
|
|
10431
10845
|
cron: requiredCron(args, "create"),
|
|
10432
|
-
method:
|
|
10846
|
+
method: requiredText2(args, "method", "create"),
|
|
10433
10847
|
body: scheduleBody(args.body_file) ?? {},
|
|
10434
10848
|
headers: scheduleHeaders(args.header_env, environment) ?? {}
|
|
10435
10849
|
};
|
|
@@ -10445,19 +10859,19 @@ function safeMutationFields(request) {
|
|
|
10445
10859
|
return safeFields;
|
|
10446
10860
|
}
|
|
10447
10861
|
function requiredName(args, action) {
|
|
10448
|
-
const name =
|
|
10862
|
+
const name = requiredText2(args, "name", action);
|
|
10449
10863
|
if (name.length > MAX_SCHEDULE_NAME_LENGTH)
|
|
10450
10864
|
throw new Error(`'name' is too long for '${action}'`);
|
|
10451
10865
|
return name;
|
|
10452
10866
|
}
|
|
10453
10867
|
function requiredSlug(args, action) {
|
|
10454
|
-
const slug =
|
|
10868
|
+
const slug = requiredText2(args, "slug", action);
|
|
10455
10869
|
if (!SAFE_SLUG_PATTERN.test(slug))
|
|
10456
10870
|
throw new Error(`'slug' is invalid for '${action}'`);
|
|
10457
10871
|
return slug;
|
|
10458
10872
|
}
|
|
10459
10873
|
function requiredCron(args, action) {
|
|
10460
|
-
const cron =
|
|
10874
|
+
const cron = requiredText2(args, "cron", action);
|
|
10461
10875
|
if (!validScheduledFunctionCron(cron))
|
|
10462
10876
|
throw new Error(`'cron' is invalid for '${action}'`);
|
|
10463
10877
|
return cron;
|
|
@@ -10484,8 +10898,8 @@ async function executeScheduleAction(http, environment, args, readOnly = false)
|
|
|
10484
10898
|
const action = args.action;
|
|
10485
10899
|
if (readOnly && action !== "list")
|
|
10486
10900
|
return readOnlyResult2();
|
|
10487
|
-
|
|
10488
|
-
const ref =
|
|
10901
|
+
assertActionArguments2(action, args);
|
|
10902
|
+
const ref = requiredText2(args, "ref", action);
|
|
10489
10903
|
if (action === "list")
|
|
10490
10904
|
return listResponse(ref, await http.get(schedulePath(ref)));
|
|
10491
10905
|
if (action === "create") {
|
|
@@ -10493,7 +10907,7 @@ async function executeScheduleAction(http, environment, args, readOnly = false)
|
|
|
10493
10907
|
const requestId = request.request_id;
|
|
10494
10908
|
return mutationResponse({ action, ref, requestId, expectedFields: safeMutationFields(request) }, await http.post(schedulePath(ref), request));
|
|
10495
10909
|
}
|
|
10496
|
-
const scheduleId =
|
|
10910
|
+
const scheduleId = requiredText2(args, "schedule_id", action);
|
|
10497
10911
|
if (action === "update") {
|
|
10498
10912
|
const request = updateRequest(args, environment);
|
|
10499
10913
|
const requestId = request.request_id;
|
|
@@ -10526,7 +10940,7 @@ var SCHEDULE_TOOL_SCHEMA = {
|
|
|
10526
10940
|
// package.json
|
|
10527
10941
|
var package_default = {
|
|
10528
10942
|
name: "@supacloud/cli",
|
|
10529
|
-
version: "0.
|
|
10943
|
+
version: "0.16.0",
|
|
10530
10944
|
description: "Project-scoped CLI for SupaCloud users",
|
|
10531
10945
|
type: "module",
|
|
10532
10946
|
main: "./dist/index.js",
|
|
@@ -10731,6 +11145,7 @@ EXAMPLES
|
|
|
10731
11145
|
${preferredCommand} frontend list --ref abc123
|
|
10732
11146
|
${preferredCommand} database query --sql "select now()"
|
|
10733
11147
|
${preferredCommand} database query --ref abc123 --file ./queries/vector-search.sql
|
|
11148
|
+
${preferredCommand} database migration_inventory --ref abc123
|
|
10734
11149
|
${preferredCommand} database push_migrations --ref abc123 --dir supabase/migrations --dry_run
|
|
10735
11150
|
${preferredCommand} supabase migration_new --name add_accounts
|
|
10736
11151
|
${preferredCommand} supabase db_diff --schema public --name add_accounts
|
|
@@ -10824,6 +11239,11 @@ function createCliTools(context, confirmProduction) {
|
|
|
10824
11239
|
})
|
|
10825
11240
|
};
|
|
10826
11241
|
}
|
|
11242
|
+
const storageContextCallback = tools.storage.callback;
|
|
11243
|
+
const storageHelpTool = captureTools((server) => registerStorageTools(server, {})).storage;
|
|
11244
|
+
if (storageHelpTool) {
|
|
11245
|
+
tools.storage = { schema: storageHelpTool.schema, callback: storageContextCallback };
|
|
11246
|
+
}
|
|
10827
11247
|
const branchContextCallback = tools.branch.callback;
|
|
10828
11248
|
const branchHelpTool = captureTools((server) => registerBranchTools(server, {}, {
|
|
10829
11249
|
readOnly: true
|