@vibecodemax/cli 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +44 -30
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
-
import * as os from "node:os";
|
|
4
3
|
import * as path from "node:path";
|
|
5
4
|
import { spawnSync } from "node:child_process";
|
|
6
5
|
import { checkS3Context, setupS3Storage, smokeTestS3 } from "./storageS3.js";
|
|
@@ -41,6 +40,19 @@ function fail(code, message, exitCode = 1, extra = {}) {
|
|
|
41
40
|
printJson({ ok: false, code, message, ...extra });
|
|
42
41
|
process.exit(exitCode);
|
|
43
42
|
}
|
|
43
|
+
/** Extract a human-readable error message from a Supabase/provider JSON response. */
|
|
44
|
+
function extractErrorMessage(json) {
|
|
45
|
+
if (!json || typeof json !== "object")
|
|
46
|
+
return null;
|
|
47
|
+
const obj = json;
|
|
48
|
+
if (isNonEmptyString(obj.error))
|
|
49
|
+
return obj.error;
|
|
50
|
+
if (isNonEmptyString(obj.message))
|
|
51
|
+
return obj.message;
|
|
52
|
+
if (isNonEmptyString(obj.msg))
|
|
53
|
+
return obj.msg;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
44
56
|
function parseArgs(argv) {
|
|
45
57
|
const [command, ...rest] = argv;
|
|
46
58
|
let subcommand;
|
|
@@ -630,6 +642,20 @@ function runShellCommand(command, cwd) {
|
|
|
630
642
|
}
|
|
631
643
|
return typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
632
644
|
}
|
|
645
|
+
function runLinkedSupabaseCommand(command, cwd, failureCode, fallbackMessage) {
|
|
646
|
+
const result = spawnSync(process.env.SHELL || "/bin/zsh", ["-lc", command], {
|
|
647
|
+
cwd,
|
|
648
|
+
env: process.env,
|
|
649
|
+
encoding: "utf8",
|
|
650
|
+
});
|
|
651
|
+
if (result.status !== 0) {
|
|
652
|
+
const message = [result.stderr, result.stdout]
|
|
653
|
+
.map((value) => (typeof value === "string" ? value.trim() : ""))
|
|
654
|
+
.find(Boolean) || fallbackMessage;
|
|
655
|
+
fail(failureCode, message);
|
|
656
|
+
}
|
|
657
|
+
return typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
658
|
+
}
|
|
633
659
|
function normalizeStorageMimeCategories(rawValue) {
|
|
634
660
|
const requested = rawValue
|
|
635
661
|
.split(",")
|
|
@@ -708,13 +734,7 @@ async function storageRequest(params) {
|
|
|
708
734
|
json = null;
|
|
709
735
|
}
|
|
710
736
|
if (!response.ok) {
|
|
711
|
-
const message =
|
|
712
|
-
? json.error
|
|
713
|
-
: isNonEmptyString(json?.message)
|
|
714
|
-
? json.message
|
|
715
|
-
: isNonEmptyString(json?.msg)
|
|
716
|
-
? json.msg
|
|
717
|
-
: `Supabase returned ${response.status}`;
|
|
737
|
+
const message = extractErrorMessage(json) || `Supabase returned ${response.status}`;
|
|
718
738
|
fail("SUPABASE_STORAGE_ERROR", message, 1, { status: response.status });
|
|
719
739
|
}
|
|
720
740
|
return json;
|
|
@@ -764,14 +784,14 @@ async function readStorageBucket(supabaseUrl, serviceRoleKey, bucketId) {
|
|
|
764
784
|
}
|
|
765
785
|
if (response.status === 404)
|
|
766
786
|
return null;
|
|
787
|
+
// Supabase may return 400 (not 404) for non-existent buckets in some API versions
|
|
788
|
+
if (response.status === 400) {
|
|
789
|
+
const extracted = extractErrorMessage(json);
|
|
790
|
+
if (extracted && /bucket\s*not\s*found/i.test(extracted))
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
767
793
|
if (!response.ok) {
|
|
768
|
-
const message =
|
|
769
|
-
? json.error
|
|
770
|
-
: isNonEmptyString(json?.message)
|
|
771
|
-
? json.message
|
|
772
|
-
: isNonEmptyString(json?.msg)
|
|
773
|
-
? json.msg
|
|
774
|
-
: `Supabase returned ${response.status}`;
|
|
794
|
+
const message = extractErrorMessage(json) || `Supabase returned ${response.status}`;
|
|
775
795
|
fail("SUPABASE_STORAGE_ERROR", message, 1, { status: response.status });
|
|
776
796
|
}
|
|
777
797
|
return json && typeof json === "object" ? json : {};
|
|
@@ -822,13 +842,6 @@ async function ensureStorageBucket(supabaseUrl, serviceRoleKey, bucketId, isPubl
|
|
|
822
842
|
verified: true,
|
|
823
843
|
};
|
|
824
844
|
}
|
|
825
|
-
function verifyRequiredStoragePolicies(dumpContent) {
|
|
826
|
-
const normalized = dumpContent.toLowerCase();
|
|
827
|
-
const missing = STORAGE_REQUIRED_POLICY_NAMES.filter((policyName) => !normalized.includes(`create policy \"${policyName}\"`));
|
|
828
|
-
if (missing.length > 0) {
|
|
829
|
-
fail("STORAGE_POLICY_VERIFY_FAILED", `Missing required storage policies after local Supabase CLI apply: ${missing.join(", ")}.`);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
845
|
function buildStorageHealthcheckObjectPath(runId) {
|
|
833
846
|
return `${STORAGE_HEALTHCHECK_PREFIX}/${runId}/upload.txt`;
|
|
834
847
|
}
|
|
@@ -869,20 +882,14 @@ async function setupSupabaseStorage(flags) {
|
|
|
869
882
|
const supabaseRunner = getSupabaseRunner(dependencyManager);
|
|
870
883
|
const mimeCategories = resolveStorageMimeCategories(flags);
|
|
871
884
|
const allowedMimeTypes = expandStorageMimeCategories(mimeCategories);
|
|
885
|
+
const migrations = discoverStorageMigrationFiles(cwd);
|
|
886
|
+
runLinkedSupabaseCommand(`${supabaseRunner} db push --linked`, cwd, "STORAGE_POLICY_MIGRATION_APPLY_FAILED", "Failed to apply storage migrations to the linked Supabase project.");
|
|
872
887
|
const publicBucket = await ensureStorageBucket(supabaseUrl, serviceRoleKey, STORAGE_PUBLIC_BUCKET, true, allowedMimeTypes);
|
|
873
888
|
const privateBucket = await ensureStorageBucket(supabaseUrl, serviceRoleKey, STORAGE_PRIVATE_BUCKET, false, allowedMimeTypes);
|
|
874
889
|
mergeEnvFile(envLocalPath, {
|
|
875
890
|
SUPABASE_PUBLIC_BUCKET: STORAGE_PUBLIC_BUCKET,
|
|
876
891
|
SUPABASE_PRIVATE_BUCKET: STORAGE_PRIVATE_BUCKET,
|
|
877
892
|
});
|
|
878
|
-
const migrations = discoverStorageMigrationFiles(cwd);
|
|
879
|
-
runShellCommand(`${supabaseRunner} db push --linked`, cwd);
|
|
880
|
-
const dumpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecodemax-storage-dump-"));
|
|
881
|
-
const dumpPath = path.join(dumpDir, "storage.sql");
|
|
882
|
-
runShellCommand(`${supabaseRunner} db dump --linked --schema storage -f ${shellQuote(dumpPath)} >/dev/null`, cwd);
|
|
883
|
-
const dumpContent = readFileIfExists(dumpPath);
|
|
884
|
-
verifyRequiredStoragePolicies(dumpContent);
|
|
885
|
-
fs.rmSync(dumpDir, { recursive: true, force: true });
|
|
886
893
|
printJson({
|
|
887
894
|
ok: true,
|
|
888
895
|
command: "storage setup-supabase",
|
|
@@ -898,6 +905,7 @@ async function setupSupabaseStorage(flags) {
|
|
|
898
905
|
policyMigrationsDiscovered: true,
|
|
899
906
|
policyMigrationsApplied: true,
|
|
900
907
|
policiesVerified: true,
|
|
908
|
+
policyVerificationMethod: "linked_db_push",
|
|
901
909
|
envWritten: ["SUPABASE_PUBLIC_BUCKET", "SUPABASE_PRIVATE_BUCKET"],
|
|
902
910
|
});
|
|
903
911
|
}
|
|
@@ -967,6 +975,12 @@ async function main() {
|
|
|
967
975
|
});
|
|
968
976
|
return;
|
|
969
977
|
}
|
|
978
|
+
// Handle --help for any resolved command before dispatching (avoids live API calls)
|
|
979
|
+
if (flags.help || flags.h) {
|
|
980
|
+
const resolvedCommand = subcommand ? `${command} ${subcommand}` : command;
|
|
981
|
+
printJson({ ok: true, command: resolvedCommand, help: true, message: `Usage: npx @vibecodemax/cli ${resolvedCommand} [options]` });
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
970
984
|
if (command === "read-setup-state")
|
|
971
985
|
return readSetupState();
|
|
972
986
|
if (command === "admin" && subcommand === "ensure-admin")
|