@itssergio91/sandman 0.2.1 → 0.3.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 +46 -266
- package/dist/cli/commands/cli-contract.test.d.ts +2 -0
- package/dist/cli/commands/cli-contract.test.d.ts.map +1 -0
- package/dist/cli/commands/cli-contract.test.js +217 -0
- package/dist/cli/commands/cli-contract.test.js.map +1 -0
- package/dist/cli/commands/connect.d.ts +1 -0
- package/dist/cli/commands/connect.d.ts.map +1 -1
- package/dist/cli/commands/connect.js +67 -42
- package/dist/cli/commands/connect.js.map +1 -1
- package/dist/cli/commands/create.d.ts +7 -2
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +158 -46
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/commands/destroy.d.ts.map +1 -1
- package/dist/cli/commands/destroy.js +27 -23
- package/dist/cli/commands/destroy.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +8 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +124 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/enable.d.ts +1 -2
- package/dist/cli/commands/enable.d.ts.map +1 -1
- package/dist/cli/commands/enable.js +75 -46
- package/dist/cli/commands/enable.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +49 -17
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/list.js +25 -18
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/providers.d.ts +6 -0
- package/dist/cli/commands/providers.d.ts.map +1 -0
- package/dist/cli/commands/providers.js +20 -0
- package/dist/cli/commands/providers.js.map +1 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +88 -50
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/env-name.d.ts +7 -0
- package/dist/cli/env-name.d.ts.map +1 -0
- package/dist/cli/env-name.js +9 -0
- package/dist/cli/env-name.js.map +1 -0
- package/dist/cli/env-name.test.d.ts +2 -0
- package/dist/cli/env-name.test.d.ts.map +1 -0
- package/dist/cli/env-name.test.js +38 -0
- package/dist/cli/env-name.test.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +43 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +28 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +60 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/output.test.d.ts +2 -0
- package/dist/cli/output.test.d.ts.map +1 -0
- package/dist/cli/output.test.js +78 -0
- package/dist/cli/output.test.js.map +1 -0
- package/dist/cli/reap.d.ts +11 -0
- package/dist/cli/reap.d.ts.map +1 -0
- package/dist/cli/reap.js +32 -0
- package/dist/cli/reap.js.map +1 -0
- package/dist/cli/secrets.d.ts +8 -0
- package/dist/cli/secrets.d.ts.map +1 -0
- package/dist/cli/secrets.js +30 -0
- package/dist/cli/secrets.js.map +1 -0
- package/dist/core/state-store.d.ts +15 -1
- package/dist/core/state-store.d.ts.map +1 -1
- package/dist/core/state-store.js +168 -33
- package/dist/core/state-store.js.map +1 -1
- package/dist/core/state-store.test.js +40 -0
- package/dist/core/state-store.test.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/providers/aws/adapter.d.ts +13 -2
- package/dist/providers/aws/adapter.d.ts.map +1 -1
- package/dist/providers/aws/adapter.js +402 -63
- package/dist/providers/aws/adapter.js.map +1 -1
- package/dist/providers/aws/adapter.test.js +300 -191
- package/dist/providers/aws/adapter.test.js.map +1 -1
- package/dist/providers/base.d.ts +1 -9
- package/dist/providers/base.d.ts.map +1 -1
- package/dist/providers/base.js.map +1 -1
- package/dist/providers/base.test.js +12 -2
- package/dist/providers/base.test.js.map +1 -1
- package/dist/providers/catalog.d.ts +15 -0
- package/dist/providers/catalog.d.ts.map +1 -0
- package/dist/providers/catalog.js +53 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/cloudflare/adapter.d.ts +5 -2
- package/dist/providers/cloudflare/adapter.d.ts.map +1 -1
- package/dist/providers/cloudflare/adapter.js +17 -5
- package/dist/providers/cloudflare/adapter.js.map +1 -1
- package/dist/providers/cloudflare/adapter.test.js +4 -4
- package/dist/providers/cloudflare/adapter.test.js.map +1 -1
- package/dist/providers/enable-result.d.ts +9 -0
- package/dist/providers/enable-result.d.ts.map +1 -0
- package/dist/providers/enable-result.js +26 -0
- package/dist/providers/enable-result.js.map +1 -0
- package/dist/providers/gcp/adapter.d.ts +9 -3
- package/dist/providers/gcp/adapter.d.ts.map +1 -1
- package/dist/providers/gcp/adapter.js +115 -20
- package/dist/providers/gcp/adapter.js.map +1 -1
- package/dist/providers/gcp/adapter.test.js +52 -0
- package/dist/providers/gcp/adapter.test.js.map +1 -1
- package/dist/providers/index.d.ts +5 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +13 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/vercel/adapter.d.ts +5 -2
- package/dist/providers/vercel/adapter.d.ts.map +1 -1
- package/dist/providers/vercel/adapter.js +17 -5
- package/dist/providers/vercel/adapter.js.map +1 -1
- package/dist/providers/vercel/adapter.test.js +4 -4
- package/dist/providers/vercel/adapter.test.js.map +1 -1
- package/dist/types/index.d.ts +40 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/cost-estimator.d.ts +2 -0
- package/dist/utils/cost-estimator.d.ts.map +1 -1
- package/dist/utils/cost-estimator.js +23 -0
- package/dist/utils/cost-estimator.js.map +1 -1
- package/dist/utils/cost-estimator.test.js +16 -1
- package/dist/utils/cost-estimator.test.js.map +1 -1
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +12 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/ttl.d.ts +13 -0
- package/dist/utils/ttl.d.ts.map +1 -0
- package/dist/utils/ttl.js +35 -0
- package/dist/utils/ttl.js.map +1 -0
- package/dist/utils/ttl.test.d.ts +2 -0
- package/dist/utils/ttl.test.d.ts.map +1 -0
- package/dist/utils/ttl.test.js +27 -0
- package/dist/utils/ttl.test.js.map +1 -0
- package/package.json +12 -5
|
@@ -1,60 +1,98 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
+
import { getAdapter } from "../../providers/index.js";
|
|
2
3
|
import { calculateRunningCost, formatCost, formatHourlyRate, } from "../../utils/cost-estimator.js";
|
|
4
|
+
import { emitErr, emitOk } from "../output.js";
|
|
5
|
+
import { reapIfExpired } from "../reap.js";
|
|
6
|
+
import { isExpired } from "../../utils/ttl.js";
|
|
3
7
|
export async function statusEnvironment(name, store, options = {}) {
|
|
4
|
-
const
|
|
5
|
-
if (!
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
const stored = await store.getEnvironment(name);
|
|
9
|
+
if (!stored) {
|
|
10
|
+
emitErr(options.json, {
|
|
11
|
+
code: "NOT_FOUND",
|
|
12
|
+
error: `Environment "${name}" not found.`,
|
|
13
|
+
next: ["sandman list --json"],
|
|
14
|
+
});
|
|
8
15
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
estimatedDaily: runningCost.estimatedDaily,
|
|
22
|
-
estimatedMonthly: runningCost.estimatedMonthly,
|
|
23
|
-
},
|
|
24
|
-
}, null, 2));
|
|
16
|
+
const reap = await reapIfExpired(store, stored);
|
|
17
|
+
if (reap.reaped) {
|
|
18
|
+
emitOk(options.json, {
|
|
19
|
+
name,
|
|
20
|
+
status: "destroyed",
|
|
21
|
+
reaped: true,
|
|
22
|
+
reason: "ttl expired",
|
|
23
|
+
expiresAt: stored.expiresAt,
|
|
24
|
+
}, () => {
|
|
25
|
+
console.log(chalk.yellow(`\nEnvironment "${name}" exceeded its TTL and was destroyed.`));
|
|
26
|
+
console.log(chalk.cyan(`→ Run "sandman create ${name}" to recreate it`));
|
|
27
|
+
});
|
|
25
28
|
return;
|
|
26
29
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
console.log(` ${chalk.gray("Project ID:")} ${env.projectId}`);
|
|
34
|
-
}
|
|
35
|
-
if (env.accountId) {
|
|
36
|
-
console.log(` ${chalk.gray("Account ID:")} ${env.accountId}`);
|
|
37
|
-
}
|
|
38
|
-
if (env.region) {
|
|
39
|
-
console.log(` ${chalk.gray("Region:")} ${env.region}`);
|
|
30
|
+
if (reap.error) {
|
|
31
|
+
emitErr(options.json, {
|
|
32
|
+
code: "PROVIDER_ERROR",
|
|
33
|
+
error: `Environment "${name}" is expired but destroy failed: ${reap.error}`,
|
|
34
|
+
next: [`sandman destroy ${name} -y --json`],
|
|
35
|
+
});
|
|
40
36
|
}
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
const adapter = getAdapter(stored.provider);
|
|
38
|
+
const env = await adapter.getStatus(stored);
|
|
39
|
+
if (env.status !== stored.status || env.error !== stored.error) {
|
|
40
|
+
await store.saveEnvironment(env);
|
|
43
41
|
}
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
const runningCost = calculateRunningCost(env.provider, env.services, env.createdAt);
|
|
43
|
+
const age = Date.now() - new Date(env.createdAt).getTime();
|
|
44
|
+
const hours = Math.floor(age / (1000 * 60 * 60));
|
|
45
|
+
const minutes = Math.floor((age % (1000 * 60 * 60)) / (1000 * 60));
|
|
46
|
+
const costEstimate = {
|
|
47
|
+
hourlyRate: runningCost.hourlyRate,
|
|
48
|
+
hoursRunning: runningCost.hoursRunning,
|
|
49
|
+
totalCost: runningCost.totalCost,
|
|
50
|
+
estimatedDaily: runningCost.estimatedDaily,
|
|
51
|
+
estimatedMonthly: runningCost.estimatedMonthly,
|
|
52
|
+
};
|
|
53
|
+
emitOk(options.json, {
|
|
54
|
+
...env,
|
|
55
|
+
costEstimate,
|
|
56
|
+
...(env.expiresAt ? { expired: isExpired(env) } : {}),
|
|
57
|
+
}, () => {
|
|
58
|
+
console.log(chalk.bold(`\nEnvironment: ${env.name}\n`));
|
|
59
|
+
console.log(` ${chalk.gray("Provider:")} ${env.provider}`);
|
|
60
|
+
console.log(` ${chalk.gray("Status:")} ${env.status === "active" ? chalk.green(env.status) : chalk.red(env.status)}`);
|
|
61
|
+
console.log(` ${chalk.gray("Created:")} ${new Date(env.createdAt).toLocaleString()}`);
|
|
62
|
+
console.log(` ${chalk.gray("Age:")} ${hours}h ${minutes}m`);
|
|
63
|
+
if (env.expiresAt) {
|
|
64
|
+
console.log(` ${chalk.gray("Expires:")} ${new Date(env.expiresAt).toLocaleString()}${env.ttl ? ` (${env.ttl})` : ""}`);
|
|
65
|
+
}
|
|
66
|
+
if (env.projectId) {
|
|
67
|
+
console.log(` ${chalk.gray("Project ID:")} ${env.projectId}`);
|
|
68
|
+
}
|
|
69
|
+
if (env.accountId) {
|
|
70
|
+
console.log(` ${chalk.gray("Account ID:")} ${env.accountId}`);
|
|
71
|
+
}
|
|
72
|
+
if (env.billingAccount) {
|
|
73
|
+
console.log(` ${chalk.gray("Billing:")} ${env.billingAccount}`);
|
|
74
|
+
}
|
|
75
|
+
if (env.region) {
|
|
76
|
+
console.log(` ${chalk.gray("Region:")} ${env.region}`);
|
|
77
|
+
}
|
|
78
|
+
if (env.services.length > 0) {
|
|
79
|
+
console.log(` ${chalk.gray("Services:")} ${env.services.join(", ")}`);
|
|
80
|
+
}
|
|
81
|
+
const resourceCount = Object.keys(env.resources).length;
|
|
82
|
+
if (resourceCount > 0) {
|
|
83
|
+
console.log(` ${chalk.gray("Resources:")} ${resourceCount} created`);
|
|
84
|
+
}
|
|
85
|
+
console.log(`\n ${chalk.gray("Cost Estimate:")}`);
|
|
86
|
+
console.log(` ${chalk.gray("Hourly rate:")} ${formatHourlyRate(runningCost.hourlyRate)}`);
|
|
87
|
+
console.log(` ${chalk.gray("Hours running:")} ${runningCost.hoursRunning}h`);
|
|
88
|
+
console.log(` ${chalk.yellow("Current cost:")} ${formatCost(runningCost.totalCost)}`);
|
|
89
|
+
console.log(` ${chalk.gray("Est. daily:")} ${formatCost(runningCost.estimatedDaily)}`);
|
|
90
|
+
console.log(` ${chalk.gray("Est. monthly:")} ${formatCost(runningCost.estimatedMonthly)}`);
|
|
91
|
+
if (env.error) {
|
|
92
|
+
console.log(`\n ${chalk.red("Error:")} ${env.error}`);
|
|
93
|
+
}
|
|
94
|
+
console.log(chalk.cyan(`\n→ Run "sandman connect ${name}" to get credentials`));
|
|
95
|
+
console.log(chalk.cyan(`→ Run "sandman destroy ${name}" to clean up`));
|
|
96
|
+
});
|
|
59
97
|
}
|
|
60
98
|
//# sourceMappingURL=status.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment names must be safe for shell, S3 bucket suffixes, GCP project IDs, and .env files.
|
|
3
|
+
*/
|
|
4
|
+
export declare const ENV_NAME_PATTERN: RegExp;
|
|
5
|
+
export declare function isValidEnvName(name: string): boolean;
|
|
6
|
+
export declare const ENV_NAME_HINT = "Use 1\u201331 characters: lowercase letters, digits, and hyphens (e.g. \"demo\", \"api-test-1\").";
|
|
7
|
+
//# sourceMappingURL=env-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-name.d.ts","sourceRoot":"","sources":["../../src/cli/env-name.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAA8B,CAAC;AAE5D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,eAAO,MAAM,aAAa,sGACkE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment names must be safe for shell, S3 bucket suffixes, GCP project IDs, and .env files.
|
|
3
|
+
*/
|
|
4
|
+
export const ENV_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,30}$/;
|
|
5
|
+
export function isValidEnvName(name) {
|
|
6
|
+
return ENV_NAME_PATTERN.test(name);
|
|
7
|
+
}
|
|
8
|
+
export const ENV_NAME_HINT = 'Use 1–31 characters: lowercase letters, digits, and hyphens (e.g. "demo", "api-test-1").';
|
|
9
|
+
//# sourceMappingURL=env-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-name.js","sourceRoot":"","sources":["../../src/cli/env-name.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAE5D,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GACxB,0FAA0F,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-name.test.d.ts","sourceRoot":"","sources":["../../src/cli/env-name.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { isValidEnvName } from "./env-name.js";
|
|
3
|
+
import { quoteEnvValue, redactSecrets } from "./secrets.js";
|
|
4
|
+
describe("environment names", () => {
|
|
5
|
+
it("accepts DNS-safe names", () => {
|
|
6
|
+
expect(isValidEnvName("demo")).toBe(true);
|
|
7
|
+
expect(isValidEnvName("api-test-1")).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
it("rejects names that would break shell or cloud IDs", () => {
|
|
10
|
+
expect(isValidEnvName("Demo")).toBe(false);
|
|
11
|
+
expect(isValidEnvName("-leading")).toBe(false);
|
|
12
|
+
expect(isValidEnvName("has space")).toBe(false);
|
|
13
|
+
expect(isValidEnvName("rm;pwd")).toBe(false);
|
|
14
|
+
expect(isValidEnvName("`curl`")).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe("secret redaction", () => {
|
|
18
|
+
it("redacts token values by default", () => {
|
|
19
|
+
const { values, secretsRedacted } = redactSecrets({
|
|
20
|
+
CLOUDFLARE_ACCOUNT_ID: "abc",
|
|
21
|
+
CLOUDFLARE_API_TOKEN: "super-secret",
|
|
22
|
+
});
|
|
23
|
+
expect(values.CLOUDFLARE_ACCOUNT_ID).toBe("abc");
|
|
24
|
+
expect(values.CLOUDFLARE_API_TOKEN).toBe("<set in environment>");
|
|
25
|
+
expect(secretsRedacted).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
it("keeps secrets when explicitly requested", () => {
|
|
28
|
+
const { values, secretsRedacted } = redactSecrets({ VERCEL_TOKEN: "tok" }, true);
|
|
29
|
+
expect(values.VERCEL_TOKEN).toBe("tok");
|
|
30
|
+
expect(secretsRedacted).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
it("quotes unsafe env values", () => {
|
|
33
|
+
expect(quoteEnvValue("plain")).toBe("plain");
|
|
34
|
+
expect(quoteEnvValue("a b")).toBe('"a b"');
|
|
35
|
+
expect(quoteEnvValue('say "hi"')).toBe('"say \\"hi\\""');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=env-name.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-name.test.js","sourceRoot":"","sources":["../../src/cli/env-name.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE5D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,aAAa,CAAC;YAChD,qBAAqB,EAAE,KAAK;YAC5B,oBAAoB,EAAE,cAAc;SACrC,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACjE,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,aAAa,CAC/C,EAAE,YAAY,EAAE,KAAK,EAAE,EACvB,IAAI,CACL,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAYpD,QAAA,MAAM,OAAO,SAAgB,CAAC;AAC9B,QAAA,MAAM,KAAK,YAAmB,CAAC;AA+H/B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -7,31 +7,46 @@ import { createEnvironment } from './commands/create.js';
|
|
|
7
7
|
import { enableServices } from './commands/enable.js';
|
|
8
8
|
import { connectEnvironment } from './commands/connect.js';
|
|
9
9
|
import { destroyEnvironment } from './commands/destroy.js';
|
|
10
|
+
import { listProviders } from './commands/providers.js';
|
|
11
|
+
import { doctor } from './commands/doctor.js';
|
|
12
|
+
import { runCommand } from './output.js';
|
|
10
13
|
const program = new Command();
|
|
11
14
|
const store = new StateStore();
|
|
12
15
|
program
|
|
13
16
|
.name('sandman')
|
|
14
17
|
.description('Provision disposable cloud environments in seconds')
|
|
15
|
-
.version('0.
|
|
18
|
+
.version('0.2.0');
|
|
16
19
|
program
|
|
17
20
|
.command('init')
|
|
18
21
|
.description('Initialize a cloud provider')
|
|
19
|
-
.argument('<provider>', 'Provider to initialize: aws | gcp')
|
|
22
|
+
.argument('<provider>', 'Provider to initialize: aws | gcp | cloudflare | vercel')
|
|
20
23
|
.option('-r, --region <region>', 'Default region')
|
|
24
|
+
.option('--billing-account <id>', 'GCP billing account ID')
|
|
21
25
|
.option('--json', 'Output as JSON')
|
|
22
26
|
.action(async (provider, options) => {
|
|
23
|
-
await initProvider(provider, options.region, store, {
|
|
27
|
+
await runCommand(options.json, () => initProvider(provider, options.region, store, {
|
|
28
|
+
json: options.json,
|
|
29
|
+
billingAccount: options.billingAccount,
|
|
30
|
+
}));
|
|
24
31
|
});
|
|
25
32
|
program
|
|
26
33
|
.command('create')
|
|
27
34
|
.description('Create a sandbox environment')
|
|
28
|
-
.argument('<name>', 'Environment name')
|
|
29
|
-
.option('-p, --provider <provider>', 'Cloud provider: aws | gcp')
|
|
35
|
+
.argument('<name>', 'Environment name (lowercase letters, digits, hyphens)')
|
|
36
|
+
.option('-p, --provider <provider>', 'Cloud provider: aws | gcp | cloudflare | vercel')
|
|
30
37
|
.option('-r, --region <region>', 'Region')
|
|
38
|
+
.option('--billing-account <id>', 'GCP billing account ID')
|
|
39
|
+
.option('--ttl <duration>', 'Auto-destroy after a duration such as 30m, 2h, or 1d')
|
|
40
|
+
.option('--strict', 'Exit non-zero when create finishes as failed/partial')
|
|
31
41
|
.option('--dry-run', 'Preview actions without executing')
|
|
32
42
|
.option('--json', 'Output as JSON')
|
|
33
43
|
.action(async (name, options) => {
|
|
34
|
-
await
|
|
44
|
+
await runCommand(options.json, () => createEnvironment(name, {
|
|
45
|
+
provider: options.provider,
|
|
46
|
+
region: options.region,
|
|
47
|
+
billingAccount: options.billingAccount,
|
|
48
|
+
ttl: options.ttl,
|
|
49
|
+
}, store, { dryRun: options.dryRun, json: options.json, strict: options.strict }));
|
|
35
50
|
});
|
|
36
51
|
program
|
|
37
52
|
.command('enable')
|
|
@@ -40,14 +55,14 @@ program
|
|
|
40
55
|
.option('-e, --environment <name>', 'Environment name')
|
|
41
56
|
.option('--json', 'Output as JSON')
|
|
42
57
|
.action(async (services, options) => {
|
|
43
|
-
await enableServices(services, options.environment, store, { json: options.json });
|
|
58
|
+
await runCommand(options.json, () => enableServices(services, options.environment, store, { json: options.json }));
|
|
44
59
|
});
|
|
45
60
|
program
|
|
46
61
|
.command('list')
|
|
47
62
|
.description('List all environments')
|
|
48
63
|
.option('--json', 'Output as JSON')
|
|
49
64
|
.action(async (options) => {
|
|
50
|
-
await listEnvironments(store, options);
|
|
65
|
+
await runCommand(options.json, () => listEnvironments(store, options));
|
|
51
66
|
});
|
|
52
67
|
program
|
|
53
68
|
.command('status')
|
|
@@ -55,15 +70,16 @@ program
|
|
|
55
70
|
.argument('<name>', 'Environment name')
|
|
56
71
|
.option('--json', 'Output as JSON')
|
|
57
72
|
.action(async (name, options) => {
|
|
58
|
-
await statusEnvironment(name, store, options);
|
|
73
|
+
await runCommand(options.json, () => statusEnvironment(name, store, options));
|
|
59
74
|
});
|
|
60
75
|
program
|
|
61
76
|
.command('connect')
|
|
62
77
|
.description('Connect to an environment and output credentials')
|
|
63
78
|
.argument('<name>', 'Environment name')
|
|
64
79
|
.option('--json', 'Output as JSON')
|
|
80
|
+
.option('--show-secrets', 'Include secret values (tokens) in output')
|
|
65
81
|
.action(async (name, options) => {
|
|
66
|
-
await connectEnvironment(name, store, options);
|
|
82
|
+
await runCommand(options.json, () => connectEnvironment(name, store, options));
|
|
67
83
|
});
|
|
68
84
|
program
|
|
69
85
|
.command('destroy')
|
|
@@ -72,7 +88,23 @@ program
|
|
|
72
88
|
.option('-y, --yes', 'Skip confirmation')
|
|
73
89
|
.option('--json', 'Output as JSON')
|
|
74
90
|
.action(async (name, options) => {
|
|
75
|
-
await destroyEnvironment(name, store, { confirmed: options.yes ?? false, json: options.json });
|
|
91
|
+
await runCommand(options.json, () => destroyEnvironment(name, store, { confirmed: options.yes ?? false, json: options.json }));
|
|
92
|
+
});
|
|
93
|
+
program
|
|
94
|
+
.command('providers')
|
|
95
|
+
.description('List supported providers and their maturity')
|
|
96
|
+
.option('--json', 'Output as JSON')
|
|
97
|
+
.action(async (options) => {
|
|
98
|
+
await runCommand(options.json, () => listProviders(options));
|
|
99
|
+
});
|
|
100
|
+
program
|
|
101
|
+
.command('doctor')
|
|
102
|
+
.alias('whoami')
|
|
103
|
+
.description('Show auth, init, identity, lock, and expired environments')
|
|
104
|
+
.option('--reap', 'Destroy environments whose TTL has expired')
|
|
105
|
+
.option('--json', 'Output as JSON')
|
|
106
|
+
.action(async (options) => {
|
|
107
|
+
await runCommand(options.json, () => doctor(store, options));
|
|
76
108
|
});
|
|
77
109
|
export { program, store };
|
|
78
110
|
//# sourceMappingURL=index.js.map
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type ResultCode = "OK" | "NOT_FOUND" | "ALREADY_EXISTS" | "NO_PROVIDER" | "INVALID_PROVIDER" | "INVALID_NAME" | "INVALID_SERVICE" | "AMBIGUOUS" | "AUTH_REQUIRED" | "CONFIRMATION_REQUIRED" | "PROVIDER_ERROR" | "STATE_CORRUPT" | "STATE_LOCKED" | "PARTIAL" | "EXPIRED" | "INVALID_TTL" | "INTERNAL";
|
|
2
|
+
export interface OkPayload extends Record<string, unknown> {
|
|
3
|
+
success: true;
|
|
4
|
+
code: "OK";
|
|
5
|
+
}
|
|
6
|
+
export interface ErrPayload extends Record<string, unknown> {
|
|
7
|
+
success: false;
|
|
8
|
+
code: ResultCode;
|
|
9
|
+
error: string;
|
|
10
|
+
hint?: string;
|
|
11
|
+
next?: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function okPayload(data?: Record<string, unknown>): OkPayload;
|
|
14
|
+
export declare function errPayload(code: ResultCode, error: string, extra?: Record<string, unknown>): ErrPayload;
|
|
15
|
+
export declare function emitOk(json: boolean | undefined, data: Record<string, unknown>, human: () => void): void;
|
|
16
|
+
export declare function emitErr(json: boolean | undefined, payload: {
|
|
17
|
+
code: ResultCode;
|
|
18
|
+
error: string;
|
|
19
|
+
hint?: string;
|
|
20
|
+
next?: string[];
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}, human?: () => void): never;
|
|
23
|
+
export declare function mapThrownError(error: unknown): {
|
|
24
|
+
code: ResultCode;
|
|
25
|
+
error: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function runCommand(json: boolean | undefined, fn: () => Promise<void>): Promise<void>;
|
|
28
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAClB,IAAI,GACJ,WAAW,GACX,gBAAgB,GAChB,aAAa,GACb,kBAAkB,GAClB,cAAc,GACd,iBAAiB,GACjB,WAAW,GACX,eAAe,GACf,uBAAuB,GACvB,gBAAgB,GAChB,eAAe,GACf,cAAc,GACd,SAAS,GACT,SAAS,GACT,aAAa,GACb,UAAU,CAAC;AAEf,MAAM,WAAW,SAAU,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxD,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,SAAS,CAEvE;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAClC,UAAU,CAEZ;AAED,wBAAgB,MAAM,CACpB,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,IAAI,GAChB,IAAI,CAMN;AAED,wBAAgB,OAAO,CACrB,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,OAAO,EAAE;IACP,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,EACD,KAAK,CAAC,EAAE,MAAM,IAAI,GACjB,KAAK,CAqBP;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,CAeA;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GACtB,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export function okPayload(data = {}) {
|
|
3
|
+
return { success: true, code: "OK", ...data };
|
|
4
|
+
}
|
|
5
|
+
export function errPayload(code, error, extra = {}) {
|
|
6
|
+
return { success: false, code, error, ...extra };
|
|
7
|
+
}
|
|
8
|
+
export function emitOk(json, data, human) {
|
|
9
|
+
if (json) {
|
|
10
|
+
console.log(JSON.stringify(okPayload(data)));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
human();
|
|
14
|
+
}
|
|
15
|
+
export function emitErr(json, payload, human) {
|
|
16
|
+
const { code, error, hint, next, ...rest } = payload;
|
|
17
|
+
if (json) {
|
|
18
|
+
const extra = { ...rest };
|
|
19
|
+
if (hint)
|
|
20
|
+
extra.hint = hint;
|
|
21
|
+
if (next)
|
|
22
|
+
extra.next = next;
|
|
23
|
+
console.log(JSON.stringify(errPayload(code, error, extra)));
|
|
24
|
+
}
|
|
25
|
+
else if (human) {
|
|
26
|
+
human();
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.error(chalk.red(`Error: ${error}`));
|
|
30
|
+
if (hint) {
|
|
31
|
+
console.error(chalk.gray(hint));
|
|
32
|
+
}
|
|
33
|
+
if (next) {
|
|
34
|
+
for (const step of next) {
|
|
35
|
+
console.error(chalk.cyan(`→ ${step}`));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
export function mapThrownError(error) {
|
|
42
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43
|
+
const maybeCode = error?.code;
|
|
44
|
+
if (maybeCode === "STATE_CORRUPT" || maybeCode === "STATE_LOCKED") {
|
|
45
|
+
return { code: maybeCode, error: message };
|
|
46
|
+
}
|
|
47
|
+
if (/credentials required|authentication required|unauthenticated|authentication failed|api[_-]?token|VERCEL_TOKEN|environment variable is required/i.test(message)) {
|
|
48
|
+
return { code: "AUTH_REQUIRED", error: message };
|
|
49
|
+
}
|
|
50
|
+
return { code: "PROVIDER_ERROR", error: message };
|
|
51
|
+
}
|
|
52
|
+
export async function runCommand(json, fn) {
|
|
53
|
+
try {
|
|
54
|
+
await fn();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
emitErr(json, mapThrownError(error));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.test.d.ts","sourceRoot":"","sources":["../../src/cli/output.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { emitErr, emitOk, errPayload, mapThrownError, okPayload, } from "./output.js";
|
|
3
|
+
import { StateError } from "../core/state-store.js";
|
|
4
|
+
describe("CLI output contract", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.restoreAllMocks();
|
|
7
|
+
});
|
|
8
|
+
it("okPayload is additive and parseable", () => {
|
|
9
|
+
const payload = okPayload({ name: "demo", provider: "aws" });
|
|
10
|
+
expect(payload).toEqual({
|
|
11
|
+
success: true,
|
|
12
|
+
code: "OK",
|
|
13
|
+
name: "demo",
|
|
14
|
+
provider: "aws",
|
|
15
|
+
});
|
|
16
|
+
expect(() => JSON.parse(JSON.stringify(payload))).not.toThrow();
|
|
17
|
+
});
|
|
18
|
+
it("errPayload keeps the error key agents already parse", () => {
|
|
19
|
+
const payload = errPayload("NOT_FOUND", 'Environment "demo" not found.', {
|
|
20
|
+
hint: "Run sandman list",
|
|
21
|
+
next: ["sandman list --json"],
|
|
22
|
+
});
|
|
23
|
+
expect(payload.success).toBe(false);
|
|
24
|
+
expect(payload.code).toBe("NOT_FOUND");
|
|
25
|
+
expect(payload.error).toContain("demo");
|
|
26
|
+
expect(payload.next).toEqual(["sandman list --json"]);
|
|
27
|
+
});
|
|
28
|
+
it("emitOk writes a single JSON line to stdout", () => {
|
|
29
|
+
const log = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
30
|
+
emitOk(true, { name: "demo" }, () => {
|
|
31
|
+
throw new Error("human renderer should not run");
|
|
32
|
+
});
|
|
33
|
+
expect(log).toHaveBeenCalledTimes(1);
|
|
34
|
+
const parsed = JSON.parse(String(log.mock.calls[0][0]));
|
|
35
|
+
expect(parsed).toEqual({ success: true, code: "OK", name: "demo" });
|
|
36
|
+
});
|
|
37
|
+
it("emitErr writes JSON then exits", () => {
|
|
38
|
+
const log = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
39
|
+
const exit = vi.spyOn(process, "exit").mockImplementation(((code) => {
|
|
40
|
+
throw new Error(`EXIT:${code}`);
|
|
41
|
+
}));
|
|
42
|
+
expect(() => emitErr(true, {
|
|
43
|
+
code: "CONFIRMATION_REQUIRED",
|
|
44
|
+
error: "pass -y",
|
|
45
|
+
next: ["sandman destroy demo -y --json"],
|
|
46
|
+
})).toThrow(/EXIT:1/);
|
|
47
|
+
const parsed = JSON.parse(String(log.mock.calls[0][0]));
|
|
48
|
+
expect(parsed.success).toBe(false);
|
|
49
|
+
expect(parsed.code).toBe("CONFIRMATION_REQUIRED");
|
|
50
|
+
expect(parsed.next[0]).toContain("-y");
|
|
51
|
+
expect(exit).toHaveBeenCalledWith(1);
|
|
52
|
+
});
|
|
53
|
+
it("maps StateError to STATE_CORRUPT", () => {
|
|
54
|
+
expect(mapThrownError(new StateError("broken"))).toEqual({
|
|
55
|
+
code: "STATE_CORRUPT",
|
|
56
|
+
error: "broken",
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
it("maps auth failures to AUTH_REQUIRED", () => {
|
|
60
|
+
expect(mapThrownError(new Error("AWS credentials required. Configure with aws configure")).code).toBe("AUTH_REQUIRED");
|
|
61
|
+
});
|
|
62
|
+
it("maps Cloudflare and Vercel token errors to AUTH_REQUIRED", () => {
|
|
63
|
+
expect(mapThrownError(new Error("CLOUDFLARE_API_TOKEN environment variable is required")).code).toBe("AUTH_REQUIRED");
|
|
64
|
+
expect(mapThrownError(new Error("authentication failed")).code).toBe("AUTH_REQUIRED");
|
|
65
|
+
expect(mapThrownError(new Error("VERCEL_TOKEN is missing")).code).toBe("AUTH_REQUIRED");
|
|
66
|
+
expect(mapThrownError(new Error("api-token expired")).code).toBe("AUTH_REQUIRED");
|
|
67
|
+
});
|
|
68
|
+
it("does not treat a bare required as AUTH_REQUIRED", () => {
|
|
69
|
+
expect(mapThrownError(new Error("bucket name is required")).code).toBe("PROVIDER_ERROR");
|
|
70
|
+
});
|
|
71
|
+
it("maps STATE_LOCKED from StateError", () => {
|
|
72
|
+
expect(mapThrownError(new StateError("locked", "STATE_LOCKED"))).toEqual({
|
|
73
|
+
code: "STATE_LOCKED",
|
|
74
|
+
error: "locked",
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=output.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.test.js","sourceRoot":"","sources":["../../src/cli/output.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StateStore } from "../core/state-store.js";
|
|
2
|
+
import { EnvironmentRecord } from "../types/index.js";
|
|
3
|
+
export interface ReapResult {
|
|
4
|
+
name: string;
|
|
5
|
+
reaped: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function destroyAndForget(store: StateStore, env: EnvironmentRecord): Promise<void>;
|
|
9
|
+
export declare function reapIfExpired(store: StateStore, env: EnvironmentRecord): Promise<ReapResult>;
|
|
10
|
+
export declare function reapExpiredEnvironments(store: StateStore): Promise<ReapResult[]>;
|
|
11
|
+
//# sourceMappingURL=reap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reap.d.ts","sourceRoot":"","sources":["../../src/cli/reap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC,CAUvB"}
|