@supacloud/cli 0.90.0 → 0.90.1
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 +15 -44
- package/dist/index.js +34 -27
- package/dist/shared/schema.d.ts +1 -1
- package/dist/tool-runtime.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -633,54 +633,25 @@ query parameters and accepts only its authoritative JSON boolean `false`
|
|
|
633
633
|
read-back. The fixture binding comes from the validated disable receipt before
|
|
634
634
|
reporting success. Management endpoint projection is checked before and after.
|
|
635
635
|
|
|
636
|
-
###
|
|
636
|
+
### Internal release scope controls
|
|
637
637
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
regenerated. Test and production scopes may share the same source commit, but
|
|
642
|
-
they are separate environment-bound release identities.
|
|
638
|
+
Scope files and manual release-controller operations are internal compatibility
|
|
639
|
+
surfaces. They are not part of the normal customer deployment path. Use the
|
|
640
|
+
project command instead:
|
|
643
641
|
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
supacloud
|
|
647
|
-
|
|
648
|
-
# Rebind multiple production files together
|
|
649
|
-
supacloud release scope_rebind --files "20260907-intake-p1-production.json,20260907-intake-p1-production-hotfix.json" --environment_id production
|
|
650
|
-
|
|
651
|
-
# Rebind staging separately; a staging scope can never be rebound as production
|
|
652
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --environment_id staging
|
|
653
|
-
|
|
654
|
-
# Rebind all scopes matching a task ID or filename prefix in standard directories
|
|
655
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --environment_id production
|
|
656
|
-
|
|
657
|
-
# Specify an explicit base commit SHA or git ref
|
|
658
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --base_commit origin/main --environment_id production
|
|
659
|
-
|
|
660
|
-
# Preview changes without modifying files
|
|
661
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --environment_id production --dry_run
|
|
662
|
-
|
|
663
|
-
# Inspect scope status, SHA-256 hash, and alignment with git HEAD / origin/main
|
|
664
|
-
supacloud release scope_inspect --file 20260907-intake-p1-production.json --environment_id production
|
|
665
|
-
|
|
666
|
-
# Scaffold a new canonical release scope
|
|
667
|
-
supacloud release scope_create \
|
|
668
|
-
--file supacloud/fa/release-scopes/20260907-intake-p1.json \
|
|
669
|
-
--task FA-INTAKE-P1 \
|
|
670
|
-
--ref abc123 \
|
|
671
|
-
--environment_id production \
|
|
672
|
-
--functions "fa-api,fa-worker" \
|
|
673
|
-
--migrations "20260907120000_init"
|
|
642
|
+
```sh
|
|
643
|
+
supacloud --env test deploy
|
|
644
|
+
supacloud --env production deploy --confirm-production PROJECT_REF
|
|
674
645
|
```
|
|
675
646
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
`
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
flow above.
|
|
647
|
+
FA deployment policy and the boundary between platform-owned operations and
|
|
648
|
+
FA-owned business features are documented in
|
|
649
|
+
[`docs/fa-deployment-capability-boundary.md`](../../docs/fa-deployment-capability-boundary.md).
|
|
650
|
+
|
|
651
|
+
Scope creation, rebinding, inspection and hash calculation are intentionally
|
|
652
|
+
undocumented here. They remain available only for internal compatibility,
|
|
653
|
+
audit and historical receipt migration. Customers do not need a scope file
|
|
654
|
+
before running the one-command deployment flow above.
|
|
684
655
|
|
|
685
656
|
The legacy `.env` fallback is unclassified and therefore does not enable the
|
|
686
657
|
production confirmation gate. Production automation must select a `prod` or
|
package/dist/index.js
CHANGED
|
@@ -8713,6 +8713,8 @@ function decodedSchema(inputSchema, outputSchema, decode, options) {
|
|
|
8713
8713
|
return result;
|
|
8714
8714
|
}
|
|
8715
8715
|
function schemaEnumValues(schema) {
|
|
8716
|
+
if (!schema)
|
|
8717
|
+
return [];
|
|
8716
8718
|
const view = schema;
|
|
8717
8719
|
if (Array.isArray(view.enum)) {
|
|
8718
8720
|
return view.enum.filter((value) => typeof value === "string" || typeof value === "number").map((value) => String(value));
|
|
@@ -8818,7 +8820,7 @@ async function runCli(cliTools, args, options = {}) {
|
|
|
8818
8820
|
`);
|
|
8819
8821
|
const getSchemaShape = (schema) => schemaProperties(schema);
|
|
8820
8822
|
const getEnumOptions = (field) => schemaEnumValues(field);
|
|
8821
|
-
const getDescription = (field) => schemaDescription(field);
|
|
8823
|
+
const getDescription = (field) => field ? schemaDescription(field) : "";
|
|
8822
8824
|
const formatToolHelp = (toolName, tool) => {
|
|
8823
8825
|
const shape = getSchemaShape(tool.schema);
|
|
8824
8826
|
const actionField = shape.action;
|
|
@@ -28024,7 +28026,8 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
28024
28026
|
}
|
|
28025
28027
|
|
|
28026
28028
|
// src/shared/tools/deploy-tools.ts
|
|
28027
|
-
import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, rm as rm3 } from "node:fs/promises";
|
|
28029
|
+
import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile6, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
|
|
28030
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
28028
28031
|
import { createHash as createHash9 } from "node:crypto";
|
|
28029
28032
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
28030
28033
|
import { basename as basename8, dirname as dirname11, join as join17, relative as relative10, resolve as resolve22, sep as sep5 } from "node:path";
|
|
@@ -28074,7 +28077,7 @@ function frontendDeployment(candidate) {
|
|
|
28074
28077
|
}
|
|
28075
28078
|
async function readJson(path) {
|
|
28076
28079
|
try {
|
|
28077
|
-
return await
|
|
28080
|
+
return JSON.parse(await readFile6(path, "utf8"));
|
|
28078
28081
|
} catch (error) {
|
|
28079
28082
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
28080
28083
|
return null;
|
|
@@ -28320,9 +28323,9 @@ function resolveInsideProject(projectDirectory, candidate, allowRoot = false) {
|
|
|
28320
28323
|
return resolved;
|
|
28321
28324
|
}
|
|
28322
28325
|
function createBuildEnvironment(names, environmentVariables) {
|
|
28323
|
-
const selectedNames = [...new Set(names || [])].sort();
|
|
28324
|
-
const selectedNameSet = new Set(selectedNames);
|
|
28325
28326
|
const values = environmentVariables ?? process.env;
|
|
28327
|
+
const selectedNames = [...new Set(names ?? Object.keys(values).filter(isPublicBuildEnv))].sort();
|
|
28328
|
+
const selectedNameSet = new Set(selectedNames);
|
|
28326
28329
|
const env = Object.fromEntries(Object.entries(process.env).filter(([name]) => !isProtectedBuildEnv(name) && (!isPublicBuildEnv(name) || selectedNameSet.has(name)) && (!SENSITIVE_BUILD_ENV.test(name) || isPublicBuildEnv(name))));
|
|
28327
28330
|
const missing = selectedNames.filter((name) => !values[name]?.trim());
|
|
28328
28331
|
for (const name of selectedNames) {
|
|
@@ -28338,15 +28341,21 @@ function assertBuildEnvironment(build, skipBuild) {
|
|
|
28338
28341
|
}
|
|
28339
28342
|
}
|
|
28340
28343
|
async function runBuild(command, cwd, environment, json) {
|
|
28341
|
-
|
|
28342
|
-
|
|
28343
|
-
|
|
28344
|
-
|
|
28344
|
+
await new Promise((resolve, reject) => {
|
|
28345
|
+
const child = spawn2("sh", ["-c", command], {
|
|
28346
|
+
cwd,
|
|
28347
|
+
env: environment,
|
|
28348
|
+
stdio: ["inherit", json ? 2 : "inherit", "inherit"]
|
|
28349
|
+
});
|
|
28350
|
+
child.once("error", reject);
|
|
28351
|
+
child.once("close", (exitCode) => {
|
|
28352
|
+
if (exitCode === 0) {
|
|
28353
|
+
resolve();
|
|
28354
|
+
} else {
|
|
28355
|
+
reject(new Error(`Build command failed (exit ${exitCode ?? "unknown"}): ${command}`));
|
|
28356
|
+
}
|
|
28357
|
+
});
|
|
28345
28358
|
});
|
|
28346
|
-
const exitCode = await child.exited;
|
|
28347
|
-
if (exitCode !== 0) {
|
|
28348
|
-
throw new Error(`Build command failed (exit ${exitCode ?? "unknown"}): ${command}`);
|
|
28349
|
-
}
|
|
28350
28359
|
}
|
|
28351
28360
|
async function collectArchiveFiles(root) {
|
|
28352
28361
|
const rootStat = await lstat5(root).catch(() => null);
|
|
@@ -28379,7 +28388,7 @@ async function collectArchiveFiles(root) {
|
|
|
28379
28388
|
const files = new Map;
|
|
28380
28389
|
const digests = [];
|
|
28381
28390
|
for (const path of paths) {
|
|
28382
|
-
const data = new Uint8Array(await
|
|
28391
|
+
const data = new Uint8Array(await readFile6(path));
|
|
28383
28392
|
bytes += data.byteLength;
|
|
28384
28393
|
if (bytes > MAX_SOURCE_BYTES)
|
|
28385
28394
|
throw new Error(`Frontend output exceeds ${MAX_SOURCE_BYTES} bytes`);
|
|
@@ -28403,7 +28412,7 @@ async function createFrontendArchive(outputDirectory) {
|
|
|
28403
28412
|
const archivePath = join17(temporaryDirectory, `${basename8(outputDirectory)}.tar.zst`);
|
|
28404
28413
|
try {
|
|
28405
28414
|
const archiveBytes = await createFrontendTarZstd(collected.files);
|
|
28406
|
-
await
|
|
28415
|
+
await writeFile6(archivePath, archiveBytes);
|
|
28407
28416
|
const sha256 = createHash9("sha256").update(archiveBytes).digest("hex");
|
|
28408
28417
|
return {
|
|
28409
28418
|
archivePath,
|
|
@@ -28792,7 +28801,7 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28792
28801
|
|
|
28793
28802
|
// src/shared/tools/remote-dev-tools.ts
|
|
28794
28803
|
import { existsSync as existsSync14 } from "node:fs";
|
|
28795
|
-
import { readFile as
|
|
28804
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
28796
28805
|
import { readdir as readdir7 } from "node:fs/promises";
|
|
28797
28806
|
import { createHash as createHash10 } from "node:crypto";
|
|
28798
28807
|
import { join as join18, resolve as resolve23 } from "node:path";
|
|
@@ -28870,7 +28879,7 @@ async function readProjectConfig(root) {
|
|
|
28870
28879
|
if (!existsSync14(file))
|
|
28871
28880
|
return {};
|
|
28872
28881
|
try {
|
|
28873
|
-
const parsed = JSON.parse(await
|
|
28882
|
+
const parsed = JSON.parse(await readFile7(file, "utf8"));
|
|
28874
28883
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
28875
28884
|
} catch (error) {
|
|
28876
28885
|
throw new Error(`Invalid supacloud.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -28896,7 +28905,7 @@ async function sourceFingerprint(root) {
|
|
|
28896
28905
|
const hash = createHash10("sha256");
|
|
28897
28906
|
for (const file of files) {
|
|
28898
28907
|
hash.update(file.slice(root.length).replace(/\\/g, "/"));
|
|
28899
|
-
hash.update(await
|
|
28908
|
+
hash.update(await readFile7(file));
|
|
28900
28909
|
}
|
|
28901
28910
|
return hash.digest("hex");
|
|
28902
28911
|
}
|
|
@@ -29062,7 +29071,7 @@ function registerRemoteDevTools(server, options = {}) {
|
|
|
29062
29071
|
|
|
29063
29072
|
// src/shared/tools/developer-experience-tools.ts
|
|
29064
29073
|
import { existsSync as existsSync15 } from "node:fs";
|
|
29065
|
-
import { mkdir as mkdir7, readdir as readdir8, readFile as
|
|
29074
|
+
import { mkdir as mkdir7, readdir as readdir8, readFile as readFile8, writeFile as writeFile7 } from "node:fs/promises";
|
|
29066
29075
|
import { dirname as dirname12, join as join19, resolve as resolve24 } from "node:path";
|
|
29067
29076
|
var initToolSchema = {
|
|
29068
29077
|
root: optional(String2(), "Project directory to initialize (default: current directory)"),
|
|
@@ -29117,7 +29126,7 @@ async function readPackageManifest(root) {
|
|
|
29117
29126
|
if (!existsSync15(path))
|
|
29118
29127
|
return {};
|
|
29119
29128
|
try {
|
|
29120
|
-
const parsed = JSON.parse(await
|
|
29129
|
+
const parsed = JSON.parse(await readFile8(path, "utf8"));
|
|
29121
29130
|
const value = record5(parsed);
|
|
29122
29131
|
if (!value)
|
|
29123
29132
|
throw new Error("package.json must contain an object");
|
|
@@ -29135,7 +29144,7 @@ async function readPackageManifest(root) {
|
|
|
29135
29144
|
}
|
|
29136
29145
|
async function writeNewFile(path, content) {
|
|
29137
29146
|
try {
|
|
29138
|
-
await
|
|
29147
|
+
await writeFile7(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
29139
29148
|
return true;
|
|
29140
29149
|
} catch (error) {
|
|
29141
29150
|
if (error instanceof Error && "code" in error && error.code === "EEXIST")
|
|
@@ -29145,7 +29154,7 @@ async function writeNewFile(path, content) {
|
|
|
29145
29154
|
}
|
|
29146
29155
|
async function ensureGitignore(root) {
|
|
29147
29156
|
const path = join19(root, ".gitignore");
|
|
29148
|
-
const existing = existsSync15(path) ? await
|
|
29157
|
+
const existing = existsSync15(path) ? await readFile8(path, "utf8") : "";
|
|
29149
29158
|
const required = [".env.supacloud.*", "!.env.supacloud.*.example"];
|
|
29150
29159
|
const missing = required.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
29151
29160
|
if (missing.length === 0)
|
|
@@ -29153,7 +29162,7 @@ async function ensureGitignore(root) {
|
|
|
29153
29162
|
const prefix = existing.length > 0 && !existing.endsWith(`
|
|
29154
29163
|
`) ? `${existing}
|
|
29155
29164
|
` : existing;
|
|
29156
|
-
await
|
|
29165
|
+
await writeFile7(path, `${prefix}${missing.join(`
|
|
29157
29166
|
`)}
|
|
29158
29167
|
`, "utf8");
|
|
29159
29168
|
return true;
|
|
@@ -29176,7 +29185,7 @@ async function initializeProject(request, options) {
|
|
|
29176
29185
|
};
|
|
29177
29186
|
const created = [];
|
|
29178
29187
|
try {
|
|
29179
|
-
await
|
|
29188
|
+
await writeFile7(configPath, `${JSON.stringify(config, null, 2)}
|
|
29180
29189
|
`, {
|
|
29181
29190
|
encoding: "utf8",
|
|
29182
29191
|
flag: "wx",
|
|
@@ -29372,7 +29381,7 @@ function registerDeveloperExperienceTools(server, options = {}) {
|
|
|
29372
29381
|
// package.json
|
|
29373
29382
|
var package_default = {
|
|
29374
29383
|
name: "@supacloud/cli",
|
|
29375
|
-
version: "0.90.
|
|
29384
|
+
version: "0.90.1",
|
|
29376
29385
|
description: "Project-scoped CLI for SupaCloud users",
|
|
29377
29386
|
type: "module",
|
|
29378
29387
|
main: "./dist/index.js",
|
|
@@ -29665,8 +29674,6 @@ EXAMPLES
|
|
|
29665
29674
|
${preferredCommand} release postgrest_restart --ref abc123
|
|
29666
29675
|
${preferredCommand} release release_canary_fixture_stage_replay --ref abc123 --subject <uuid> --request_id <uuid>
|
|
29667
29676
|
${preferredCommand} release release_canary_fixture_disable_replay --ref abc123 --fixture_id <uuid> --disable_request_id <uuid> --issuer <issuer-url> --subject <uuid>
|
|
29668
|
-
${preferredCommand} release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
|
|
29669
|
-
${preferredCommand} release scope_inspect --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
|
|
29670
29677
|
${preferredCommand} queue stats --queue emails
|
|
29671
29678
|
${preferredCommand} queue dlq --queue emails --limit 20
|
|
29672
29679
|
${preferredCommand} frontend list --ref abc123
|
package/dist/shared/schema.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export declare function stringEnum<const Values extends readonly [string, ...str
|
|
|
8
8
|
export declare function withDescription<T extends TSchema>(schema: T, description: string): T;
|
|
9
9
|
export declare function optional<T extends TSchema>(schema: T, description?: string): "~optional" extends keyof T ? T : Type.TOptional<T>;
|
|
10
10
|
export declare function decodedSchema<TInput extends TSchema, TOutput extends TSchema>(inputSchema: TInput, outputSchema: TOutput, decode: (input: StaticDecode<TInput>) => unknown, options?: TSchemaOptions): Type.TCodec<TInput, import("typebox").StaticType<[], "Decode", {}, {}, TOutput>>;
|
|
11
|
-
export declare function schemaEnumValues(schema: TSchema): string[];
|
|
11
|
+
export declare function schemaEnumValues(schema: TSchema | undefined): string[];
|
|
12
12
|
export declare function schemaDescription(schema: TSchema): string;
|
|
13
13
|
export declare function schemaProperties(schema: TSchema | ToolSchema): ToolSchema;
|
package/dist/tool-runtime.js
CHANGED
|
@@ -97,6 +97,8 @@ function decodedSchema(inputSchema, outputSchema, decode, options) {
|
|
|
97
97
|
return result;
|
|
98
98
|
}
|
|
99
99
|
function schemaEnumValues(schema) {
|
|
100
|
+
if (!schema)
|
|
101
|
+
return [];
|
|
100
102
|
const view = schema;
|
|
101
103
|
if (Array.isArray(view.enum)) {
|
|
102
104
|
return view.enum.filter((value) => typeof value === "string" || typeof value === "number").map((value) => String(value));
|