create-svc 0.1.13 → 0.1.15

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/src/gcp.ts CHANGED
@@ -1,6 +1,3 @@
1
- import { CloudBillingClient } from "@google-cloud/billing";
2
- import { ProjectsClient } from "@google-cloud/resource-manager";
3
-
4
1
  export type GcpProject = {
5
2
  projectId: string;
6
3
  name: string;
@@ -20,58 +17,29 @@ export type GcpApi = {
20
17
  attachBillingAccount(projectId: string, billingAccountName: string): Promise<void>;
21
18
  };
22
19
 
23
- export function createGcpApi(
24
- projectsClient = new ProjectsClient(),
25
- billingClient = new CloudBillingClient()
26
- ): GcpApi {
20
+ export function createGcpApi(): GcpApi {
27
21
  return {
28
22
  async listProjects() {
29
- const projects: GcpProject[] = [];
30
- for await (const project of projectsClient.searchProjectsAsync({}, { autoPaginate: false })) {
31
- projects.push({
32
- projectId: project.projectId ?? "",
33
- name: project.displayName ?? project.projectId ?? "",
34
- lifecycleState: `${project.state ?? ""}`,
35
- });
36
- }
37
-
38
- return projects
39
- .filter((project) => project.projectId && project.lifecycleState !== "DELETE_REQUESTED")
40
- .sort((left, right) => left.name.localeCompare(right.name));
23
+ return parseJson<GcpProject[]>(
24
+ runGcloud(["projects", "list", "--format=json(projectId,name,lifecycleState)"]).stdout,
25
+ "GCP project discovery"
26
+ );
41
27
  },
42
28
 
43
29
  async listBillingAccounts() {
44
- const accounts: BillingAccount[] = [];
45
- for await (const account of billingClient.listBillingAccountsAsync({}, { autoPaginate: false })) {
46
- accounts.push({
47
- name: account.name ?? "",
48
- displayName: account.displayName ?? account.name ?? "",
49
- open: Boolean(account.open),
50
- });
51
- }
52
-
53
- return accounts
54
- .filter((account) => account.name && account.open)
55
- .sort((left, right) => left.displayName.localeCompare(right.displayName));
30
+ return parseJson<BillingAccount[]>(
31
+ runGcloud(["billing", "accounts", "list", "--format=json(name,displayName,open)"]).stdout,
32
+ "billing account discovery"
33
+ );
56
34
  },
57
35
 
58
36
  async createProject(projectId: string, name: string) {
59
- const [operation] = await projectsClient.createProject({
60
- project: {
61
- projectId,
62
- displayName: name,
63
- },
64
- });
65
- await operation.promise();
37
+ runGcloud(["projects", "create", projectId, "--name", name]);
66
38
  },
67
39
 
68
40
  async attachBillingAccount(projectId: string, billingAccountName: string) {
69
- await billingClient.updateProjectBillingInfo({
70
- name: `projects/${projectId}`,
71
- projectBillingInfo: {
72
- billingAccountName,
73
- },
74
- });
41
+ const account = billingAccountName.replace(/^billingAccounts\//, "");
42
+ runGcloud(["billing", "projects", "link", projectId, "--billing-account", account]);
75
43
  },
76
44
  };
77
45
  }
@@ -95,3 +63,26 @@ export async function createProject(projectId: string, name: string, api = creat
95
63
  export async function attachBillingAccount(projectId: string, billingAccountName: string, api = createGcpApi()) {
96
64
  await api.attachBillingAccount(projectId, billingAccountName);
97
65
  }
66
+
67
+ function runGcloud(args: string[]) {
68
+ const result = Bun.spawnSync(["gcloud", ...args], {
69
+ stdout: "pipe",
70
+ stderr: "pipe",
71
+ });
72
+
73
+ if (result.exitCode !== 0) {
74
+ throw new Error(result.stderr.toString().trim() || `gcloud ${args.join(" ")} failed`);
75
+ }
76
+
77
+ return {
78
+ stdout: result.stdout.toString(),
79
+ };
80
+ }
81
+
82
+ function parseJson<T>(value: string, label: string): T {
83
+ try {
84
+ return JSON.parse(value) as T;
85
+ } catch (error) {
86
+ throw new Error(`Unable to parse ${label} output: ${(error as Error).message}`);
87
+ }
88
+ }
@@ -30,19 +30,21 @@ export async function bootstrapGitHubRepository(targetDir: string, config: GitBo
30
30
  return { status: "skipped-existing-worktree", root: existingRoot };
31
31
  }
32
32
 
33
- run(["git", "--version"], targetDir, "git is required to initialize the generated repository");
34
- run(["gh", "--version"], targetDir, "GitHub CLI `gh` is required to create the generated repository");
35
- run(["gh", "auth", "status"], targetDir, "Authenticate GitHub CLI with `gh auth login` before creating the repository");
33
+ run(["git", "--version"], targetDir, "git is required to initialize the generated repository", { quiet: true });
34
+ run(["gh", "--version"], targetDir, "GitHub CLI `gh` is required to create the generated repository", { quiet: true });
35
+ run(["gh", "auth", "status"], targetDir, "Authenticate GitHub CLI with `gh auth login` before creating the repository", { quiet: true });
36
36
 
37
- run(["git", "init", "-b", "main"], targetDir);
37
+ run(["git", "init", "-b", "main", "--quiet"], targetDir);
38
38
  run(["git", "add", "."], targetDir);
39
39
 
40
40
  if (hasStagedChanges(targetDir)) {
41
- run(["git", "commit", "-m", "Initial commit"], targetDir);
41
+ run(["git", "commit", "--quiet", "-m", "Initial commit"], targetDir);
42
42
  }
43
43
 
44
44
  const repository = `${config.owner}/${config.repository}`;
45
- run(["gh", "repo", "create", repository, "--private", "--source", ".", "--remote", "origin", "--push"], targetDir);
45
+ run(["gh", "repo", "create", repository, "--private", "--source", ".", "--remote", "origin", "--push"], targetDir, undefined, {
46
+ quiet: true,
47
+ });
46
48
 
47
49
  return {
48
50
  status: "created",
@@ -55,8 +57,8 @@ export function commitAndPushGeneratedArtifacts(targetDir: string, message: stri
55
57
  if (!hasStagedChanges(targetDir)) {
56
58
  return { committed: false };
57
59
  }
58
- run(["git", "commit", "-m", message], targetDir);
59
- run(["git", "push"], targetDir);
60
+ run(["git", "commit", "--quiet", "-m", message], targetDir);
61
+ run(["git", "push", "--quiet"], targetDir, undefined, { quiet: true });
60
62
  return { committed: true };
61
63
  }
62
64
 
@@ -94,17 +96,18 @@ function hasStagedChanges(cwd: string) {
94
96
  return result.exitCode === 1;
95
97
  }
96
98
 
97
- function run(command: string[], cwd: string, message?: string) {
99
+ function run(command: string[], cwd: string, message?: string, options: { quiet?: boolean } = {}) {
98
100
  const result = Bun.spawnSync(command, {
99
101
  cwd,
100
102
  stdin: "inherit",
101
- stdout: "inherit",
103
+ stdout: options.quiet ? "pipe" : "inherit",
102
104
  stderr: "pipe",
103
105
  });
104
106
  if (result.exitCode === 0) {
105
107
  return;
106
108
  }
107
109
 
110
+ const output = result.stdout?.toString().trim() ?? "";
108
111
  const detail = result.stderr.toString().trim();
109
- throw new Error([message, `Command failed: ${command.join(" ")}`, detail].filter(Boolean).join("\n"));
112
+ throw new Error([message, `Command failed: ${command.join(" ")}`, output, detail].filter(Boolean).join("\n"));
110
113
  }
@@ -11,7 +11,7 @@ test("deriveDefaults uses the service name for project, repo, and database namin
11
11
  neonDatabaseName: "edge_api",
12
12
  localDatabasePort: deriveLocalPostgresPort("edge-api"),
13
13
  apiHostname: "api.edge-api.anmho.com",
14
- modulePath: "example.com/edge-api",
14
+ modulePath: "github.com/anmho/edge-api",
15
15
  });
16
16
  });
17
17
 
package/src/naming.ts CHANGED
@@ -94,7 +94,7 @@ export function deriveDefaults(serviceName: string) {
94
94
  neonDatabaseName: compactDatabaseName(normalizedServiceName),
95
95
  localDatabasePort: deriveLocalPostgresPort(normalizedServiceName),
96
96
  apiHostname: `api.${normalizedServiceName}.anmho.com`,
97
- modulePath: `example.com/${normalizedServiceName}`,
97
+ modulePath: `github.com/anmho/${normalizedServiceName}`,
98
98
  };
99
99
  }
100
100
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { buildPostScaffoldCommands } from "./post-scaffold";
2
+ import { buildDeploymentVerificationCommands, buildPostScaffoldCommands } from "./post-scaffold";
3
3
 
4
4
  describe("buildPostScaffoldCommands", () => {
5
5
  test("runs create and deploy for HTTP services", () => {
@@ -17,3 +17,19 @@ describe("buildPostScaffoldCommands", () => {
17
17
  ]);
18
18
  });
19
19
  });
20
+
21
+ describe("buildDeploymentVerificationCommands", () => {
22
+ test("uses curl health checks for HTTP services", () => {
23
+ expect(buildDeploymentVerificationCommands({ apiHostname: "api.launch.anmho.com", framework: "hono", runtime: "bun" })).toEqual([
24
+ { command: "curl", args: ["--fail", "--show-error", "--silent", "https://api.launch.anmho.com/healthz"] },
25
+ { command: "curl", args: ["--fail", "--show-error", "--silent", "https://api.launch.anmho.com/readyz"] },
26
+ ]);
27
+ });
28
+
29
+ test("uses grpcurl for Go ConnectRPC services", () => {
30
+ expect(buildDeploymentVerificationCommands({ apiHostname: "api.launch.anmho.com", framework: "connectrpc", runtime: "go" })).toContainEqual({
31
+ command: "grpcurl",
32
+ args: ["api.launch.anmho.com:443", "list"],
33
+ });
34
+ });
35
+ });
@@ -4,6 +4,7 @@ type CommandOptions = {
4
4
  cwd: string;
5
5
  allowFailure?: boolean;
6
6
  input?: string;
7
+ quiet?: boolean;
7
8
  };
8
9
 
9
10
  type CommandResult = {
@@ -26,12 +27,32 @@ export async function runPostScaffoldFlow(config: ScaffoldConfig, cwd: string) {
26
27
  for (const command of buildPostScaffoldCommands(config)) {
27
28
  run(command.command, command.args, { cwd });
28
29
  }
29
- return { message: "Dependencies installed, service created, and service deployed" };
30
+ for (const command of buildDeploymentVerificationCommands(config)) {
31
+ run(command.command, command.args, { cwd, quiet: true });
32
+ }
33
+ return { message: "Dependencies installed, service created, service deployed, and production health verified" };
30
34
  }
31
35
 
32
36
  return { message: "Backend package generated" };
33
37
  }
34
38
 
39
+ export function buildDeploymentVerificationCommands(
40
+ config: Pick<ScaffoldConfig, "apiHostname" | "framework" | "runtime">
41
+ ): PostScaffoldCommand[] {
42
+ const origin = `https://${config.apiHostname}`;
43
+ return [
44
+ { command: "curl", args: ["--fail", "--show-error", "--silent", `${origin}/healthz`] },
45
+ { command: "curl", args: ["--fail", "--show-error", "--silent", `${origin}/readyz`] },
46
+ ...(config.framework === "connectrpc"
47
+ ? [
48
+ config.runtime === "go"
49
+ ? { command: "grpcurl", args: [`${config.apiHostname}:443`, "list"] }
50
+ : { command: "curl", args: ["--fail", "--show-error", "--silent", `${origin}/debug/connectrpc`] },
51
+ ]
52
+ : []),
53
+ ];
54
+ }
55
+
35
56
  export function buildPostScaffoldCommands(config: Pick<ScaffoldConfig, "framework">): PostScaffoldCommand[] {
36
57
  return [
37
58
  ...(config.framework === "connectrpc" ? [{ command: "bun", args: ["run", "service", "--", "sdk", "build"] }] : []),
@@ -56,8 +77,8 @@ function run(command: string, args: string[], options: CommandOptions): CommandR
56
77
  cwd: options.cwd,
57
78
  env: process.env,
58
79
  stdin: options.input === undefined ? undefined : encoder.encode(options.input),
59
- stdout: options.allowFailure ? "pipe" : "inherit",
60
- stderr: options.allowFailure ? "pipe" : "inherit",
80
+ stdout: options.allowFailure || options.quiet ? "pipe" : "inherit",
81
+ stderr: options.allowFailure || options.quiet ? "pipe" : "inherit",
61
82
  });
62
83
 
63
84
  const stdout = result.stdout ? decoder.decode(result.stdout).trim() : "";
@@ -9,7 +9,7 @@ function baseConfig(overrides: Partial<ScaffoldConfig> = {}): ScaffoldConfig {
9
9
  return {
10
10
  directory: "svc",
11
11
  serviceName: "dns-api",
12
- modulePath: "example.com/dns-api",
12
+ modulePath: "github.com/anmho/dns-api",
13
13
  target: "cloudrun",
14
14
  runtime: "bun",
15
15
  framework: "hono",
@@ -156,11 +156,17 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
156
156
 
157
157
  if (variant.runtime === "go") {
158
158
  const goMod = await Bun.file(join(generatedRoot, "go.mod")).text();
159
- expect(goMod).toContain("module example.com/dns-api");
160
- expect(goMod).not.toContain("module github.com/anmho/dns-api");
159
+ const packageJson = await Bun.file(join(generatedRoot, "package.json")).text();
160
+ expect(goMod).toContain("module github.com/anmho/dns-api");
161
+ expect(goMod).not.toContain("module example.com/dns-api");
162
+ expect(packageJson).toContain('"dev": "make dev"');
163
+ expect(packageJson).toContain('"migrate": "make migrate"');
164
+ expect(packageJson).toContain('"create": "bun run ./scripts/cloudrun/cli.ts create"');
165
+ expect(packageJson).toContain('"deploy": "bun run ./scripts/cloudrun/cli.ts deploy"');
166
+ expect(packageJson).toContain('"destroy": "bun run ./scripts/cloudrun/cli.ts destroy"');
161
167
 
162
168
  const mainGo = await Bun.file(join(generatedRoot, "cmd", "server", "main.go")).text();
163
- expect(mainGo).toContain("example.com/dns-api");
169
+ expect(mainGo).toContain("github.com/anmho/dns-api");
164
170
  if (variant.framework === "connectrpc") {
165
171
  expect(goMod).toContain("connectrpc.com/connect");
166
172
  expect(mainGo).toContain("NewWaitlistService");
@@ -202,7 +208,8 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
202
208
  expect(packageJson).toContain('"auth": "bun run ./scripts/cloudrun/cli.ts auth"');
203
209
  expect(packageJson).toContain('"destroy": "bun run ./scripts/cloudrun/cli.ts destroy"');
204
210
  const serviceCli = await Bun.file(join(generatedRoot, "scripts", "cloudrun", "cli.ts")).text();
205
- expect(serviceCli).toContain("service <create|deploy|migrate|seed|dashboards|dns|doctor|destroy|auth|sdk>");
211
+ expect(serviceCli).toContain("service <command> [args]");
212
+ expect(serviceCli).toContain("Provision auth, database, migrations, and first deploy");
206
213
  expect(serviceCli).toContain("assertServiceNameAvailable(config.serviceName)");
207
214
  expect(serviceCli).toContain("ensureAuthResourceServer");
208
215
  expect(serviceCli).toContain('["resources", "push", "--path", "./grafana"]');
@@ -2,7 +2,7 @@ import { expect, test } from "bun:test";
2
2
  import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
- import { findGeneratedServiceRoot, normalizeScaffoldArgs } from "./service";
5
+ import { findGeneratedServiceRoot, generatedDependenciesInstalled, normalizeScaffoldArgs } from "./service";
6
6
 
7
7
  test("normalizeScaffoldArgs treats service create as the scaffold command outside a service repo", () => {
8
8
  expect(normalizeScaffoldArgs(["create", "launch-api", "--yes"])).toEqual(["launch-api", "--yes"]);
@@ -28,3 +28,14 @@ test("findGeneratedServiceRoot detects generated service context from nested dir
28
28
  expect(findGeneratedServiceRoot(nested)).toBe(serviceRoot);
29
29
  expect(findGeneratedServiceRoot(root)).toBeUndefined();
30
30
  });
31
+
32
+ test("generatedDependenciesInstalled requires node_modules when package.json exists", async () => {
33
+ const root = await mkdtemp(join(tmpdir(), "create-svc-generated-deps-"));
34
+ expect(generatedDependenciesInstalled(root)).toBeTrue();
35
+
36
+ await writeFile(join(root, "package.json"), "{}");
37
+ expect(generatedDependenciesInstalled(root)).toBeFalse();
38
+
39
+ await mkdir(join(root, "node_modules"));
40
+ expect(generatedDependenciesInstalled(root)).toBeTrue();
41
+ });
package/src/service.ts CHANGED
@@ -48,6 +48,8 @@ function isGeneratedServiceRoot(path: string) {
48
48
  }
49
49
 
50
50
  function delegateToGeneratedService(serviceRoot: string, argv: string[]) {
51
+ ensureGeneratedDependencies(serviceRoot);
52
+
51
53
  const cliPath = existsSync(join(serviceRoot, "scripts", "cloudrun", "cli.ts"))
52
54
  ? "./scripts/cloudrun/cli.ts"
53
55
  : "./scripts/workers/cli.ts";
@@ -63,3 +65,27 @@ function delegateToGeneratedService(serviceRoot: string, argv: string[]) {
63
65
  process.exit(result.exitCode || 1);
64
66
  }
65
67
  }
68
+
69
+ export function generatedDependenciesInstalled(serviceRoot: string) {
70
+ return !existsSync(join(serviceRoot, "package.json")) || existsSync(join(serviceRoot, "node_modules"));
71
+ }
72
+
73
+ function ensureGeneratedDependencies(serviceRoot: string) {
74
+ if (generatedDependenciesInstalled(serviceRoot)) {
75
+ return;
76
+ }
77
+
78
+ const result = Bun.spawnSync(["bun", "install", "--silent"], {
79
+ cwd: serviceRoot,
80
+ env: process.env,
81
+ stdin: "inherit",
82
+ stdout: "pipe",
83
+ stderr: "pipe",
84
+ });
85
+
86
+ if (!result.success) {
87
+ const output = [result.stdout.toString().trim(), result.stderr.toString().trim()].filter(Boolean).join("\n");
88
+ console.error(["Failed to install generated service dependencies with bun install --silent", output].filter(Boolean).join("\n"));
89
+ process.exit(result.exitCode || 1);
90
+ }
91
+ }
@@ -216,7 +216,7 @@ function authctl(args: string[], options: { allowFailure?: boolean; quiet?: bool
216
216
  };
217
217
 
218
218
  if (!output.success && !options.allowFailure) {
219
- throw new Error(`authctl ${args.join(" ")} failed with exit code ${output.exitCode}\n${output.stderr || output.stdout}`);
219
+ throw new Error(formatAuthctlFailure(args, output));
220
220
  }
221
221
 
222
222
  if (output.stdout && !options.quiet) {
@@ -226,6 +226,22 @@ function authctl(args: string[], options: { allowFailure?: boolean; quiet?: bool
226
226
  return output;
227
227
  }
228
228
 
229
+ function formatAuthctlFailure(args: string[], output: CommandResult) {
230
+ const detail = output.stderr || output.stdout;
231
+ if (detail.includes("status_code\":401") || detail.includes("Forbidden. You don't have permission")) {
232
+ return [
233
+ `authctl ${args.join(" ")} failed with exit code ${output.exitCode}`,
234
+ "authctl reached the auth internal API, but Cloudflare Access rejected the request.",
235
+ "Export the authctl Cloudflare Access service token before running service create:",
236
+ ' export AUTH_INTERNAL_BASE_URL="$(vault kv get -mount=secret -field=AUTH_INTERNAL_BASE_URL prod/apps/auth/authctl/cloudflare-access)"',
237
+ ' export CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_ID="$(vault kv get -mount=secret -field=CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_ID prod/apps/auth/authctl/cloudflare-access)"',
238
+ ' export CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_SECRET="$(vault kv get -mount=secret -field=CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_SECRET prod/apps/auth/authctl/cloudflare-access)"',
239
+ ].join("\n");
240
+ }
241
+
242
+ return `authctl ${args.join(" ")} failed with exit code ${output.exitCode}\n${detail}`;
243
+ }
244
+
229
245
  function authctlPath() {
230
246
  return existsSync("./node_modules/.bin/authctl") ? "./node_modules/.bin/authctl" : Bun.which("authctl");
231
247
  }
@@ -27,7 +27,7 @@ async function main(argv = Bun.argv.slice(2)) {
27
27
  const [command, ...rest] = argv;
28
28
 
29
29
  if (!command || command === "--help" || command === "-h" || command === "help") {
30
- console.log("Usage: service <create|deploy|migrate|seed|dashboards|dns|doctor|destroy|auth|sdk> [args]");
30
+ console.log(formatHelp());
31
31
  return;
32
32
  }
33
33
 
@@ -92,7 +92,26 @@ async function main(argv = Bun.argv.slice(2)) {
92
92
  return;
93
93
  }
94
94
 
95
- throw new Error("Usage: service <create|deploy|migrate|seed|dashboards|dns|doctor|destroy|auth|sdk> [args]");
95
+ throw new Error(`Unknown command: ${command}\n\n${formatHelp()}`);
96
+ }
97
+
98
+ function formatHelp() {
99
+ return [
100
+ "Usage:",
101
+ " service <command> [args]",
102
+ "",
103
+ "Commands:",
104
+ " create Provision auth, database, migrations, and first deploy",
105
+ " deploy Deploy the current service",
106
+ " migrate Apply database migrations",
107
+ " seed Run the seed script when configured",
108
+ " doctor Check local tools and cloud access",
109
+ " auth Manage auth resource server and clients",
110
+ " sdk Build or publish generated SDK artifacts",
111
+ " dns Repair or inspect DNS mappings",
112
+ " dashboards Publish Grafana resources",
113
+ " destroy Remove service-managed cloud resources",
114
+ ].join("\n");
96
115
  }
97
116
 
98
117
  function runLanguageTask(task: "migrate", env?: Record<string, string | undefined>) {
@@ -18,7 +18,7 @@ async function main(argv = Bun.argv.slice(2)) {
18
18
  const [command, ...rest] = argv;
19
19
 
20
20
  if (!command || command === "--help" || command === "-h" || command === "help") {
21
- console.log("Usage: service <create|deploy|migrate|seed|dashboards|dns|doctor|destroy|auth|sdk> [args]");
21
+ console.log(formatHelp());
22
22
  return;
23
23
  }
24
24
 
@@ -87,7 +87,25 @@ async function main(argv = Bun.argv.slice(2)) {
87
87
  throw new Error("SDK commands are only available for ConnectRPC services");
88
88
  }
89
89
 
90
- throw new Error("Usage: service <create|deploy|migrate|seed|dashboards|dns|doctor|destroy|auth|sdk> [args]");
90
+ throw new Error(`Unknown command: ${command}\n\n${formatHelp()}`);
91
+ }
92
+
93
+ function formatHelp() {
94
+ return [
95
+ "Usage:",
96
+ " service <command> [args]",
97
+ "",
98
+ "Commands:",
99
+ " create Provision auth, database, Hyperdrive, and first deploy",
100
+ " deploy Deploy the Worker",
101
+ " migrate Apply database schema",
102
+ " seed Report seed status",
103
+ " doctor Check local tools and cloud access",
104
+ " auth Manage auth resource server and clients",
105
+ " dns Show Workers custom-domain configuration",
106
+ " dashboards Publish Grafana resources",
107
+ " destroy Remove service-managed Worker resources",
108
+ ].join("\n");
91
109
  }
92
110
 
93
111
  function run(command: string, args: string[], options: { allowFailure?: boolean; capture?: boolean } = {}) {
@@ -6,9 +6,17 @@
6
6
  "service": "./scripts/cloudrun/cli.ts"
7
7
  },
8
8
  "scripts": {
9
+ "dev": "make dev",
9
10
  "service": "bun run ./scripts/cloudrun/cli.ts",
11
+ "migrate": "make migrate",
12
+ "gen": "make gen",
13
+ "lint": "make lint",
14
+ "test": "make test",
15
+ "create": "bun run ./scripts/cloudrun/cli.ts create",
16
+ "deploy": "bun run ./scripts/cloudrun/cli.ts deploy",
10
17
  "auth": "bun run ./scripts/cloudrun/cli.ts auth",
11
- "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards"
18
+ "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards",
19
+ "destroy": "bun run ./scripts/cloudrun/cli.ts destroy"
12
20
  },
13
21
  "dependencies": {
14
22
  "@anmho/authctl": "0.1.1",
@@ -6,9 +6,17 @@
6
6
  "service": "./scripts/cloudrun/cli.ts"
7
7
  },
8
8
  "scripts": {
9
+ "dev": "make dev",
9
10
  "service": "bun run ./scripts/cloudrun/cli.ts",
11
+ "migrate": "make migrate",
12
+ "gen": "make gen",
13
+ "lint": "make lint",
14
+ "test": "make test",
15
+ "create": "bun run ./scripts/cloudrun/cli.ts create",
16
+ "deploy": "bun run ./scripts/cloudrun/cli.ts deploy",
10
17
  "auth": "bun run ./scripts/cloudrun/cli.ts auth",
11
- "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards"
18
+ "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards",
19
+ "destroy": "bun run ./scripts/cloudrun/cli.ts destroy"
12
20
  },
13
21
  "dependencies": {
14
22
  "@anmho/authctl": "0.1.1",