@wardby/cli 0.1.0 → 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/README.md +183 -96
- package/deploy/local/docker-compose.yml +5 -5
- package/dist/claude-coding-worker/driver.d.ts +1 -0
- package/dist/claude-coding-worker/driver.js +4 -1
- package/dist/claude-coding-worker/main.js +3 -2
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +5 -0
- package/dist/cli.js +0 -0
- package/dist/coding/protocol.d.ts +28 -3
- package/dist/coding/protocol.js +53 -2
- package/dist/coding-worker/artifact.d.ts +12 -0
- package/dist/coding-worker/artifact.js +48 -6
- package/dist/coding-worker/driver.d.ts +1 -0
- package/dist/coding-worker/driver.js +4 -1
- package/dist/coding-worker/errors.d.ts +7 -0
- package/dist/coding-worker/errors.js +18 -0
- package/dist/coding-worker/main.js +3 -2
- package/dist/core/budget-groups.d.ts +1 -1
- package/dist/core/coding-queue.d.ts +1 -1
- package/dist/core/datastores.d.ts +1 -1
- package/dist/core/db.d.ts +36 -2
- package/dist/core/db.js +69 -2
- package/dist/core/dispatch.d.ts +16 -1
- package/dist/core/dispatch.js +33 -5
- package/dist/core/http-runtime.js +3 -4
- package/dist/core/lease.d.ts +1 -1
- package/dist/core/private-directory.d.ts +20 -0
- package/dist/core/private-directory.js +36 -0
- package/dist/core/reconciler.d.ts +1 -1
- package/dist/core/runner.d.ts +1 -1
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/secrets.d.ts +1 -1
- package/dist/core/subagent-memory-tools.d.ts +1 -1
- package/dist/core/webhooks.d.ts +1 -1
- package/dist/env.js +8 -1
- package/dist/generated/prisma/browser.d.ts +228 -0
- package/dist/generated/prisma/browser.js +17 -0
- package/dist/generated/prisma/client.d.ts +247 -0
- package/dist/generated/prisma/client.js +34 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +809 -0
- package/dist/generated/prisma/commonInputTypes.js +10 -0
- package/dist/generated/prisma/enums.d.ts +53 -0
- package/dist/generated/prisma/enums.js +54 -0
- package/dist/generated/prisma/internal/class.d.ts +462 -0
- package/dist/generated/prisma/internal/class.js +49 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +3325 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +455 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +449 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +426 -0
- package/dist/generated/prisma/models/Agent.d.ts +3267 -0
- package/dist/generated/prisma/models/Agent.js +1 -0
- package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
- package/dist/generated/prisma/models/AgentDatastore.js +1 -0
- package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
- package/dist/generated/prisma/models/AgentMemory.js +1 -0
- package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
- package/dist/generated/prisma/models/AgentSecret.js +1 -0
- package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
- package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +1309 -0
- package/dist/generated/prisma/models/AgentTool.js +1 -0
- package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
- package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
- package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
- package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
- package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
- package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
- package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
- package/dist/generated/prisma/models/AuthSession.js +1 -0
- package/dist/generated/prisma/models/AuthUser.d.ts +1645 -0
- package/dist/generated/prisma/models/AuthUser.js +1 -0
- package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
- package/dist/generated/prisma/models/BudgetGroup.js +1 -0
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1430 -0
- package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
- package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
- package/dist/generated/prisma/models/CodingProxySession.d.ts +1505 -0
- package/dist/generated/prisma/models/CodingProxySession.js +1 -0
- package/dist/generated/prisma/models/CodingRun.d.ts +2402 -0
- package/dist/generated/prisma/models/CodingRun.js +1 -0
- package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
- package/dist/generated/prisma/models/Datastore.js +1 -0
- package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
- package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
- package/dist/generated/prisma/models/OAuthClient.js +1 -0
- package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
- package/dist/generated/prisma/models/OAuthFamily.js +1 -0
- package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
- package/dist/generated/prisma/models/OAuthGrant.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +1790 -0
- package/dist/generated/prisma/models/Principal.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +2281 -0
- package/dist/generated/prisma/models/Run.js +1 -0
- package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
- package/dist/generated/prisma/models/SchedulerLease.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +1399 -0
- package/dist/generated/prisma/models/Secret.js +1 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
- package/dist/generated/prisma/models/Task.d.ts +1176 -0
- package/dist/generated/prisma/models/Task.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +1478 -0
- package/dist/generated/prisma/models/Tool.js +1 -0
- package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
- package/dist/generated/prisma/models/Webhook.js +1 -0
- package/dist/generated/prisma/models.d.ts +32 -0
- package/dist/generated/prisma/models.js +1 -0
- package/dist/import/create.d.ts +1 -1
- package/dist/import/create.js +1 -1
- package/dist/import/index.d.ts +1 -1
- package/dist/mcp/auth/ownership.d.ts +19 -19
- package/dist/mcp/auth/principal.d.ts +1 -1
- package/dist/mcp/auth/resource-server.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/browser.js +20 -6
- package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/credentials.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/session.d.ts +1 -1
- package/dist/mcp/context.d.ts +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/shared/page-style.d.ts +16 -0
- package/dist/mcp/shared/page-style.js +129 -0
- package/dist/mcp/tasks/manager.d.ts +1 -1
- package/dist/mcp/tools/agents.js +1 -1
- package/dist/mcp/tools/datastore.js +1 -1
- package/dist/mcp/tools/scheduling.js +1 -1
- package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
- package/dist/mcp/tools/secret-elicitation-form.js +10 -28
- package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
- package/dist/mcp/tools/subagents.js +1 -1
- package/dist/mcp/tools/tools.js +1 -1
- package/dist/mcp/transport/streamable-http.d.ts +1 -1
- package/dist/mcp/unattended-schedules.d.ts +1 -1
- package/dist/mcp/webhooks/ingress.d.ts +1 -1
- package/dist/providers/auth/index.d.ts +1 -1
- package/dist/providers/auth/self-hosted.d.ts +2 -2
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -1
- package/dist/providers/coding-proxy/runtime.d.ts +1 -1
- package/dist/providers/datastore/postgres.d.ts +1 -1
- package/dist/providers/executor/build.d.ts +1 -1
- package/dist/providers/executor/composition.d.ts +1 -1
- package/dist/providers/executor/container.d.ts +1 -1
- package/dist/providers/executor/container.js +10 -3
- package/dist/providers/executor/dbos.d.ts +1 -1
- package/dist/providers/jobs/docker.d.ts +11 -0
- package/dist/providers/jobs/docker.js +34 -18
- package/dist/providers/jobs/kubernetes.js +5 -10
- package/dist/providers/jobs/types.d.ts +2 -0
- package/dist/providers/memory/postgres.d.ts +1 -1
- package/dist/providers/vcs/git.js +2 -1
- package/dist/quickstart/config.d.ts +30 -0
- package/dist/quickstart/config.js +106 -0
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +527 -0
- package/dist/quickstart/migrate.d.ts +23 -0
- package/dist/quickstart/migrate.js +125 -0
- package/dist/wardby-bin.js +35 -9
- package/package.json +22 -9
- package/prisma/migrate.config.mjs +20 -0
- package/prisma/schema.prisma +10 -2
- package/dist/providers/auth/release-gate.d.ts +0 -1
- package/dist/providers/auth/release-gate.js +0 -4
package/dist/coding/protocol.js
CHANGED
|
@@ -6,6 +6,14 @@ export const MAX_CODING_SUMMARY_BYTES = 8 * 1024;
|
|
|
6
6
|
export const MAX_CODING_TESTS = 64;
|
|
7
7
|
export const MAX_CODING_TEST_COMMAND_BYTES = 2 * 1024;
|
|
8
8
|
export const MAX_TAG_BYTES = 32;
|
|
9
|
+
/**
|
|
10
|
+
* A worker's output-schema failure, reduced to where and how it failed, never
|
|
11
|
+
* what the model wrote: `<path>:<zod issue code>`, where the path is dot-joined
|
|
12
|
+
* schema keys and array indices (`$` for the root). Unrecognized keys the model
|
|
13
|
+
* invented are carried only as the `unrecognized_keys` code, never by name.
|
|
14
|
+
*/
|
|
15
|
+
export const MAX_CODING_OUTPUT_ISSUES = 8;
|
|
16
|
+
export const SAFE_CODING_OUTPUT_ISSUE = /^(?:\$|[a-z][A-Za-z]{0,31}(?:\.(?:[a-z][A-Za-z]{0,31}|\d{1,3})){0,7}):[a-z_]{1,32}$/;
|
|
9
17
|
const MAX_REPOSITORY_INPUT_BYTES = 512;
|
|
10
18
|
const MAX_REF_BYTES = 255;
|
|
11
19
|
const MAX_MODEL_BYTES = 128;
|
|
@@ -108,6 +116,24 @@ const repositorySchema = z
|
|
|
108
116
|
.transform(normalizeGitHubRepository);
|
|
109
117
|
export const CodingBaseRefSchema = z.string().refine(isGitRef, "must be a safe branch ref").transform(normalizeGitRef);
|
|
110
118
|
export const CodingTaskOverrideSchema = boundedText(MAX_CODING_TASK_BYTES);
|
|
119
|
+
/**
|
|
120
|
+
* A coding worker receives only its task text, so a coding agent's own
|
|
121
|
+
* instructions (its systemPrompt) travel inside that text, ahead of the
|
|
122
|
+
* request. Blank instructions leave the task unchanged. The combination must
|
|
123
|
+
* still fit MAX_CODING_TASK_BYTES; exceeding it is an error naming both parts
|
|
124
|
+
* rather than a silent truncation of either.
|
|
125
|
+
*/
|
|
126
|
+
export function composeCodingTask(instructions, task) {
|
|
127
|
+
const standing = instructions?.trim();
|
|
128
|
+
if (!standing)
|
|
129
|
+
return task;
|
|
130
|
+
const composed = `Standing instructions for this coding agent:\n${standing}\n\nRequest:\n${task}`;
|
|
131
|
+
if (byteLength(composed) > MAX_CODING_TASK_BYTES) {
|
|
132
|
+
throw new Error(`The coding agent's instructions (${byteLength(standing)} bytes) plus this task (${byteLength(task)} bytes) ` +
|
|
133
|
+
`exceed the ${MAX_CODING_TASK_BYTES}-byte coding task limit; shorten one of them.`);
|
|
134
|
+
}
|
|
135
|
+
return composed;
|
|
136
|
+
}
|
|
111
137
|
const runIdSchema = boundedText(MAX_RUN_ID_BYTES, true).refine((value) => /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value), "must be an opaque identifier");
|
|
112
138
|
const modelSchema = boundedText(MAX_MODEL_BYTES, true).refine((value) => /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value), "must be a model identifier");
|
|
113
139
|
const usageSchema = z
|
|
@@ -117,6 +143,30 @@ const usageSchema = z
|
|
|
117
143
|
costUsd: z.number().finite().nonnegative().max(MAX_COST_USD),
|
|
118
144
|
})
|
|
119
145
|
.strict();
|
|
146
|
+
const SAFE_TAG = new RegExp(`^[A-Za-z0-9][A-Za-z0-9._/-]{0,${MAX_TAG_BYTES - 1}}$`);
|
|
147
|
+
/**
|
|
148
|
+
* The tag only labels a pull request title, so a model's malformed tag must not
|
|
149
|
+
* sink an otherwise finished run. A tag that already passes is kept as-is; any
|
|
150
|
+
* other string becomes a lowercase slug (runs of disallowed characters to "-",
|
|
151
|
+
* no leading or trailing punctuation, at most MAX_TAG_BYTES), and one with
|
|
152
|
+
* nothing usable left, or a non-string, becomes null. GitHub finalization still
|
|
153
|
+
* re-validates whatever survives.
|
|
154
|
+
*/
|
|
155
|
+
export function normalizeCodingTag(value) {
|
|
156
|
+
if (value === undefined || value === null)
|
|
157
|
+
return value;
|
|
158
|
+
if (typeof value !== "string")
|
|
159
|
+
return null;
|
|
160
|
+
if (SAFE_TAG.test(value))
|
|
161
|
+
return value;
|
|
162
|
+
const slug = value
|
|
163
|
+
.toLowerCase()
|
|
164
|
+
.replace(/[^a-z0-9._/-]+/g, "-")
|
|
165
|
+
.replace(/^[^a-z0-9]+/, "")
|
|
166
|
+
.slice(0, MAX_TAG_BYTES)
|
|
167
|
+
.replace(/[^a-z0-9]+$/, "");
|
|
168
|
+
return SAFE_TAG.test(slug) ? slug : null;
|
|
169
|
+
}
|
|
120
170
|
/**
|
|
121
171
|
* A short, caller-visible reference (e.g. a ticket ID) surfaced in the PR title. Never free text.
|
|
122
172
|
* Accepts null as well as undefined: OpenAI's strict Structured Outputs mode requires every
|
|
@@ -126,7 +176,7 @@ const usageSchema = z
|
|
|
126
176
|
*/
|
|
127
177
|
const tagSchema = z
|
|
128
178
|
.string()
|
|
129
|
-
.regex(
|
|
179
|
+
.regex(SAFE_TAG, "must be a short, safe tag")
|
|
130
180
|
.nullable()
|
|
131
181
|
.optional()
|
|
132
182
|
.transform((value) => value ?? undefined);
|
|
@@ -175,7 +225,8 @@ export const CodingAgentOutputSchema = z
|
|
|
175
225
|
outcome: z.enum(["changes_ready", "no_changes", "budget_exhausted"]),
|
|
176
226
|
summary: boundedText(MAX_CODING_SUMMARY_BYTES),
|
|
177
227
|
tests: z.array(testResultSchema).max(MAX_CODING_TESTS),
|
|
178
|
-
|
|
228
|
+
// Model-written, so normalized rather than rejected; see normalizeCodingTag.
|
|
229
|
+
tag: z.preprocess(normalizeCodingTag, tagSchema),
|
|
179
230
|
})
|
|
180
231
|
.strict()
|
|
181
232
|
.transform((value) => ({
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { type CodingAgentOutput } from "../coding/protocol.js";
|
|
2
|
+
/**
|
|
3
|
+
* Reads a regular file of at most `maxBytes`, refusing symlinks and anything
|
|
4
|
+
* that is not a regular file.
|
|
5
|
+
*
|
|
6
|
+
* Every check runs against the open handle, never against the path before
|
|
7
|
+
* opening it: an lstat followed by a read of the same path leaves a window in
|
|
8
|
+
* which the file can be swapped for a symlink or a larger file. O_NOFOLLOW
|
|
9
|
+
* refuses a symlink at open time, O_NONBLOCK keeps a FIFO from hanging the
|
|
10
|
+
* open, and fstat on the descriptor checks what was actually opened. The lstat
|
|
11
|
+
* afterwards covers platforms without O_NOFOLLOW: it must be the same inode.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readBoundedRegularFile(path: string, maxBytes: number, errorCode: string): Promise<string>;
|
|
2
14
|
export declare function readCodingInput(path: string): Promise<{
|
|
3
15
|
repository: string;
|
|
4
16
|
schemaVersion: 1;
|
|
@@ -1,12 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
2
3
|
import { dirname, join } from "node:path";
|
|
3
4
|
import { MAX_CODING_ARTIFACT_BYTES, parseCodingTaskInputJson } from "../coding/protocol.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Reads a regular file of at most `maxBytes`, refusing symlinks and anything
|
|
7
|
+
* that is not a regular file.
|
|
8
|
+
*
|
|
9
|
+
* Every check runs against the open handle, never against the path before
|
|
10
|
+
* opening it: an lstat followed by a read of the same path leaves a window in
|
|
11
|
+
* which the file can be swapped for a symlink or a larger file. O_NOFOLLOW
|
|
12
|
+
* refuses a symlink at open time, O_NONBLOCK keeps a FIFO from hanging the
|
|
13
|
+
* open, and fstat on the descriptor checks what was actually opened. The lstat
|
|
14
|
+
* afterwards covers platforms without O_NOFOLLOW: it must be the same inode.
|
|
15
|
+
*/
|
|
16
|
+
export async function readBoundedRegularFile(path, maxBytes, errorCode) {
|
|
17
|
+
let file;
|
|
18
|
+
try {
|
|
19
|
+
file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// ELOOP: the path is a symlink.
|
|
23
|
+
if (error.code === "ELOOP")
|
|
24
|
+
throw new Error(errorCode, { cause: error });
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const metadata = await file.stat();
|
|
29
|
+
if (!metadata.isFile() || metadata.size > maxBytes)
|
|
30
|
+
throw new Error(errorCode);
|
|
31
|
+
const link = await lstat(path);
|
|
32
|
+
if (link.isSymbolicLink() || link.ino !== metadata.ino || link.dev !== metadata.dev)
|
|
33
|
+
throw new Error(errorCode);
|
|
34
|
+
const buffer = Buffer.alloc(maxBytes + 1);
|
|
35
|
+
let length = 0;
|
|
36
|
+
while (length < buffer.length) {
|
|
37
|
+
const { bytesRead } = await file.read(buffer, length, buffer.length - length, length);
|
|
38
|
+
if (bytesRead === 0)
|
|
39
|
+
break;
|
|
40
|
+
length += bytesRead;
|
|
41
|
+
}
|
|
42
|
+
if (length > maxBytes)
|
|
43
|
+
throw new Error(errorCode);
|
|
44
|
+
return buffer.subarray(0, length).toString("utf8");
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await file.close();
|
|
8
48
|
}
|
|
9
|
-
|
|
49
|
+
}
|
|
50
|
+
export async function readCodingInput(path) {
|
|
51
|
+
return parseCodingTaskInputJson(await readBoundedRegularFile(path, MAX_CODING_ARTIFACT_BYTES, "coding_input_invalid_file"));
|
|
10
52
|
}
|
|
11
53
|
export async function writeCodingOutputAtomic(path, output) {
|
|
12
54
|
const directory = dirname(path);
|
|
@@ -20,6 +20,7 @@ export declare const CODING_OUTPUT_JSON_SCHEMA: {
|
|
|
20
20
|
};
|
|
21
21
|
readonly tag: {
|
|
22
22
|
readonly type: readonly ["string", "null"];
|
|
23
|
+
readonly description: "Optional short label shown in the pull request title, such as a ticket id. At most 32 characters: letters, digits, '.', '_', '/', '-', starting with a letter or digit, with no spaces. Example: \"add-jokes\". Use null when there is no natural label.";
|
|
23
24
|
};
|
|
24
25
|
readonly tests: {
|
|
25
26
|
readonly type: "array";
|
|
@@ -20,7 +20,10 @@ export const CODING_OUTPUT_JSON_SCHEMA = {
|
|
|
20
20
|
runId: { type: "string" },
|
|
21
21
|
outcome: { type: "string", enum: ["changes_ready", "no_changes", "budget_exhausted"] },
|
|
22
22
|
summary: { type: "string" },
|
|
23
|
-
tag: {
|
|
23
|
+
tag: {
|
|
24
|
+
type: ["string", "null"],
|
|
25
|
+
description: "Optional short label shown in the pull request title, such as a ticket id. At most 32 characters: letters, digits, '.', '_', '/', '-', starting with a letter or digit, with no spaces. Example: \"add-jokes\". Use null when there is no natural label.",
|
|
26
|
+
},
|
|
24
27
|
tests: {
|
|
25
28
|
type: "array",
|
|
26
29
|
maxItems: 64,
|
|
@@ -1 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* For `coding_output_invalid`, where the model's final answer failed the output
|
|
3
|
+
* schema: the failing schema paths and issue codes, so the operator log can say
|
|
4
|
+
* which field was wrong without ever carrying the value. Undefined when the
|
|
5
|
+
* cause is not a schema failure.
|
|
6
|
+
*/
|
|
7
|
+
export declare function safeOutputIssues(error: unknown): string[] | undefined;
|
|
1
8
|
export declare function safeWorkerErrorCode(error: unknown): string;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ZodError } from "zod";
|
|
2
|
+
import { MAX_CODING_OUTPUT_ISSUES, SAFE_CODING_OUTPUT_ISSUE } from "../coding/protocol.js";
|
|
1
3
|
const SAFE_WORKER_ERROR_CODES = new Set([
|
|
2
4
|
"wardby_proxy_url_missing",
|
|
3
5
|
"wardby_run_capability_missing",
|
|
@@ -13,6 +15,22 @@ const SAFE_WORKER_ERROR_CODES = new Set([
|
|
|
13
15
|
"coding_output_invalid",
|
|
14
16
|
"coding_output_run_mismatch",
|
|
15
17
|
]);
|
|
18
|
+
/**
|
|
19
|
+
* For `coding_output_invalid`, where the model's final answer failed the output
|
|
20
|
+
* schema: the failing schema paths and issue codes, so the operator log can say
|
|
21
|
+
* which field was wrong without ever carrying the value. Undefined when the
|
|
22
|
+
* cause is not a schema failure.
|
|
23
|
+
*/
|
|
24
|
+
export function safeOutputIssues(error) {
|
|
25
|
+
const cause = error instanceof Error ? error.cause : undefined;
|
|
26
|
+
if (!(cause instanceof ZodError))
|
|
27
|
+
return undefined;
|
|
28
|
+
const issues = cause.issues
|
|
29
|
+
.map((issue) => `${issue.path.length === 0 ? "$" : issue.path.join(".")}:${issue.code}`)
|
|
30
|
+
.filter((issue) => SAFE_CODING_OUTPUT_ISSUE.test(issue))
|
|
31
|
+
.slice(0, MAX_CODING_OUTPUT_ISSUES);
|
|
32
|
+
return issues.length > 0 ? issues : undefined;
|
|
33
|
+
}
|
|
16
34
|
export function safeWorkerErrorCode(error) {
|
|
17
35
|
if (!(error instanceof Error))
|
|
18
36
|
return "worker_failed";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readCodingInput, writeCodingOutputAtomic } from "./artifact.js";
|
|
3
3
|
import { runCodingWorker } from "./driver.js";
|
|
4
|
-
import { safeWorkerErrorCode } from "./errors.js";
|
|
4
|
+
import { safeOutputIssues, safeWorkerErrorCode } from "./errors.js";
|
|
5
5
|
import { createCodexSdkClient } from "./sdk.js";
|
|
6
6
|
const INPUT_PATH = "/run/wardby/input/input.json";
|
|
7
7
|
const OUTPUT_PATH = "/run/wardby/output/result.json";
|
|
@@ -38,6 +38,7 @@ catch (error) {
|
|
|
38
38
|
: safeCode === "worker_failed"
|
|
39
39
|
? `worker_${stage}_failed`
|
|
40
40
|
: safeCode;
|
|
41
|
-
|
|
41
|
+
const issues = code === "coding_output_invalid" ? safeOutputIssues(error) : undefined;
|
|
42
|
+
process.stderr.write(`${JSON.stringify({ error: code, ...(issues ? { issues } : {}) })}\n`);
|
|
42
43
|
process.exitCode = controller.signal.aborted ? 143 : 1;
|
|
43
44
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* budget of 0, which the engine's turn-1 gate already refuses exactly
|
|
13
13
|
* like any other zero/negative budget.
|
|
14
14
|
*/
|
|
15
|
-
import type { Agent, BudgetGroup, PrismaClient } from "
|
|
15
|
+
import type { Agent, BudgetGroup, PrismaClient } from "#prisma";
|
|
16
16
|
export type Period = "day" | "week" | "month";
|
|
17
17
|
export type BudgetGroupsDb = Pick<PrismaClient, "budgetGroup" | "run" | "agent">;
|
|
18
18
|
/**
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Called on the scheduler leader's tick and immediately in any process
|
|
15
15
|
* whose coding run just finished (ContainerExecutor onSlotReleased).
|
|
16
16
|
*/
|
|
17
|
-
import type { PrismaClient } from "
|
|
17
|
+
import type { PrismaClient } from "#prisma";
|
|
18
18
|
import type { Executor } from "../providers/executor/types.js";
|
|
19
19
|
export declare const CODING_QUEUE_TIMEOUT_ERROR = "coding_queue_timeout";
|
|
20
20
|
export type CodingQueueDb = Pick<PrismaClient, "run" | "codingRun" | "$transaction">;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* by a per-attachment `boundName` (sharedDatastore.get(boundName, key)) —
|
|
5
5
|
* same boundName convention as AgentSecret.
|
|
6
6
|
*/
|
|
7
|
-
import type { PrismaClient, Datastore as DatastoreRow } from "
|
|
7
|
+
import type { PrismaClient, Datastore as DatastoreRow } from "#prisma";
|
|
8
8
|
import type { Datastore, DatastoreSetOptions, DatastoreValue } from "../providers/datastore/types.js";
|
|
9
9
|
export type DatastoreMetadata = Pick<DatastoreRow, "id" | "name" | "ownerId" | "createdAt" | "updatedAt">;
|
|
10
10
|
export interface SharedDatastoreAccessor {
|
package/dist/core/db.d.ts
CHANGED
|
@@ -1,2 +1,36 @@
|
|
|
1
|
-
import { PrismaClient } from "
|
|
2
|
-
|
|
1
|
+
import { PrismaClient } from "#prisma";
|
|
2
|
+
/**
|
|
3
|
+
* Connection-pool sizing for the node-postgres pool behind @prisma/adapter-pg.
|
|
4
|
+
*
|
|
5
|
+
* Prisma 6's query engine owned the pool: `connection_limit` defaulted to
|
|
6
|
+
* cpus * 2 + 1 and a caller waited at most `pool_timeout` = 10 s for a free
|
|
7
|
+
* connection (then P2024). Prisma 7 hands the pool to `pg`, whose defaults are
|
|
8
|
+
* max = 10 and connectionTimeoutMillis = 0 — wait for a connection *forever*,
|
|
9
|
+
* so a saturated pool would hang requests instead of failing them. Both values
|
|
10
|
+
* are therefore set explicitly, to Prisma 6's defaults:
|
|
11
|
+
*
|
|
12
|
+
* - max: availableParallelism() * 2 + 1 (Prisma 6 counted physical cores; the
|
|
13
|
+
* logical count is the closest portable Node equivalent).
|
|
14
|
+
* - connectionTimeoutMillis: 10 000 (Prisma 6's pool_timeout). A timeout now
|
|
15
|
+
* surfaces as a pg "timeout exceeded when trying to connect" error, not P2024.
|
|
16
|
+
*
|
|
17
|
+
* `pg` ignores Prisma's own URL parameters, so the two that tuned these same
|
|
18
|
+
* knobs (`connection_limit`, `pool_timeout` in seconds) are still honoured here
|
|
19
|
+
* for URLs that set them. idleTimeoutMillis stays at pg's 10 s default.
|
|
20
|
+
*
|
|
21
|
+
* TLS is whatever the URL asks for (`sslmode`, `sslrootcert`, ...); certificate
|
|
22
|
+
* validation is never disabled here.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_POOL_TIMEOUT_MS = 10000;
|
|
25
|
+
export declare function defaultPoolMax(): number;
|
|
26
|
+
export interface PoolSettings {
|
|
27
|
+
max: number;
|
|
28
|
+
connectionTimeoutMillis: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function poolSettings(connectionString: string): PoolSettings;
|
|
31
|
+
/**
|
|
32
|
+
* The one way wardby builds a Prisma client. `url` defaults to DATABASE_URL
|
|
33
|
+
* as read at call time.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createPrismaClient(url?: string | undefined): PrismaClient;
|
|
36
|
+
export declare const prisma: PrismaClient;
|
package/dist/core/db.js
CHANGED
|
@@ -1,2 +1,69 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { availableParallelism } from "node:os";
|
|
2
|
+
import { PrismaPg } from "@prisma/adapter-pg";
|
|
3
|
+
import { PrismaClient } from "#prisma";
|
|
4
|
+
/**
|
|
5
|
+
* Connection-pool sizing for the node-postgres pool behind @prisma/adapter-pg.
|
|
6
|
+
*
|
|
7
|
+
* Prisma 6's query engine owned the pool: `connection_limit` defaulted to
|
|
8
|
+
* cpus * 2 + 1 and a caller waited at most `pool_timeout` = 10 s for a free
|
|
9
|
+
* connection (then P2024). Prisma 7 hands the pool to `pg`, whose defaults are
|
|
10
|
+
* max = 10 and connectionTimeoutMillis = 0 — wait for a connection *forever*,
|
|
11
|
+
* so a saturated pool would hang requests instead of failing them. Both values
|
|
12
|
+
* are therefore set explicitly, to Prisma 6's defaults:
|
|
13
|
+
*
|
|
14
|
+
* - max: availableParallelism() * 2 + 1 (Prisma 6 counted physical cores; the
|
|
15
|
+
* logical count is the closest portable Node equivalent).
|
|
16
|
+
* - connectionTimeoutMillis: 10 000 (Prisma 6's pool_timeout). A timeout now
|
|
17
|
+
* surfaces as a pg "timeout exceeded when trying to connect" error, not P2024.
|
|
18
|
+
*
|
|
19
|
+
* `pg` ignores Prisma's own URL parameters, so the two that tuned these same
|
|
20
|
+
* knobs (`connection_limit`, `pool_timeout` in seconds) are still honoured here
|
|
21
|
+
* for URLs that set them. idleTimeoutMillis stays at pg's 10 s default.
|
|
22
|
+
*
|
|
23
|
+
* TLS is whatever the URL asks for (`sslmode`, `sslrootcert`, ...); certificate
|
|
24
|
+
* validation is never disabled here.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_POOL_TIMEOUT_MS = 10_000;
|
|
27
|
+
export function defaultPoolMax() {
|
|
28
|
+
return availableParallelism() * 2 + 1;
|
|
29
|
+
}
|
|
30
|
+
export function poolSettings(connectionString) {
|
|
31
|
+
const settings = { max: defaultPoolMax(), connectionTimeoutMillis: DEFAULT_POOL_TIMEOUT_MS };
|
|
32
|
+
let params;
|
|
33
|
+
try {
|
|
34
|
+
params = new URL(connectionString).searchParams;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return settings;
|
|
38
|
+
}
|
|
39
|
+
const limit = Number(params.get("connection_limit"));
|
|
40
|
+
if (Number.isInteger(limit) && limit > 0)
|
|
41
|
+
settings.max = limit;
|
|
42
|
+
const timeout = params.get("pool_timeout");
|
|
43
|
+
if (timeout !== null && timeout !== "" && Number.isFinite(Number(timeout)) && Number(timeout) >= 0) {
|
|
44
|
+
// Prisma's pool_timeout=0 meant "no timeout"; pg's 0 means the same.
|
|
45
|
+
settings.connectionTimeoutMillis = Number(timeout) * 1000;
|
|
46
|
+
}
|
|
47
|
+
return settings;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Without a URL, pg would silently fall back to PGHOST/localhost:5432 as the
|
|
51
|
+
* OS user. Prisma 6 failed the first query instead ("Environment variable not
|
|
52
|
+
* found: DATABASE_URL"); keep that: construction succeeds, connecting fails.
|
|
53
|
+
*/
|
|
54
|
+
class MissingUrlAdapter extends PrismaPg {
|
|
55
|
+
async connect() {
|
|
56
|
+
throw new Error("DATABASE_URL is not set: cannot connect to the database.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The one way wardby builds a Prisma client. `url` defaults to DATABASE_URL
|
|
61
|
+
* as read at call time.
|
|
62
|
+
*/
|
|
63
|
+
export function createPrismaClient(url = process.env.DATABASE_URL) {
|
|
64
|
+
const adapter = url
|
|
65
|
+
? new PrismaPg({ connectionString: url, ...poolSettings(url) })
|
|
66
|
+
: new MissingUrlAdapter({ connectionString: "" });
|
|
67
|
+
return new PrismaClient({ adapter });
|
|
68
|
+
}
|
|
69
|
+
export const prisma = createPrismaClient();
|
package/dist/core/dispatch.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Prisma, PrismaClient, Run, RunTrigger, Task } from "
|
|
1
|
+
import type { Prisma, PrismaClient, Run, RunTrigger, Task } from "#prisma";
|
|
2
2
|
import type { Executor } from "../providers/executor/types.js";
|
|
3
3
|
export type DispatchDb = Pick<PrismaClient, "agent" | "run" | "codingRun" | "task" | "webhook" | "$transaction" | "$queryRaw">;
|
|
4
4
|
type DispatchTx = Pick<Prisma.TransactionClient, "agent" | "run" | "codingRun" | "task" | "webhook" | "$queryRaw">;
|
|
@@ -63,6 +63,21 @@ export interface DispatchRunResult {
|
|
|
63
63
|
run: Run;
|
|
64
64
|
task?: Task;
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* A transient transaction conflict in the Serializable persist transaction:
|
|
68
|
+
* a serialization failure (40001) or a deadlock (40P01). It arrives in one of
|
|
69
|
+
* three shapes:
|
|
70
|
+
* - on a model statement: P2034;
|
|
71
|
+
* - on a raw statement ($queryRaw, e.g. the FOR UPDATE SKIP LOCKED below, or
|
|
72
|
+
* anything a beforePersist callback runs): P2010, with the SQLSTATE at
|
|
73
|
+
* meta.driverAdapterError.cause.originalCode under the pg driver adapter
|
|
74
|
+
* (Prisma 7) -- Prisma 6's engine put 40001 at meta.code, still accepted;
|
|
75
|
+
* - at COMMIT (e.g. SSI write skew): an unwrapped DriverAdapterError, no
|
|
76
|
+
* code or meta, with the SQLSTATE at cause.originalCode.
|
|
77
|
+
*
|
|
78
|
+
* @internal Exported only for the real-PostgreSQL tests.
|
|
79
|
+
*/
|
|
80
|
+
export declare function isSerializationConflict(err: unknown): boolean;
|
|
66
81
|
/**
|
|
67
82
|
* Record an executor-level failure on a run that never reached a terminal
|
|
68
83
|
* state itself. Deliberately idempotent: the same failure can arrive twice —
|
package/dist/core/dispatch.js
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
|
-
import { CODING_PROTOCOL_VERSION, CodingTaskInputSchema, normalizeGitHubRepository, publicCodingRunResult, } from "../coding/protocol.js";
|
|
1
|
+
import { CODING_PROTOCOL_VERSION, CodingTaskInputSchema, normalizeGitHubRepository, publicCodingRunResult, composeCodingTask, } from "../coding/protocol.js";
|
|
2
2
|
import { assertCodingProviderModel } from "../coding/provider.js";
|
|
3
3
|
import { logger } from "./logger.js";
|
|
4
4
|
const dispatchLog = logger.child({ module: "dispatch" });
|
|
5
|
-
|
|
5
|
+
/** SQLSTATEs worth retrying the persist transaction for: serialization failure, deadlock. */
|
|
6
|
+
const RETRYABLE_SQLSTATES = new Set(["40001", "40P01"]);
|
|
7
|
+
/**
|
|
8
|
+
* A transient transaction conflict in the Serializable persist transaction:
|
|
9
|
+
* a serialization failure (40001) or a deadlock (40P01). It arrives in one of
|
|
10
|
+
* three shapes:
|
|
11
|
+
* - on a model statement: P2034;
|
|
12
|
+
* - on a raw statement ($queryRaw, e.g. the FOR UPDATE SKIP LOCKED below, or
|
|
13
|
+
* anything a beforePersist callback runs): P2010, with the SQLSTATE at
|
|
14
|
+
* meta.driverAdapterError.cause.originalCode under the pg driver adapter
|
|
15
|
+
* (Prisma 7) -- Prisma 6's engine put 40001 at meta.code, still accepted;
|
|
16
|
+
* - at COMMIT (e.g. SSI write skew): an unwrapped DriverAdapterError, no
|
|
17
|
+
* code or meta, with the SQLSTATE at cause.originalCode.
|
|
18
|
+
*
|
|
19
|
+
* @internal Exported only for the real-PostgreSQL tests.
|
|
20
|
+
*/
|
|
21
|
+
export function isSerializationConflict(err) {
|
|
6
22
|
if (!err || typeof err !== "object")
|
|
7
23
|
return false;
|
|
8
24
|
const candidate = err;
|
|
9
|
-
|
|
25
|
+
if (candidate.code === "P2034")
|
|
26
|
+
return true;
|
|
27
|
+
if (candidate.code === "P2010") {
|
|
28
|
+
const sqlState = candidate.meta?.driverAdapterError?.cause?.originalCode;
|
|
29
|
+
return (typeof sqlState === "string" && RETRYABLE_SQLSTATES.has(sqlState)) || candidate.meta?.code === "40001";
|
|
30
|
+
}
|
|
31
|
+
if (candidate.name === "DriverAdapterError") {
|
|
32
|
+
const sqlState = candidate.cause?.originalCode;
|
|
33
|
+
return typeof sqlState === "string" && RETRYABLE_SQLSTATES.has(sqlState);
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
10
36
|
}
|
|
11
37
|
/**
|
|
12
38
|
* Record an executor-level failure on a run that never reached a terminal
|
|
@@ -66,9 +92,11 @@ export async function dispatchRun(options) {
|
|
|
66
92
|
if (!agent.codingProfile)
|
|
67
93
|
throw new Error(`Coding agent "${agent.id}" has no coding profile.`);
|
|
68
94
|
assertCodingProviderModel(agent.codingProfile.provider, agent.model);
|
|
69
|
-
const
|
|
70
|
-
if (!
|
|
95
|
+
const request = options.codingTask ?? agent.codingProfile.defaultTask;
|
|
96
|
+
if (!request)
|
|
71
97
|
throw new Error(`Coding agent "${agent.id}" requires a task.`);
|
|
98
|
+
// The worker sees only the task text, so the agent's own instructions ride in it.
|
|
99
|
+
const task = composeCodingTask(agent.systemPrompt, request);
|
|
72
100
|
const budgetUsd = options.budgetUsdOverride ?? Number(agent.budgetUsd);
|
|
73
101
|
let baseRef = options.codingBaseRef ?? agent.codingProfile.baseRef;
|
|
74
102
|
let headRef = `wardby/run-${run.id}`;
|
|
@@ -57,11 +57,10 @@
|
|
|
57
57
|
* later in the process, would replace this Agent with an h2-enabled one and
|
|
58
58
|
* silently restore the corruption — the price of fixing this with a dispatcher
|
|
59
59
|
* instead of owning the fetch stack. Nothing in the runtime image does that
|
|
60
|
-
* today: the only other `setGlobalDispatcher`
|
|
61
|
-
* copies bundled inside `@prisma/client/runtime/binary.*` (guarded by the same
|
|
62
|
-
* `=== undefined` check, and the binary engine is not the default) and
|
|
60
|
+
* today: the only other `setGlobalDispatcher` call in the tree is
|
|
63
61
|
* `node-fetch-native` under the `prisma` CLI, which `deploy/Dockerfile:25`
|
|
64
|
-
* asserts is absent from the runtime image.
|
|
62
|
+
* asserts is absent from the runtime image. (Prisma 7's client runtime and
|
|
63
|
+
* `@prisma/adapter-pg` contain none; Prisma 6's `runtime/binary.*` did.)
|
|
65
64
|
*
|
|
66
65
|
* Restoring Node's OWN dispatcher instead is not possible, though not for the
|
|
67
66
|
* reason one might guess: undici defines the legacy symbol `configurable: false`
|
package/dist/core/lease.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* expired, or already ours, and `RETURNING` tells the caller which case it
|
|
8
8
|
* was — zero rows means someone else holds an unexpired lease.
|
|
9
9
|
*/
|
|
10
|
-
import type { PrismaClient } from "
|
|
10
|
+
import type { PrismaClient } from "#prisma";
|
|
11
11
|
export type LeaseDb = Pick<PrismaClient, "$queryRaw">;
|
|
12
12
|
/** Attempts to acquire (or renew) the lease for `scope`. Returns whether `holder` now holds it. */
|
|
13
13
|
export declare function tryAcquireLease(db: LeaseDb, scope: string, holder: string, ttlMs: number): Promise<boolean>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates `path` as a private directory, or verifies an existing one is safe to
|
|
3
|
+
* trust.
|
|
4
|
+
*
|
|
5
|
+
* `mkdir(..., { mode: 0o700 })` does nothing to a directory that already
|
|
6
|
+
* exists. The coding state roots default to fixed names under the OS temp
|
|
7
|
+
* directory, so on a shared host another user could create one first -- or
|
|
8
|
+
* point it somewhere else with a symlink -- and then read or plant the job
|
|
9
|
+
* records, workspaces and artifacts wardby keeps there. This refuses:
|
|
10
|
+
*
|
|
11
|
+
* - a symlink, or anything that is not a directory;
|
|
12
|
+
* - a directory owned by a different user (root excepted: an operator-mounted
|
|
13
|
+
* volume is commonly root-owned);
|
|
14
|
+
* - a directory any other user can write to.
|
|
15
|
+
*
|
|
16
|
+
* Group write is allowed, since a Kubernetes `fsGroup` volume is group-writable
|
|
17
|
+
* by design and its group is chosen by the operator. Ownership and mode are not
|
|
18
|
+
* checked on platforms without POSIX uids.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ensurePrivateDirectory(path: string): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { lstat, mkdir } from "node:fs/promises";
|
|
2
|
+
/**
|
|
3
|
+
* Creates `path` as a private directory, or verifies an existing one is safe to
|
|
4
|
+
* trust.
|
|
5
|
+
*
|
|
6
|
+
* `mkdir(..., { mode: 0o700 })` does nothing to a directory that already
|
|
7
|
+
* exists. The coding state roots default to fixed names under the OS temp
|
|
8
|
+
* directory, so on a shared host another user could create one first -- or
|
|
9
|
+
* point it somewhere else with a symlink -- and then read or plant the job
|
|
10
|
+
* records, workspaces and artifacts wardby keeps there. This refuses:
|
|
11
|
+
*
|
|
12
|
+
* - a symlink, or anything that is not a directory;
|
|
13
|
+
* - a directory owned by a different user (root excepted: an operator-mounted
|
|
14
|
+
* volume is commonly root-owned);
|
|
15
|
+
* - a directory any other user can write to.
|
|
16
|
+
*
|
|
17
|
+
* Group write is allowed, since a Kubernetes `fsGroup` volume is group-writable
|
|
18
|
+
* by design and its group is chosen by the operator. Ownership and mode are not
|
|
19
|
+
* checked on platforms without POSIX uids.
|
|
20
|
+
*/
|
|
21
|
+
export async function ensurePrivateDirectory(path) {
|
|
22
|
+
await mkdir(path, { recursive: true, mode: 0o700 });
|
|
23
|
+
const metadata = await lstat(path);
|
|
24
|
+
if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
|
|
25
|
+
throw new Error(`${path} must be a real directory, not a symlink.`);
|
|
26
|
+
}
|
|
27
|
+
const uid = process.getuid?.();
|
|
28
|
+
if (uid === undefined)
|
|
29
|
+
return;
|
|
30
|
+
if (metadata.uid !== uid && metadata.uid !== 0) {
|
|
31
|
+
throw new Error(`${path} is owned by uid ${metadata.uid}, not this process (uid ${uid}). Remove it or choose another path.`);
|
|
32
|
+
}
|
|
33
|
+
if ((metadata.mode & 0o002) !== 0) {
|
|
34
|
+
throw new Error(`${path} is writable by other users. Run chmod o-w on it or choose another path.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* second one's WHERE simply matches zero rows — double-reconcile is a
|
|
29
29
|
* no-op, not a race.
|
|
30
30
|
*/
|
|
31
|
-
import type { PrismaClient } from "
|
|
31
|
+
import type { PrismaClient } from "#prisma";
|
|
32
32
|
import type { Executor } from "../providers/executor/types.js";
|
|
33
33
|
export type ReconcilerDb = Pick<PrismaClient, "run">;
|
|
34
34
|
/** Runs one reconciliation pass. Returns the number of runs marked `lost`. */
|
package/dist/core/runner.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* down) lives inside the engine now — there is exactly one place that
|
|
17
17
|
* reasons about cost, not one here plus one in the engine.
|
|
18
18
|
*/
|
|
19
|
-
import type { PrismaClient, Run, RunTrigger } from "
|
|
19
|
+
import type { PrismaClient, Run, RunTrigger } from "#prisma";
|
|
20
20
|
import type { ProviderRegistry } from "../providers/index.js";
|
|
21
21
|
import { type StepRunner } from "../providers/engine/types.js";
|
|
22
22
|
import type { Executor } from "../providers/executor/types.js";
|
package/dist/core/scheduler.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* create — never by the lease alone (the lease only prevents concurrent
|
|
7
7
|
* *ticking*, not a race within a single tick against a future one).
|
|
8
8
|
*/
|
|
9
|
-
import type { Agent } from "
|
|
9
|
+
import type { Agent } from "#prisma";
|
|
10
10
|
import type { Executor } from "../providers/executor/types.js";
|
|
11
11
|
import { type DispatchDb } from "./dispatch.js";
|
|
12
12
|
export type SchedulerDb = DispatchDb;
|
package/dist/core/secrets.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* always resolve a name within one owner's (or, at get-time, one agent's)
|
|
7
7
|
* scope, never globally.
|
|
8
8
|
*/
|
|
9
|
-
import type { PrismaClient, Secret } from "
|
|
9
|
+
import type { PrismaClient, Secret } from "#prisma";
|
|
10
10
|
import type { SecretCipher } from "../providers/secrets/types.js";
|
|
11
11
|
export type SecretMetadata = Pick<Secret, "id" | "name" | "keyId" | "ownerId" | "createdAt" | "updatedAt">;
|
|
12
12
|
export interface SecretsAccessor {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PrismaClient } from "
|
|
1
|
+
import type { PrismaClient } from "#prisma";
|
|
2
2
|
import type { LoadedTool } from "../providers/engine/types.js";
|
|
3
3
|
import type { AgentMemoryStore } from "../providers/memory/types.js";
|
|
4
4
|
export declare const SUBAGENT_MEMORY_TOOL_NAMES: ReadonlySet<string>;
|
package/dist/core/webhooks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PrismaClient, Webhook } from "
|
|
1
|
+
import type { PrismaClient, Webhook } from "#prisma";
|
|
2
2
|
import type { Executor } from "../providers/executor/types.js";
|
|
3
3
|
export type WebhookMetadata = Pick<Webhook, "id" | "agentId" | "status" | "ownerId" | "createdAt" | "lastFiredAt">;
|
|
4
4
|
/** Creates a webhook and returns the raw secret ONCE — only its hash is ever stored. */
|
package/dist/env.js
CHANGED
|
@@ -10,4 +10,11 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import "./core/http-runtime.js";
|
|
12
12
|
import dotenvFlow from "dotenv-flow";
|
|
13
|
-
|
|
13
|
+
import { join, resolve } from "node:path";
|
|
14
|
+
const projectDir = resolve(process.env.WARDBY_PROJECT_DIR || process.cwd());
|
|
15
|
+
// A quickstart installation owns this directory, so load it before an
|
|
16
|
+
// application's root env files. dotenv-flow never overwrites an existing
|
|
17
|
+
// process variable, preserving explicit shell/container configuration as the
|
|
18
|
+
// highest-priority source in either mode.
|
|
19
|
+
dotenvFlow.config({ path: join(projectDir, ".wardby"), silent: true });
|
|
20
|
+
dotenvFlow.config({ path: projectDir });
|