@dura-run/cli 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dura.js +267 -88
- package/package.json +1 -1
- package/skills/dura-develop.md +3 -4
package/dist/dura.js
CHANGED
|
@@ -2262,9 +2262,20 @@ function formatObject(data) {
|
|
|
2262
2262
|
}
|
|
2263
2263
|
|
|
2264
2264
|
// src/lib/config-store.ts
|
|
2265
|
-
import {
|
|
2265
|
+
import {
|
|
2266
|
+
chmodSync,
|
|
2267
|
+
existsSync,
|
|
2268
|
+
mkdirSync,
|
|
2269
|
+
readFileSync,
|
|
2270
|
+
writeFileSync
|
|
2271
|
+
} from "node:fs";
|
|
2266
2272
|
import { join } from "node:path";
|
|
2267
2273
|
import { homedir } from "node:os";
|
|
2274
|
+
function chmodPosix(path, mode) {
|
|
2275
|
+
if (process.platform === "win32")
|
|
2276
|
+
return;
|
|
2277
|
+
chmodSync(path, mode);
|
|
2278
|
+
}
|
|
2268
2279
|
function getConfigDir() {
|
|
2269
2280
|
const explicitDir = process.env["DURA_CONFIG_DIR"];
|
|
2270
2281
|
if (explicitDir)
|
|
@@ -2301,12 +2312,15 @@ function readConfig() {
|
|
|
2301
2312
|
function writeConfig(config) {
|
|
2302
2313
|
const dir = getConfigDir();
|
|
2303
2314
|
if (!existsSync(dir)) {
|
|
2304
|
-
mkdirSync(dir, { recursive: true });
|
|
2315
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2305
2316
|
}
|
|
2306
|
-
|
|
2317
|
+
chmodPosix(dir, 448);
|
|
2318
|
+
const path = getConfigPath();
|
|
2319
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + `
|
|
2307
2320
|
`, {
|
|
2308
2321
|
mode: 384
|
|
2309
2322
|
});
|
|
2323
|
+
chmodPosix(path, 384);
|
|
2310
2324
|
}
|
|
2311
2325
|
function updateConfig(partial) {
|
|
2312
2326
|
const current = readConfig();
|
|
@@ -3003,10 +3017,19 @@ var init_project_id = () => {};
|
|
|
3003
3017
|
// src/commands/secrets.ts
|
|
3004
3018
|
var exports_secrets = {};
|
|
3005
3019
|
__export(exports_secrets, {
|
|
3006
|
-
registerSecretsCommand: () => registerSecretsCommand
|
|
3020
|
+
registerSecretsCommand: () => registerSecretsCommand,
|
|
3021
|
+
escapeEnvValue: () => escapeEnvValue
|
|
3007
3022
|
});
|
|
3008
|
-
import {
|
|
3023
|
+
import {
|
|
3024
|
+
writeFileSync as writeFileSync3,
|
|
3025
|
+
existsSync as existsSync4,
|
|
3026
|
+
readFileSync as readFileSync4,
|
|
3027
|
+
appendFileSync
|
|
3028
|
+
} from "node:fs";
|
|
3009
3029
|
import { join as join4 } from "node:path";
|
|
3030
|
+
function escapeEnvValue(v) {
|
|
3031
|
+
return v.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
|
|
3032
|
+
}
|
|
3010
3033
|
function resolveAuth(opts, output) {
|
|
3011
3034
|
const projectId = resolveProjectId({ project: opts.project });
|
|
3012
3035
|
if (!projectId) {
|
|
@@ -3066,28 +3089,65 @@ function registerSecretsCommand(program2) {
|
|
|
3066
3089
|
reportApiError(output, err, "SECRET_REMOVE_FAILED");
|
|
3067
3090
|
}
|
|
3068
3091
|
});
|
|
3069
|
-
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) => {
|
|
3092
|
+
secrets.command("pull").description("Write secrets to .env.local for local development (file holds DECRYPTED plaintext secrets)").option("--project <id>", "Project ID (defaults to dura.json)").option("--confirm", "Overwrite an existing .env.local").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) => {
|
|
3070
3093
|
const output = getOutput(cmd);
|
|
3094
|
+
const envPath = join4(opts.dir, ".env.local");
|
|
3095
|
+
if (existsSync4(envPath) && !opts.confirm) {
|
|
3096
|
+
output.error("FILE_EXISTS", `${envPath} already exists. Re-run with --confirm to overwrite.`);
|
|
3097
|
+
return;
|
|
3098
|
+
}
|
|
3071
3099
|
const auth = resolveAuth(opts, output);
|
|
3072
3100
|
if (!auth)
|
|
3073
3101
|
return;
|
|
3074
3102
|
const { client, projectId } = auth;
|
|
3075
3103
|
try {
|
|
3076
3104
|
const values = await client.get(`/api/v1/projects/${projectId}/secrets/pull`);
|
|
3077
|
-
const lines = Object.entries(values).map(([k, v]) => `${k}="${v
|
|
3078
|
-
const envPath = join4(opts.dir, ".env.local");
|
|
3105
|
+
const lines = Object.entries(values).map(([k, v]) => `${k}="${escapeEnvValue(v)}"`);
|
|
3079
3106
|
writeFileSync3(envPath, lines.join(`
|
|
3080
3107
|
`) + `
|
|
3081
3108
|
`, { mode: 384 });
|
|
3109
|
+
let gitignore;
|
|
3110
|
+
try {
|
|
3111
|
+
gitignore = ensureGitignored(opts.dir) ? "added" : "present";
|
|
3112
|
+
} catch {
|
|
3113
|
+
gitignore = "failed";
|
|
3114
|
+
output.warn(`Could not update ${join4(opts.dir, ".gitignore")} — add ".env.local" to it yourself; the file holds plaintext secrets.`);
|
|
3115
|
+
}
|
|
3082
3116
|
output.success({
|
|
3083
3117
|
written: envPath,
|
|
3084
|
-
count: Object.keys(values).length
|
|
3118
|
+
count: Object.keys(values).length,
|
|
3119
|
+
gitignore
|
|
3085
3120
|
});
|
|
3086
3121
|
} catch (err) {
|
|
3087
3122
|
reportApiError(output, err, "SECRET_PULL_FAILED");
|
|
3088
3123
|
}
|
|
3089
3124
|
});
|
|
3090
3125
|
}
|
|
3126
|
+
function ensureGitignored(dir) {
|
|
3127
|
+
const gitignorePath = join4(dir, ".gitignore");
|
|
3128
|
+
const covers = new Set([
|
|
3129
|
+
".env.local",
|
|
3130
|
+
"/.env.local",
|
|
3131
|
+
".env.local/",
|
|
3132
|
+
".env*",
|
|
3133
|
+
".env.*",
|
|
3134
|
+
"*.local"
|
|
3135
|
+
]);
|
|
3136
|
+
let existing = "";
|
|
3137
|
+
if (existsSync4(gitignorePath)) {
|
|
3138
|
+
existing = readFileSync4(gitignorePath, "utf-8");
|
|
3139
|
+
const alreadyCovered = existing.split(`
|
|
3140
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).some((line) => covers.has(line));
|
|
3141
|
+
if (alreadyCovered)
|
|
3142
|
+
return false;
|
|
3143
|
+
}
|
|
3144
|
+
const prefix = existing.length > 0 && !existing.endsWith(`
|
|
3145
|
+
`) ? `
|
|
3146
|
+
` : "";
|
|
3147
|
+
appendFileSync(gitignorePath, `${prefix}.env.local
|
|
3148
|
+
`);
|
|
3149
|
+
return true;
|
|
3150
|
+
}
|
|
3091
3151
|
var init_secrets = __esm(() => {
|
|
3092
3152
|
init_src3();
|
|
3093
3153
|
init_api_client();
|
|
@@ -3096,6 +3156,9 @@ var init_secrets = __esm(() => {
|
|
|
3096
3156
|
init_project_id();
|
|
3097
3157
|
});
|
|
3098
3158
|
|
|
3159
|
+
// ../shared/src/types/queue.ts
|
|
3160
|
+
var init_queue = () => {};
|
|
3161
|
+
|
|
3099
3162
|
// ../shared/src/constants/defaults.ts
|
|
3100
3163
|
var DEFAULT_TIMEOUT_MS = 30000, DEFAULT_MEMORY_LIMIT_MB = 128;
|
|
3101
3164
|
// ../shared/src/constants/billing.ts
|
|
@@ -7272,15 +7335,15 @@ var init_marketplace = __esm(() => {
|
|
|
7272
7335
|
readme: exports_external.string().max(50000).optional()
|
|
7273
7336
|
}).strict();
|
|
7274
7337
|
installAdapterSchema = exports_external.object({
|
|
7275
|
-
adapterId: exports_external.string().
|
|
7338
|
+
adapterId: exports_external.string().uuid("Adapter ID must be a valid UUID"),
|
|
7276
7339
|
config: exports_external.record(exports_external.unknown()).optional()
|
|
7277
7340
|
});
|
|
7278
7341
|
searchAdaptersSchema = exports_external.object({
|
|
7279
7342
|
query: exports_external.string().optional(),
|
|
7280
7343
|
category: exports_external.string().optional(),
|
|
7281
7344
|
status: exports_external.enum(["draft", "published", "deprecated"]).optional(),
|
|
7282
|
-
limit: exports_external.number().int().positive().max(100).default(20),
|
|
7283
|
-
offset: exports_external.number().int().nonnegative().default(0)
|
|
7345
|
+
limit: exports_external.coerce.number().int().positive().max(100).default(20),
|
|
7346
|
+
offset: exports_external.coerce.number().int().nonnegative().default(0)
|
|
7284
7347
|
});
|
|
7285
7348
|
});
|
|
7286
7349
|
|
|
@@ -7787,7 +7850,7 @@ function Queue(initial = []) {
|
|
|
7787
7850
|
};
|
|
7788
7851
|
}
|
|
7789
7852
|
var queue_default;
|
|
7790
|
-
var
|
|
7853
|
+
var init_queue2 = __esm(() => {
|
|
7791
7854
|
queue_default = Queue;
|
|
7792
7855
|
});
|
|
7793
7856
|
|
|
@@ -8565,7 +8628,7 @@ var init_connection = __esm(() => {
|
|
|
8565
8628
|
init_types2();
|
|
8566
8629
|
init_errors2();
|
|
8567
8630
|
init_result();
|
|
8568
|
-
|
|
8631
|
+
init_queue2();
|
|
8569
8632
|
init_query();
|
|
8570
8633
|
init_bytes();
|
|
8571
8634
|
connection_default = Connection;
|
|
@@ -9245,7 +9308,7 @@ var init_src = __esm(() => {
|
|
|
9245
9308
|
init_types2();
|
|
9246
9309
|
init_connection();
|
|
9247
9310
|
init_query();
|
|
9248
|
-
|
|
9311
|
+
init_queue2();
|
|
9249
9312
|
init_errors2();
|
|
9250
9313
|
init_large();
|
|
9251
9314
|
Object.assign(Postgres, {
|
|
@@ -12789,7 +12852,8 @@ var init_deployments = __esm(() => {
|
|
|
12789
12852
|
index("deployments_project_idx").on(table3.projectId),
|
|
12790
12853
|
index("deployments_project_status_idx").on(table3.projectId, table3.status),
|
|
12791
12854
|
index("deployments_environment_idx").on(table3.environmentId),
|
|
12792
|
-
index("deployments_project_created_idx").on(table3.projectId, desc(table3.createdAt))
|
|
12855
|
+
index("deployments_project_created_idx").on(table3.projectId, desc(table3.createdAt)),
|
|
12856
|
+
uniqueIndex("deployments_one_active_per_project").on(table3.projectId).where(sql`${table3.status} = 'active'`)
|
|
12793
12857
|
]);
|
|
12794
12858
|
});
|
|
12795
12859
|
|
|
@@ -12894,6 +12958,32 @@ var init_metrics = __esm(() => {
|
|
|
12894
12958
|
]);
|
|
12895
12959
|
});
|
|
12896
12960
|
|
|
12961
|
+
// ../shared/src/db/schema/spans.ts
|
|
12962
|
+
var spans;
|
|
12963
|
+
var init_spans = __esm(() => {
|
|
12964
|
+
init_pg_core();
|
|
12965
|
+
init_execution_records();
|
|
12966
|
+
init_projects();
|
|
12967
|
+
spans = pgTable("spans", {
|
|
12968
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
12969
|
+
executionId: uuid("execution_id").notNull().references(() => executionRecords.id, { onDelete: "cascade" }),
|
|
12970
|
+
projectId: uuid("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
12971
|
+
traceId: text("trace_id").notNull(),
|
|
12972
|
+
spanId: text("span_id").notNull(),
|
|
12973
|
+
parentSpanId: text("parent_span_id"),
|
|
12974
|
+
name: text("name").notNull(),
|
|
12975
|
+
kind: text("kind").notNull(),
|
|
12976
|
+
startedAt: timestamp("started_at", { withTimezone: true }).notNull(),
|
|
12977
|
+
endedAt: timestamp("ended_at", { withTimezone: true }).notNull(),
|
|
12978
|
+
attributes: jsonb("attributes").$type(),
|
|
12979
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
12980
|
+
}, (table3) => [
|
|
12981
|
+
index("spans_execution_idx").on(table3.executionId),
|
|
12982
|
+
index("spans_project_idx").on(table3.projectId),
|
|
12983
|
+
index("spans_trace_idx").on(table3.traceId)
|
|
12984
|
+
]);
|
|
12985
|
+
});
|
|
12986
|
+
|
|
12897
12987
|
// ../shared/src/db/schema/artifacts.ts
|
|
12898
12988
|
var artifacts;
|
|
12899
12989
|
var init_artifacts = __esm(() => {
|
|
@@ -13091,6 +13181,7 @@ var init_credit_ledger = __esm(() => {
|
|
|
13091
13181
|
}, (table3) => [
|
|
13092
13182
|
index("credit_ledger_org_created_idx").on(table3.organizationId, table3.createdAt),
|
|
13093
13183
|
uniqueIndex("credit_ledger_execution_debit_dedup").on(sql`((metadata->>'executionId'))`).where(sql`entry_type IN ('execution_debit', 'overage_debit') AND metadata ? 'executionId'`),
|
|
13184
|
+
uniqueIndex("credit_ledger_execution_refund_dedup").on(sql`((metadata->>'executionId'))`).where(sql`entry_type = 'execution_refund' AND metadata ? 'executionId'`),
|
|
13094
13185
|
uniqueIndex("credit_ledger_included_grant_dedup").on(table3.organizationId, sql`((metadata->>'billingCycle'))`, sql`((metadata->>'plan'))`).where(sql`entry_type = 'included_grant' AND metadata ? 'billingCycle' AND metadata ? 'plan'`)
|
|
13095
13186
|
]);
|
|
13096
13187
|
});
|
|
@@ -13412,12 +13503,14 @@ var init_workflows = __esm(() => {
|
|
|
13412
13503
|
]);
|
|
13413
13504
|
stepRuns = pgTable("step_runs", {
|
|
13414
13505
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
13415
|
-
workflowRunId: uuid("workflow_run_id").notNull().references(() => workflowRuns.id),
|
|
13506
|
+
workflowRunId: uuid("workflow_run_id").notNull().references(() => workflowRuns.id, { onDelete: "cascade" }),
|
|
13416
13507
|
stepName: text("step_name").notNull(),
|
|
13417
13508
|
status: text("status").notNull().default("pending"),
|
|
13418
13509
|
input: jsonb("input"),
|
|
13419
13510
|
output: jsonb("output"),
|
|
13420
|
-
executionId: uuid("execution_id").references(() => executionRecords.id
|
|
13511
|
+
executionId: uuid("execution_id").references(() => executionRecords.id, {
|
|
13512
|
+
onDelete: "set null"
|
|
13513
|
+
}),
|
|
13421
13514
|
attempt: integer("attempt").notNull().default(1),
|
|
13422
13515
|
startedAt: timestamp("started_at", { withTimezone: true }),
|
|
13423
13516
|
completedAt: timestamp("completed_at", { withTimezone: true }),
|
|
@@ -13430,7 +13523,7 @@ var init_workflows = __esm(() => {
|
|
|
13430
13523
|
]);
|
|
13431
13524
|
approvalRequests = pgTable("approval_requests", {
|
|
13432
13525
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
13433
|
-
stepRunId: uuid("step_run_id").notNull().references(() => stepRuns.id),
|
|
13526
|
+
stepRunId: uuid("step_run_id").notNull().references(() => stepRuns.id, { onDelete: "cascade" }),
|
|
13434
13527
|
approvers: jsonb("approvers").$type().notNull(),
|
|
13435
13528
|
message: text("message"),
|
|
13436
13529
|
status: text("status").notNull().default("pending"),
|
|
@@ -13732,6 +13825,27 @@ var init_admin_audit_log = __esm(() => {
|
|
|
13732
13825
|
]);
|
|
13733
13826
|
});
|
|
13734
13827
|
|
|
13828
|
+
// ../shared/src/db/schema/custom-domains.ts
|
|
13829
|
+
var customDomains;
|
|
13830
|
+
var init_custom_domains = __esm(() => {
|
|
13831
|
+
init_pg_core();
|
|
13832
|
+
init_projects();
|
|
13833
|
+
customDomains = pgTable("custom_domains", {
|
|
13834
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
13835
|
+
projectId: uuid("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
13836
|
+
domain: text("domain").notNull(),
|
|
13837
|
+
status: text("status").notNull(),
|
|
13838
|
+
txtRecord: text("txt_record").notNull(),
|
|
13839
|
+
verifiedAt: timestamp("verified_at", { withTimezone: true }),
|
|
13840
|
+
tlsProvisionedAt: timestamp("tls_provisioned_at", { withTimezone: true }),
|
|
13841
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
13842
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
13843
|
+
}, (table3) => [
|
|
13844
|
+
uniqueIndex("custom_domains_domain_idx").on(table3.domain),
|
|
13845
|
+
index("custom_domains_project_idx").on(table3.projectId)
|
|
13846
|
+
]);
|
|
13847
|
+
});
|
|
13848
|
+
|
|
13735
13849
|
// ../shared/src/db/schema/index.ts
|
|
13736
13850
|
var init_schema2 = __esm(() => {
|
|
13737
13851
|
init_users();
|
|
@@ -13744,6 +13858,7 @@ var init_schema2 = __esm(() => {
|
|
|
13744
13858
|
init_execution_records();
|
|
13745
13859
|
init_log_entries();
|
|
13746
13860
|
init_metrics();
|
|
13861
|
+
init_spans();
|
|
13747
13862
|
init_artifacts();
|
|
13748
13863
|
init_secrets2();
|
|
13749
13864
|
init_api_keys();
|
|
@@ -13768,6 +13883,7 @@ var init_schema2 = __esm(() => {
|
|
|
13768
13883
|
init_auth();
|
|
13769
13884
|
init_admin_sessions();
|
|
13770
13885
|
init_admin_audit_log();
|
|
13886
|
+
init_custom_domains();
|
|
13771
13887
|
});
|
|
13772
13888
|
|
|
13773
13889
|
// ../shared/src/db/index.ts
|
|
@@ -13800,6 +13916,7 @@ function parseHttpBody(body, _headers) {
|
|
|
13800
13916
|
|
|
13801
13917
|
// ../shared/src/index.ts
|
|
13802
13918
|
var init_src2 = __esm(() => {
|
|
13919
|
+
init_queue();
|
|
13803
13920
|
init_billing();
|
|
13804
13921
|
init_limits();
|
|
13805
13922
|
init_native_deps();
|
|
@@ -13813,16 +13930,16 @@ var init_src2 = __esm(() => {
|
|
|
13813
13930
|
});
|
|
13814
13931
|
|
|
13815
13932
|
// src/lib/manifest.ts
|
|
13816
|
-
import { existsSync as
|
|
13933
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
13817
13934
|
import { join as join5 } from "node:path";
|
|
13818
13935
|
function readManifest(dir) {
|
|
13819
13936
|
const path = join5(dir, "dura.json");
|
|
13820
|
-
if (!
|
|
13937
|
+
if (!existsSync5(path)) {
|
|
13821
13938
|
throw new ManifestError(`No dura.json found in ${dir}`);
|
|
13822
13939
|
}
|
|
13823
13940
|
let raw;
|
|
13824
13941
|
try {
|
|
13825
|
-
raw = JSON.parse(
|
|
13942
|
+
raw = JSON.parse(readFileSync5(path, "utf-8"));
|
|
13826
13943
|
} catch {
|
|
13827
13944
|
throw new ManifestError("dura.json is not valid JSON");
|
|
13828
13945
|
}
|
|
@@ -13834,10 +13951,10 @@ function readManifest(dir) {
|
|
|
13834
13951
|
}
|
|
13835
13952
|
function readRawManifest(dir) {
|
|
13836
13953
|
const path = join5(dir, "dura.json");
|
|
13837
|
-
if (!
|
|
13954
|
+
if (!existsSync5(path)) {
|
|
13838
13955
|
throw new ManifestError(`No dura.json found in ${dir}`);
|
|
13839
13956
|
}
|
|
13840
|
-
return JSON.parse(
|
|
13957
|
+
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
13841
13958
|
}
|
|
13842
13959
|
function writeRawManifest(dir, data) {
|
|
13843
13960
|
const path = join5(dir, "dura.json");
|
|
@@ -14078,8 +14195,8 @@ var init_parity_check = __esm(() => {
|
|
|
14078
14195
|
// src/lib/bundler.ts
|
|
14079
14196
|
import { join as join6, resolve as resolve2 } from "node:path";
|
|
14080
14197
|
import {
|
|
14081
|
-
existsSync as
|
|
14082
|
-
readFileSync as
|
|
14198
|
+
existsSync as existsSync6,
|
|
14199
|
+
readFileSync as readFileSync6,
|
|
14083
14200
|
writeFileSync as writeFileSync5,
|
|
14084
14201
|
mkdirSync as mkdirSync3,
|
|
14085
14202
|
readdirSync,
|
|
@@ -14100,14 +14217,14 @@ async function createBundle(projectDir, options) {
|
|
|
14100
14217
|
};
|
|
14101
14218
|
}
|
|
14102
14219
|
const buildDir = join6(projectDir, ".dura", "build");
|
|
14103
|
-
if (
|
|
14220
|
+
if (existsSync6(buildDir)) {
|
|
14104
14221
|
rmSync(buildDir, { recursive: true });
|
|
14105
14222
|
}
|
|
14106
14223
|
mkdirSync3(buildDir, { recursive: true });
|
|
14107
14224
|
const parityWarnings = [];
|
|
14108
14225
|
for (const auto of duraManifest.automations) {
|
|
14109
14226
|
const entryPath = resolve2(projectDir, auto.entrypoint);
|
|
14110
|
-
if (!
|
|
14227
|
+
if (!existsSync6(entryPath)) {
|
|
14111
14228
|
rmSync(buildDir, { recursive: true });
|
|
14112
14229
|
return {
|
|
14113
14230
|
success: false,
|
|
@@ -14116,7 +14233,7 @@ async function createBundle(projectDir, options) {
|
|
|
14116
14233
|
}
|
|
14117
14234
|
const outFile = join6(buildDir, `${auto.name}.js`);
|
|
14118
14235
|
try {
|
|
14119
|
-
const source =
|
|
14236
|
+
const source = readFileSync6(entryPath, "utf-8");
|
|
14120
14237
|
const xformed = await esbuild2.transform(source, {
|
|
14121
14238
|
loader: "ts",
|
|
14122
14239
|
target: "es2022",
|
|
@@ -14187,7 +14304,7 @@ function collectFiles(dir, base) {
|
|
|
14187
14304
|
const relativePath = fullPath.slice(base.length + 1);
|
|
14188
14305
|
result.push({
|
|
14189
14306
|
name: relativePath,
|
|
14190
|
-
content: new Uint8Array(
|
|
14307
|
+
content: new Uint8Array(readFileSync6(fullPath))
|
|
14191
14308
|
});
|
|
14192
14309
|
} else if (entry.isDirectory()) {
|
|
14193
14310
|
result.push(...collectFiles(fullPath, base));
|
|
@@ -14415,12 +14532,13 @@ __export(exports_logs, {
|
|
|
14415
14532
|
parseFilters: () => parseFilters,
|
|
14416
14533
|
parseDuration: () => parseDuration,
|
|
14417
14534
|
formatLogEntry: () => formatLogEntry,
|
|
14535
|
+
fetchWsTicket: () => fetchWsTicket,
|
|
14418
14536
|
createFollowClient: () => createFollowClient,
|
|
14419
14537
|
buildWsUrl: () => buildWsUrl
|
|
14420
14538
|
});
|
|
14421
|
-
function buildWsUrl(apiUrl, projectId, filters,
|
|
14539
|
+
function buildWsUrl(apiUrl, projectId, filters, ticket = "") {
|
|
14422
14540
|
const wsBase = apiUrl.replace(/^http:\/\//, "ws://").replace(/^https:\/\//, "wss://");
|
|
14423
|
-
const params = new URLSearchParams({
|
|
14541
|
+
const params = new URLSearchParams({ ticket, projectId });
|
|
14424
14542
|
if (filters.level)
|
|
14425
14543
|
params.set("level", filters.level);
|
|
14426
14544
|
if (filters.executionId)
|
|
@@ -14429,6 +14547,10 @@ function buildWsUrl(apiUrl, projectId, filters, token = "") {
|
|
|
14429
14547
|
params.set("automationName", filters.automationName);
|
|
14430
14548
|
return `${wsBase}/api/v1/ws?${params.toString()}`;
|
|
14431
14549
|
}
|
|
14550
|
+
async function fetchWsTicket(client) {
|
|
14551
|
+
const res = await client.post("/api/v1/logs/ws-ticket");
|
|
14552
|
+
return res.ticket;
|
|
14553
|
+
}
|
|
14432
14554
|
function formatLogEntry(entry) {
|
|
14433
14555
|
const fieldsStr = entry.fields && Object.keys(entry.fields).length > 0 ? " " + JSON.stringify(entry.fields) : "";
|
|
14434
14556
|
return `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.message}${fieldsStr}`;
|
|
@@ -14530,7 +14652,14 @@ function registerLogsCommand(program2) {
|
|
|
14530
14652
|
}
|
|
14531
14653
|
if (executionId)
|
|
14532
14654
|
filters.executionId = executionId;
|
|
14533
|
-
|
|
14655
|
+
let ticket;
|
|
14656
|
+
try {
|
|
14657
|
+
ticket = await fetchWsTicket(client);
|
|
14658
|
+
} catch (err) {
|
|
14659
|
+
reportApiError(output, err, "WS_TICKET_FAILED");
|
|
14660
|
+
return;
|
|
14661
|
+
}
|
|
14662
|
+
const wsUrl = buildWsUrl(apiUrl, projectId, filters, ticket);
|
|
14534
14663
|
if (!output.isJson()) {
|
|
14535
14664
|
output.info(`Connecting to live log stream for project ${projectId}…`);
|
|
14536
14665
|
}
|
|
@@ -14811,9 +14940,20 @@ var init_schedule = __esm(() => {
|
|
|
14811
14940
|
});
|
|
14812
14941
|
|
|
14813
14942
|
// src/lib/dev-trust.ts
|
|
14814
|
-
import { existsSync as
|
|
14943
|
+
import { chmodSync as chmodSync2, existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
14815
14944
|
import { dirname, join as join7 } from "node:path";
|
|
14945
|
+
function chmodPosix2(path, mode) {
|
|
14946
|
+
if (process.platform === "win32")
|
|
14947
|
+
return;
|
|
14948
|
+
chmodSync2(path, mode);
|
|
14949
|
+
}
|
|
14816
14950
|
function hasStoredCredentials() {
|
|
14951
|
+
const envToken = process.env["DURA_TOKEN"];
|
|
14952
|
+
if (typeof envToken === "string" && envToken.length > 0)
|
|
14953
|
+
return true;
|
|
14954
|
+
const envApiKey = process.env["DURA_API_KEY"];
|
|
14955
|
+
if (typeof envApiKey === "string" && envApiKey.length > 0)
|
|
14956
|
+
return true;
|
|
14817
14957
|
try {
|
|
14818
14958
|
const cfg = readConfig();
|
|
14819
14959
|
const apiKey = typeof cfg.apiKey === "string" ? cfg.apiKey : "";
|
|
@@ -14824,14 +14964,15 @@ function hasStoredCredentials() {
|
|
|
14824
14964
|
}
|
|
14825
14965
|
}
|
|
14826
14966
|
function hasProjectTrustMarker(projectDir) {
|
|
14827
|
-
return
|
|
14967
|
+
return existsSync7(join7(projectDir, TRUST_MARKER_RELATIVE_PATH));
|
|
14828
14968
|
}
|
|
14829
14969
|
function grantProjectTrust(projectDir) {
|
|
14830
14970
|
const markerPath = join7(projectDir, TRUST_MARKER_RELATIVE_PATH);
|
|
14831
14971
|
const dir = dirname(markerPath);
|
|
14832
|
-
if (!
|
|
14833
|
-
mkdirSync4(dir, { recursive: true });
|
|
14972
|
+
if (!existsSync7(dir)) {
|
|
14973
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
14834
14974
|
}
|
|
14975
|
+
chmodPosix2(dir, 448);
|
|
14835
14976
|
const note = [
|
|
14836
14977
|
"# dura dev trust marker",
|
|
14837
14978
|
"#",
|
|
@@ -14843,6 +14984,7 @@ function grantProjectTrust(projectDir) {
|
|
|
14843
14984
|
].join(`
|
|
14844
14985
|
`);
|
|
14845
14986
|
writeFileSync6(markerPath, note, { mode: 384 });
|
|
14987
|
+
chmodPosix2(markerPath, 384);
|
|
14846
14988
|
}
|
|
14847
14989
|
function isEnvTruthy(val) {
|
|
14848
14990
|
if (!val)
|
|
@@ -15445,7 +15587,7 @@ __export(exports_file_watcher, {
|
|
|
15445
15587
|
resolveAffectedAutomation: () => resolveAffectedAutomation,
|
|
15446
15588
|
createFileWatcher: () => createFileWatcher
|
|
15447
15589
|
});
|
|
15448
|
-
import { watch, existsSync as
|
|
15590
|
+
import { watch, existsSync as existsSync8 } from "node:fs";
|
|
15449
15591
|
import { join as join8 } from "node:path";
|
|
15450
15592
|
function resolveAffectedAutomation(manifest, changedPath) {
|
|
15451
15593
|
const normalized = changedPath.replace(/\\/g, "/");
|
|
@@ -15482,7 +15624,7 @@ function createFileWatcher(options) {
|
|
|
15482
15624
|
watching = true;
|
|
15483
15625
|
for (const dir of watchDirs) {
|
|
15484
15626
|
const fullDir = join8(projectDir, dir);
|
|
15485
|
-
if (!
|
|
15627
|
+
if (!existsSync8(fullDir))
|
|
15486
15628
|
continue;
|
|
15487
15629
|
try {
|
|
15488
15630
|
const fsWatcher = watch(fullDir, { recursive: true }, (_eventType, filename) => {
|
|
@@ -15533,7 +15675,10 @@ function parseEnvFile(content) {
|
|
|
15533
15675
|
}
|
|
15534
15676
|
const key = line3.slice(0, eqIndex).trim();
|
|
15535
15677
|
let value = line3.slice(eqIndex + 1).trim();
|
|
15536
|
-
if (value.
|
|
15678
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
15679
|
+
value = value.slice(1, -1).replace(/\\(.)/g, (_, c) => c === "n" ? `
|
|
15680
|
+
` : c);
|
|
15681
|
+
} else if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
15537
15682
|
value = value.slice(1, -1);
|
|
15538
15683
|
}
|
|
15539
15684
|
if (key.length > 0) {
|
|
@@ -15594,12 +15739,12 @@ function registerDevCommand(program2) {
|
|
|
15594
15739
|
}
|
|
15595
15740
|
throw err;
|
|
15596
15741
|
}
|
|
15597
|
-
const { existsSync:
|
|
15742
|
+
const { existsSync: existsSync9, readFileSync: readFileSync7 } = await import("node:fs");
|
|
15598
15743
|
const { join: join9 } = await import("node:path");
|
|
15599
15744
|
const envPath = join9(projectDir, ".env.local");
|
|
15600
15745
|
let secrets2 = {};
|
|
15601
|
-
if (
|
|
15602
|
-
const content =
|
|
15746
|
+
if (existsSync9(envPath)) {
|
|
15747
|
+
const content = readFileSync7(envPath, "utf-8");
|
|
15603
15748
|
secrets2 = parseEnvFile(content);
|
|
15604
15749
|
output.info(`Loaded ${Object.keys(secrets2).length} secret(s) from .env.local`);
|
|
15605
15750
|
}
|
|
@@ -15950,9 +16095,9 @@ var init_comparator = __esm(() => {
|
|
|
15950
16095
|
|
|
15951
16096
|
// src/snapshot/file-manager.ts
|
|
15952
16097
|
import {
|
|
15953
|
-
existsSync as
|
|
16098
|
+
existsSync as existsSync9,
|
|
15954
16099
|
mkdirSync as mkdirSync5,
|
|
15955
|
-
readFileSync as
|
|
16100
|
+
readFileSync as readFileSync7,
|
|
15956
16101
|
writeFileSync as writeFileSync7,
|
|
15957
16102
|
readdirSync as readdirSync2,
|
|
15958
16103
|
unlinkSync
|
|
@@ -15974,16 +16119,16 @@ class SnapshotFileManager {
|
|
|
15974
16119
|
return join9(this.snapshotsDir, automationNameToFileName(automationName));
|
|
15975
16120
|
}
|
|
15976
16121
|
ensureDir() {
|
|
15977
|
-
if (!
|
|
16122
|
+
if (!existsSync9(this.snapshotsDir)) {
|
|
15978
16123
|
mkdirSync5(this.snapshotsDir, { recursive: true });
|
|
15979
16124
|
}
|
|
15980
16125
|
}
|
|
15981
16126
|
read(automationName) {
|
|
15982
16127
|
const path = this.filePath(automationName);
|
|
15983
|
-
if (!
|
|
16128
|
+
if (!existsSync9(path))
|
|
15984
16129
|
return null;
|
|
15985
16130
|
try {
|
|
15986
|
-
const raw =
|
|
16131
|
+
const raw = readFileSync7(path, "utf-8");
|
|
15987
16132
|
return JSON.parse(raw);
|
|
15988
16133
|
} catch {
|
|
15989
16134
|
return null;
|
|
@@ -16013,14 +16158,14 @@ class SnapshotFileManager {
|
|
|
16013
16158
|
});
|
|
16014
16159
|
}
|
|
16015
16160
|
listAutomationNames() {
|
|
16016
|
-
if (!
|
|
16161
|
+
if (!existsSync9(this.snapshotsDir))
|
|
16017
16162
|
return [];
|
|
16018
16163
|
const files = readdirSync2(this.snapshotsDir).filter((f) => f.endsWith(".snap.json"));
|
|
16019
16164
|
const names = [];
|
|
16020
16165
|
for (const file of files) {
|
|
16021
16166
|
const path = join9(this.snapshotsDir, file);
|
|
16022
16167
|
try {
|
|
16023
|
-
const raw =
|
|
16168
|
+
const raw = readFileSync7(path, "utf-8");
|
|
16024
16169
|
const parsed = JSON.parse(raw);
|
|
16025
16170
|
if (parsed.automationName) {
|
|
16026
16171
|
names.push(parsed.automationName);
|
|
@@ -16031,7 +16176,7 @@ class SnapshotFileManager {
|
|
|
16031
16176
|
}
|
|
16032
16177
|
delete(automationName) {
|
|
16033
16178
|
const path = this.filePath(automationName);
|
|
16034
|
-
if (
|
|
16179
|
+
if (existsSync9(path)) {
|
|
16035
16180
|
unlinkSync(path);
|
|
16036
16181
|
}
|
|
16037
16182
|
}
|
|
@@ -16040,7 +16185,7 @@ var SNAPSHOTS_DIR = "__snapshots__";
|
|
|
16040
16185
|
var init_file_manager = () => {};
|
|
16041
16186
|
|
|
16042
16187
|
// src/snapshot/config-reader.ts
|
|
16043
|
-
import { existsSync as
|
|
16188
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
|
|
16044
16189
|
import { join as join10 } from "node:path";
|
|
16045
16190
|
function readSnapshotConfig(projectDir) {
|
|
16046
16191
|
const durajsonPath = join10(projectDir, "dura.json");
|
|
@@ -16048,17 +16193,17 @@ function readSnapshotConfig(projectDir) {
|
|
|
16048
16193
|
let fromDuraJson = {};
|
|
16049
16194
|
let fromTestJson = {};
|
|
16050
16195
|
let mocks;
|
|
16051
|
-
if (
|
|
16196
|
+
if (existsSync10(durajsonPath)) {
|
|
16052
16197
|
try {
|
|
16053
|
-
const raw = JSON.parse(
|
|
16198
|
+
const raw = JSON.parse(readFileSync8(durajsonPath, "utf-8"));
|
|
16054
16199
|
if (raw["snapshots"] && typeof raw["snapshots"] === "object" && !Array.isArray(raw["snapshots"])) {
|
|
16055
16200
|
fromDuraJson = raw["snapshots"];
|
|
16056
16201
|
}
|
|
16057
16202
|
} catch {}
|
|
16058
16203
|
}
|
|
16059
|
-
if (
|
|
16204
|
+
if (existsSync10(testjsonPath)) {
|
|
16060
16205
|
try {
|
|
16061
|
-
const raw = JSON.parse(
|
|
16206
|
+
const raw = JSON.parse(readFileSync8(testjsonPath, "utf-8"));
|
|
16062
16207
|
if (raw["snapshots"] && typeof raw["snapshots"] === "object" && !Array.isArray(raw["snapshots"])) {
|
|
16063
16208
|
fromTestJson = raw["snapshots"];
|
|
16064
16209
|
}
|
|
@@ -16808,7 +16953,7 @@ __export(exports_events, {
|
|
|
16808
16953
|
resolvePayload: () => resolvePayload,
|
|
16809
16954
|
registerEventsCommand: () => registerEventsCommand
|
|
16810
16955
|
});
|
|
16811
|
-
import { readFileSync as
|
|
16956
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
16812
16957
|
function resolvePayload(opts) {
|
|
16813
16958
|
if (opts.payload !== undefined && opts.payloadFile !== undefined) {
|
|
16814
16959
|
throw new Error("Use either --payload or --payload-file, not both");
|
|
@@ -16816,7 +16961,7 @@ function resolvePayload(opts) {
|
|
|
16816
16961
|
if (opts.payload === undefined && opts.payloadFile === undefined) {
|
|
16817
16962
|
throw new Error("--payload or --payload-file is required");
|
|
16818
16963
|
}
|
|
16819
|
-
const raw = opts.payload !== undefined ? opts.payload :
|
|
16964
|
+
const raw = opts.payload !== undefined ? opts.payload : readFileSync9(opts.payloadFile, "utf-8");
|
|
16820
16965
|
try {
|
|
16821
16966
|
return JSON.parse(raw);
|
|
16822
16967
|
} catch (err) {
|
|
@@ -17102,8 +17247,8 @@ __export(exports_export, {
|
|
|
17102
17247
|
collectExportFiles: () => collectExportFiles
|
|
17103
17248
|
});
|
|
17104
17249
|
import {
|
|
17105
|
-
existsSync as
|
|
17106
|
-
readFileSync as
|
|
17250
|
+
existsSync as existsSync11,
|
|
17251
|
+
readFileSync as readFileSync10,
|
|
17107
17252
|
readdirSync as readdirSync3,
|
|
17108
17253
|
statSync,
|
|
17109
17254
|
writeFileSync as writeFileSync8
|
|
@@ -17123,7 +17268,7 @@ function collectExportFiles(baseDir, currentDir) {
|
|
|
17123
17268
|
}
|
|
17124
17269
|
} else if (stat.isFile()) {
|
|
17125
17270
|
if (!EXCLUDED_FILES.has(item)) {
|
|
17126
|
-
const content =
|
|
17271
|
+
const content = readFileSync10(fullPath, "utf-8");
|
|
17127
17272
|
entries.push({ relativePath: relPath, content });
|
|
17128
17273
|
}
|
|
17129
17274
|
}
|
|
@@ -17148,13 +17293,13 @@ function registerExportCommand(program2) {
|
|
|
17148
17293
|
const output = getOutput(cmd);
|
|
17149
17294
|
const projectDir = resolve4(opts.dir ?? ".");
|
|
17150
17295
|
const manifestPath = join11(projectDir, "dura.json");
|
|
17151
|
-
if (!
|
|
17296
|
+
if (!existsSync11(manifestPath)) {
|
|
17152
17297
|
output.error("EXPORT_NO_MANIFEST", "No dura.json found in project directory", "Run this command from a dura project directory or use --dir");
|
|
17153
17298
|
return;
|
|
17154
17299
|
}
|
|
17155
17300
|
let projectName;
|
|
17156
17301
|
try {
|
|
17157
|
-
const manifestContent =
|
|
17302
|
+
const manifestContent = readFileSync10(manifestPath, "utf-8");
|
|
17158
17303
|
const manifest = JSON.parse(manifestContent);
|
|
17159
17304
|
projectName = manifest.name ?? "unnamed-project";
|
|
17160
17305
|
} catch {
|
|
@@ -18021,17 +18166,18 @@ var init_diagnose = __esm(() => {
|
|
|
18021
18166
|
// src/commands/create.ts
|
|
18022
18167
|
var exports_create = {};
|
|
18023
18168
|
__export(exports_create, {
|
|
18169
|
+
writeGeneratedFiles: () => writeGeneratedFiles,
|
|
18024
18170
|
registerCreateCommand: () => registerCreateCommand,
|
|
18025
18171
|
registerAddCommand: () => registerAddCommand
|
|
18026
18172
|
});
|
|
18027
18173
|
import {
|
|
18028
|
-
existsSync as
|
|
18174
|
+
existsSync as existsSync12,
|
|
18029
18175
|
mkdirSync as mkdirSync6,
|
|
18030
18176
|
writeFileSync as writeFileSync9,
|
|
18031
|
-
readFileSync as
|
|
18177
|
+
readFileSync as readFileSync11,
|
|
18032
18178
|
readdirSync as readdirSync4
|
|
18033
18179
|
} from "node:fs";
|
|
18034
|
-
import { join as join12, resolve as resolve5, dirname as dirname2 } from "node:path";
|
|
18180
|
+
import { join as join12, resolve as resolve5, dirname as dirname2, isAbsolute as isAbsolute2, sep } from "node:path";
|
|
18035
18181
|
function slugifyDescription(description) {
|
|
18036
18182
|
return description.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 30).replace(/-+$/, "");
|
|
18037
18183
|
}
|
|
@@ -18039,13 +18185,13 @@ function getExistingRoutes(projectDir) {
|
|
|
18039
18185
|
const routesDir = join12(projectDir, "routes");
|
|
18040
18186
|
const jobsDir = join12(projectDir, "jobs");
|
|
18041
18187
|
const routes = [];
|
|
18042
|
-
if (
|
|
18188
|
+
if (existsSync12(routesDir)) {
|
|
18043
18189
|
try {
|
|
18044
18190
|
const files = readdirSync4(routesDir);
|
|
18045
18191
|
routes.push(...files.map((f) => `routes/${f}`));
|
|
18046
18192
|
} catch {}
|
|
18047
18193
|
}
|
|
18048
|
-
if (
|
|
18194
|
+
if (existsSync12(jobsDir)) {
|
|
18049
18195
|
try {
|
|
18050
18196
|
const files = readdirSync4(jobsDir);
|
|
18051
18197
|
routes.push(...files.map((f) => `jobs/${f}`));
|
|
@@ -18054,13 +18200,26 @@ function getExistingRoutes(projectDir) {
|
|
|
18054
18200
|
return routes;
|
|
18055
18201
|
}
|
|
18056
18202
|
function writeGeneratedFiles(projectDir, files) {
|
|
18057
|
-
|
|
18058
|
-
|
|
18059
|
-
|
|
18060
|
-
|
|
18203
|
+
const root = resolve5(projectDir);
|
|
18204
|
+
const resolved = files.map((file) => {
|
|
18205
|
+
if (isAbsolute2(file.path)) {
|
|
18206
|
+
throw new Error(`Refusing to write generated file with absolute path "${file.path}" — paths must be relative to the project directory.`);
|
|
18207
|
+
}
|
|
18208
|
+
if (file.path.split(/[\\/]/).includes("..")) {
|
|
18209
|
+
throw new Error(`Refusing to write generated file "${file.path}" — path traversal ("..") is not allowed.`);
|
|
18210
|
+
}
|
|
18211
|
+
const abs = resolve5(root, file.path);
|
|
18212
|
+
if (abs !== root && !abs.startsWith(root + sep)) {
|
|
18213
|
+
throw new Error(`Refusing to write generated file "${file.path}" — resolved path "${abs}" is outside the project directory.`);
|
|
18214
|
+
}
|
|
18215
|
+
return { abs, content: file.content };
|
|
18216
|
+
});
|
|
18217
|
+
for (const { abs, content } of resolved) {
|
|
18218
|
+
const dir = dirname2(abs);
|
|
18219
|
+
if (!existsSync12(dir)) {
|
|
18061
18220
|
mkdirSync6(dir, { recursive: true });
|
|
18062
18221
|
}
|
|
18063
|
-
writeFileSync9(
|
|
18222
|
+
writeFileSync9(abs, content, "utf-8");
|
|
18064
18223
|
}
|
|
18065
18224
|
}
|
|
18066
18225
|
async function confirm(question) {
|
|
@@ -18090,7 +18249,7 @@ function registerCreateCommand(program2) {
|
|
|
18090
18249
|
const projectName = opts.name || slugifyDescription(description);
|
|
18091
18250
|
const parentDir = opts.dir ? resolve5(opts.dir) : process.cwd();
|
|
18092
18251
|
const projectDir = join12(parentDir, projectName);
|
|
18093
|
-
if (
|
|
18252
|
+
if (existsSync12(projectDir)) {
|
|
18094
18253
|
output.error("DIRECTORY_EXISTS", `Directory "${projectName}" already exists`, "Choose a different name with --name or remove the existing directory");
|
|
18095
18254
|
return;
|
|
18096
18255
|
}
|
|
@@ -18117,7 +18276,12 @@ function registerCreateCommand(program2) {
|
|
|
18117
18276
|
}
|
|
18118
18277
|
mkdirSync6(join12(projectDir, "routes"), { recursive: true });
|
|
18119
18278
|
mkdirSync6(join12(projectDir, "jobs"), { recursive: true });
|
|
18120
|
-
|
|
18279
|
+
try {
|
|
18280
|
+
writeGeneratedFiles(projectDir, generateResult.files);
|
|
18281
|
+
} catch (err) {
|
|
18282
|
+
output.error("UNSAFE_GENERATED_PATH", err instanceof Error ? err.message : "Unsafe generated file path", "The AI response contained a file path outside the project directory and was rejected.");
|
|
18283
|
+
return;
|
|
18284
|
+
}
|
|
18121
18285
|
output.info(`Files written to ${projectDir}`);
|
|
18122
18286
|
if (opts.deploy !== false) {
|
|
18123
18287
|
output.info("Deploying...");
|
|
@@ -18148,14 +18312,14 @@ function registerAddCommand(program2) {
|
|
|
18148
18312
|
}
|
|
18149
18313
|
const projectDir = opts.dir ? resolve5(opts.dir) : process.cwd();
|
|
18150
18314
|
const duraJsonPath = join12(projectDir, "dura.json");
|
|
18151
|
-
if (!
|
|
18315
|
+
if (!existsSync12(duraJsonPath)) {
|
|
18152
18316
|
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");
|
|
18153
18317
|
return;
|
|
18154
18318
|
}
|
|
18155
18319
|
const existingRoutes = getExistingRoutes(projectDir);
|
|
18156
18320
|
let projectConfig = {};
|
|
18157
18321
|
try {
|
|
18158
|
-
projectConfig = JSON.parse(
|
|
18322
|
+
projectConfig = JSON.parse(readFileSync11(duraJsonPath, "utf-8"));
|
|
18159
18323
|
} catch {}
|
|
18160
18324
|
const apiUrl = getApiUrl();
|
|
18161
18325
|
const client = new ApiClient(apiUrl, token);
|
|
@@ -18184,7 +18348,12 @@ function registerAddCommand(program2) {
|
|
|
18184
18348
|
return;
|
|
18185
18349
|
}
|
|
18186
18350
|
}
|
|
18187
|
-
|
|
18351
|
+
try {
|
|
18352
|
+
writeGeneratedFiles(projectDir, generateResult.files);
|
|
18353
|
+
} catch (err) {
|
|
18354
|
+
output.error("UNSAFE_GENERATED_PATH", err instanceof Error ? err.message : "Unsafe generated file path", "The AI response contained a file path outside the project directory and was rejected.");
|
|
18355
|
+
return;
|
|
18356
|
+
}
|
|
18188
18357
|
output.info(`Files written to ${projectDir}`);
|
|
18189
18358
|
if (opts.deploy !== false) {
|
|
18190
18359
|
output.info("Run `dura deploy --project " + projectId + " --dir " + projectDir + "` to deploy");
|
|
@@ -19249,7 +19418,7 @@ var init_heal = __esm(() => {
|
|
|
19249
19418
|
});
|
|
19250
19419
|
|
|
19251
19420
|
// src/lib/skill-installer.ts
|
|
19252
|
-
import { existsSync as
|
|
19421
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "node:fs";
|
|
19253
19422
|
import { join as join13, dirname as dirname3 } from "node:path";
|
|
19254
19423
|
import { homedir as homedir2 } from "node:os";
|
|
19255
19424
|
import { fileURLToPath } from "node:url";
|
|
@@ -19265,14 +19434,14 @@ function getLocalSkillsDir(projectDir) {
|
|
|
19265
19434
|
}
|
|
19266
19435
|
function installSkills(targetDir) {
|
|
19267
19436
|
const sourceDir = getSkillSourceDir();
|
|
19268
|
-
if (!
|
|
19437
|
+
if (!existsSync13(targetDir)) {
|
|
19269
19438
|
mkdirSync7(targetDir, { recursive: true });
|
|
19270
19439
|
}
|
|
19271
19440
|
const installedFiles = [];
|
|
19272
19441
|
for (const file of SKILL_FILES) {
|
|
19273
19442
|
const sourcePath = join13(sourceDir, file);
|
|
19274
19443
|
const targetPath = join13(targetDir, file);
|
|
19275
|
-
const content =
|
|
19444
|
+
const content = readFileSync12(sourcePath, "utf-8");
|
|
19276
19445
|
writeFileSync10(targetPath, content, "utf-8");
|
|
19277
19446
|
installedFiles.push(targetPath);
|
|
19278
19447
|
}
|
|
@@ -19297,7 +19466,7 @@ var exports_init = {};
|
|
|
19297
19466
|
__export(exports_init, {
|
|
19298
19467
|
registerInitCommand: () => registerInitCommand
|
|
19299
19468
|
});
|
|
19300
|
-
import { existsSync as
|
|
19469
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
19301
19470
|
import { join as join14, resolve as resolve6, basename } from "node:path";
|
|
19302
19471
|
function registerInitCommand(program2) {
|
|
19303
19472
|
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) => {
|
|
@@ -19311,24 +19480,24 @@ function registerInitCommand(program2) {
|
|
|
19311
19480
|
mkdirSync8(join14(projectDir, "routes"), { recursive: true });
|
|
19312
19481
|
mkdirSync8(join14(projectDir, "jobs"), { recursive: true });
|
|
19313
19482
|
const manifestPath = join14(projectDir, "dura.json");
|
|
19314
|
-
const alreadyInitialized =
|
|
19483
|
+
const alreadyInitialized = existsSync14(manifestPath);
|
|
19315
19484
|
if (!alreadyInitialized) {
|
|
19316
19485
|
writeFileSync11(manifestPath, duraJsonTemplate(projectName));
|
|
19317
19486
|
} else {
|
|
19318
19487
|
output.info(`dura.json already exists in ${projectDir} — leaving it in place.`);
|
|
19319
19488
|
}
|
|
19320
19489
|
const helloPath = join14(projectDir, "routes", "hello.ts");
|
|
19321
|
-
if (
|
|
19490
|
+
if (existsSync14(helloPath)) {
|
|
19322
19491
|
output.warn("routes/hello.ts already exists — skipping");
|
|
19323
19492
|
} else {
|
|
19324
19493
|
writeFileSync11(helloPath, HELLO_TEMPLATE);
|
|
19325
19494
|
}
|
|
19326
19495
|
const gitignorePath = join14(projectDir, ".gitignore");
|
|
19327
|
-
if (!
|
|
19496
|
+
if (!existsSync14(gitignorePath)) {
|
|
19328
19497
|
writeFileSync11(gitignorePath, GITIGNORE_CONTENT2);
|
|
19329
19498
|
}
|
|
19330
19499
|
const pkgPath = join14(projectDir, "package.json");
|
|
19331
|
-
if (!
|
|
19500
|
+
if (!existsSync14(pkgPath)) {
|
|
19332
19501
|
writeFileSync11(pkgPath, packageJsonTemplate(projectName));
|
|
19333
19502
|
}
|
|
19334
19503
|
if (alreadyInitialized) {
|
|
@@ -19548,29 +19717,36 @@ async function registerAllCommands(program2) {
|
|
|
19548
19717
|
const { registerHealCommand: registerHealCommand2 } = await Promise.resolve().then(() => (init_heal(), exports_heal));
|
|
19549
19718
|
const { registerInitCommand: registerInitCommand2 } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
19550
19719
|
const { registerProjectsCommand: registerProjectsCommand2 } = await Promise.resolve().then(() => (init_projects2(), exports_projects));
|
|
19720
|
+
program2.commandsGroup("Auth & setup:");
|
|
19551
19721
|
registerLoginCommand2(program2);
|
|
19552
19722
|
registerLogoutCommand2(program2);
|
|
19553
19723
|
registerConfigCommand2(program2);
|
|
19724
|
+
program2.commandsGroup("Projects:");
|
|
19554
19725
|
registerNewCommand2(program2);
|
|
19555
19726
|
registerInitCommand2(program2);
|
|
19556
19727
|
registerCreateCommand2(program2);
|
|
19557
19728
|
registerAddCommand2(program2);
|
|
19558
19729
|
registerProjectsCommand2(program2);
|
|
19730
|
+
program2.commandsGroup("Templates:");
|
|
19559
19731
|
registerTemplateCommand2(program2);
|
|
19732
|
+
program2.commandsGroup("Config:");
|
|
19560
19733
|
registerSecretsCommand2(program2);
|
|
19561
19734
|
registerDomainsCommand2(program2);
|
|
19562
19735
|
registerEndpointKeysCommand2(program2);
|
|
19563
19736
|
registerEnvCommand2(program2);
|
|
19737
|
+
program2.commandsGroup("Deploy lifecycle:");
|
|
19564
19738
|
registerDeployCommand2(program2);
|
|
19565
19739
|
registerRollbackCommand2(program2);
|
|
19566
19740
|
registerDeploymentsCommand2(program2);
|
|
19567
19741
|
registerPromoteCommand2(program2);
|
|
19568
19742
|
registerCanaryCommand2(program2);
|
|
19743
|
+
program2.commandsGroup("Run & develop:");
|
|
19569
19744
|
registerDevCommand2(program2);
|
|
19570
19745
|
registerTestCommand2(program2);
|
|
19571
19746
|
registerRunCommand2(program2);
|
|
19572
19747
|
registerScheduleCommand2(program2);
|
|
19573
19748
|
registerEventsCommand2(program2);
|
|
19749
|
+
program2.commandsGroup("Observe & debug:");
|
|
19574
19750
|
registerStatusCommand2(program2);
|
|
19575
19751
|
registerLogsCommand2(program2);
|
|
19576
19752
|
registerUsageCommand2(program2);
|
|
@@ -19578,6 +19754,7 @@ async function registerAllCommands(program2) {
|
|
|
19578
19754
|
registerReplayCommand2(program2);
|
|
19579
19755
|
registerReplaysCommand2(program2);
|
|
19580
19756
|
registerDiagnoseCommand2(program2);
|
|
19757
|
+
program2.commandsGroup("Workflows & ops:");
|
|
19581
19758
|
registerWorkflowsCommand2(program2);
|
|
19582
19759
|
registerWorkflowCommand2(program2);
|
|
19583
19760
|
registerApprovalsCommand2(program2);
|
|
@@ -19585,14 +19762,16 @@ async function registerAllCommands(program2) {
|
|
|
19585
19762
|
registerRejectCommand2(program2);
|
|
19586
19763
|
registerHealCommand2(program2);
|
|
19587
19764
|
registerReportsCommand2(program2);
|
|
19765
|
+
program2.commandsGroup("Integrations & storage:");
|
|
19588
19766
|
registerWebhookCommand2(program2);
|
|
19589
19767
|
registerMarketplaceCommand2(program2);
|
|
19590
19768
|
registerKvCommand2(program2);
|
|
19769
|
+
program2.commandsGroup("Export & introspection:");
|
|
19591
19770
|
registerExportCommand2(program2);
|
|
19592
19771
|
registerOpenApiCommand2(program2);
|
|
19593
19772
|
return program2;
|
|
19594
19773
|
}
|
|
19595
|
-
var CLI_VERSION = "0.
|
|
19774
|
+
var CLI_VERSION = "0.4.0";
|
|
19596
19775
|
var init_src3 = __esm(() => {
|
|
19597
19776
|
init_esm();
|
|
19598
19777
|
if (import.meta.url === `file://${realpathSync(process.argv[1] ?? "").replace(/\\/g, "/")}`) {
|
|
@@ -19609,4 +19788,4 @@ export {
|
|
|
19609
19788
|
CLI_VERSION
|
|
19610
19789
|
};
|
|
19611
19790
|
|
|
19612
|
-
//# debugId=
|
|
19791
|
+
//# debugId=BACD50D24CC529CE64756E2164756E21
|
package/package.json
CHANGED
package/skills/dura-develop.md
CHANGED
|
@@ -10,13 +10,12 @@ Handlers run in a V8 isolate on dura.run. **No Node built-ins, no filesystem, no
|
|
|
10
10
|
|
|
11
11
|
- Standard JavaScript globals (`Date`, `Math`, `JSON`, `Promise`, `Uint8Array`, etc.)
|
|
12
12
|
- `crypto.randomUUID()` and `crypto.getRandomValues(typedArray)` (WebCrypto-spec random)
|
|
13
|
+
- `crypto.subtle.*` — full WebCrypto: `digest`, `sign`/`verify`, `encrypt`/`decrypt`, `generateKey`, `importKey`/`exportKey`, `deriveKey`/`deriveBits`, `wrapKey`/`unwrapKey`
|
|
13
14
|
- `TextEncoder` and `TextDecoder` (UTF-8 only)
|
|
14
15
|
- The fetch API (via `dura.fetch`)
|
|
15
16
|
- The `dura.*` SDK surface: `fetch`, `log`, `env`, `kv`, `trigger`, `triggerAndWait`
|
|
16
17
|
|
|
17
|
-
**
|
|
18
|
-
|
|
19
|
-
- `crypto.subtle.*` (hashing, signing, key derivation). For these, call a trusted external service via `dura.fetch`.
|
|
18
|
+
**Note on `crypto.subtle`:** it is bridged to the host runtime, so each call blocks the isolate while it runs. `await`ing a few signs/hashes per request is fine, but `Promise.all([...subtle calls])` executes serially, not concurrently — don't rely on parallelism for throughput, and avoid bulk hashing in tight loops.
|
|
20
19
|
|
|
21
20
|
**Use these replacements for common Node-isms:**
|
|
22
21
|
|
|
@@ -25,7 +24,7 @@ Handlers run in a V8 isolate on dura.run. **No Node built-ins, no filesystem, no
|
|
|
25
24
|
| `node:fs` | `dura.kv` or the artifact APIs |
|
|
26
25
|
| `node:crypto` for IDs | `crypto.randomUUID()` |
|
|
27
26
|
| `node:crypto` for random bytes | `crypto.getRandomValues(new Uint8Array(n))` |
|
|
28
|
-
| `node:crypto` for hashing/signing |
|
|
27
|
+
| `node:crypto` for hashing/signing | `crypto.subtle.digest` / `crypto.subtle.sign` / `crypto.subtle.verify` (WebCrypto) |
|
|
29
28
|
| `Buffer` | `Uint8Array` with `TextEncoder` / `TextDecoder` |
|
|
30
29
|
| `node:child_process` | Not supported — there is no process boundary to use |
|
|
31
30
|
| `node:http` / `https` | `dura.fetch` |
|