@dura-run/cli 0.1.5 → 0.2.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/LICENSE +201 -21
- package/README.md +36 -6
- package/dist/dura.js +428 -253
- package/package.json +7 -10
- package/skills/dura-debug.md +360 -0
- package/skills/dura-deploy.md +272 -0
- package/skills/dura-develop.md +379 -0
- package/skills/dura-overview.md +196 -0
package/dist/dura.js
CHANGED
|
@@ -2425,7 +2425,7 @@ async function openBrowser(url) {
|
|
|
2425
2425
|
child.unref();
|
|
2426
2426
|
}
|
|
2427
2427
|
function registerLoginCommand(program2) {
|
|
2428
|
-
program2.command("login").description("Authenticate with the
|
|
2428
|
+
program2.command("login").description("Authenticate with the dura control plane").option("--api-key <key>", "Authenticate using an API key").option("--api-url <url>", "Set the API base URL").action(async (opts, cmd) => {
|
|
2429
2429
|
const output = getOutput(cmd);
|
|
2430
2430
|
if (opts.apiKey) {
|
|
2431
2431
|
const updates = { apiKey: opts.apiKey };
|
|
@@ -2677,7 +2677,7 @@ __export(exports_logout, {
|
|
|
2677
2677
|
registerLogoutCommand: () => registerLogoutCommand
|
|
2678
2678
|
});
|
|
2679
2679
|
function registerLogoutCommand(program2) {
|
|
2680
|
-
program2.command("logout").description("Log out from the
|
|
2680
|
+
program2.command("logout").description("Log out from the dura control plane").option("--revoke", "Also revoke the API key on the server").option("--api-url <url>", "API base URL override").action(async (opts, cmd) => {
|
|
2681
2681
|
const output = getOutput(cmd);
|
|
2682
2682
|
const token = getAuthToken();
|
|
2683
2683
|
if (!token) {
|
|
@@ -2708,20 +2708,20 @@ var init_logout = __esm(() => {
|
|
|
2708
2708
|
});
|
|
2709
2709
|
|
|
2710
2710
|
// src/templates/dura.json.ts
|
|
2711
|
-
function duraJsonTemplate(name) {
|
|
2711
|
+
function duraJsonTemplate(name, kind = "get") {
|
|
2712
|
+
const trigger = kind === "cron" ? { type: "cron", schedule: "0 * * * *" } : {
|
|
2713
|
+
type: "http",
|
|
2714
|
+
method: kind.toUpperCase(),
|
|
2715
|
+
path: "/hello",
|
|
2716
|
+
public: false
|
|
2717
|
+
};
|
|
2712
2718
|
const manifest = {
|
|
2713
2719
|
name,
|
|
2714
2720
|
automations: [
|
|
2715
2721
|
{
|
|
2716
2722
|
name: "hello",
|
|
2717
2723
|
entrypoint: "routes/hello.ts",
|
|
2718
|
-
triggers: [
|
|
2719
|
-
{
|
|
2720
|
-
type: "http",
|
|
2721
|
-
method: "GET",
|
|
2722
|
-
path: "/hello"
|
|
2723
|
-
}
|
|
2724
|
-
]
|
|
2724
|
+
triggers: [trigger]
|
|
2725
2725
|
}
|
|
2726
2726
|
]
|
|
2727
2727
|
};
|
|
@@ -2730,7 +2730,18 @@ function duraJsonTemplate(name) {
|
|
|
2730
2730
|
}
|
|
2731
2731
|
|
|
2732
2732
|
// src/templates/hello.ts
|
|
2733
|
-
|
|
2733
|
+
function helloTemplate(kind) {
|
|
2734
|
+
switch (kind) {
|
|
2735
|
+
case "post":
|
|
2736
|
+
return POST_TEMPLATE;
|
|
2737
|
+
case "cron":
|
|
2738
|
+
return CRON_TEMPLATE;
|
|
2739
|
+
case "get":
|
|
2740
|
+
default:
|
|
2741
|
+
return GET_TEMPLATE;
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
var GET_TEMPLATE = `import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
2734
2745
|
|
|
2735
2746
|
export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
2736
2747
|
return {
|
|
@@ -2739,7 +2750,30 @@ export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
|
2739
2750
|
body: { message: "Hello from Dura!" },
|
|
2740
2751
|
};
|
|
2741
2752
|
}
|
|
2742
|
-
|
|
2753
|
+
`, POST_TEMPLATE = `import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
2754
|
+
|
|
2755
|
+
export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
2756
|
+
const { name = "world" } = (req.body ?? {}) as { name?: string };
|
|
2757
|
+
return {
|
|
2758
|
+
status: 200,
|
|
2759
|
+
headers: { "content-type": "application/json" },
|
|
2760
|
+
body: { greeting: \`hello, \${name}\` },
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
`, CRON_TEMPLATE = `import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
2764
|
+
|
|
2765
|
+
export default async function handler(_req: DuraRequest): Promise<DuraResponse> {
|
|
2766
|
+
// Runs on the cron schedule defined in dura.json. Add your job here.
|
|
2767
|
+
return {
|
|
2768
|
+
status: 200,
|
|
2769
|
+
headers: { "content-type": "application/json" },
|
|
2770
|
+
body: { ranAt: new Date().toISOString() },
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
`, HELLO_TEMPLATE;
|
|
2774
|
+
var init_hello = __esm(() => {
|
|
2775
|
+
HELLO_TEMPLATE = GET_TEMPLATE;
|
|
2776
|
+
});
|
|
2743
2777
|
|
|
2744
2778
|
// src/commands/new.ts
|
|
2745
2779
|
var exports_new = {};
|
|
@@ -2749,12 +2783,17 @@ __export(exports_new, {
|
|
|
2749
2783
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2750
2784
|
import { join as join2, resolve } from "node:path";
|
|
2751
2785
|
function registerNewCommand(program2) {
|
|
2752
|
-
program2.command("new <name>").description("Scaffold a new
|
|
2786
|
+
program2.command("new <name>").description("Scaffold a new dura project").option("--dir <path>", "Parent directory (defaults to current dir)").option("--org <id>", "Organization ID for API registration").option("--template <slug>", "Fork a template as the starting point").option("--trigger <kind>", "Default trigger kind for the scaffold: get | post | cron", "get").action(async (name, opts, cmd) => {
|
|
2753
2787
|
const output = getOutput(cmd);
|
|
2754
2788
|
if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
|
|
2755
2789
|
output.error("INVALID_NAME", "Project name must start with a letter and contain only letters, numbers, hyphens, and underscores");
|
|
2756
2790
|
return;
|
|
2757
2791
|
}
|
|
2792
|
+
const triggerKind = (opts.trigger ?? "get").toLowerCase();
|
|
2793
|
+
if (!["get", "post", "cron"].includes(triggerKind)) {
|
|
2794
|
+
output.error("INVALID_TRIGGER", `Unknown trigger kind: ${opts.trigger}`, "Use one of: get, post, cron");
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2758
2797
|
const parentDir = opts.dir ? resolve(opts.dir) : process.cwd();
|
|
2759
2798
|
const projectDir = join2(parentDir, name);
|
|
2760
2799
|
if (existsSync2(projectDir)) {
|
|
@@ -2802,8 +2841,8 @@ Set required secrets: ${result.requiredSecrets.join(", ")}`);
|
|
|
2802
2841
|
}
|
|
2803
2842
|
mkdirSync2(join2(projectDir, "routes"), { recursive: true });
|
|
2804
2843
|
mkdirSync2(join2(projectDir, "jobs"), { recursive: true });
|
|
2805
|
-
writeFileSync2(join2(projectDir, "dura.json"), duraJsonTemplate(name));
|
|
2806
|
-
writeFileSync2(join2(projectDir, "routes", "hello.ts"),
|
|
2844
|
+
writeFileSync2(join2(projectDir, "dura.json"), duraJsonTemplate(name, triggerKind));
|
|
2845
|
+
writeFileSync2(join2(projectDir, "routes", "hello.ts"), helloTemplate(triggerKind));
|
|
2807
2846
|
writeFileSync2(join2(projectDir, ".gitignore"), GITIGNORE_CONTENT);
|
|
2808
2847
|
const token = getAuthToken();
|
|
2809
2848
|
if (token) {
|
|
@@ -2830,7 +2869,7 @@ Set required secrets: ${result.requiredSecrets.join(", ")}`);
|
|
|
2830
2869
|
output.info("No org specified. Run dura login and set a default org to register projects.");
|
|
2831
2870
|
}
|
|
2832
2871
|
} else {
|
|
2833
|
-
output.info("Run dura login to register your project with the
|
|
2872
|
+
output.info("Run dura login to register your project with the dura control plane.");
|
|
2834
2873
|
}
|
|
2835
2874
|
output.success({
|
|
2836
2875
|
created: true,
|
|
@@ -2847,36 +2886,61 @@ dist
|
|
|
2847
2886
|
`;
|
|
2848
2887
|
var init_new = __esm(() => {
|
|
2849
2888
|
init_src3();
|
|
2889
|
+
init_hello();
|
|
2850
2890
|
init_api_client();
|
|
2851
2891
|
init_config_store();
|
|
2852
2892
|
});
|
|
2853
2893
|
|
|
2894
|
+
// src/lib/project-id.ts
|
|
2895
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
2896
|
+
import { join as join3 } from "node:path";
|
|
2897
|
+
function resolveProjectId(opts = {}) {
|
|
2898
|
+
if (opts.project && opts.project.length > 0)
|
|
2899
|
+
return opts.project;
|
|
2900
|
+
const dir = opts.dir ?? process.cwd();
|
|
2901
|
+
const manifestPath = join3(dir, "dura.json");
|
|
2902
|
+
if (!existsSync3(manifestPath))
|
|
2903
|
+
return null;
|
|
2904
|
+
try {
|
|
2905
|
+
const raw = JSON.parse(readFileSync3(manifestPath, "utf-8"));
|
|
2906
|
+
return raw.projectId ?? null;
|
|
2907
|
+
} catch {
|
|
2908
|
+
return null;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
var init_project_id = () => {};
|
|
2912
|
+
|
|
2854
2913
|
// src/commands/secrets.ts
|
|
2855
2914
|
var exports_secrets = {};
|
|
2856
2915
|
__export(exports_secrets, {
|
|
2857
2916
|
registerSecretsCommand: () => registerSecretsCommand
|
|
2858
2917
|
});
|
|
2859
2918
|
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
2860
|
-
import { join as
|
|
2919
|
+
import { join as join4 } from "node:path";
|
|
2861
2920
|
function resolveAuth(opts, output) {
|
|
2921
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
2922
|
+
if (!projectId) {
|
|
2923
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
2924
|
+
return null;
|
|
2925
|
+
}
|
|
2862
2926
|
const token = opts.token || getAuthToken();
|
|
2863
2927
|
if (!token) {
|
|
2864
2928
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
2865
2929
|
return null;
|
|
2866
2930
|
}
|
|
2867
2931
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
2868
|
-
return { client: new ApiClient(apiUrl, token) };
|
|
2932
|
+
return { client: new ApiClient(apiUrl, token), projectId };
|
|
2869
2933
|
}
|
|
2870
2934
|
function registerSecretsCommand(program2) {
|
|
2871
2935
|
const secrets = program2.command("secrets").description("Manage project secrets");
|
|
2872
|
-
secrets.command("set <name> <value>").description("Set a secret value").
|
|
2936
|
+
secrets.command("set <name> <value>").description("Set a secret value").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (name, value, opts, cmd) => {
|
|
2873
2937
|
const output = getOutput(cmd);
|
|
2874
2938
|
const auth = resolveAuth(opts, output);
|
|
2875
2939
|
if (!auth)
|
|
2876
2940
|
return;
|
|
2877
|
-
const { client } = auth;
|
|
2941
|
+
const { client, projectId } = auth;
|
|
2878
2942
|
try {
|
|
2879
|
-
const result = await client.put(`/api/v1/projects/${
|
|
2943
|
+
const result = await client.put(`/api/v1/projects/${projectId}/secrets`, { name, value });
|
|
2880
2944
|
output.success(result);
|
|
2881
2945
|
} catch (err) {
|
|
2882
2946
|
if (err instanceof Error) {
|
|
@@ -2884,14 +2948,14 @@ function registerSecretsCommand(program2) {
|
|
|
2884
2948
|
}
|
|
2885
2949
|
}
|
|
2886
2950
|
});
|
|
2887
|
-
secrets.command("list").description("List secret names (values never shown)").
|
|
2951
|
+
secrets.command("list").description("List secret names (values never shown)").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
2888
2952
|
const output = getOutput(cmd);
|
|
2889
2953
|
const auth = resolveAuth(opts, output);
|
|
2890
2954
|
if (!auth)
|
|
2891
2955
|
return;
|
|
2892
|
-
const { client } = auth;
|
|
2956
|
+
const { client, projectId } = auth;
|
|
2893
2957
|
try {
|
|
2894
|
-
const result = await client.get(`/api/v1/projects/${
|
|
2958
|
+
const result = await client.get(`/api/v1/projects/${projectId}/secrets`);
|
|
2895
2959
|
output.table(["Name", "Created", "Updated"], result.map((s) => [s.name, s.createdAt, s.updatedAt]));
|
|
2896
2960
|
} catch (err) {
|
|
2897
2961
|
if (err instanceof Error) {
|
|
@@ -2899,7 +2963,7 @@ function registerSecretsCommand(program2) {
|
|
|
2899
2963
|
}
|
|
2900
2964
|
}
|
|
2901
2965
|
});
|
|
2902
|
-
secrets.command("remove <name>").description("Remove a secret").
|
|
2966
|
+
secrets.command("remove <name>").description("Remove a secret").option("--project <id>", "Project ID (defaults to dura.json)").option("--confirm", "Confirm this destructive operation").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (name, opts, cmd) => {
|
|
2903
2967
|
const output = getOutput(cmd);
|
|
2904
2968
|
if (!opts.confirm) {
|
|
2905
2969
|
output.error("CONFIRM_REQUIRED", "This is a destructive operation. Re-run with --confirm to proceed.");
|
|
@@ -2908,9 +2972,9 @@ function registerSecretsCommand(program2) {
|
|
|
2908
2972
|
const auth = resolveAuth(opts, output);
|
|
2909
2973
|
if (!auth)
|
|
2910
2974
|
return;
|
|
2911
|
-
const { client } = auth;
|
|
2975
|
+
const { client, projectId } = auth;
|
|
2912
2976
|
try {
|
|
2913
|
-
await client.delete(`/api/v1/projects/${
|
|
2977
|
+
await client.delete(`/api/v1/projects/${projectId}/secrets/${name}`);
|
|
2914
2978
|
output.success({ deleted: true, name });
|
|
2915
2979
|
} catch (err) {
|
|
2916
2980
|
if (err instanceof Error) {
|
|
@@ -2918,17 +2982,16 @@ function registerSecretsCommand(program2) {
|
|
|
2918
2982
|
}
|
|
2919
2983
|
}
|
|
2920
2984
|
});
|
|
2921
|
-
secrets.command("pull").description("Write secrets to .env.local for local development").
|
|
2985
|
+
secrets.command("pull").description("Write secrets to .env.local for local development").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").option("--dir <path>", "Directory to write .env.local to", ".").action(async (opts, cmd) => {
|
|
2922
2986
|
const output = getOutput(cmd);
|
|
2923
2987
|
const auth = resolveAuth(opts, output);
|
|
2924
2988
|
if (!auth)
|
|
2925
2989
|
return;
|
|
2926
|
-
const { client } = auth;
|
|
2990
|
+
const { client, projectId } = auth;
|
|
2927
2991
|
try {
|
|
2928
|
-
await client.get(`/api/v1/projects/${
|
|
2929
|
-
const values = await client.get(`/api/v1/projects/${opts.project}/secrets/pull`);
|
|
2992
|
+
const values = await client.get(`/api/v1/projects/${projectId}/secrets/pull`);
|
|
2930
2993
|
const lines = Object.entries(values).map(([k, v]) => `${k}="${v.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n")}"`);
|
|
2931
|
-
const envPath =
|
|
2994
|
+
const envPath = join4(opts.dir, ".env.local");
|
|
2932
2995
|
writeFileSync3(envPath, lines.join(`
|
|
2933
2996
|
`) + `
|
|
2934
2997
|
`, { mode: 384 });
|
|
@@ -2947,6 +3010,7 @@ var init_secrets = __esm(() => {
|
|
|
2947
3010
|
init_src3();
|
|
2948
3011
|
init_api_client();
|
|
2949
3012
|
init_config_store();
|
|
3013
|
+
init_project_id();
|
|
2950
3014
|
});
|
|
2951
3015
|
|
|
2952
3016
|
// ../shared/src/constants/defaults.ts
|
|
@@ -7010,7 +7074,8 @@ var init_manifest = __esm(() => {
|
|
|
7010
7074
|
exports_external.object({
|
|
7011
7075
|
type: exports_external.literal("http"),
|
|
7012
7076
|
method: exports_external.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional(),
|
|
7013
|
-
path: exports_external.string().optional()
|
|
7077
|
+
path: exports_external.string().optional(),
|
|
7078
|
+
public: exports_external.boolean().optional()
|
|
7014
7079
|
}),
|
|
7015
7080
|
exports_external.object({
|
|
7016
7081
|
type: exports_external.literal("cron"),
|
|
@@ -10021,7 +10086,7 @@ var init_sql = __esm(() => {
|
|
|
10021
10086
|
return new SQL([new StringChunk(str)]);
|
|
10022
10087
|
}
|
|
10023
10088
|
sql2.raw = raw;
|
|
10024
|
-
function
|
|
10089
|
+
function join5(chunks, separator) {
|
|
10025
10090
|
const result = [];
|
|
10026
10091
|
for (const [i, chunk] of chunks.entries()) {
|
|
10027
10092
|
if (i > 0 && separator !== undefined) {
|
|
@@ -10031,7 +10096,7 @@ var init_sql = __esm(() => {
|
|
|
10031
10096
|
}
|
|
10032
10097
|
return new SQL(result);
|
|
10033
10098
|
}
|
|
10034
|
-
sql2.join =
|
|
10099
|
+
sql2.join = join5;
|
|
10035
10100
|
function identifier(value) {
|
|
10036
10101
|
return new Name(value);
|
|
10037
10102
|
}
|
|
@@ -11921,7 +11986,7 @@ var init_select2 = __esm(() => {
|
|
|
11921
11986
|
return (table, on) => {
|
|
11922
11987
|
const baseTableName = this.tableName;
|
|
11923
11988
|
const tableName = getTableLikeName(table);
|
|
11924
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
11989
|
+
if (typeof tableName === "string" && this.config.joins?.some((join5) => join5.alias === tableName)) {
|
|
11925
11990
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
11926
11991
|
}
|
|
11927
11992
|
if (!this.isPartialSelect) {
|
|
@@ -12161,7 +12226,7 @@ var init_update = __esm(() => {
|
|
|
12161
12226
|
createJoin(joinType) {
|
|
12162
12227
|
return (table, on) => {
|
|
12163
12228
|
const tableName = getTableLikeName(table);
|
|
12164
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
12229
|
+
if (typeof tableName === "string" && this.config.joins.some((join5) => join5.alias === tableName)) {
|
|
12165
12230
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
12166
12231
|
}
|
|
12167
12232
|
if (typeof on === "function") {
|
|
@@ -12211,10 +12276,10 @@ var init_update = __esm(() => {
|
|
|
12211
12276
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
12212
12277
|
fields[tableName] = fromFields;
|
|
12213
12278
|
}
|
|
12214
|
-
for (const
|
|
12215
|
-
const tableName2 = getTableLikeName(
|
|
12216
|
-
if (typeof tableName2 === "string" && !is(
|
|
12217
|
-
const fromFields = this.getTableLikeFields(
|
|
12279
|
+
for (const join5 of this.config.joins) {
|
|
12280
|
+
const tableName2 = getTableLikeName(join5.table);
|
|
12281
|
+
if (typeof tableName2 === "string" && !is(join5.table, SQL)) {
|
|
12282
|
+
const fromFields = this.getTableLikeFields(join5.table);
|
|
12218
12283
|
fields[tableName2] = fromFields;
|
|
12219
12284
|
}
|
|
12220
12285
|
}
|
|
@@ -13588,16 +13653,16 @@ var init_src2 = __esm(() => {
|
|
|
13588
13653
|
});
|
|
13589
13654
|
|
|
13590
13655
|
// src/lib/manifest.ts
|
|
13591
|
-
import { existsSync as
|
|
13592
|
-
import { join as
|
|
13656
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
13657
|
+
import { join as join5 } from "node:path";
|
|
13593
13658
|
function readManifest(dir) {
|
|
13594
|
-
const path =
|
|
13595
|
-
if (!
|
|
13659
|
+
const path = join5(dir, "dura.json");
|
|
13660
|
+
if (!existsSync4(path)) {
|
|
13596
13661
|
throw new ManifestError(`No dura.json found in ${dir}`);
|
|
13597
13662
|
}
|
|
13598
13663
|
let raw;
|
|
13599
13664
|
try {
|
|
13600
|
-
raw = JSON.parse(
|
|
13665
|
+
raw = JSON.parse(readFileSync4(path, "utf-8"));
|
|
13601
13666
|
} catch {
|
|
13602
13667
|
throw new ManifestError("dura.json is not valid JSON");
|
|
13603
13668
|
}
|
|
@@ -13608,14 +13673,14 @@ function readManifest(dir) {
|
|
|
13608
13673
|
return result.manifest;
|
|
13609
13674
|
}
|
|
13610
13675
|
function readRawManifest(dir) {
|
|
13611
|
-
const path =
|
|
13612
|
-
if (!
|
|
13676
|
+
const path = join5(dir, "dura.json");
|
|
13677
|
+
if (!existsSync4(path)) {
|
|
13613
13678
|
throw new ManifestError(`No dura.json found in ${dir}`);
|
|
13614
13679
|
}
|
|
13615
|
-
return JSON.parse(
|
|
13680
|
+
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
13616
13681
|
}
|
|
13617
13682
|
function writeRawManifest(dir, data) {
|
|
13618
|
-
const path =
|
|
13683
|
+
const path = join5(dir, "dura.json");
|
|
13619
13684
|
writeFileSync4(path, JSON.stringify(data, null, 2) + `
|
|
13620
13685
|
`);
|
|
13621
13686
|
}
|
|
@@ -13800,10 +13865,10 @@ function generateDeploymentManifest(_projectDir, manifest) {
|
|
|
13800
13865
|
var init_manifest_generator = () => {};
|
|
13801
13866
|
|
|
13802
13867
|
// src/lib/bundler.ts
|
|
13803
|
-
import { join as
|
|
13868
|
+
import { join as join6, resolve as resolve2 } from "node:path";
|
|
13804
13869
|
import {
|
|
13805
|
-
existsSync as
|
|
13806
|
-
readFileSync as
|
|
13870
|
+
existsSync as existsSync5,
|
|
13871
|
+
readFileSync as readFileSync5,
|
|
13807
13872
|
writeFileSync as writeFileSync5,
|
|
13808
13873
|
mkdirSync as mkdirSync3,
|
|
13809
13874
|
readdirSync,
|
|
@@ -13811,6 +13876,7 @@ import {
|
|
|
13811
13876
|
} from "node:fs";
|
|
13812
13877
|
import { createGzip } from "node:zlib";
|
|
13813
13878
|
import { Readable } from "node:stream";
|
|
13879
|
+
import * as esbuild from "esbuild";
|
|
13814
13880
|
async function createBundle(projectDir, options) {
|
|
13815
13881
|
const maxSize = options?.maxBundleSize ?? MAX_BUNDLE_SIZE_BYTES;
|
|
13816
13882
|
let duraManifest;
|
|
@@ -13822,25 +13888,29 @@ async function createBundle(projectDir, options) {
|
|
|
13822
13888
|
error: `Failed to read dura.json: ${err instanceof Error ? err.message : String(err)}`
|
|
13823
13889
|
};
|
|
13824
13890
|
}
|
|
13825
|
-
const buildDir =
|
|
13826
|
-
if (
|
|
13891
|
+
const buildDir = join6(projectDir, ".dura", "build");
|
|
13892
|
+
if (existsSync5(buildDir)) {
|
|
13827
13893
|
rmSync(buildDir, { recursive: true });
|
|
13828
13894
|
}
|
|
13829
13895
|
mkdirSync3(buildDir, { recursive: true });
|
|
13830
13896
|
for (const auto of duraManifest.automations) {
|
|
13831
13897
|
const entryPath = resolve2(projectDir, auto.entrypoint);
|
|
13832
|
-
if (!
|
|
13898
|
+
if (!existsSync5(entryPath)) {
|
|
13833
13899
|
rmSync(buildDir, { recursive: true });
|
|
13834
13900
|
return {
|
|
13835
13901
|
success: false,
|
|
13836
13902
|
error: `Entry point not found: ${auto.entrypoint}`
|
|
13837
13903
|
};
|
|
13838
13904
|
}
|
|
13839
|
-
const outFile =
|
|
13905
|
+
const outFile = join6(buildDir, `${auto.name}.js`);
|
|
13840
13906
|
try {
|
|
13841
|
-
const source =
|
|
13842
|
-
const
|
|
13843
|
-
|
|
13907
|
+
const source = readFileSync5(entryPath, "utf-8");
|
|
13908
|
+
const xformed = await esbuild.transform(source, {
|
|
13909
|
+
loader: "ts",
|
|
13910
|
+
target: "es2022",
|
|
13911
|
+
sourcemap: false
|
|
13912
|
+
});
|
|
13913
|
+
writeFileSync5(outFile, xformed.code);
|
|
13844
13914
|
} catch (err) {
|
|
13845
13915
|
rmSync(buildDir, { recursive: true });
|
|
13846
13916
|
return {
|
|
@@ -13850,7 +13920,7 @@ async function createBundle(projectDir, options) {
|
|
|
13850
13920
|
}
|
|
13851
13921
|
}
|
|
13852
13922
|
const deployManifest = generateDeploymentManifest(projectDir, duraManifest);
|
|
13853
|
-
writeFileSync5(
|
|
13923
|
+
writeFileSync5(join6(buildDir, "manifest.json"), JSON.stringify(deployManifest, null, 2));
|
|
13854
13924
|
const archiveBuffer = await createTarGz(buildDir);
|
|
13855
13925
|
if (archiveBuffer.length > maxSize) {
|
|
13856
13926
|
rmSync(buildDir, { recursive: true });
|
|
@@ -13867,11 +13937,6 @@ async function createBundle(projectDir, options) {
|
|
|
13867
13937
|
sizeBytes: archiveBuffer.length
|
|
13868
13938
|
};
|
|
13869
13939
|
}
|
|
13870
|
-
function stripTypeAnnotations(source) {
|
|
13871
|
-
let result = source.replace(/^import\s+type\s+.*$/gm, "");
|
|
13872
|
-
result = result.replace(/:\s*(?:string|number|boolean|void|unknown|any|never|Promise<[^>]*>|Record<[^>]*>|Array<[^>]*>|[A-Z][a-zA-Z]*(?:<[^>]*>)?)\s*(?=[,)\]=;{])/g, "");
|
|
13873
|
-
return result;
|
|
13874
|
-
}
|
|
13875
13940
|
async function createTarGz(dir) {
|
|
13876
13941
|
const files = collectFiles(dir, dir);
|
|
13877
13942
|
const tarBuffer = createTarBuffer(files);
|
|
@@ -13898,12 +13963,12 @@ function collectFiles(dir, base) {
|
|
|
13898
13963
|
const result = [];
|
|
13899
13964
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
13900
13965
|
for (const entry of entries) {
|
|
13901
|
-
const fullPath =
|
|
13966
|
+
const fullPath = join6(dir, entry.name);
|
|
13902
13967
|
if (entry.isFile()) {
|
|
13903
13968
|
const relativePath = fullPath.slice(base.length + 1);
|
|
13904
13969
|
result.push({
|
|
13905
13970
|
name: relativePath,
|
|
13906
|
-
content: new Uint8Array(
|
|
13971
|
+
content: new Uint8Array(readFileSync5(fullPath))
|
|
13907
13972
|
});
|
|
13908
13973
|
} else if (entry.isDirectory()) {
|
|
13909
13974
|
result.push(...collectFiles(fullPath, base));
|
|
@@ -14212,8 +14277,13 @@ function parseFilters(filter) {
|
|
|
14212
14277
|
return params;
|
|
14213
14278
|
}
|
|
14214
14279
|
function registerLogsCommand(program2) {
|
|
14215
|
-
program2.command("logs [executionId]").description("View execution logs").
|
|
14280
|
+
program2.command("logs [executionId]").description("View execution logs").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").option("--limit <n>", "Number of entries", "50").option("--filter <filter>", "Filter expression (e.g., 'level=error automation=my-auto')").option("--last <duration>", "Show logs from last duration (e.g., 1h, 30m, 7d)").option("--order <order>", "Sort order: asc or desc", "desc").option("-f, --follow", "Stream logs in real time (live tail)").action(async (executionId, opts, cmd) => {
|
|
14216
14281
|
const output = getOutput(cmd);
|
|
14282
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
14283
|
+
if (!projectId) {
|
|
14284
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
14285
|
+
return;
|
|
14286
|
+
}
|
|
14217
14287
|
const token = opts.token || getAuthToken();
|
|
14218
14288
|
if (!token) {
|
|
14219
14289
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -14234,10 +14304,11 @@ function registerLogsCommand(program2) {
|
|
|
14234
14304
|
}
|
|
14235
14305
|
if (executionId)
|
|
14236
14306
|
filters.executionId = executionId;
|
|
14237
|
-
const wsUrl = buildWsUrl(apiUrl,
|
|
14307
|
+
const wsUrl = buildWsUrl(apiUrl, projectId, filters, token);
|
|
14238
14308
|
if (!output.isJson()) {
|
|
14239
|
-
output.info(`Connecting to live log stream for project ${
|
|
14309
|
+
output.info(`Connecting to live log stream for project ${projectId}…`);
|
|
14240
14310
|
}
|
|
14311
|
+
const resolvedProjectId = projectId;
|
|
14241
14312
|
await new Promise((resolve3) => {
|
|
14242
14313
|
const MAX_RETRIES = 5;
|
|
14243
14314
|
let retries = 0;
|
|
@@ -14248,7 +14319,7 @@ function registerLogsCommand(program2) {
|
|
|
14248
14319
|
currentClient?.disconnect();
|
|
14249
14320
|
});
|
|
14250
14321
|
function connect() {
|
|
14251
|
-
currentClient = createFollowClient(wsUrl,
|
|
14322
|
+
currentClient = createFollowClient(wsUrl, resolvedProjectId, filters, {
|
|
14252
14323
|
wsFactory: (url) => new WebSocket(url),
|
|
14253
14324
|
onEntry: (entry) => {
|
|
14254
14325
|
if (output.isJson()) {
|
|
@@ -14317,7 +14388,7 @@ function registerLogsCommand(program2) {
|
|
|
14317
14388
|
}
|
|
14318
14389
|
query["start"] = new Date(Date.now() - durationMs).toISOString();
|
|
14319
14390
|
}
|
|
14320
|
-
const result = await client.get(`/api/v1/projects/${
|
|
14391
|
+
const result = await client.get(`/api/v1/projects/${projectId}/logs`, query);
|
|
14321
14392
|
output.table(["Timestamp", "Level", "Automation", "Message"], result.entries.map((e) => [
|
|
14322
14393
|
e.timestamp,
|
|
14323
14394
|
e.level,
|
|
@@ -14328,7 +14399,7 @@ function registerLogsCommand(program2) {
|
|
|
14328
14399
|
output.info(`Total: ${result.totalCount} entries`);
|
|
14329
14400
|
}
|
|
14330
14401
|
} else if (executionId) {
|
|
14331
|
-
const execution = await client.get(`/api/v1/projects/${
|
|
14402
|
+
const execution = await client.get(`/api/v1/projects/${projectId}/executions/${executionId}`);
|
|
14332
14403
|
if ((execution.status === "failed" || execution.status === "timeout") && (execution.errorName || execution.errorMessage)) {
|
|
14333
14404
|
if (output.isJson()) {
|
|
14334
14405
|
output.success({
|
|
@@ -14359,10 +14430,10 @@ Error: ${name}
|
|
|
14359
14430
|
`);
|
|
14360
14431
|
}
|
|
14361
14432
|
}
|
|
14362
|
-
const logs = await client.get(`/api/v1/projects/${
|
|
14433
|
+
const logs = await client.get(`/api/v1/projects/${projectId}/executions/${executionId}/logs`);
|
|
14363
14434
|
output.table(["Timestamp", "Level", "Message"], logs.map((l) => [l.timestamp, l.level, l.message]));
|
|
14364
14435
|
} else {
|
|
14365
|
-
const executions = await client.get(`/api/v1/projects/${
|
|
14436
|
+
const executions = await client.get(`/api/v1/projects/${projectId}/executions`, { limit: opts.limit });
|
|
14366
14437
|
output.table(["ID", "Status", "Trigger", "Duration", "Created"], executions.map((e) => [
|
|
14367
14438
|
e.id,
|
|
14368
14439
|
e.status,
|
|
@@ -14384,6 +14455,7 @@ var init_logs = __esm(() => {
|
|
|
14384
14455
|
init_src3();
|
|
14385
14456
|
init_api_client();
|
|
14386
14457
|
init_config_store();
|
|
14458
|
+
init_project_id();
|
|
14387
14459
|
});
|
|
14388
14460
|
|
|
14389
14461
|
// src/commands/run.ts
|
|
@@ -14445,15 +14517,20 @@ function getClient(opts) {
|
|
|
14445
14517
|
}
|
|
14446
14518
|
function registerScheduleCommand(program2) {
|
|
14447
14519
|
const schedule = program2.command("schedule").description("Manage cron schedules for automations");
|
|
14448
|
-
schedule.command("set").description("Create or update a cron schedule").requiredOption("--cron <expression>", "Cron expression (e.g. '0 9 * * *')").requiredOption("--automation <name>", "Automation name").
|
|
14520
|
+
schedule.command("set").description("Create or update a cron schedule").requiredOption("--cron <expression>", "Cron expression (e.g. '0 9 * * *')").requiredOption("--automation <name>", "Automation name").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
14449
14521
|
const output = getOutput(cmd);
|
|
14522
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
14523
|
+
if (!projectId) {
|
|
14524
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
14525
|
+
return;
|
|
14526
|
+
}
|
|
14450
14527
|
const auth = getClient(opts);
|
|
14451
14528
|
if (!auth) {
|
|
14452
14529
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
14453
14530
|
return;
|
|
14454
14531
|
}
|
|
14455
14532
|
try {
|
|
14456
|
-
const result = await auth.client.post(`/api/v1/projects/${
|
|
14533
|
+
const result = await auth.client.post(`/api/v1/projects/${projectId}/schedules`, {
|
|
14457
14534
|
automationName: opts.automation,
|
|
14458
14535
|
cron: opts.cron
|
|
14459
14536
|
});
|
|
@@ -14466,15 +14543,20 @@ function registerScheduleCommand(program2) {
|
|
|
14466
14543
|
}
|
|
14467
14544
|
}
|
|
14468
14545
|
});
|
|
14469
|
-
schedule.command("list").description("List cron schedules for a project").
|
|
14546
|
+
schedule.command("list").description("List cron schedules for a project").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
14470
14547
|
const output = getOutput(cmd);
|
|
14548
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
14549
|
+
if (!projectId) {
|
|
14550
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
14551
|
+
return;
|
|
14552
|
+
}
|
|
14471
14553
|
const auth = getClient(opts);
|
|
14472
14554
|
if (!auth) {
|
|
14473
14555
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
14474
14556
|
return;
|
|
14475
14557
|
}
|
|
14476
14558
|
try {
|
|
14477
|
-
const result = await auth.client.get(`/api/v1/projects/${
|
|
14559
|
+
const result = await auth.client.get(`/api/v1/projects/${projectId}/schedules`);
|
|
14478
14560
|
output.success(result);
|
|
14479
14561
|
} catch (err) {
|
|
14480
14562
|
if (err instanceof ApiClientError) {
|
|
@@ -14484,19 +14566,24 @@ function registerScheduleCommand(program2) {
|
|
|
14484
14566
|
}
|
|
14485
14567
|
}
|
|
14486
14568
|
});
|
|
14487
|
-
schedule.command("remove <schedule-id>").description("Remove a cron schedule").
|
|
14569
|
+
schedule.command("remove <schedule-id>").description("Remove a cron schedule").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").option("--confirm", "Confirm the destructive delete operation").action(async (scheduleId, opts, cmd) => {
|
|
14488
14570
|
const output = getOutput(cmd);
|
|
14489
14571
|
if (!opts.confirm) {
|
|
14490
14572
|
output.error("CONFIRM_REQUIRED", "This will permanently delete the schedule. Add --confirm to proceed.");
|
|
14491
14573
|
return;
|
|
14492
14574
|
}
|
|
14575
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
14576
|
+
if (!projectId) {
|
|
14577
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
14578
|
+
return;
|
|
14579
|
+
}
|
|
14493
14580
|
const auth = getClient(opts);
|
|
14494
14581
|
if (!auth) {
|
|
14495
14582
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
14496
14583
|
return;
|
|
14497
14584
|
}
|
|
14498
14585
|
try {
|
|
14499
|
-
const result = await auth.client.delete(`/api/v1/projects/${
|
|
14586
|
+
const result = await auth.client.delete(`/api/v1/projects/${projectId}/schedules/${scheduleId}`);
|
|
14500
14587
|
output.success(result);
|
|
14501
14588
|
} catch (err) {
|
|
14502
14589
|
if (err instanceof ApiClientError) {
|
|
@@ -14511,6 +14598,7 @@ var init_schedule = __esm(() => {
|
|
|
14511
14598
|
init_src3();
|
|
14512
14599
|
init_api_client();
|
|
14513
14600
|
init_config_store();
|
|
14601
|
+
init_project_id();
|
|
14514
14602
|
});
|
|
14515
14603
|
|
|
14516
14604
|
// src/dev/server.ts
|
|
@@ -15043,8 +15131,8 @@ __export(exports_file_watcher, {
|
|
|
15043
15131
|
resolveAffectedAutomation: () => resolveAffectedAutomation,
|
|
15044
15132
|
createFileWatcher: () => createFileWatcher
|
|
15045
15133
|
});
|
|
15046
|
-
import { watch, existsSync as
|
|
15047
|
-
import { join as
|
|
15134
|
+
import { watch, existsSync as existsSync6 } from "node:fs";
|
|
15135
|
+
import { join as join7 } from "node:path";
|
|
15048
15136
|
function resolveAffectedAutomation(manifest, changedPath) {
|
|
15049
15137
|
const normalized = changedPath.replace(/\\/g, "/");
|
|
15050
15138
|
const directMatch = manifest.automations.filter((a) => a.entrypoint.replace(/\\/g, "/") === normalized);
|
|
@@ -15063,7 +15151,7 @@ function createFileWatcher(options) {
|
|
|
15063
15151
|
return;
|
|
15064
15152
|
if (!/\.(ts|tsx|js|jsx|mjs|mts)$/.test(filename))
|
|
15065
15153
|
return;
|
|
15066
|
-
const relativePath =
|
|
15154
|
+
const relativePath = join7(dir, filename).replace(/\\/g, "/");
|
|
15067
15155
|
const existing = debounceTimers.get(relativePath);
|
|
15068
15156
|
if (existing) {
|
|
15069
15157
|
clearTimeout(existing);
|
|
@@ -15079,8 +15167,8 @@ function createFileWatcher(options) {
|
|
|
15079
15167
|
return;
|
|
15080
15168
|
watching = true;
|
|
15081
15169
|
for (const dir of watchDirs) {
|
|
15082
|
-
const fullDir =
|
|
15083
|
-
if (!
|
|
15170
|
+
const fullDir = join7(projectDir, dir);
|
|
15171
|
+
if (!existsSync6(fullDir))
|
|
15084
15172
|
continue;
|
|
15085
15173
|
try {
|
|
15086
15174
|
const fsWatcher = watch(fullDir, { recursive: true }, (_eventType, filename) => {
|
|
@@ -15179,12 +15267,12 @@ function registerDevCommand(program2) {
|
|
|
15179
15267
|
}
|
|
15180
15268
|
throw err;
|
|
15181
15269
|
}
|
|
15182
|
-
const { existsSync:
|
|
15183
|
-
const { join:
|
|
15184
|
-
const envPath =
|
|
15270
|
+
const { existsSync: existsSync7, readFileSync: readFileSync6 } = await import("node:fs");
|
|
15271
|
+
const { join: join8 } = await import("node:path");
|
|
15272
|
+
const envPath = join8(projectDir, ".env.local");
|
|
15185
15273
|
let secrets2 = {};
|
|
15186
|
-
if (
|
|
15187
|
-
const content =
|
|
15274
|
+
if (existsSync7(envPath)) {
|
|
15275
|
+
const content = readFileSync6(envPath, "utf-8");
|
|
15188
15276
|
secrets2 = parseEnvFile(content);
|
|
15189
15277
|
output.info(`Loaded ${Object.keys(secrets2).length} secret(s) from .env.local`);
|
|
15190
15278
|
}
|
|
@@ -15206,6 +15294,14 @@ function registerDevCommand(program2) {
|
|
|
15206
15294
|
}
|
|
15207
15295
|
}
|
|
15208
15296
|
}
|
|
15297
|
+
if (typeof globalThis.Bun === "undefined") {
|
|
15298
|
+
try {
|
|
15299
|
+
const { register } = await import("tsx/esm/api");
|
|
15300
|
+
register();
|
|
15301
|
+
} catch (err) {
|
|
15302
|
+
output.warn(`Failed to register tsx loader — .ts handlers may fail to load. ${err instanceof Error ? err.message : ""}`);
|
|
15303
|
+
}
|
|
15304
|
+
}
|
|
15209
15305
|
const { createLocalExecutor: createLocalExecutor2 } = await Promise.resolve().then(() => (init_executor(), exports_executor));
|
|
15210
15306
|
const { installDevGlobal: installDevGlobal2 } = await Promise.resolve().then(() => exports_dev_globals);
|
|
15211
15307
|
const executor = createLocalExecutor2({ projectDir });
|
|
@@ -15526,14 +15622,14 @@ var init_comparator = __esm(() => {
|
|
|
15526
15622
|
|
|
15527
15623
|
// src/snapshot/file-manager.ts
|
|
15528
15624
|
import {
|
|
15529
|
-
existsSync as
|
|
15625
|
+
existsSync as existsSync7,
|
|
15530
15626
|
mkdirSync as mkdirSync4,
|
|
15531
|
-
readFileSync as
|
|
15627
|
+
readFileSync as readFileSync6,
|
|
15532
15628
|
writeFileSync as writeFileSync6,
|
|
15533
15629
|
readdirSync as readdirSync2,
|
|
15534
15630
|
unlinkSync
|
|
15535
15631
|
} from "node:fs";
|
|
15536
|
-
import { join as
|
|
15632
|
+
import { join as join8 } from "node:path";
|
|
15537
15633
|
function automationNameToFileName(automationName) {
|
|
15538
15634
|
return automationName.replace(/\//g, "-") + ".snap.json";
|
|
15539
15635
|
}
|
|
@@ -15544,22 +15640,22 @@ function fileNameToAutomationName(fileName) {
|
|
|
15544
15640
|
class SnapshotFileManager {
|
|
15545
15641
|
snapshotsDir;
|
|
15546
15642
|
constructor(projectDir) {
|
|
15547
|
-
this.snapshotsDir =
|
|
15643
|
+
this.snapshotsDir = join8(projectDir, SNAPSHOTS_DIR);
|
|
15548
15644
|
}
|
|
15549
15645
|
filePath(automationName) {
|
|
15550
|
-
return
|
|
15646
|
+
return join8(this.snapshotsDir, automationNameToFileName(automationName));
|
|
15551
15647
|
}
|
|
15552
15648
|
ensureDir() {
|
|
15553
|
-
if (!
|
|
15649
|
+
if (!existsSync7(this.snapshotsDir)) {
|
|
15554
15650
|
mkdirSync4(this.snapshotsDir, { recursive: true });
|
|
15555
15651
|
}
|
|
15556
15652
|
}
|
|
15557
15653
|
read(automationName) {
|
|
15558
15654
|
const path = this.filePath(automationName);
|
|
15559
|
-
if (!
|
|
15655
|
+
if (!existsSync7(path))
|
|
15560
15656
|
return null;
|
|
15561
15657
|
try {
|
|
15562
|
-
const raw =
|
|
15658
|
+
const raw = readFileSync6(path, "utf-8");
|
|
15563
15659
|
return JSON.parse(raw);
|
|
15564
15660
|
} catch {
|
|
15565
15661
|
return null;
|
|
@@ -15589,14 +15685,14 @@ class SnapshotFileManager {
|
|
|
15589
15685
|
});
|
|
15590
15686
|
}
|
|
15591
15687
|
listAutomationNames() {
|
|
15592
|
-
if (!
|
|
15688
|
+
if (!existsSync7(this.snapshotsDir))
|
|
15593
15689
|
return [];
|
|
15594
15690
|
const files = readdirSync2(this.snapshotsDir).filter((f) => f.endsWith(".snap.json"));
|
|
15595
15691
|
const names = [];
|
|
15596
15692
|
for (const file of files) {
|
|
15597
|
-
const path =
|
|
15693
|
+
const path = join8(this.snapshotsDir, file);
|
|
15598
15694
|
try {
|
|
15599
|
-
const raw =
|
|
15695
|
+
const raw = readFileSync6(path, "utf-8");
|
|
15600
15696
|
const parsed = JSON.parse(raw);
|
|
15601
15697
|
if (parsed.automationName) {
|
|
15602
15698
|
names.push(parsed.automationName);
|
|
@@ -15607,7 +15703,7 @@ class SnapshotFileManager {
|
|
|
15607
15703
|
}
|
|
15608
15704
|
delete(automationName) {
|
|
15609
15705
|
const path = this.filePath(automationName);
|
|
15610
|
-
if (
|
|
15706
|
+
if (existsSync7(path)) {
|
|
15611
15707
|
unlinkSync(path);
|
|
15612
15708
|
}
|
|
15613
15709
|
}
|
|
@@ -15616,25 +15712,25 @@ var SNAPSHOTS_DIR = "__snapshots__";
|
|
|
15616
15712
|
var init_file_manager = () => {};
|
|
15617
15713
|
|
|
15618
15714
|
// src/snapshot/config-reader.ts
|
|
15619
|
-
import { existsSync as
|
|
15620
|
-
import { join as
|
|
15715
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
|
|
15716
|
+
import { join as join9 } from "node:path";
|
|
15621
15717
|
function readSnapshotConfig(projectDir) {
|
|
15622
|
-
const durajsonPath =
|
|
15623
|
-
const testjsonPath =
|
|
15718
|
+
const durajsonPath = join9(projectDir, "dura.json");
|
|
15719
|
+
const testjsonPath = join9(projectDir, "dura.test.json");
|
|
15624
15720
|
let fromDuraJson = {};
|
|
15625
15721
|
let fromTestJson = {};
|
|
15626
15722
|
let mocks;
|
|
15627
|
-
if (
|
|
15723
|
+
if (existsSync8(durajsonPath)) {
|
|
15628
15724
|
try {
|
|
15629
|
-
const raw = JSON.parse(
|
|
15725
|
+
const raw = JSON.parse(readFileSync7(durajsonPath, "utf-8"));
|
|
15630
15726
|
if (raw["snapshots"] && typeof raw["snapshots"] === "object" && !Array.isArray(raw["snapshots"])) {
|
|
15631
15727
|
fromDuraJson = raw["snapshots"];
|
|
15632
15728
|
}
|
|
15633
15729
|
} catch {}
|
|
15634
15730
|
}
|
|
15635
|
-
if (
|
|
15731
|
+
if (existsSync8(testjsonPath)) {
|
|
15636
15732
|
try {
|
|
15637
|
-
const raw = JSON.parse(
|
|
15733
|
+
const raw = JSON.parse(readFileSync7(testjsonPath, "utf-8"));
|
|
15638
15734
|
if (raw["snapshots"] && typeof raw["snapshots"] === "object" && !Array.isArray(raw["snapshots"])) {
|
|
15639
15735
|
fromTestJson = raw["snapshots"];
|
|
15640
15736
|
}
|
|
@@ -16199,24 +16295,29 @@ __export(exports_domains, {
|
|
|
16199
16295
|
registerDomainsCommand: () => registerDomainsCommand
|
|
16200
16296
|
});
|
|
16201
16297
|
function resolveAuth2(opts, output) {
|
|
16298
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
16299
|
+
if (!projectId) {
|
|
16300
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
16301
|
+
return null;
|
|
16302
|
+
}
|
|
16202
16303
|
const token = opts.token || getAuthToken();
|
|
16203
16304
|
if (!token) {
|
|
16204
16305
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
16205
16306
|
return null;
|
|
16206
16307
|
}
|
|
16207
16308
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
16208
|
-
return { client: new ApiClient(apiUrl, token) };
|
|
16309
|
+
return { client: new ApiClient(apiUrl, token), projectId };
|
|
16209
16310
|
}
|
|
16210
16311
|
function registerDomainsCommand(program2) {
|
|
16211
16312
|
const domains = program2.command("domains").description("Manage custom domains for a project");
|
|
16212
|
-
domains.command("add <domain>").description("Add a custom domain").
|
|
16313
|
+
domains.command("add <domain>").description("Add a custom domain").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (domain, opts, cmd) => {
|
|
16213
16314
|
const output = getOutput(cmd);
|
|
16214
16315
|
const auth = resolveAuth2(opts, output);
|
|
16215
16316
|
if (!auth)
|
|
16216
16317
|
return;
|
|
16217
|
-
const { client } = auth;
|
|
16318
|
+
const { client, projectId } = auth;
|
|
16218
16319
|
try {
|
|
16219
|
-
const result = await client.post(`/api/v1/projects/${
|
|
16320
|
+
const result = await client.post(`/api/v1/projects/${projectId}/domains`, { domain });
|
|
16220
16321
|
output.success(result);
|
|
16221
16322
|
if (!output.isJson()) {
|
|
16222
16323
|
output.info(`
|
|
@@ -16224,7 +16325,7 @@ Add this TXT record to your DNS:
|
|
|
16224
16325
|
` + ` Host: _dura-verification.${domain}
|
|
16225
16326
|
` + ` Value: ${result.txtRecord}
|
|
16226
16327
|
` + `
|
|
16227
|
-
Then run: dura domains verify ${result.id} --project ${
|
|
16328
|
+
Then run: dura domains verify ${result.id} --project ${projectId}`);
|
|
16228
16329
|
}
|
|
16229
16330
|
} catch (err) {
|
|
16230
16331
|
if (err instanceof Error) {
|
|
@@ -16232,14 +16333,14 @@ Then run: dura domains verify ${result.id} --project ${opts.project}`);
|
|
|
16232
16333
|
}
|
|
16233
16334
|
}
|
|
16234
16335
|
});
|
|
16235
|
-
domains.command("list").description("List custom domains").
|
|
16336
|
+
domains.command("list").description("List custom domains").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
16236
16337
|
const output = getOutput(cmd);
|
|
16237
16338
|
const auth = resolveAuth2(opts, output);
|
|
16238
16339
|
if (!auth)
|
|
16239
16340
|
return;
|
|
16240
|
-
const { client } = auth;
|
|
16341
|
+
const { client, projectId } = auth;
|
|
16241
16342
|
try {
|
|
16242
|
-
const result = await client.get(`/api/v1/projects/${
|
|
16343
|
+
const result = await client.get(`/api/v1/projects/${projectId}/domains`);
|
|
16243
16344
|
output.table(["Domain", "Status", "Verified", "Created"], result.map((d) => [
|
|
16244
16345
|
d.domain,
|
|
16245
16346
|
d.status,
|
|
@@ -16252,7 +16353,7 @@ Then run: dura domains verify ${result.id} --project ${opts.project}`);
|
|
|
16252
16353
|
}
|
|
16253
16354
|
}
|
|
16254
16355
|
});
|
|
16255
|
-
domains.command("remove <domainId>").description("Remove a custom domain").
|
|
16356
|
+
domains.command("remove <domainId>").description("Remove a custom domain").option("--project <id>", "Project ID (defaults to dura.json)").option("--confirm", "Confirm this destructive operation").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (domainId, opts, cmd) => {
|
|
16256
16357
|
const output = getOutput(cmd);
|
|
16257
16358
|
if (!opts.confirm) {
|
|
16258
16359
|
output.error("CONFIRM_REQUIRED", "This is a destructive operation. Re-run with --confirm to proceed.");
|
|
@@ -16261,9 +16362,9 @@ Then run: dura domains verify ${result.id} --project ${opts.project}`);
|
|
|
16261
16362
|
const auth = resolveAuth2(opts, output);
|
|
16262
16363
|
if (!auth)
|
|
16263
16364
|
return;
|
|
16264
|
-
const { client } = auth;
|
|
16365
|
+
const { client, projectId } = auth;
|
|
16265
16366
|
try {
|
|
16266
|
-
await client.delete(`/api/v1/projects/${
|
|
16367
|
+
await client.delete(`/api/v1/projects/${projectId}/domains/${domainId}`);
|
|
16267
16368
|
output.success({ deleted: true, domainId });
|
|
16268
16369
|
} catch (err) {
|
|
16269
16370
|
if (err instanceof Error) {
|
|
@@ -16271,14 +16372,14 @@ Then run: dura domains verify ${result.id} --project ${opts.project}`);
|
|
|
16271
16372
|
}
|
|
16272
16373
|
}
|
|
16273
16374
|
});
|
|
16274
|
-
domains.command("verify <domainId>").description("Verify a domain's DNS TXT record").
|
|
16375
|
+
domains.command("verify <domainId>").description("Verify a domain's DNS TXT record").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (domainId, opts, cmd) => {
|
|
16275
16376
|
const output = getOutput(cmd);
|
|
16276
16377
|
const auth = resolveAuth2(opts, output);
|
|
16277
16378
|
if (!auth)
|
|
16278
16379
|
return;
|
|
16279
|
-
const { client } = auth;
|
|
16380
|
+
const { client, projectId } = auth;
|
|
16280
16381
|
try {
|
|
16281
|
-
const result = await client.post(`/api/v1/projects/${
|
|
16382
|
+
const result = await client.post(`/api/v1/projects/${projectId}/domains/${domainId}/verify`, {});
|
|
16282
16383
|
output.success(result);
|
|
16283
16384
|
} catch (err) {
|
|
16284
16385
|
if (err instanceof Error) {
|
|
@@ -16291,6 +16392,7 @@ var init_domains = __esm(() => {
|
|
|
16291
16392
|
init_src3();
|
|
16292
16393
|
init_api_client();
|
|
16293
16394
|
init_config_store();
|
|
16395
|
+
init_project_id();
|
|
16294
16396
|
});
|
|
16295
16397
|
|
|
16296
16398
|
// src/commands/endpoint-keys.ts
|
|
@@ -16299,24 +16401,29 @@ __export(exports_endpoint_keys, {
|
|
|
16299
16401
|
registerEndpointKeysCommand: () => registerEndpointKeysCommand
|
|
16300
16402
|
});
|
|
16301
16403
|
function resolveAuth3(opts, output) {
|
|
16404
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
16405
|
+
if (!projectId) {
|
|
16406
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
16407
|
+
return null;
|
|
16408
|
+
}
|
|
16302
16409
|
const token = opts.token || getAuthToken();
|
|
16303
16410
|
if (!token) {
|
|
16304
16411
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
16305
16412
|
return null;
|
|
16306
16413
|
}
|
|
16307
16414
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
16308
|
-
return { client: new ApiClient(apiUrl, token) };
|
|
16415
|
+
return { client: new ApiClient(apiUrl, token), projectId };
|
|
16309
16416
|
}
|
|
16310
16417
|
function registerEndpointKeysCommand(program2) {
|
|
16311
16418
|
const keys = program2.command("endpoint-keys").description("Manage per-project endpoint keys for automation auth");
|
|
16312
|
-
keys.command("create <name>").description("Create a new endpoint key").
|
|
16419
|
+
keys.command("create <name>").description("Create a new endpoint key").option("--project <id>", "Project ID (defaults to dura.json)").option("--expires <date>", "Expiration date (ISO 8601)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (name, opts, cmd) => {
|
|
16313
16420
|
const output = getOutput(cmd);
|
|
16314
16421
|
const auth = resolveAuth3(opts, output);
|
|
16315
16422
|
if (!auth)
|
|
16316
16423
|
return;
|
|
16317
|
-
const { client } = auth;
|
|
16424
|
+
const { client, projectId } = auth;
|
|
16318
16425
|
try {
|
|
16319
|
-
const result = await client.post(`/api/v1/projects/${
|
|
16426
|
+
const result = await client.post(`/api/v1/projects/${projectId}/endpoint-keys`, {
|
|
16320
16427
|
name,
|
|
16321
16428
|
expiresAt: opts.expires
|
|
16322
16429
|
});
|
|
@@ -16330,14 +16437,14 @@ function registerEndpointKeysCommand(program2) {
|
|
|
16330
16437
|
}
|
|
16331
16438
|
}
|
|
16332
16439
|
});
|
|
16333
|
-
keys.command("list").description("List endpoint keys for a project").
|
|
16440
|
+
keys.command("list").description("List endpoint keys for a project").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
16334
16441
|
const output = getOutput(cmd);
|
|
16335
16442
|
const auth = resolveAuth3(opts, output);
|
|
16336
16443
|
if (!auth)
|
|
16337
16444
|
return;
|
|
16338
|
-
const { client } = auth;
|
|
16445
|
+
const { client, projectId } = auth;
|
|
16339
16446
|
try {
|
|
16340
|
-
const result = await client.get(`/api/v1/projects/${
|
|
16447
|
+
const result = await client.get(`/api/v1/projects/${projectId}/endpoint-keys`);
|
|
16341
16448
|
output.table(["Name", "Prefix", "Status", "Expires", "Last Used", "Created"], result.map((k) => [
|
|
16342
16449
|
k.name,
|
|
16343
16450
|
k.keyPrefix,
|
|
@@ -16352,7 +16459,7 @@ function registerEndpointKeysCommand(program2) {
|
|
|
16352
16459
|
}
|
|
16353
16460
|
}
|
|
16354
16461
|
});
|
|
16355
|
-
keys.command("revoke <keyId>").description("Revoke an endpoint key").
|
|
16462
|
+
keys.command("revoke <keyId>").description("Revoke an endpoint key").option("--project <id>", "Project ID (defaults to dura.json)").option("--confirm", "Confirm this destructive operation").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (keyId, opts, cmd) => {
|
|
16356
16463
|
const output = getOutput(cmd);
|
|
16357
16464
|
if (!opts.confirm) {
|
|
16358
16465
|
output.error("CONFIRM_REQUIRED", "This is a destructive operation. Re-run with --confirm to proceed.");
|
|
@@ -16361,9 +16468,9 @@ function registerEndpointKeysCommand(program2) {
|
|
|
16361
16468
|
const auth = resolveAuth3(opts, output);
|
|
16362
16469
|
if (!auth)
|
|
16363
16470
|
return;
|
|
16364
|
-
const { client } = auth;
|
|
16471
|
+
const { client, projectId } = auth;
|
|
16365
16472
|
try {
|
|
16366
|
-
await client.post(`/api/v1/projects/${
|
|
16473
|
+
await client.post(`/api/v1/projects/${projectId}/endpoint-keys/${keyId}/revoke`, {});
|
|
16367
16474
|
output.success({ revoked: true, keyId });
|
|
16368
16475
|
} catch (err) {
|
|
16369
16476
|
if (err instanceof Error) {
|
|
@@ -16376,6 +16483,7 @@ var init_endpoint_keys2 = __esm(() => {
|
|
|
16376
16483
|
init_src3();
|
|
16377
16484
|
init_api_client();
|
|
16378
16485
|
init_config_store();
|
|
16486
|
+
init_project_id();
|
|
16379
16487
|
});
|
|
16380
16488
|
|
|
16381
16489
|
// src/commands/usage.ts
|
|
@@ -16622,19 +16730,19 @@ __export(exports_export, {
|
|
|
16622
16730
|
collectExportFiles: () => collectExportFiles
|
|
16623
16731
|
});
|
|
16624
16732
|
import {
|
|
16625
|
-
existsSync as
|
|
16626
|
-
readFileSync as
|
|
16733
|
+
existsSync as existsSync9,
|
|
16734
|
+
readFileSync as readFileSync8,
|
|
16627
16735
|
readdirSync as readdirSync3,
|
|
16628
16736
|
statSync,
|
|
16629
16737
|
writeFileSync as writeFileSync7
|
|
16630
16738
|
} from "node:fs";
|
|
16631
|
-
import { join as
|
|
16739
|
+
import { join as join10, relative, resolve as resolve4 } from "node:path";
|
|
16632
16740
|
function collectExportFiles(baseDir, currentDir) {
|
|
16633
16741
|
const dir = currentDir ?? baseDir;
|
|
16634
16742
|
const entries = [];
|
|
16635
16743
|
const items = readdirSync3(dir);
|
|
16636
16744
|
for (const item of items) {
|
|
16637
|
-
const fullPath =
|
|
16745
|
+
const fullPath = join10(dir, item);
|
|
16638
16746
|
const relPath = relative(baseDir, fullPath);
|
|
16639
16747
|
const stat = statSync(fullPath);
|
|
16640
16748
|
if (stat.isDirectory()) {
|
|
@@ -16643,7 +16751,7 @@ function collectExportFiles(baseDir, currentDir) {
|
|
|
16643
16751
|
}
|
|
16644
16752
|
} else if (stat.isFile()) {
|
|
16645
16753
|
if (!EXCLUDED_FILES.has(item)) {
|
|
16646
|
-
const content =
|
|
16754
|
+
const content = readFileSync8(fullPath, "utf-8");
|
|
16647
16755
|
entries.push({ relativePath: relPath, content });
|
|
16648
16756
|
}
|
|
16649
16757
|
}
|
|
@@ -16667,14 +16775,14 @@ function registerExportCommand(program2) {
|
|
|
16667
16775
|
program2.command("export").description("Export project source + config into a portable archive").option("--dir <path>", "Project directory", ".").option("--output <path>", "Output file path").action(async (opts, cmd) => {
|
|
16668
16776
|
const output = getOutput(cmd);
|
|
16669
16777
|
const projectDir = resolve4(opts.dir ?? ".");
|
|
16670
|
-
const manifestPath =
|
|
16671
|
-
if (!
|
|
16778
|
+
const manifestPath = join10(projectDir, "dura.json");
|
|
16779
|
+
if (!existsSync9(manifestPath)) {
|
|
16672
16780
|
output.error("EXPORT_NO_MANIFEST", "No dura.json found in project directory", "Run this command from a dura project directory or use --dir");
|
|
16673
16781
|
return;
|
|
16674
16782
|
}
|
|
16675
16783
|
let projectName;
|
|
16676
16784
|
try {
|
|
16677
|
-
const manifestContent =
|
|
16785
|
+
const manifestContent = readFileSync8(manifestPath, "utf-8");
|
|
16678
16786
|
const manifest = JSON.parse(manifestContent);
|
|
16679
16787
|
projectName = manifest.name ?? "unnamed-project";
|
|
16680
16788
|
} catch {
|
|
@@ -16688,7 +16796,7 @@ function registerExportCommand(program2) {
|
|
|
16688
16796
|
return;
|
|
16689
16797
|
}
|
|
16690
16798
|
const archive = createArchive(projectName, files);
|
|
16691
|
-
const outputPath = opts.output ??
|
|
16799
|
+
const outputPath = opts.output ?? join10(projectDir, `${projectName}-export.json`);
|
|
16692
16800
|
try {
|
|
16693
16801
|
writeFileSync7(outputPath, archive, "utf-8");
|
|
16694
16802
|
} catch (err) {
|
|
@@ -16724,8 +16832,13 @@ __export(exports_replay, {
|
|
|
16724
16832
|
registerReplayCommand: () => registerReplayCommand
|
|
16725
16833
|
});
|
|
16726
16834
|
function registerReplayCommand(program2) {
|
|
16727
|
-
program2.command("replay <execution-id>").description("Replay an execution with original inputs (optionally with overrides)").
|
|
16835
|
+
program2.command("replay <execution-id>").description("Replay an execution with original inputs (optionally with overrides)").option("--project <id>", "Project ID (defaults to dura.json)").option("--body <json>", "Override the request body as JSON").option("--dry-run", "Show payload without replaying").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (executionId, opts, cmd) => {
|
|
16728
16836
|
const output = getOutput(cmd);
|
|
16837
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
16838
|
+
if (!projectId) {
|
|
16839
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
16840
|
+
return;
|
|
16841
|
+
}
|
|
16729
16842
|
const token = opts.token || getAuthToken();
|
|
16730
16843
|
if (!token) {
|
|
16731
16844
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -16735,7 +16848,7 @@ function registerReplayCommand(program2) {
|
|
|
16735
16848
|
const client = new ApiClient(apiUrl, token);
|
|
16736
16849
|
try {
|
|
16737
16850
|
if (opts.dryRun) {
|
|
16738
|
-
const payload = await client.get(`/api/v1/projects/${
|
|
16851
|
+
const payload = await client.get(`/api/v1/projects/${projectId}/executions/${executionId}/payload`);
|
|
16739
16852
|
output.success(payload);
|
|
16740
16853
|
return;
|
|
16741
16854
|
}
|
|
@@ -16754,7 +16867,7 @@ function registerReplayCommand(program2) {
|
|
|
16754
16867
|
return;
|
|
16755
16868
|
}
|
|
16756
16869
|
}
|
|
16757
|
-
const result = await client.post(`/api/v1/projects/${
|
|
16870
|
+
const result = await client.post(`/api/v1/projects/${projectId}/executions/${executionId}/replay`, { overrides });
|
|
16758
16871
|
output.success(result);
|
|
16759
16872
|
} catch (err) {
|
|
16760
16873
|
if (err instanceof ApiClientError) {
|
|
@@ -16766,8 +16879,13 @@ function registerReplayCommand(program2) {
|
|
|
16766
16879
|
});
|
|
16767
16880
|
}
|
|
16768
16881
|
function registerReplaysCommand(program2) {
|
|
16769
|
-
program2.command("replays <execution-id>").description("List all replays of an execution").
|
|
16882
|
+
program2.command("replays <execution-id>").description("List all replays of an execution").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (executionId, opts, cmd) => {
|
|
16770
16883
|
const output = getOutput(cmd);
|
|
16884
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
16885
|
+
if (!projectId) {
|
|
16886
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
16887
|
+
return;
|
|
16888
|
+
}
|
|
16771
16889
|
const token = opts.token || getAuthToken();
|
|
16772
16890
|
if (!token) {
|
|
16773
16891
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -16776,7 +16894,7 @@ function registerReplaysCommand(program2) {
|
|
|
16776
16894
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
16777
16895
|
const client = new ApiClient(apiUrl, token);
|
|
16778
16896
|
try {
|
|
16779
|
-
const replays = await client.get(`/api/v1/projects/${
|
|
16897
|
+
const replays = await client.get(`/api/v1/projects/${projectId}/executions/${executionId}/replays`);
|
|
16780
16898
|
if (replays.length === 0) {
|
|
16781
16899
|
if (!output.isJson()) {
|
|
16782
16900
|
output.info("No replays found for this execution.");
|
|
@@ -16805,6 +16923,7 @@ var init_replay = __esm(() => {
|
|
|
16805
16923
|
init_src3();
|
|
16806
16924
|
init_api_client();
|
|
16807
16925
|
init_config_store();
|
|
16926
|
+
init_project_id();
|
|
16808
16927
|
});
|
|
16809
16928
|
|
|
16810
16929
|
// src/commands/kv.ts
|
|
@@ -16813,29 +16932,34 @@ __export(exports_kv, {
|
|
|
16813
16932
|
registerKvCommand: () => registerKvCommand
|
|
16814
16933
|
});
|
|
16815
16934
|
function resolveAuth4(opts, output) {
|
|
16935
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
16936
|
+
if (!projectId) {
|
|
16937
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
16938
|
+
return null;
|
|
16939
|
+
}
|
|
16816
16940
|
const token = opts.token || getAuthToken();
|
|
16817
16941
|
if (!token) {
|
|
16818
16942
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
16819
16943
|
return null;
|
|
16820
16944
|
}
|
|
16821
16945
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
16822
|
-
return { client: new ApiClient(apiUrl, token) };
|
|
16946
|
+
return { client: new ApiClient(apiUrl, token), projectId };
|
|
16823
16947
|
}
|
|
16824
16948
|
function registerKvCommand(program2) {
|
|
16825
16949
|
const kv = program2.command("kv").description("Manage the built-in per-project key-value store");
|
|
16826
|
-
kv.command("list").description("List keys in the project KV namespace").
|
|
16950
|
+
kv.command("list").description("List keys in the project KV namespace").option("--project <id>", "Project ID (defaults to dura.json)").option("--pattern <glob>", "Filter keys by glob pattern (e.g. user:*)").option("--limit <n>", "Maximum number of keys to return").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
16827
16951
|
const output = getOutput(cmd);
|
|
16828
16952
|
const auth = resolveAuth4(opts, output);
|
|
16829
16953
|
if (!auth)
|
|
16830
16954
|
return;
|
|
16831
|
-
const { client } = auth;
|
|
16955
|
+
const { client, projectId } = auth;
|
|
16832
16956
|
const query = {};
|
|
16833
16957
|
if (opts.pattern)
|
|
16834
16958
|
query["pattern"] = opts.pattern;
|
|
16835
16959
|
if (opts.limit)
|
|
16836
16960
|
query["limit"] = opts.limit;
|
|
16837
16961
|
try {
|
|
16838
|
-
const keys = await client.get(`/api/v1/projects/${
|
|
16962
|
+
const keys = await client.get(`/api/v1/projects/${projectId}/kv`, query);
|
|
16839
16963
|
if (output.isJson()) {
|
|
16840
16964
|
output.success(keys);
|
|
16841
16965
|
return;
|
|
@@ -16853,14 +16977,14 @@ function registerKvCommand(program2) {
|
|
|
16853
16977
|
}
|
|
16854
16978
|
}
|
|
16855
16979
|
});
|
|
16856
|
-
kv.command("get <key>").description("Get the value for a key").
|
|
16980
|
+
kv.command("get <key>").description("Get the value for a key").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (key, opts, cmd) => {
|
|
16857
16981
|
const output = getOutput(cmd);
|
|
16858
16982
|
const auth = resolveAuth4(opts, output);
|
|
16859
16983
|
if (!auth)
|
|
16860
16984
|
return;
|
|
16861
|
-
const { client } = auth;
|
|
16985
|
+
const { client, projectId } = auth;
|
|
16862
16986
|
try {
|
|
16863
|
-
const result = await client.get(`/api/v1/projects/${
|
|
16987
|
+
const result = await client.get(`/api/v1/projects/${projectId}/kv/${encodeURIComponent(key)}`);
|
|
16864
16988
|
output.success(result);
|
|
16865
16989
|
} catch (err) {
|
|
16866
16990
|
if (err instanceof ApiClientError) {
|
|
@@ -16870,12 +16994,12 @@ function registerKvCommand(program2) {
|
|
|
16870
16994
|
}
|
|
16871
16995
|
}
|
|
16872
16996
|
});
|
|
16873
|
-
kv.command("set <key> <value>").description("Set a value for a key").
|
|
16997
|
+
kv.command("set <key> <value>").description("Set a value for a key").option("--project <id>", "Project ID (defaults to dura.json)").option("--ttl <seconds>", "Time-to-live in seconds (0 = no expiry)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (key, valueStr, opts, cmd) => {
|
|
16874
16998
|
const output = getOutput(cmd);
|
|
16875
16999
|
const auth = resolveAuth4(opts, output);
|
|
16876
17000
|
if (!auth)
|
|
16877
17001
|
return;
|
|
16878
|
-
const { client } = auth;
|
|
17002
|
+
const { client, projectId } = auth;
|
|
16879
17003
|
let value;
|
|
16880
17004
|
try {
|
|
16881
17005
|
value = JSON.parse(valueStr);
|
|
@@ -16887,7 +17011,7 @@ function registerKvCommand(program2) {
|
|
|
16887
17011
|
body.ttl = parseInt(opts.ttl, 10);
|
|
16888
17012
|
}
|
|
16889
17013
|
try {
|
|
16890
|
-
const result = await client.put(`/api/v1/projects/${
|
|
17014
|
+
const result = await client.put(`/api/v1/projects/${projectId}/kv/${encodeURIComponent(key)}`, body);
|
|
16891
17015
|
output.success(result);
|
|
16892
17016
|
} catch (err) {
|
|
16893
17017
|
if (err instanceof ApiClientError) {
|
|
@@ -16897,7 +17021,7 @@ function registerKvCommand(program2) {
|
|
|
16897
17021
|
}
|
|
16898
17022
|
}
|
|
16899
17023
|
});
|
|
16900
|
-
kv.command("delete <key>").description("Delete a key from the KV namespace").
|
|
17024
|
+
kv.command("delete <key>").description("Delete a key from the KV namespace").option("--project <id>", "Project ID (defaults to dura.json)").option("--confirm", "Required to actually perform the delete").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (key, opts, cmd) => {
|
|
16901
17025
|
const output = getOutput(cmd);
|
|
16902
17026
|
if (!opts.confirm) {
|
|
16903
17027
|
output.error("KV_DELETE_CONFIRM_REQUIRED", `This will permanently delete the key "${key}".`, "Re-run with --confirm to proceed.");
|
|
@@ -16906,9 +17030,9 @@ function registerKvCommand(program2) {
|
|
|
16906
17030
|
const auth = resolveAuth4(opts, output);
|
|
16907
17031
|
if (!auth)
|
|
16908
17032
|
return;
|
|
16909
|
-
const { client } = auth;
|
|
17033
|
+
const { client, projectId } = auth;
|
|
16910
17034
|
try {
|
|
16911
|
-
await client.delete(`/api/v1/projects/${
|
|
17035
|
+
await client.delete(`/api/v1/projects/${projectId}/kv/${encodeURIComponent(key)}`);
|
|
16912
17036
|
output.success({ deleted: true, key });
|
|
16913
17037
|
} catch (err) {
|
|
16914
17038
|
if (err instanceof ApiClientError) {
|
|
@@ -16918,7 +17042,7 @@ function registerKvCommand(program2) {
|
|
|
16918
17042
|
}
|
|
16919
17043
|
}
|
|
16920
17044
|
});
|
|
16921
|
-
kv.command("flush").description("Remove all keys in the project KV namespace").
|
|
17045
|
+
kv.command("flush").description("Remove all keys in the project KV namespace").option("--project <id>", "Project ID (defaults to dura.json)").option("--confirm", "Required to actually perform the flush").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
16922
17046
|
const output = getOutput(cmd);
|
|
16923
17047
|
if (!opts.confirm) {
|
|
16924
17048
|
output.error("KV_FLUSH_CONFIRM_REQUIRED", "This will permanently delete ALL keys for the project.", "Re-run with --confirm to proceed.");
|
|
@@ -16927,9 +17051,9 @@ function registerKvCommand(program2) {
|
|
|
16927
17051
|
const auth = resolveAuth4(opts, output);
|
|
16928
17052
|
if (!auth)
|
|
16929
17053
|
return;
|
|
16930
|
-
const { client } = auth;
|
|
17054
|
+
const { client, projectId } = auth;
|
|
16931
17055
|
try {
|
|
16932
|
-
await client.delete(`/api/v1/projects/${
|
|
17056
|
+
await client.delete(`/api/v1/projects/${projectId}/kv`);
|
|
16933
17057
|
output.success({ flushed: true });
|
|
16934
17058
|
} catch (err) {
|
|
16935
17059
|
if (err instanceof ApiClientError) {
|
|
@@ -16939,14 +17063,14 @@ function registerKvCommand(program2) {
|
|
|
16939
17063
|
}
|
|
16940
17064
|
}
|
|
16941
17065
|
});
|
|
16942
|
-
kv.command("stats").description("Show KV usage statistics for the project").
|
|
17066
|
+
kv.command("stats").description("Show KV usage statistics for the project").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
16943
17067
|
const output = getOutput(cmd);
|
|
16944
17068
|
const auth = resolveAuth4(opts, output);
|
|
16945
17069
|
if (!auth)
|
|
16946
17070
|
return;
|
|
16947
|
-
const { client } = auth;
|
|
17071
|
+
const { client, projectId } = auth;
|
|
16948
17072
|
try {
|
|
16949
|
-
const stats = await client.get(`/api/v1/projects/${
|
|
17073
|
+
const stats = await client.get(`/api/v1/projects/${projectId}/kv/stats`);
|
|
16950
17074
|
output.success(stats);
|
|
16951
17075
|
} catch (err) {
|
|
16952
17076
|
if (err instanceof ApiClientError) {
|
|
@@ -16961,6 +17085,7 @@ var init_kv2 = __esm(() => {
|
|
|
16961
17085
|
init_src3();
|
|
16962
17086
|
init_api_client();
|
|
16963
17087
|
init_config_store();
|
|
17088
|
+
init_project_id();
|
|
16964
17089
|
});
|
|
16965
17090
|
|
|
16966
17091
|
// src/commands/webhook.ts
|
|
@@ -17001,10 +17126,10 @@ function registerWebhookCommand(program2) {
|
|
|
17001
17126
|
return;
|
|
17002
17127
|
}
|
|
17003
17128
|
const events = opts.events ? opts.events.split(",").map((e) => e.trim()) : undefined;
|
|
17004
|
-
let
|
|
17129
|
+
let transform2;
|
|
17005
17130
|
if (opts.transform) {
|
|
17006
17131
|
try {
|
|
17007
|
-
|
|
17132
|
+
transform2 = JSON.parse(opts.transform);
|
|
17008
17133
|
} catch {
|
|
17009
17134
|
output.error("VALIDATION_ERROR", "--transform must be valid JSON");
|
|
17010
17135
|
return;
|
|
@@ -17015,7 +17140,7 @@ function registerWebhookCommand(program2) {
|
|
|
17015
17140
|
slug: opts.slug ?? opts.name.toLowerCase().replace(/[^a-z0-9]+/g, "-"),
|
|
17016
17141
|
source: { type: opts.source, events },
|
|
17017
17142
|
destinations: [{ url: opts.forward, method: "POST" }],
|
|
17018
|
-
transform:
|
|
17143
|
+
transform: transform2 ? { template: transform2 } : undefined
|
|
17019
17144
|
};
|
|
17020
17145
|
try {
|
|
17021
17146
|
const result = await client.post(webhookBase(opts.project), body);
|
|
@@ -17406,24 +17531,29 @@ __export(exports_deployments, {
|
|
|
17406
17531
|
registerDeploymentsCommand: () => registerDeploymentsCommand
|
|
17407
17532
|
});
|
|
17408
17533
|
function resolveAuth6(opts, output) {
|
|
17534
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
17535
|
+
if (!projectId) {
|
|
17536
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
17537
|
+
return null;
|
|
17538
|
+
}
|
|
17409
17539
|
const token = opts.token || getAuthToken();
|
|
17410
17540
|
if (!token) {
|
|
17411
17541
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
17412
17542
|
return null;
|
|
17413
17543
|
}
|
|
17414
17544
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
17415
|
-
return { client: new ApiClient(apiUrl, token) };
|
|
17545
|
+
return { client: new ApiClient(apiUrl, token), projectId };
|
|
17416
17546
|
}
|
|
17417
17547
|
function registerDeploymentsCommand(program2) {
|
|
17418
17548
|
const deployments2 = program2.command("deployments").description("View deployment history for a project");
|
|
17419
|
-
deployments2.command("list").description("List deployments for a project").
|
|
17549
|
+
deployments2.command("list").description("List deployments for a project").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
17420
17550
|
const output = getOutput(cmd);
|
|
17421
17551
|
const auth = resolveAuth6(opts, output);
|
|
17422
17552
|
if (!auth)
|
|
17423
17553
|
return;
|
|
17424
|
-
const { client } = auth;
|
|
17554
|
+
const { client, projectId } = auth;
|
|
17425
17555
|
try {
|
|
17426
|
-
const deploymentList = await client.get(`/api/v1/projects/${
|
|
17556
|
+
const deploymentList = await client.get(`/api/v1/projects/${projectId}/deployments`);
|
|
17427
17557
|
if (output.isJson()) {
|
|
17428
17558
|
output.success(deploymentList);
|
|
17429
17559
|
return;
|
|
@@ -17441,14 +17571,14 @@ function registerDeploymentsCommand(program2) {
|
|
|
17441
17571
|
}
|
|
17442
17572
|
}
|
|
17443
17573
|
});
|
|
17444
|
-
deployments2.command("get").description("Show details for a specific deployment").
|
|
17574
|
+
deployments2.command("get").description("Show details for a specific deployment").option("--project <id>", "Project ID (defaults to dura.json)").requiredOption("--id <deploymentId>", "Deployment ID").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
17445
17575
|
const output = getOutput(cmd);
|
|
17446
17576
|
const auth = resolveAuth6(opts, output);
|
|
17447
17577
|
if (!auth)
|
|
17448
17578
|
return;
|
|
17449
|
-
const { client } = auth;
|
|
17579
|
+
const { client, projectId } = auth;
|
|
17450
17580
|
try {
|
|
17451
|
-
const deployment = await client.get(`/api/v1/projects/${
|
|
17581
|
+
const deployment = await client.get(`/api/v1/projects/${projectId}/deployments/${opts.id}`);
|
|
17452
17582
|
output.success(deployment);
|
|
17453
17583
|
} catch (err) {
|
|
17454
17584
|
if (err instanceof ApiClientError) {
|
|
@@ -17463,6 +17593,7 @@ var init_deployments2 = __esm(() => {
|
|
|
17463
17593
|
init_src3();
|
|
17464
17594
|
init_api_client();
|
|
17465
17595
|
init_config_store();
|
|
17596
|
+
init_project_id();
|
|
17466
17597
|
});
|
|
17467
17598
|
|
|
17468
17599
|
// src/commands/diagnose.ts
|
|
@@ -17529,8 +17660,13 @@ Prevention: ${preventionAdvice}`);
|
|
|
17529
17660
|
`);
|
|
17530
17661
|
}
|
|
17531
17662
|
function registerDiagnoseCommand(program2) {
|
|
17532
|
-
program2.command("diagnose [execution-id]").description("AI-powered diagnosis of a failed execution — identify root cause and suggested fix").
|
|
17663
|
+
program2.command("diagnose [execution-id]").description("AI-powered diagnosis of a failed execution — identify root cause and suggested fix").option("--project <id>", "Project ID (defaults to dura.json)").option("--latest", "Diagnose the most recent failed execution").option("--lookback <duration>", "Lookback window for related executions (e.g. 24h, 48h, 7d)").option("--force", "Force regeneration even if a cached diagnosis exists").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (executionId, opts, cmd) => {
|
|
17533
17664
|
const output = getOutput(cmd);
|
|
17665
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
17666
|
+
if (!projectId) {
|
|
17667
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
17668
|
+
return;
|
|
17669
|
+
}
|
|
17534
17670
|
const token = opts.token || getAuthToken();
|
|
17535
17671
|
if (!token) {
|
|
17536
17672
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -17545,7 +17681,7 @@ function registerDiagnoseCommand(program2) {
|
|
|
17545
17681
|
try {
|
|
17546
17682
|
let targetExecutionId = executionId;
|
|
17547
17683
|
if (opts.latest) {
|
|
17548
|
-
const executions = await client.get(`/api/v1/projects/${
|
|
17684
|
+
const executions = await client.get(`/api/v1/projects/${projectId}/executions?status=failed&limit=1`);
|
|
17549
17685
|
if (!executions || executions.length === 0) {
|
|
17550
17686
|
output.error("NO_FAILED_EXECUTIONS", "No failed executions found for this project");
|
|
17551
17687
|
return;
|
|
@@ -17557,7 +17693,7 @@ function registerDiagnoseCommand(program2) {
|
|
|
17557
17693
|
Analyzing execution ${targetExecutionId}...`);
|
|
17558
17694
|
}
|
|
17559
17695
|
const lookbackHours = parseLookbackHours(opts.lookback);
|
|
17560
|
-
const diagnosis = await client.post(`/api/v1/projects/${
|
|
17696
|
+
const diagnosis = await client.post(`/api/v1/projects/${projectId}/executions/${targetExecutionId}/diagnose`, {
|
|
17561
17697
|
lookbackHours,
|
|
17562
17698
|
force: opts.force ?? false
|
|
17563
17699
|
});
|
|
@@ -17579,6 +17715,7 @@ var init_diagnose = __esm(() => {
|
|
|
17579
17715
|
init_src3();
|
|
17580
17716
|
init_api_client();
|
|
17581
17717
|
init_config_store();
|
|
17718
|
+
init_project_id();
|
|
17582
17719
|
});
|
|
17583
17720
|
|
|
17584
17721
|
// src/commands/create.ts
|
|
@@ -17588,27 +17725,27 @@ __export(exports_create, {
|
|
|
17588
17725
|
registerAddCommand: () => registerAddCommand
|
|
17589
17726
|
});
|
|
17590
17727
|
import {
|
|
17591
|
-
existsSync as
|
|
17728
|
+
existsSync as existsSync10,
|
|
17592
17729
|
mkdirSync as mkdirSync5,
|
|
17593
17730
|
writeFileSync as writeFileSync8,
|
|
17594
|
-
readFileSync as
|
|
17731
|
+
readFileSync as readFileSync9,
|
|
17595
17732
|
readdirSync as readdirSync4
|
|
17596
17733
|
} from "node:fs";
|
|
17597
|
-
import { join as
|
|
17734
|
+
import { join as join11, resolve as resolve5, dirname } from "node:path";
|
|
17598
17735
|
function slugifyDescription(description) {
|
|
17599
17736
|
return description.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 30).replace(/-+$/, "");
|
|
17600
17737
|
}
|
|
17601
17738
|
function getExistingRoutes(projectDir) {
|
|
17602
|
-
const routesDir =
|
|
17603
|
-
const jobsDir =
|
|
17739
|
+
const routesDir = join11(projectDir, "routes");
|
|
17740
|
+
const jobsDir = join11(projectDir, "jobs");
|
|
17604
17741
|
const routes = [];
|
|
17605
|
-
if (
|
|
17742
|
+
if (existsSync10(routesDir)) {
|
|
17606
17743
|
try {
|
|
17607
17744
|
const files = readdirSync4(routesDir);
|
|
17608
17745
|
routes.push(...files.map((f) => `routes/${f}`));
|
|
17609
17746
|
} catch {}
|
|
17610
17747
|
}
|
|
17611
|
-
if (
|
|
17748
|
+
if (existsSync10(jobsDir)) {
|
|
17612
17749
|
try {
|
|
17613
17750
|
const files = readdirSync4(jobsDir);
|
|
17614
17751
|
routes.push(...files.map((f) => `jobs/${f}`));
|
|
@@ -17618,9 +17755,9 @@ function getExistingRoutes(projectDir) {
|
|
|
17618
17755
|
}
|
|
17619
17756
|
function writeGeneratedFiles(projectDir, files) {
|
|
17620
17757
|
for (const file of files) {
|
|
17621
|
-
const filePath =
|
|
17758
|
+
const filePath = join11(projectDir, file.path);
|
|
17622
17759
|
const dir = dirname(filePath);
|
|
17623
|
-
if (!
|
|
17760
|
+
if (!existsSync10(dir)) {
|
|
17624
17761
|
mkdirSync5(dir, { recursive: true });
|
|
17625
17762
|
}
|
|
17626
17763
|
writeFileSync8(filePath, file.content, "utf-8");
|
|
@@ -17652,8 +17789,8 @@ function registerCreateCommand(program2) {
|
|
|
17652
17789
|
}
|
|
17653
17790
|
const projectName = opts.name || slugifyDescription(description);
|
|
17654
17791
|
const parentDir = opts.dir ? resolve5(opts.dir) : process.cwd();
|
|
17655
|
-
const projectDir =
|
|
17656
|
-
if (
|
|
17792
|
+
const projectDir = join11(parentDir, projectName);
|
|
17793
|
+
if (existsSync10(projectDir)) {
|
|
17657
17794
|
output.error("DIRECTORY_EXISTS", `Directory "${projectName}" already exists`, "Choose a different name with --name or remove the existing directory");
|
|
17658
17795
|
return;
|
|
17659
17796
|
}
|
|
@@ -17682,8 +17819,8 @@ function registerCreateCommand(program2) {
|
|
|
17682
17819
|
return;
|
|
17683
17820
|
}
|
|
17684
17821
|
}
|
|
17685
|
-
mkdirSync5(
|
|
17686
|
-
mkdirSync5(
|
|
17822
|
+
mkdirSync5(join11(projectDir, "routes"), { recursive: true });
|
|
17823
|
+
mkdirSync5(join11(projectDir, "jobs"), { recursive: true });
|
|
17687
17824
|
writeGeneratedFiles(projectDir, generateResult.files);
|
|
17688
17825
|
output.info(`Files written to ${projectDir}`);
|
|
17689
17826
|
if (opts.deploy !== false) {
|
|
@@ -17714,15 +17851,15 @@ function registerAddCommand(program2) {
|
|
|
17714
17851
|
return;
|
|
17715
17852
|
}
|
|
17716
17853
|
const projectDir = opts.dir ? resolve5(opts.dir) : process.cwd();
|
|
17717
|
-
const duraJsonPath =
|
|
17718
|
-
if (!
|
|
17719
|
-
output.error("NOT_A_DURA_PROJECT", "No dura.json found in the current directory", "Run this command from a
|
|
17854
|
+
const duraJsonPath = join11(projectDir, "dura.json");
|
|
17855
|
+
if (!existsSync10(duraJsonPath)) {
|
|
17856
|
+
output.error("NOT_A_DURA_PROJECT", "No dura.json found in the current directory", "Run this command from a dura project directory or use --dir");
|
|
17720
17857
|
return;
|
|
17721
17858
|
}
|
|
17722
17859
|
const existingRoutes = getExistingRoutes(projectDir);
|
|
17723
17860
|
let projectConfig = {};
|
|
17724
17861
|
try {
|
|
17725
|
-
projectConfig = JSON.parse(
|
|
17862
|
+
projectConfig = JSON.parse(readFileSync9(duraJsonPath, "utf-8"));
|
|
17726
17863
|
} catch {}
|
|
17727
17864
|
const apiUrl = getApiUrl();
|
|
17728
17865
|
const client = new ApiClient(apiUrl, token);
|
|
@@ -17994,23 +18131,29 @@ __export(exports_env, {
|
|
|
17994
18131
|
registerCanaryCommand: () => registerCanaryCommand
|
|
17995
18132
|
});
|
|
17996
18133
|
function resolveClient(opts, output) {
|
|
18134
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
18135
|
+
if (!projectId) {
|
|
18136
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
18137
|
+
return null;
|
|
18138
|
+
}
|
|
17997
18139
|
const token = opts.token || getAuthToken();
|
|
17998
18140
|
if (!token) {
|
|
17999
18141
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
18000
18142
|
return null;
|
|
18001
18143
|
}
|
|
18002
18144
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
18003
|
-
return new ApiClient(apiUrl, token);
|
|
18145
|
+
return { client: new ApiClient(apiUrl, token), projectId };
|
|
18004
18146
|
}
|
|
18005
18147
|
function registerEnvCommand(program2) {
|
|
18006
18148
|
const env = program2.command("env").description("Manage project environments");
|
|
18007
|
-
env.command("create <name>").description("Create a new environment").
|
|
18149
|
+
env.command("create <name>").description("Create a new environment").option("--project <id>", "Project ID (defaults to dura.json)").option("--type <type>", "Environment type: production, staging, preview, custom", "custom").option("--slug <slug>", "URL slug (defaults to name)").option("--inherit-secrets <env>", "Inherit secrets from this environment ID").option("--auto-deploy", "Enable auto-deploy").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (name, opts, cmd) => {
|
|
18008
18150
|
const output = getOutput(cmd);
|
|
18009
|
-
const
|
|
18010
|
-
if (!
|
|
18151
|
+
const resolved = resolveClient(opts, output);
|
|
18152
|
+
if (!resolved)
|
|
18011
18153
|
return;
|
|
18154
|
+
const { client, projectId } = resolved;
|
|
18012
18155
|
try {
|
|
18013
|
-
const env2 = await client.post(`/api/v1/projects/${
|
|
18156
|
+
const env2 = await client.post(`/api/v1/projects/${projectId}/environments`, {
|
|
18014
18157
|
name,
|
|
18015
18158
|
slug: opts.slug ?? name.toLowerCase().replace(/[^a-z0-9-]/g, "-"),
|
|
18016
18159
|
type: opts.type,
|
|
@@ -18026,13 +18169,14 @@ function registerEnvCommand(program2) {
|
|
|
18026
18169
|
}
|
|
18027
18170
|
}
|
|
18028
18171
|
});
|
|
18029
|
-
env.command("list").description("List all environments for a project").
|
|
18172
|
+
env.command("list").description("List all environments for a project").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
18030
18173
|
const output = getOutput(cmd);
|
|
18031
|
-
const
|
|
18032
|
-
if (!
|
|
18174
|
+
const resolved = resolveClient(opts, output);
|
|
18175
|
+
if (!resolved)
|
|
18033
18176
|
return;
|
|
18177
|
+
const { client, projectId } = resolved;
|
|
18034
18178
|
try {
|
|
18035
|
-
const envs = await client.get(`/api/v1/projects/${
|
|
18179
|
+
const envs = await client.get(`/api/v1/projects/${projectId}/environments`);
|
|
18036
18180
|
output.success(envs);
|
|
18037
18181
|
} catch (err) {
|
|
18038
18182
|
if (err instanceof ApiClientError) {
|
|
@@ -18042,19 +18186,20 @@ function registerEnvCommand(program2) {
|
|
|
18042
18186
|
}
|
|
18043
18187
|
}
|
|
18044
18188
|
});
|
|
18045
|
-
env.command("delete <name>").description("Delete an environment").
|
|
18189
|
+
env.command("delete <name>").description("Delete an environment").option("--project <id>", "Project ID (defaults to dura.json)").option("--env-id <id>", "Environment ID (alternative to name)").option("--confirm", "Confirm deletion (required for destructive operation)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (name, opts, cmd) => {
|
|
18046
18190
|
const output = getOutput(cmd);
|
|
18047
18191
|
if (!opts.confirm) {
|
|
18048
18192
|
output.error("CONFIRM_REQUIRED", `Pass --confirm to delete environment "${name}"`, "This action is irreversible");
|
|
18049
18193
|
return;
|
|
18050
18194
|
}
|
|
18051
|
-
const
|
|
18052
|
-
if (!
|
|
18195
|
+
const resolved = resolveClient(opts, output);
|
|
18196
|
+
if (!resolved)
|
|
18053
18197
|
return;
|
|
18198
|
+
const { client, projectId } = resolved;
|
|
18054
18199
|
try {
|
|
18055
18200
|
let envId = opts.envId;
|
|
18056
18201
|
if (!envId) {
|
|
18057
|
-
const envs = await client.get(`/api/v1/projects/${
|
|
18202
|
+
const envs = await client.get(`/api/v1/projects/${projectId}/environments`);
|
|
18058
18203
|
const found = envs.find((e) => e.name === name);
|
|
18059
18204
|
if (!found) {
|
|
18060
18205
|
output.error("ENV_NOT_FOUND", `Environment "${name}" not found`);
|
|
@@ -18062,7 +18207,7 @@ function registerEnvCommand(program2) {
|
|
|
18062
18207
|
}
|
|
18063
18208
|
envId = found.id;
|
|
18064
18209
|
}
|
|
18065
|
-
await client.delete(`/api/v1/projects/${
|
|
18210
|
+
await client.delete(`/api/v1/projects/${projectId}/environments/${envId}`);
|
|
18066
18211
|
output.success({ deleted: true, name });
|
|
18067
18212
|
} catch (err) {
|
|
18068
18213
|
if (err instanceof ApiClientError) {
|
|
@@ -18072,13 +18217,14 @@ function registerEnvCommand(program2) {
|
|
|
18072
18217
|
}
|
|
18073
18218
|
}
|
|
18074
18219
|
});
|
|
18075
|
-
env.command("diff <env1> <env2>").description("Compare two environments").
|
|
18220
|
+
env.command("diff <env1> <env2>").description("Compare two environments").option("--project <id>", "Project ID (defaults to dura.json)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (env1, env2, opts, cmd) => {
|
|
18076
18221
|
const output = getOutput(cmd);
|
|
18077
|
-
const
|
|
18078
|
-
if (!
|
|
18222
|
+
const resolved = resolveClient(opts, output);
|
|
18223
|
+
if (!resolved)
|
|
18079
18224
|
return;
|
|
18225
|
+
const { client, projectId } = resolved;
|
|
18080
18226
|
try {
|
|
18081
|
-
const envs = await client.get(`/api/v1/projects/${
|
|
18227
|
+
const envs = await client.get(`/api/v1/projects/${projectId}/environments`);
|
|
18082
18228
|
const e1 = envs.find((e) => e.name === env1 || e.slug === env1);
|
|
18083
18229
|
const e2 = envs.find((e) => e.name === env2 || e.slug === env2);
|
|
18084
18230
|
if (!e1) {
|
|
@@ -18105,8 +18251,13 @@ function registerEnvCommand(program2) {
|
|
|
18105
18251
|
});
|
|
18106
18252
|
}
|
|
18107
18253
|
function registerPromoteCommand(program2) {
|
|
18108
|
-
program2.command("promote <source> <target>").description("Promote a deployment from source to target environment").
|
|
18254
|
+
program2.command("promote <source> <target>").description("Promote a deployment from source to target environment").option("--project <id>", "Project ID (defaults to dura.json)").option("--canary", "Use canary rollout (gradual traffic shift)").option("--steps <weights>", "Canary weight steps, comma-separated (e.g. 10,25,50,100)", "10,25,50,100").option("--interval <duration>", "Time between canary steps (e.g. 5m)", "5m").option("--deployment-id <id>", "Specific deployment ID to promote (defaults to source active)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (source, target, opts, cmd) => {
|
|
18109
18255
|
const output = getOutput(cmd);
|
|
18256
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
18257
|
+
if (!projectId) {
|
|
18258
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
18259
|
+
return;
|
|
18260
|
+
}
|
|
18110
18261
|
const token = opts.token || getAuthToken();
|
|
18111
18262
|
if (!token) {
|
|
18112
18263
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -18115,7 +18266,7 @@ function registerPromoteCommand(program2) {
|
|
|
18115
18266
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
18116
18267
|
const client = new ApiClient(apiUrl, token);
|
|
18117
18268
|
try {
|
|
18118
|
-
const envs = await client.get(`/api/v1/projects/${
|
|
18269
|
+
const envs = await client.get(`/api/v1/projects/${projectId}/environments`);
|
|
18119
18270
|
const sourceEnv = envs.find((e) => e.name === source || e.slug === source);
|
|
18120
18271
|
const targetEnv = envs.find((e) => e.name === target || e.slug === target);
|
|
18121
18272
|
if (!sourceEnv) {
|
|
@@ -18139,7 +18290,7 @@ function registerPromoteCommand(program2) {
|
|
|
18139
18290
|
delayMs: intervalMs
|
|
18140
18291
|
}));
|
|
18141
18292
|
}
|
|
18142
|
-
const promotion = await client.post(`/api/v1/projects/${
|
|
18293
|
+
const promotion = await client.post(`/api/v1/projects/${projectId}/promote`, {
|
|
18143
18294
|
sourceEnvId: sourceEnv.id,
|
|
18144
18295
|
targetEnvId: targetEnv.id,
|
|
18145
18296
|
deploymentId,
|
|
@@ -18158,8 +18309,13 @@ function registerPromoteCommand(program2) {
|
|
|
18158
18309
|
}
|
|
18159
18310
|
function registerCanaryCommand(program2) {
|
|
18160
18311
|
const canary = program2.command("canary").description("Manage canary deployments");
|
|
18161
|
-
canary.command("status").description("Show active canary deployment status").
|
|
18312
|
+
canary.command("status").description("Show active canary deployment status").option("--project <id>", "Project ID (defaults to dura.json)").requiredOption("--env <id>", "Environment ID").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
18162
18313
|
const output = getOutput(cmd);
|
|
18314
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
18315
|
+
if (!projectId) {
|
|
18316
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
18317
|
+
return;
|
|
18318
|
+
}
|
|
18163
18319
|
const token = opts.token || getAuthToken();
|
|
18164
18320
|
if (!token) {
|
|
18165
18321
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -18168,7 +18324,7 @@ function registerCanaryCommand(program2) {
|
|
|
18168
18324
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
18169
18325
|
const client = new ApiClient(apiUrl, token);
|
|
18170
18326
|
try {
|
|
18171
|
-
const canary2 = await client.get(`/api/v1/projects/${
|
|
18327
|
+
const canary2 = await client.get(`/api/v1/projects/${projectId}/canary?envId=${opts.env}`);
|
|
18172
18328
|
output.success(canary2);
|
|
18173
18329
|
} catch (err) {
|
|
18174
18330
|
if (err instanceof ApiClientError) {
|
|
@@ -18178,8 +18334,13 @@ function registerCanaryCommand(program2) {
|
|
|
18178
18334
|
}
|
|
18179
18335
|
}
|
|
18180
18336
|
});
|
|
18181
|
-
canary.command("complete").description("Force-complete an active canary (100% traffic to new)").
|
|
18337
|
+
canary.command("complete").description("Force-complete an active canary (100% traffic to new)").option("--project <id>", "Project ID (defaults to dura.json)").requiredOption("--canary-id <id>", "Canary deployment ID").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
18182
18338
|
const output = getOutput(cmd);
|
|
18339
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
18340
|
+
if (!projectId) {
|
|
18341
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
18342
|
+
return;
|
|
18343
|
+
}
|
|
18183
18344
|
const token = opts.token || getAuthToken();
|
|
18184
18345
|
if (!token) {
|
|
18185
18346
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -18188,7 +18349,7 @@ function registerCanaryCommand(program2) {
|
|
|
18188
18349
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
18189
18350
|
const client = new ApiClient(apiUrl, token);
|
|
18190
18351
|
try {
|
|
18191
|
-
const canary2 = await client.post(`/api/v1/projects/${
|
|
18352
|
+
const canary2 = await client.post(`/api/v1/projects/${projectId}/canary/complete`, { canaryId: opts.canaryId });
|
|
18192
18353
|
output.success(canary2);
|
|
18193
18354
|
} catch (err) {
|
|
18194
18355
|
if (err instanceof ApiClientError) {
|
|
@@ -18198,12 +18359,17 @@ function registerCanaryCommand(program2) {
|
|
|
18198
18359
|
}
|
|
18199
18360
|
}
|
|
18200
18361
|
});
|
|
18201
|
-
canary.command("rollback").description("Force-rollback an active canary (restore old deployment)").
|
|
18362
|
+
canary.command("rollback").description("Force-rollback an active canary (restore old deployment)").option("--project <id>", "Project ID (defaults to dura.json)").requiredOption("--canary-id <id>", "Canary deployment ID").option("--reason <reason>", "Reason for rollback").option("--confirm", "Confirm rollback (required)").option("--api-url <url>", "API base URL").option("--token <token>", "Auth token").action(async (opts, cmd) => {
|
|
18202
18363
|
const output = getOutput(cmd);
|
|
18203
18364
|
if (!opts.confirm) {
|
|
18204
18365
|
output.error("CONFIRM_REQUIRED", "Pass --confirm to rollback the canary", "This will restore 100% traffic to the old deployment");
|
|
18205
18366
|
return;
|
|
18206
18367
|
}
|
|
18368
|
+
const projectId = resolveProjectId({ project: opts.project });
|
|
18369
|
+
if (!projectId) {
|
|
18370
|
+
output.error("PROJECT_REQUIRED", "No project ID. Use --project or run this command inside a dura.json project.");
|
|
18371
|
+
return;
|
|
18372
|
+
}
|
|
18207
18373
|
const token = opts.token || getAuthToken();
|
|
18208
18374
|
if (!token) {
|
|
18209
18375
|
output.error("AUTH_REQUIRED", "Not logged in", "Run: dura login");
|
|
@@ -18212,7 +18378,7 @@ function registerCanaryCommand(program2) {
|
|
|
18212
18378
|
const apiUrl = opts.apiUrl || getApiUrl();
|
|
18213
18379
|
const client = new ApiClient(apiUrl, token);
|
|
18214
18380
|
try {
|
|
18215
|
-
const canary2 = await client.post(`/api/v1/projects/${
|
|
18381
|
+
const canary2 = await client.post(`/api/v1/projects/${projectId}/canary/rollback`, { canaryId: opts.canaryId, reason: opts.reason });
|
|
18216
18382
|
output.success(canary2);
|
|
18217
18383
|
} catch (err) {
|
|
18218
18384
|
if (err instanceof ApiClientError) {
|
|
@@ -18246,6 +18412,7 @@ var init_env = __esm(() => {
|
|
|
18246
18412
|
init_src3();
|
|
18247
18413
|
init_api_client();
|
|
18248
18414
|
init_config_store();
|
|
18415
|
+
init_project_id();
|
|
18249
18416
|
});
|
|
18250
18417
|
|
|
18251
18418
|
// src/commands/reports.ts
|
|
@@ -18881,30 +19048,30 @@ var init_heal = __esm(() => {
|
|
|
18881
19048
|
});
|
|
18882
19049
|
|
|
18883
19050
|
// src/lib/skill-installer.ts
|
|
18884
|
-
import { existsSync as
|
|
18885
|
-
import { join as
|
|
19051
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
|
|
19052
|
+
import { join as join12, dirname as dirname2 } from "node:path";
|
|
18886
19053
|
import { homedir as homedir2 } from "node:os";
|
|
18887
19054
|
import { fileURLToPath } from "node:url";
|
|
18888
19055
|
function getSkillSourceDir() {
|
|
18889
19056
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
18890
|
-
return
|
|
19057
|
+
return join12(dirname2(dirname2(__filename2)), "skills");
|
|
18891
19058
|
}
|
|
18892
19059
|
function getGlobalSkillsDir() {
|
|
18893
|
-
return
|
|
19060
|
+
return join12(homedir2(), ".claude", "skills", "dura");
|
|
18894
19061
|
}
|
|
18895
19062
|
function getLocalSkillsDir(projectDir) {
|
|
18896
|
-
return
|
|
19063
|
+
return join12(projectDir, ".claude", "skills", "dura");
|
|
18897
19064
|
}
|
|
18898
19065
|
function installSkills(targetDir) {
|
|
18899
19066
|
const sourceDir = getSkillSourceDir();
|
|
18900
|
-
if (!
|
|
19067
|
+
if (!existsSync11(targetDir)) {
|
|
18901
19068
|
mkdirSync6(targetDir, { recursive: true });
|
|
18902
19069
|
}
|
|
18903
19070
|
const installedFiles = [];
|
|
18904
19071
|
for (const file of SKILL_FILES) {
|
|
18905
|
-
const sourcePath =
|
|
18906
|
-
const targetPath =
|
|
18907
|
-
const content =
|
|
19072
|
+
const sourcePath = join12(sourceDir, file);
|
|
19073
|
+
const targetPath = join12(targetDir, file);
|
|
19074
|
+
const content = readFileSync10(sourcePath, "utf-8");
|
|
18908
19075
|
writeFileSync9(targetPath, content, "utf-8");
|
|
18909
19076
|
installedFiles.push(targetPath);
|
|
18910
19077
|
}
|
|
@@ -18929,10 +19096,10 @@ var exports_init = {};
|
|
|
18929
19096
|
__export(exports_init, {
|
|
18930
19097
|
registerInitCommand: () => registerInitCommand
|
|
18931
19098
|
});
|
|
18932
|
-
import { existsSync as
|
|
18933
|
-
import { join as
|
|
19099
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync10 } from "node:fs";
|
|
19100
|
+
import { join as join13, resolve as resolve6, basename } from "node:path";
|
|
18934
19101
|
function registerInitCommand(program2) {
|
|
18935
|
-
program2.command("init").description("Initialize a
|
|
19102
|
+
program2.command("init").description("Initialize a dura project in the current directory and install AI agent skills").option("--dir <path>", "Project directory (defaults to current dir)").option("--name <name>", "Project name (defaults to directory name)").option("--global", "Install AI agent skills globally (~/.claude/skills/dura/)").option("--local", "Install AI agent skills locally (.claude/skills/dura/)").option("--skip-skills", "Skip AI agent skill installation").action(async (opts, cmd) => {
|
|
18936
19103
|
const output = getOutput(cmd);
|
|
18937
19104
|
const projectDir = resolve6(opts.dir ?? process.cwd());
|
|
18938
19105
|
const projectName = opts.name ?? sanitizeName(basename(projectDir));
|
|
@@ -18940,24 +19107,30 @@ function registerInitCommand(program2) {
|
|
|
18940
19107
|
output.error("INVALID_NAME", `Invalid project name "${projectName}"`, "Project name must start with a letter and contain only letters, numbers, hyphens, and underscores. Use --name to specify a valid name.");
|
|
18941
19108
|
return;
|
|
18942
19109
|
}
|
|
18943
|
-
|
|
18944
|
-
|
|
18945
|
-
|
|
19110
|
+
mkdirSync7(join13(projectDir, "routes"), { recursive: true });
|
|
19111
|
+
mkdirSync7(join13(projectDir, "jobs"), { recursive: true });
|
|
19112
|
+
const manifestPath = join13(projectDir, "dura.json");
|
|
19113
|
+
const alreadyInitialized = existsSync12(manifestPath);
|
|
19114
|
+
if (!alreadyInitialized) {
|
|
19115
|
+
writeFileSync10(manifestPath, duraJsonTemplate(projectName));
|
|
19116
|
+
} else {
|
|
19117
|
+
output.info(`dura.json already exists in ${projectDir} — leaving it in place.`);
|
|
18946
19118
|
}
|
|
18947
|
-
|
|
18948
|
-
|
|
18949
|
-
writeFileSync10(join12(projectDir, "dura.json"), duraJsonTemplate(projectName));
|
|
18950
|
-
const helloPath = join12(projectDir, "routes", "hello.ts");
|
|
18951
|
-
if (existsSync11(helloPath)) {
|
|
19119
|
+
const helloPath = join13(projectDir, "routes", "hello.ts");
|
|
19120
|
+
if (existsSync12(helloPath)) {
|
|
18952
19121
|
output.warn("routes/hello.ts already exists — skipping");
|
|
18953
19122
|
} else {
|
|
18954
19123
|
writeFileSync10(helloPath, HELLO_TEMPLATE);
|
|
18955
19124
|
}
|
|
18956
|
-
const gitignorePath =
|
|
18957
|
-
if (!
|
|
19125
|
+
const gitignorePath = join13(projectDir, ".gitignore");
|
|
19126
|
+
if (!existsSync12(gitignorePath)) {
|
|
18958
19127
|
writeFileSync10(gitignorePath, GITIGNORE_CONTENT2);
|
|
18959
19128
|
}
|
|
18960
|
-
|
|
19129
|
+
if (alreadyInitialized) {
|
|
19130
|
+
output.info(`Re-checked dura project "${projectName}" in ${projectDir}`);
|
|
19131
|
+
} else {
|
|
19132
|
+
output.info(`Initialized dura project "${projectName}"`);
|
|
19133
|
+
}
|
|
18961
19134
|
if (!opts.skipSkills) {
|
|
18962
19135
|
let skillScope;
|
|
18963
19136
|
if (opts.global) {
|
|
@@ -18982,6 +19155,7 @@ function registerInitCommand(program2) {
|
|
|
18982
19155
|
}
|
|
18983
19156
|
output.success({
|
|
18984
19157
|
initialized: true,
|
|
19158
|
+
alreadyInitialized,
|
|
18985
19159
|
name: projectName,
|
|
18986
19160
|
path: projectDir,
|
|
18987
19161
|
files: ["dura.json", "routes/hello.ts", "jobs/", ".gitignore"]
|
|
@@ -18990,7 +19164,7 @@ function registerInitCommand(program2) {
|
|
|
18990
19164
|
output.info("");
|
|
18991
19165
|
output.info("Quickstart:");
|
|
18992
19166
|
output.info(" dura dev # Start the local dev server");
|
|
18993
|
-
output.info(" dura deploy # Deploy to the
|
|
19167
|
+
output.info(" dura deploy # Deploy to the dura cloud");
|
|
18994
19168
|
output.info(" dura run hello # Manually trigger an automation");
|
|
18995
19169
|
}
|
|
18996
19170
|
});
|
|
@@ -19027,6 +19201,7 @@ dist
|
|
|
19027
19201
|
`;
|
|
19028
19202
|
var init_init = __esm(() => {
|
|
19029
19203
|
init_src3();
|
|
19204
|
+
init_hello();
|
|
19030
19205
|
init_skill_installer();
|
|
19031
19206
|
});
|
|
19032
19207
|
|
|
@@ -19111,7 +19286,7 @@ var init_projects2 = __esm(() => {
|
|
|
19111
19286
|
import { realpathSync } from "node:fs";
|
|
19112
19287
|
function createProgram() {
|
|
19113
19288
|
const program2 = new Command;
|
|
19114
|
-
program2.name("dura").description("
|
|
19289
|
+
program2.name("dura").description("dura.run CLI — manage your automations from the command line").version(CLI_VERSION, "-v, --version").option("--json", "Output in JSON format").hook("preAction", (thisCommand) => {
|
|
19115
19290
|
const opts = thisCommand.optsWithGlobals();
|
|
19116
19291
|
const output = new Output({ json: opts.json });
|
|
19117
19292
|
thisCommand.setOptionValue("_output", output);
|
|
@@ -19211,7 +19386,7 @@ async function registerAllCommands(program2) {
|
|
|
19211
19386
|
registerOpenApiCommand2(program2);
|
|
19212
19387
|
return program2;
|
|
19213
19388
|
}
|
|
19214
|
-
var CLI_VERSION = "0.1
|
|
19389
|
+
var CLI_VERSION = "0.2.1";
|
|
19215
19390
|
var init_src3 = __esm(() => {
|
|
19216
19391
|
init_esm();
|
|
19217
19392
|
if (import.meta.url === `file://${realpathSync(process.argv[1] ?? "").replace(/\\/g, "/")}`) {
|
|
@@ -19228,4 +19403,4 @@ export {
|
|
|
19228
19403
|
CLI_VERSION
|
|
19229
19404
|
};
|
|
19230
19405
|
|
|
19231
|
-
//# debugId=
|
|
19406
|
+
//# debugId=25AB4373003CF5A164756E2164756E21
|