donobu 5.39.1 → 5.41.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/codegen/runGenerateSiteTests.js +1 -1
- package/dist/envVars.d.ts +2 -0
- package/dist/envVars.js +12 -0
- package/dist/esm/codegen/runGenerateSiteTests.js +1 -1
- package/dist/esm/envVars.d.ts +2 -0
- package/dist/esm/envVars.js +12 -0
- package/dist/esm/lib/test/utils/donobuTestStack.js +1 -1
- package/dist/esm/main.d.ts +2 -0
- package/dist/esm/main.js +1 -1
- package/dist/esm/managers/AdminApiController.d.ts +1 -1
- package/dist/esm/managers/DonobuStack.d.ts +2 -0
- package/dist/esm/managers/DonobuStack.js +1 -1
- package/dist/esm/managers/TestsManager.js +5 -2
- package/dist/esm/persistence/DonobuApiClient.js +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceRegistry.d.ts +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceRegistry.js +7 -1
- package/dist/esm/persistence/suites/SuitesPersistenceRegistry.d.ts +1 -1
- package/dist/esm/persistence/suites/SuitesPersistenceRegistry.js +7 -1
- package/dist/esm/persistence/tests/TestsPersistenceDonobuApi.js +3 -0
- package/dist/esm/persistence/tests/TestsPersistenceRegistry.d.ts +1 -1
- package/dist/esm/persistence/tests/TestsPersistenceRegistry.js +7 -1
- package/dist/esm/persistence/tests/TestsPersistenceSqlite.js +4 -1
- package/dist/esm/persistence/tests/TestsPersistenceVolatile.js +4 -1
- package/dist/esm/reporter/render.js +27 -2
- package/dist/lib/test/utils/donobuTestStack.js +1 -1
- package/dist/main.d.ts +2 -0
- package/dist/main.js +1 -1
- package/dist/managers/AdminApiController.d.ts +1 -1
- package/dist/managers/DonobuStack.d.ts +2 -0
- package/dist/managers/DonobuStack.js +1 -1
- package/dist/managers/TestsManager.js +5 -2
- package/dist/persistence/DonobuApiClient.js +1 -1
- package/dist/persistence/flows/FlowsPersistenceRegistry.d.ts +1 -1
- package/dist/persistence/flows/FlowsPersistenceRegistry.js +7 -1
- package/dist/persistence/suites/SuitesPersistenceRegistry.d.ts +1 -1
- package/dist/persistence/suites/SuitesPersistenceRegistry.js +7 -1
- package/dist/persistence/tests/TestsPersistenceDonobuApi.js +3 -0
- package/dist/persistence/tests/TestsPersistenceRegistry.d.ts +1 -1
- package/dist/persistence/tests/TestsPersistenceRegistry.js +7 -1
- package/dist/persistence/tests/TestsPersistenceSqlite.js +4 -1
- package/dist/persistence/tests/TestsPersistenceVolatile.js +4 -1
- package/dist/reporter/render.js +27 -2
- package/package.json +1 -1
|
@@ -769,7 +769,7 @@ async function runGenerateSiteTests(opts, onProgress, shouldCancel) {
|
|
|
769
769
|
}
|
|
770
770
|
const storageState = await loadStorageState(storageStatePath);
|
|
771
771
|
assertNotCancelled('loading storage state');
|
|
772
|
-
const donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(snapshotEnv()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_REGION', 'AWS_S3_BUCKET', 'AWS_S3_REGION', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'GOOGLE_CLOUD_STORAGE_BUCKET', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
772
|
+
const donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(snapshotEnv()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_REGION', 'AWS_S3_BUCKET', 'AWS_S3_REGION', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'GOOGLE_CLOUD_STORAGE_BUCKET', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
773
773
|
await ensureGptAvailability(donobuStack, gptConfigName);
|
|
774
774
|
assertNotCancelled('ensuring GPT availability');
|
|
775
775
|
const allowedTools = buildAllowedTools(toolAllowlist, toolDenylist);
|
package/dist/envVars.d.ts
CHANGED
|
@@ -74,6 +74,7 @@ export declare const env: Env<{
|
|
|
74
74
|
AWS_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
75
75
|
EXPERIMENTAL_FEATURES_ENABLED: z.ZodDefault<z.ZodBoolean>;
|
|
76
76
|
DONOBU_API_KEY: z.ZodOptional<z.ZodString>;
|
|
77
|
+
DONOBU_PERSISTENCE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
77
78
|
GOOGLE_CLOUD_STORAGE_BUCKET: z.ZodOptional<z.ZodString>;
|
|
78
79
|
SCREENSHOT_TIMEOUT_MS: z.ZodDefault<z.ZodNumber>;
|
|
79
80
|
SELF_HEAL_TESTS_ENABLED: z.ZodOptional<z.ZodString>;
|
|
@@ -127,6 +128,7 @@ export declare const env: Env<{
|
|
|
127
128
|
AWS_ACCESS_KEY_ID?: string | undefined;
|
|
128
129
|
AWS_SECRET_ACCESS_KEY?: string | undefined;
|
|
129
130
|
DONOBU_API_KEY?: string | undefined;
|
|
131
|
+
DONOBU_PERSISTENCE_API_KEY?: string | undefined;
|
|
130
132
|
GOOGLE_CLOUD_STORAGE_BUCKET?: string | undefined;
|
|
131
133
|
SELF_HEAL_TESTS_ENABLED?: string | undefined;
|
|
132
134
|
PROXY_SERVER?: string | undefined;
|
package/dist/envVars.js
CHANGED
|
@@ -188,6 +188,18 @@ flows. If specified, AWS credentials must also be present.`),
|
|
|
188
188
|
.optional()
|
|
189
189
|
.describe(`The API key for the Donobu API. Used to create GPT configurations backed
|
|
190
190
|
by the Donobu API, and to persist flows via the Donobu API when set.`),
|
|
191
|
+
DONOBU_PERSISTENCE_API_KEY: v4_1.z
|
|
192
|
+
.string()
|
|
193
|
+
.optional()
|
|
194
|
+
.describe(`Persistence-only fallback for the Donobu API key. When set, the DONOBU
|
|
195
|
+
persistence layer will instantiate using this key if DONOBU_API_KEY is
|
|
196
|
+
absent — but unlike DONOBU_API_KEY, this value is NOT consulted by
|
|
197
|
+
DonobuFlowsManager.createGptClient's env-var fallback chain.
|
|
198
|
+
|
|
199
|
+
Intended for hosts (e.g. the Donobu Studio desktop app) that drive AI
|
|
200
|
+
inference through an explicit gpt-config / flow-runner agent and want
|
|
201
|
+
to enable Donobu Cloud persistence without their persistence credential
|
|
202
|
+
silently overriding the user's flow-runner pick.`),
|
|
191
203
|
GOOGLE_CLOUD_STORAGE_BUCKET: v4_1.z.string().optional()
|
|
192
204
|
.describe(`Directs Donobu flows to be persisted using this Google Cloud Storage
|
|
193
205
|
bucket.`),
|
|
@@ -769,7 +769,7 @@ async function runGenerateSiteTests(opts, onProgress, shouldCancel) {
|
|
|
769
769
|
}
|
|
770
770
|
const storageState = await loadStorageState(storageStatePath);
|
|
771
771
|
assertNotCancelled('loading storage state');
|
|
772
|
-
const donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(snapshotEnv()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_REGION', 'AWS_S3_BUCKET', 'AWS_S3_REGION', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'GOOGLE_CLOUD_STORAGE_BUCKET', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
772
|
+
const donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(snapshotEnv()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_REGION', 'AWS_S3_BUCKET', 'AWS_S3_REGION', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'GOOGLE_CLOUD_STORAGE_BUCKET', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
773
773
|
await ensureGptAvailability(donobuStack, gptConfigName);
|
|
774
774
|
assertNotCancelled('ensuring GPT availability');
|
|
775
775
|
const allowedTools = buildAllowedTools(toolAllowlist, toolDenylist);
|
package/dist/esm/envVars.d.ts
CHANGED
|
@@ -74,6 +74,7 @@ export declare const env: Env<{
|
|
|
74
74
|
AWS_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
75
75
|
EXPERIMENTAL_FEATURES_ENABLED: z.ZodDefault<z.ZodBoolean>;
|
|
76
76
|
DONOBU_API_KEY: z.ZodOptional<z.ZodString>;
|
|
77
|
+
DONOBU_PERSISTENCE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
77
78
|
GOOGLE_CLOUD_STORAGE_BUCKET: z.ZodOptional<z.ZodString>;
|
|
78
79
|
SCREENSHOT_TIMEOUT_MS: z.ZodDefault<z.ZodNumber>;
|
|
79
80
|
SELF_HEAL_TESTS_ENABLED: z.ZodOptional<z.ZodString>;
|
|
@@ -127,6 +128,7 @@ export declare const env: Env<{
|
|
|
127
128
|
AWS_ACCESS_KEY_ID?: string | undefined;
|
|
128
129
|
AWS_SECRET_ACCESS_KEY?: string | undefined;
|
|
129
130
|
DONOBU_API_KEY?: string | undefined;
|
|
131
|
+
DONOBU_PERSISTENCE_API_KEY?: string | undefined;
|
|
130
132
|
GOOGLE_CLOUD_STORAGE_BUCKET?: string | undefined;
|
|
131
133
|
SELF_HEAL_TESTS_ENABLED?: string | undefined;
|
|
132
134
|
PROXY_SERVER?: string | undefined;
|
package/dist/esm/envVars.js
CHANGED
|
@@ -188,6 +188,18 @@ flows. If specified, AWS credentials must also be present.`),
|
|
|
188
188
|
.optional()
|
|
189
189
|
.describe(`The API key for the Donobu API. Used to create GPT configurations backed
|
|
190
190
|
by the Donobu API, and to persist flows via the Donobu API when set.`),
|
|
191
|
+
DONOBU_PERSISTENCE_API_KEY: v4_1.z
|
|
192
|
+
.string()
|
|
193
|
+
.optional()
|
|
194
|
+
.describe(`Persistence-only fallback for the Donobu API key. When set, the DONOBU
|
|
195
|
+
persistence layer will instantiate using this key if DONOBU_API_KEY is
|
|
196
|
+
absent — but unlike DONOBU_API_KEY, this value is NOT consulted by
|
|
197
|
+
DonobuFlowsManager.createGptClient's env-var fallback chain.
|
|
198
|
+
|
|
199
|
+
Intended for hosts (e.g. the Donobu Studio desktop app) that drive AI
|
|
200
|
+
inference through an explicit gpt-config / flow-runner agent and want
|
|
201
|
+
to enable Donobu Cloud persistence without their persistence credential
|
|
202
|
+
silently overriding the user's flow-runner pick.`),
|
|
191
203
|
GOOGLE_CLOUD_STORAGE_BUCKET: v4_1.z.string().optional()
|
|
192
204
|
.describe(`Directs Donobu flows to be persisted using this Google Cloud Storage
|
|
193
205
|
bucket.`),
|
|
@@ -20,7 +20,7 @@ function getEnvSnapshot() {
|
|
|
20
20
|
}
|
|
21
21
|
async function getOrCreateDonobuStack() {
|
|
22
22
|
if (!donobuStack) {
|
|
23
|
-
donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(getEnvSnapshot()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
23
|
+
donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(getEnvSnapshot()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
24
24
|
return donobuStack;
|
|
25
25
|
}
|
|
26
26
|
else {
|
package/dist/esm/main.d.ts
CHANGED
|
@@ -105,6 +105,7 @@ export declare function startDonobuServer({ port, controlPanelHost, environ, }?:
|
|
|
105
105
|
AWS_ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
|
|
106
106
|
AWS_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
107
107
|
DONOBU_API_KEY: z.ZodOptional<z.ZodString>;
|
|
108
|
+
DONOBU_PERSISTENCE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
108
109
|
}, {
|
|
109
110
|
BASE64_GPT_CONFIG?: string | undefined;
|
|
110
111
|
BROWSERBASE_API_KEY?: string | undefined;
|
|
@@ -124,6 +125,7 @@ export declare function startDonobuServer({ port, controlPanelHost, environ, }?:
|
|
|
124
125
|
AWS_ACCESS_KEY_ID?: string | undefined;
|
|
125
126
|
AWS_SECRET_ACCESS_KEY?: string | undefined;
|
|
126
127
|
DONOBU_API_KEY?: string | undefined;
|
|
128
|
+
DONOBU_PERSISTENCE_API_KEY?: string | undefined;
|
|
127
129
|
}> | undefined;
|
|
128
130
|
}): Promise<void>;
|
|
129
131
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/esm/main.js
CHANGED
|
@@ -113,7 +113,7 @@ const DEFAULT_PORT = 31000;
|
|
|
113
113
|
* Starts a Donobu API server at the given port. The server assumes that the
|
|
114
114
|
* Playwright browsers have been installed.
|
|
115
115
|
*/
|
|
116
|
-
async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
|
|
116
|
+
async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
|
|
117
117
|
try {
|
|
118
118
|
const adminController = await AdminApiController_1.AdminApiController.create(environ.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, environ);
|
|
119
119
|
await adminController.start(port);
|
|
@@ -59,7 +59,7 @@ export declare class AdminApiController {
|
|
|
59
59
|
* - no checking for flow ownership is performed, as all flows are considered owned by the
|
|
60
60
|
* local environment.
|
|
61
61
|
*/
|
|
62
|
-
static create(donobuDeploymentEnvironment: DonobuDeploymentEnvironment, controlPanelFactory: ControlPanelFactory, environ: EnvPick<typeof env, 'ANTHROPIC_API_KEY' | 'ANTHROPIC_MODEL_NAME' | 'AWS_ACCESS_KEY_ID' | 'AWS_BEDROCK_MODEL_NAME' | 'AWS_SECRET_ACCESS_KEY' | 'BASE64_GPT_CONFIG' | 'BROWSERBASE_API_KEY' | 'BROWSERBASE_PROJECT_ID' | 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'GOOGLE_GENERATIVE_AI_API_KEY' | 'GOOGLE_GENERATIVE_AI_MODEL_NAME' | 'OLLAMA_API_URL' | 'OLLAMA_MODEL_NAME' | 'OPENAI_API_KEY' | 'OPENAI_API_MODEL_NAME' | 'PERSISTENCE_PRIORITY'>): Promise<AdminApiController>;
|
|
62
|
+
static create(donobuDeploymentEnvironment: DonobuDeploymentEnvironment, controlPanelFactory: ControlPanelFactory, environ: EnvPick<typeof env, 'ANTHROPIC_API_KEY' | 'ANTHROPIC_MODEL_NAME' | 'AWS_ACCESS_KEY_ID' | 'AWS_BEDROCK_MODEL_NAME' | 'AWS_SECRET_ACCESS_KEY' | 'BASE64_GPT_CONFIG' | 'BROWSERBASE_API_KEY' | 'BROWSERBASE_PROJECT_ID' | 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'GOOGLE_GENERATIVE_AI_API_KEY' | 'GOOGLE_GENERATIVE_AI_MODEL_NAME' | 'OLLAMA_API_URL' | 'OLLAMA_MODEL_NAME' | 'OPENAI_API_KEY' | 'OPENAI_API_MODEL_NAME' | 'PERSISTENCE_PRIORITY'>): Promise<AdminApiController>;
|
|
63
63
|
private constructor();
|
|
64
64
|
/**
|
|
65
65
|
* Binds the API/web-asset server to `port` and resolves once the socket is
|
|
@@ -60,6 +60,7 @@ export declare function setupDonobuStack(donobuDeploymentEnvironment: DonobuDepl
|
|
|
60
60
|
AWS_ACCESS_KEY_ID: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
61
61
|
AWS_SECRET_ACCESS_KEY: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
62
62
|
DONOBU_API_KEY: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
63
|
+
DONOBU_PERSISTENCE_API_KEY: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
63
64
|
}, {
|
|
64
65
|
BASE64_GPT_CONFIG?: string | undefined;
|
|
65
66
|
BROWSERBASE_API_KEY?: string | undefined;
|
|
@@ -78,5 +79,6 @@ export declare function setupDonobuStack(donobuDeploymentEnvironment: DonobuDepl
|
|
|
78
79
|
AWS_ACCESS_KEY_ID?: string | undefined;
|
|
79
80
|
AWS_SECRET_ACCESS_KEY?: string | undefined;
|
|
80
81
|
DONOBU_API_KEY?: string | undefined;
|
|
82
|
+
DONOBU_PERSISTENCE_API_KEY?: string | undefined;
|
|
81
83
|
}>): Promise<DonobuStack>;
|
|
82
84
|
//# sourceMappingURL=DonobuStack.d.ts.map
|
|
@@ -33,7 +33,7 @@ const ToolRegistry_1 = require("./ToolRegistry");
|
|
|
33
33
|
* then having this snapshot is relevant so that tests can use normal
|
|
34
34
|
* environment variables.
|
|
35
35
|
*/
|
|
36
|
-
async function setupDonobuStack(donobuDeploymentEnvironment, controlPanelFactory, envPersistenceVolatile, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY')) {
|
|
36
|
+
async function setupDonobuStack(donobuDeploymentEnvironment, controlPanelFactory, envPersistenceVolatile, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY')) {
|
|
37
37
|
const loadedPlugins = await loadDefaultPlugins();
|
|
38
38
|
const resolvedToolRegistry = await (0, ToolRegistry_1.createDefaultToolRegistry)(loadedPlugins.tools);
|
|
39
39
|
const persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry(loadedPlugins.persistencePlugins);
|
|
@@ -174,8 +174,11 @@ class TestsManager {
|
|
|
174
174
|
try {
|
|
175
175
|
await persistence.deleteTest(testId);
|
|
176
176
|
}
|
|
177
|
-
catch {
|
|
178
|
-
// Ignore errors from layers that don't have this test.
|
|
177
|
+
catch (e) {
|
|
178
|
+
// Ignore TestNotFoundException errors from layers that don't have this test.
|
|
179
|
+
if (!(e instanceof TestNotFoundException_1.TestNotFoundException)) {
|
|
180
|
+
throw e;
|
|
181
|
+
}
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
// Cascade-delete flows belonging to this test. Paginate through all flows
|
|
@@ -24,7 +24,7 @@ class DonobuApiClient {
|
|
|
24
24
|
async jsonRequest(path, method, body) {
|
|
25
25
|
return this.request(path, {
|
|
26
26
|
method,
|
|
27
|
-
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
headers: body !== undefined ? { 'Content-Type': 'application/json' } : {},
|
|
28
28
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
29
29
|
});
|
|
30
30
|
}
|
|
@@ -46,7 +46,7 @@ export declare class FlowsPersistenceRegistryImpl implements FlowsPersistenceReg
|
|
|
46
46
|
* Creates an instance by reading environment variables and eagerly constructing
|
|
47
47
|
* all applicable persistence layers.
|
|
48
48
|
*/
|
|
49
|
-
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<FlowsPersistenceRegistryImpl>;
|
|
49
|
+
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<FlowsPersistenceRegistryImpl>;
|
|
50
50
|
/**
|
|
51
51
|
* Returns the primary persistence layer.
|
|
52
52
|
*/
|
|
@@ -26,7 +26,13 @@ class FlowsPersistenceRegistryImpl {
|
|
|
26
26
|
*/
|
|
27
27
|
static async fromEnvironment(environ, persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry()) {
|
|
28
28
|
const donobuApiBaseUrl = environ.data.DONOBU_API_BASE_URL;
|
|
29
|
-
|
|
29
|
+
// Persistence credential resolution: prefer the primary DONOBU_API_KEY
|
|
30
|
+
// (which also drives the inference fallback chain in
|
|
31
|
+
// DonobuFlowsManager.createGptClient), then fall back to the
|
|
32
|
+
// persistence-only DONOBU_PERSISTENCE_API_KEY for hosts that route
|
|
33
|
+
// inference through an explicit gpt-config and don't want their
|
|
34
|
+
// persistence credential to short-circuit the agent lookup.
|
|
35
|
+
const donobuApiKey = environ.data.DONOBU_API_KEY ?? environ.data.DONOBU_PERSISTENCE_API_KEY;
|
|
30
36
|
const layers = [];
|
|
31
37
|
for (const key of environ.data.PERSISTENCE_PRIORITY) {
|
|
32
38
|
switch (key) {
|
|
@@ -26,7 +26,7 @@ export interface SuitesPersistenceRegistry {
|
|
|
26
26
|
export declare class SuitesPersistenceRegistryImpl implements SuitesPersistenceRegistry {
|
|
27
27
|
private readonly layers;
|
|
28
28
|
constructor(layers: SuitesPersistenceLayer[]);
|
|
29
|
-
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<SuitesPersistenceRegistryImpl>;
|
|
29
|
+
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<SuitesPersistenceRegistryImpl>;
|
|
30
30
|
get(): Promise<SuitesPersistence>;
|
|
31
31
|
getAll(): Promise<SuitesPersistence[]>;
|
|
32
32
|
getEntries(): Promise<SuitesPersistenceLayer[]>;
|
|
@@ -15,7 +15,13 @@ class SuitesPersistenceRegistryImpl {
|
|
|
15
15
|
}
|
|
16
16
|
static async fromEnvironment(environ, persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry()) {
|
|
17
17
|
const donobuApiBaseUrl = environ.data.DONOBU_API_BASE_URL;
|
|
18
|
-
|
|
18
|
+
// Persistence credential resolution: prefer the primary DONOBU_API_KEY
|
|
19
|
+
// (which also drives the inference fallback chain in
|
|
20
|
+
// DonobuFlowsManager.createGptClient), then fall back to the
|
|
21
|
+
// persistence-only DONOBU_PERSISTENCE_API_KEY for hosts that route
|
|
22
|
+
// inference through an explicit gpt-config and don't want their
|
|
23
|
+
// persistence credential to short-circuit the agent lookup.
|
|
24
|
+
const donobuApiKey = environ.data.DONOBU_API_KEY ?? environ.data.DONOBU_PERSISTENCE_API_KEY;
|
|
19
25
|
const layers = [];
|
|
20
26
|
for (const key of environ.data.PERSISTENCE_PRIORITY) {
|
|
21
27
|
switch (key) {
|
|
@@ -103,6 +103,9 @@ class TestsPersistenceDonobuApi extends DonobuApiClient_1.DonobuApiClient {
|
|
|
103
103
|
// DB FK constraint.
|
|
104
104
|
const response = await this.jsonRequest(`/v1/tests/${encodeURIComponent(testId)}`, 'DELETE');
|
|
105
105
|
if (!response.ok) {
|
|
106
|
+
if (response.status === 404) {
|
|
107
|
+
throw TestNotFoundException_1.TestNotFoundException.forId(testId);
|
|
108
|
+
}
|
|
106
109
|
throw new Error(`Failed to delete test: ${response.status} ${response.statusText}`);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
@@ -27,7 +27,7 @@ export interface TestsPersistenceRegistry {
|
|
|
27
27
|
export declare class TestsPersistenceRegistryImpl implements TestsPersistenceRegistry {
|
|
28
28
|
private readonly layers;
|
|
29
29
|
constructor(layers: TestsPersistenceLayer[]);
|
|
30
|
-
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'PERSISTENCE_PRIORITY'>,
|
|
30
|
+
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'PERSISTENCE_PRIORITY'>,
|
|
31
31
|
/**
|
|
32
32
|
* Flows registry — used by the volatile tests layer to compute
|
|
33
33
|
* flow-derived sort keys (flow_count, latest_flow_created_at).
|
|
@@ -22,7 +22,13 @@ class TestsPersistenceRegistryImpl {
|
|
|
22
22
|
*/
|
|
23
23
|
flowsRegistry, persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry()) {
|
|
24
24
|
const donobuApiBaseUrl = environ.data.DONOBU_API_BASE_URL;
|
|
25
|
-
|
|
25
|
+
// Persistence credential resolution: prefer the primary DONOBU_API_KEY
|
|
26
|
+
// (which also drives the inference fallback chain in
|
|
27
|
+
// DonobuFlowsManager.createGptClient), then fall back to the
|
|
28
|
+
// persistence-only DONOBU_PERSISTENCE_API_KEY for hosts that route
|
|
29
|
+
// inference through an explicit gpt-config and don't want their
|
|
30
|
+
// persistence credential to short-circuit the agent lookup.
|
|
31
|
+
const donobuApiKey = environ.data.DONOBU_API_KEY ?? environ.data.DONOBU_PERSISTENCE_API_KEY;
|
|
26
32
|
const layers = [];
|
|
27
33
|
for (const key of environ.data.PERSISTENCE_PRIORITY) {
|
|
28
34
|
switch (key) {
|
|
@@ -151,7 +151,10 @@ class TestsPersistenceSqlite {
|
|
|
151
151
|
async deleteTest(testId) {
|
|
152
152
|
// Flows are cascade-deleted by the DB FK constraint.
|
|
153
153
|
const stmt = this.db.prepare('DELETE FROM test_metadata WHERE id = ?');
|
|
154
|
-
stmt.run(testId);
|
|
154
|
+
const { changes } = stmt.run(testId);
|
|
155
|
+
if (changes === 0) {
|
|
156
|
+
throw TestNotFoundException_1.TestNotFoundException.forId(testId);
|
|
157
|
+
}
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
exports.TestsPersistenceSqlite = TestsPersistenceSqlite;
|
|
@@ -124,7 +124,10 @@ class TestsPersistenceVolatile {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
async deleteTest(testId) {
|
|
127
|
-
this.tests.delete(testId);
|
|
127
|
+
const didExist = this.tests.delete(testId);
|
|
128
|
+
if (!didExist) {
|
|
129
|
+
throw TestNotFoundException_1.TestNotFoundException.forId(testId);
|
|
130
|
+
}
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
exports.TestsPersistenceVolatile = TestsPersistenceVolatile;
|
|
@@ -64,14 +64,14 @@ function ansiToHtml(str) {
|
|
|
64
64
|
* Read a few lines of source around `targetLine` (1-based) and return an HTML
|
|
65
65
|
* snippet block, or null if the file is unreadable.
|
|
66
66
|
*/
|
|
67
|
-
function readSourceSnippet(file, targetLine) {
|
|
67
|
+
function readSourceSnippet(file, targetLine, wrapperClass = 'native-step-snippet') {
|
|
68
68
|
try {
|
|
69
69
|
const source = (0, fs_1.readFileSync)(file, 'utf8');
|
|
70
70
|
const lines = source.split('\n');
|
|
71
71
|
const context = 2;
|
|
72
72
|
const start = Math.max(0, targetLine - 1 - context);
|
|
73
73
|
const end = Math.min(lines.length - 1, targetLine - 1 + context);
|
|
74
|
-
let html =
|
|
74
|
+
let html = `<div class="${wrapperClass}">`;
|
|
75
75
|
for (let i = start; i <= end; i++) {
|
|
76
76
|
const lineNum = i + 1;
|
|
77
77
|
const isTarget = lineNum === targetLine;
|
|
@@ -1515,6 +1515,26 @@ function renderHtml(report, triage, outputDir) {
|
|
|
1515
1515
|
const lastResult = test.results.at(-1);
|
|
1516
1516
|
const displayFileName = test.file.split('/').pop() ?? test.file;
|
|
1517
1517
|
let detailsHtml = '';
|
|
1518
|
+
// 0. Skip reason — surface the description and call site from
|
|
1519
|
+
// test.skip()/test.fixme() so a skipped test explains itself.
|
|
1520
|
+
if (test.status === 'skipped') {
|
|
1521
|
+
const skipAnnotation = test.annotations.find((a) => a.type === 'skip' || a.type === 'fixme');
|
|
1522
|
+
const label = skipAnnotation?.type === 'fixme' ? 'Fixme reason' : 'Skip reason';
|
|
1523
|
+
const description = skipAnnotation?.description?.trim();
|
|
1524
|
+
const body = description
|
|
1525
|
+
? esc(description)
|
|
1526
|
+
: 'No description was provided when this test was skipped. The reason may be implicit in the surrounding test code or condition.';
|
|
1527
|
+
let locationHtml = '';
|
|
1528
|
+
let snippetHtml = '';
|
|
1529
|
+
const loc = skipAnnotation?.location;
|
|
1530
|
+
if (loc?.file) {
|
|
1531
|
+
const fileBase = (0, path_1.basename)(loc.file);
|
|
1532
|
+
locationHtml = `<div class="skip-location">at <span class="skip-location-ref">${esc(fileBase)}:${loc.line}</span></div>`;
|
|
1533
|
+
snippetHtml =
|
|
1534
|
+
readSourceSnippet(loc.file, loc.line, 'source-snippet') ?? '';
|
|
1535
|
+
}
|
|
1536
|
+
detailsHtml += `<div class="detail-section"><div class="detail-label">${label}</div><div class="detail-objective">${body}</div>${locationHtml}${snippetHtml}</div>`;
|
|
1537
|
+
}
|
|
1518
1538
|
// 1. AI analysis — why it failed (the headline)
|
|
1519
1539
|
if (test.plan) {
|
|
1520
1540
|
detailsHtml += renderFailureSummary(test.plan);
|
|
@@ -1824,6 +1844,11 @@ details.native-step>summary::-webkit-details-marker{display:none}
|
|
|
1824
1844
|
details.native-step[open]>summary .native-step-chevron{transform:rotate(90deg)}
|
|
1825
1845
|
.native-step-error{font-size:11px;font-family:var(--mono);padding:4px 0 2px 44px;margin:0;white-space:pre-wrap;word-break:break-word;color:var(--text-muted)}
|
|
1826
1846
|
.native-step-snippet{font-size:11px;font-family:var(--mono);margin:4px 0 2px 44px;overflow:hidden}
|
|
1847
|
+
.skip-location{font-size:11px;color:var(--text-muted);margin-top:8px;font-family:var(--mono)}
|
|
1848
|
+
.skip-location-ref{color:var(--text)}
|
|
1849
|
+
.source-snippet{font-size:11px;font-family:var(--mono);margin:6px 0 0 0;overflow:hidden;border:1px solid var(--border-subtle);border-radius:var(--radius);padding:6px 0;background:var(--bg)}
|
|
1850
|
+
.source-snippet .snippet-line--target{background:rgba(255,127,58,.10)}
|
|
1851
|
+
.source-snippet .snippet-line--target .snippet-linenum{color:var(--orange,#FF7F3A)}
|
|
1827
1852
|
.native-step-children{display:flex;flex-direction:column;margin:4px 0 0 10px;border-left:1px solid var(--border-subtle);padding-left:8px}
|
|
1828
1853
|
.native-step-children>.filmstrip-step{padding-left:8px}
|
|
1829
1854
|
|
|
@@ -20,7 +20,7 @@ function getEnvSnapshot() {
|
|
|
20
20
|
}
|
|
21
21
|
async function getOrCreateDonobuStack() {
|
|
22
22
|
if (!donobuStack) {
|
|
23
|
-
donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(getEnvSnapshot()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
23
|
+
donobuStack = await (0, DonobuStack_1.setupDonobuStack)('LOCAL', ControlPanel_1.NoOpControlPanelFactory, new EnvPersistenceVolatile_1.EnvPersistenceVolatile(getEnvSnapshot()), envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'));
|
|
24
24
|
return donobuStack;
|
|
25
25
|
}
|
|
26
26
|
else {
|
package/dist/main.d.ts
CHANGED
|
@@ -105,6 +105,7 @@ export declare function startDonobuServer({ port, controlPanelHost, environ, }?:
|
|
|
105
105
|
AWS_ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
|
|
106
106
|
AWS_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
107
107
|
DONOBU_API_KEY: z.ZodOptional<z.ZodString>;
|
|
108
|
+
DONOBU_PERSISTENCE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
108
109
|
}, {
|
|
109
110
|
BASE64_GPT_CONFIG?: string | undefined;
|
|
110
111
|
BROWSERBASE_API_KEY?: string | undefined;
|
|
@@ -124,6 +125,7 @@ export declare function startDonobuServer({ port, controlPanelHost, environ, }?:
|
|
|
124
125
|
AWS_ACCESS_KEY_ID?: string | undefined;
|
|
125
126
|
AWS_SECRET_ACCESS_KEY?: string | undefined;
|
|
126
127
|
DONOBU_API_KEY?: string | undefined;
|
|
128
|
+
DONOBU_PERSISTENCE_API_KEY?: string | undefined;
|
|
127
129
|
}> | undefined;
|
|
128
130
|
}): Promise<void>;
|
|
129
131
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/main.js
CHANGED
|
@@ -113,7 +113,7 @@ const DEFAULT_PORT = 31000;
|
|
|
113
113
|
* Starts a Donobu API server at the given port. The server assumes that the
|
|
114
114
|
* Playwright browsers have been installed.
|
|
115
115
|
*/
|
|
116
|
-
async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
|
|
116
|
+
async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
|
|
117
117
|
try {
|
|
118
118
|
const adminController = await AdminApiController_1.AdminApiController.create(environ.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, environ);
|
|
119
119
|
await adminController.start(port);
|
|
@@ -59,7 +59,7 @@ export declare class AdminApiController {
|
|
|
59
59
|
* - no checking for flow ownership is performed, as all flows are considered owned by the
|
|
60
60
|
* local environment.
|
|
61
61
|
*/
|
|
62
|
-
static create(donobuDeploymentEnvironment: DonobuDeploymentEnvironment, controlPanelFactory: ControlPanelFactory, environ: EnvPick<typeof env, 'ANTHROPIC_API_KEY' | 'ANTHROPIC_MODEL_NAME' | 'AWS_ACCESS_KEY_ID' | 'AWS_BEDROCK_MODEL_NAME' | 'AWS_SECRET_ACCESS_KEY' | 'BASE64_GPT_CONFIG' | 'BROWSERBASE_API_KEY' | 'BROWSERBASE_PROJECT_ID' | 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'GOOGLE_GENERATIVE_AI_API_KEY' | 'GOOGLE_GENERATIVE_AI_MODEL_NAME' | 'OLLAMA_API_URL' | 'OLLAMA_MODEL_NAME' | 'OPENAI_API_KEY' | 'OPENAI_API_MODEL_NAME' | 'PERSISTENCE_PRIORITY'>): Promise<AdminApiController>;
|
|
62
|
+
static create(donobuDeploymentEnvironment: DonobuDeploymentEnvironment, controlPanelFactory: ControlPanelFactory, environ: EnvPick<typeof env, 'ANTHROPIC_API_KEY' | 'ANTHROPIC_MODEL_NAME' | 'AWS_ACCESS_KEY_ID' | 'AWS_BEDROCK_MODEL_NAME' | 'AWS_SECRET_ACCESS_KEY' | 'BASE64_GPT_CONFIG' | 'BROWSERBASE_API_KEY' | 'BROWSERBASE_PROJECT_ID' | 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'GOOGLE_GENERATIVE_AI_API_KEY' | 'GOOGLE_GENERATIVE_AI_MODEL_NAME' | 'OLLAMA_API_URL' | 'OLLAMA_MODEL_NAME' | 'OPENAI_API_KEY' | 'OPENAI_API_MODEL_NAME' | 'PERSISTENCE_PRIORITY'>): Promise<AdminApiController>;
|
|
63
63
|
private constructor();
|
|
64
64
|
/**
|
|
65
65
|
* Binds the API/web-asset server to `port` and resolves once the socket is
|
|
@@ -60,6 +60,7 @@ export declare function setupDonobuStack(donobuDeploymentEnvironment: DonobuDepl
|
|
|
60
60
|
AWS_ACCESS_KEY_ID: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
61
61
|
AWS_SECRET_ACCESS_KEY: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
62
62
|
DONOBU_API_KEY: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
63
|
+
DONOBU_PERSISTENCE_API_KEY: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
|
|
63
64
|
}, {
|
|
64
65
|
BASE64_GPT_CONFIG?: string | undefined;
|
|
65
66
|
BROWSERBASE_API_KEY?: string | undefined;
|
|
@@ -78,5 +79,6 @@ export declare function setupDonobuStack(donobuDeploymentEnvironment: DonobuDepl
|
|
|
78
79
|
AWS_ACCESS_KEY_ID?: string | undefined;
|
|
79
80
|
AWS_SECRET_ACCESS_KEY?: string | undefined;
|
|
80
81
|
DONOBU_API_KEY?: string | undefined;
|
|
82
|
+
DONOBU_PERSISTENCE_API_KEY?: string | undefined;
|
|
81
83
|
}>): Promise<DonobuStack>;
|
|
82
84
|
//# sourceMappingURL=DonobuStack.d.ts.map
|
|
@@ -33,7 +33,7 @@ const ToolRegistry_1 = require("./ToolRegistry");
|
|
|
33
33
|
* then having this snapshot is relevant so that tests can use normal
|
|
34
34
|
* environment variables.
|
|
35
35
|
*/
|
|
36
|
-
async function setupDonobuStack(donobuDeploymentEnvironment, controlPanelFactory, envPersistenceVolatile, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY')) {
|
|
36
|
+
async function setupDonobuStack(donobuDeploymentEnvironment, controlPanelFactory, envPersistenceVolatile, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'DONOBU_PERSISTENCE_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY')) {
|
|
37
37
|
const loadedPlugins = await loadDefaultPlugins();
|
|
38
38
|
const resolvedToolRegistry = await (0, ToolRegistry_1.createDefaultToolRegistry)(loadedPlugins.tools);
|
|
39
39
|
const persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry(loadedPlugins.persistencePlugins);
|
|
@@ -174,8 +174,11 @@ class TestsManager {
|
|
|
174
174
|
try {
|
|
175
175
|
await persistence.deleteTest(testId);
|
|
176
176
|
}
|
|
177
|
-
catch {
|
|
178
|
-
// Ignore errors from layers that don't have this test.
|
|
177
|
+
catch (e) {
|
|
178
|
+
// Ignore TestNotFoundException errors from layers that don't have this test.
|
|
179
|
+
if (!(e instanceof TestNotFoundException_1.TestNotFoundException)) {
|
|
180
|
+
throw e;
|
|
181
|
+
}
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
// Cascade-delete flows belonging to this test. Paginate through all flows
|
|
@@ -24,7 +24,7 @@ class DonobuApiClient {
|
|
|
24
24
|
async jsonRequest(path, method, body) {
|
|
25
25
|
return this.request(path, {
|
|
26
26
|
method,
|
|
27
|
-
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
headers: body !== undefined ? { 'Content-Type': 'application/json' } : {},
|
|
28
28
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
29
29
|
});
|
|
30
30
|
}
|
|
@@ -46,7 +46,7 @@ export declare class FlowsPersistenceRegistryImpl implements FlowsPersistenceReg
|
|
|
46
46
|
* Creates an instance by reading environment variables and eagerly constructing
|
|
47
47
|
* all applicable persistence layers.
|
|
48
48
|
*/
|
|
49
|
-
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<FlowsPersistenceRegistryImpl>;
|
|
49
|
+
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<FlowsPersistenceRegistryImpl>;
|
|
50
50
|
/**
|
|
51
51
|
* Returns the primary persistence layer.
|
|
52
52
|
*/
|
|
@@ -26,7 +26,13 @@ class FlowsPersistenceRegistryImpl {
|
|
|
26
26
|
*/
|
|
27
27
|
static async fromEnvironment(environ, persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry()) {
|
|
28
28
|
const donobuApiBaseUrl = environ.data.DONOBU_API_BASE_URL;
|
|
29
|
-
|
|
29
|
+
// Persistence credential resolution: prefer the primary DONOBU_API_KEY
|
|
30
|
+
// (which also drives the inference fallback chain in
|
|
31
|
+
// DonobuFlowsManager.createGptClient), then fall back to the
|
|
32
|
+
// persistence-only DONOBU_PERSISTENCE_API_KEY for hosts that route
|
|
33
|
+
// inference through an explicit gpt-config and don't want their
|
|
34
|
+
// persistence credential to short-circuit the agent lookup.
|
|
35
|
+
const donobuApiKey = environ.data.DONOBU_API_KEY ?? environ.data.DONOBU_PERSISTENCE_API_KEY;
|
|
30
36
|
const layers = [];
|
|
31
37
|
for (const key of environ.data.PERSISTENCE_PRIORITY) {
|
|
32
38
|
switch (key) {
|
|
@@ -26,7 +26,7 @@ export interface SuitesPersistenceRegistry {
|
|
|
26
26
|
export declare class SuitesPersistenceRegistryImpl implements SuitesPersistenceRegistry {
|
|
27
27
|
private readonly layers;
|
|
28
28
|
constructor(layers: SuitesPersistenceLayer[]);
|
|
29
|
-
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<SuitesPersistenceRegistryImpl>;
|
|
29
|
+
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'PERSISTENCE_PRIORITY'>, persistencePlugins?: PersistencePluginRegistry): Promise<SuitesPersistenceRegistryImpl>;
|
|
30
30
|
get(): Promise<SuitesPersistence>;
|
|
31
31
|
getAll(): Promise<SuitesPersistence[]>;
|
|
32
32
|
getEntries(): Promise<SuitesPersistenceLayer[]>;
|
|
@@ -15,7 +15,13 @@ class SuitesPersistenceRegistryImpl {
|
|
|
15
15
|
}
|
|
16
16
|
static async fromEnvironment(environ, persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry()) {
|
|
17
17
|
const donobuApiBaseUrl = environ.data.DONOBU_API_BASE_URL;
|
|
18
|
-
|
|
18
|
+
// Persistence credential resolution: prefer the primary DONOBU_API_KEY
|
|
19
|
+
// (which also drives the inference fallback chain in
|
|
20
|
+
// DonobuFlowsManager.createGptClient), then fall back to the
|
|
21
|
+
// persistence-only DONOBU_PERSISTENCE_API_KEY for hosts that route
|
|
22
|
+
// inference through an explicit gpt-config and don't want their
|
|
23
|
+
// persistence credential to short-circuit the agent lookup.
|
|
24
|
+
const donobuApiKey = environ.data.DONOBU_API_KEY ?? environ.data.DONOBU_PERSISTENCE_API_KEY;
|
|
19
25
|
const layers = [];
|
|
20
26
|
for (const key of environ.data.PERSISTENCE_PRIORITY) {
|
|
21
27
|
switch (key) {
|
|
@@ -103,6 +103,9 @@ class TestsPersistenceDonobuApi extends DonobuApiClient_1.DonobuApiClient {
|
|
|
103
103
|
// DB FK constraint.
|
|
104
104
|
const response = await this.jsonRequest(`/v1/tests/${encodeURIComponent(testId)}`, 'DELETE');
|
|
105
105
|
if (!response.ok) {
|
|
106
|
+
if (response.status === 404) {
|
|
107
|
+
throw TestNotFoundException_1.TestNotFoundException.forId(testId);
|
|
108
|
+
}
|
|
106
109
|
throw new Error(`Failed to delete test: ${response.status} ${response.statusText}`);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
@@ -27,7 +27,7 @@ export interface TestsPersistenceRegistry {
|
|
|
27
27
|
export declare class TestsPersistenceRegistryImpl implements TestsPersistenceRegistry {
|
|
28
28
|
private readonly layers;
|
|
29
29
|
constructor(layers: TestsPersistenceLayer[]);
|
|
30
|
-
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'PERSISTENCE_PRIORITY'>,
|
|
30
|
+
static fromEnvironment(environ: EnvPick<typeof env, 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'DONOBU_PERSISTENCE_API_KEY' | 'PERSISTENCE_PRIORITY'>,
|
|
31
31
|
/**
|
|
32
32
|
* Flows registry — used by the volatile tests layer to compute
|
|
33
33
|
* flow-derived sort keys (flow_count, latest_flow_created_at).
|
|
@@ -22,7 +22,13 @@ class TestsPersistenceRegistryImpl {
|
|
|
22
22
|
*/
|
|
23
23
|
flowsRegistry, persistencePlugins = new PersistencePlugin_1.PersistencePluginRegistry()) {
|
|
24
24
|
const donobuApiBaseUrl = environ.data.DONOBU_API_BASE_URL;
|
|
25
|
-
|
|
25
|
+
// Persistence credential resolution: prefer the primary DONOBU_API_KEY
|
|
26
|
+
// (which also drives the inference fallback chain in
|
|
27
|
+
// DonobuFlowsManager.createGptClient), then fall back to the
|
|
28
|
+
// persistence-only DONOBU_PERSISTENCE_API_KEY for hosts that route
|
|
29
|
+
// inference through an explicit gpt-config and don't want their
|
|
30
|
+
// persistence credential to short-circuit the agent lookup.
|
|
31
|
+
const donobuApiKey = environ.data.DONOBU_API_KEY ?? environ.data.DONOBU_PERSISTENCE_API_KEY;
|
|
26
32
|
const layers = [];
|
|
27
33
|
for (const key of environ.data.PERSISTENCE_PRIORITY) {
|
|
28
34
|
switch (key) {
|
|
@@ -151,7 +151,10 @@ class TestsPersistenceSqlite {
|
|
|
151
151
|
async deleteTest(testId) {
|
|
152
152
|
// Flows are cascade-deleted by the DB FK constraint.
|
|
153
153
|
const stmt = this.db.prepare('DELETE FROM test_metadata WHERE id = ?');
|
|
154
|
-
stmt.run(testId);
|
|
154
|
+
const { changes } = stmt.run(testId);
|
|
155
|
+
if (changes === 0) {
|
|
156
|
+
throw TestNotFoundException_1.TestNotFoundException.forId(testId);
|
|
157
|
+
}
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
exports.TestsPersistenceSqlite = TestsPersistenceSqlite;
|
|
@@ -124,7 +124,10 @@ class TestsPersistenceVolatile {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
async deleteTest(testId) {
|
|
127
|
-
this.tests.delete(testId);
|
|
127
|
+
const didExist = this.tests.delete(testId);
|
|
128
|
+
if (!didExist) {
|
|
129
|
+
throw TestNotFoundException_1.TestNotFoundException.forId(testId);
|
|
130
|
+
}
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
exports.TestsPersistenceVolatile = TestsPersistenceVolatile;
|
package/dist/reporter/render.js
CHANGED
|
@@ -64,14 +64,14 @@ function ansiToHtml(str) {
|
|
|
64
64
|
* Read a few lines of source around `targetLine` (1-based) and return an HTML
|
|
65
65
|
* snippet block, or null if the file is unreadable.
|
|
66
66
|
*/
|
|
67
|
-
function readSourceSnippet(file, targetLine) {
|
|
67
|
+
function readSourceSnippet(file, targetLine, wrapperClass = 'native-step-snippet') {
|
|
68
68
|
try {
|
|
69
69
|
const source = (0, fs_1.readFileSync)(file, 'utf8');
|
|
70
70
|
const lines = source.split('\n');
|
|
71
71
|
const context = 2;
|
|
72
72
|
const start = Math.max(0, targetLine - 1 - context);
|
|
73
73
|
const end = Math.min(lines.length - 1, targetLine - 1 + context);
|
|
74
|
-
let html =
|
|
74
|
+
let html = `<div class="${wrapperClass}">`;
|
|
75
75
|
for (let i = start; i <= end; i++) {
|
|
76
76
|
const lineNum = i + 1;
|
|
77
77
|
const isTarget = lineNum === targetLine;
|
|
@@ -1515,6 +1515,26 @@ function renderHtml(report, triage, outputDir) {
|
|
|
1515
1515
|
const lastResult = test.results.at(-1);
|
|
1516
1516
|
const displayFileName = test.file.split('/').pop() ?? test.file;
|
|
1517
1517
|
let detailsHtml = '';
|
|
1518
|
+
// 0. Skip reason — surface the description and call site from
|
|
1519
|
+
// test.skip()/test.fixme() so a skipped test explains itself.
|
|
1520
|
+
if (test.status === 'skipped') {
|
|
1521
|
+
const skipAnnotation = test.annotations.find((a) => a.type === 'skip' || a.type === 'fixme');
|
|
1522
|
+
const label = skipAnnotation?.type === 'fixme' ? 'Fixme reason' : 'Skip reason';
|
|
1523
|
+
const description = skipAnnotation?.description?.trim();
|
|
1524
|
+
const body = description
|
|
1525
|
+
? esc(description)
|
|
1526
|
+
: 'No description was provided when this test was skipped. The reason may be implicit in the surrounding test code or condition.';
|
|
1527
|
+
let locationHtml = '';
|
|
1528
|
+
let snippetHtml = '';
|
|
1529
|
+
const loc = skipAnnotation?.location;
|
|
1530
|
+
if (loc?.file) {
|
|
1531
|
+
const fileBase = (0, path_1.basename)(loc.file);
|
|
1532
|
+
locationHtml = `<div class="skip-location">at <span class="skip-location-ref">${esc(fileBase)}:${loc.line}</span></div>`;
|
|
1533
|
+
snippetHtml =
|
|
1534
|
+
readSourceSnippet(loc.file, loc.line, 'source-snippet') ?? '';
|
|
1535
|
+
}
|
|
1536
|
+
detailsHtml += `<div class="detail-section"><div class="detail-label">${label}</div><div class="detail-objective">${body}</div>${locationHtml}${snippetHtml}</div>`;
|
|
1537
|
+
}
|
|
1518
1538
|
// 1. AI analysis — why it failed (the headline)
|
|
1519
1539
|
if (test.plan) {
|
|
1520
1540
|
detailsHtml += renderFailureSummary(test.plan);
|
|
@@ -1824,6 +1844,11 @@ details.native-step>summary::-webkit-details-marker{display:none}
|
|
|
1824
1844
|
details.native-step[open]>summary .native-step-chevron{transform:rotate(90deg)}
|
|
1825
1845
|
.native-step-error{font-size:11px;font-family:var(--mono);padding:4px 0 2px 44px;margin:0;white-space:pre-wrap;word-break:break-word;color:var(--text-muted)}
|
|
1826
1846
|
.native-step-snippet{font-size:11px;font-family:var(--mono);margin:4px 0 2px 44px;overflow:hidden}
|
|
1847
|
+
.skip-location{font-size:11px;color:var(--text-muted);margin-top:8px;font-family:var(--mono)}
|
|
1848
|
+
.skip-location-ref{color:var(--text)}
|
|
1849
|
+
.source-snippet{font-size:11px;font-family:var(--mono);margin:6px 0 0 0;overflow:hidden;border:1px solid var(--border-subtle);border-radius:var(--radius);padding:6px 0;background:var(--bg)}
|
|
1850
|
+
.source-snippet .snippet-line--target{background:rgba(255,127,58,.10)}
|
|
1851
|
+
.source-snippet .snippet-line--target .snippet-linenum{color:var(--orange,#FF7F3A)}
|
|
1827
1852
|
.native-step-children{display:flex;flex-direction:column;margin:4px 0 0 10px;border-left:1px solid var(--border-subtle);padding-left:8px}
|
|
1828
1853
|
.native-step-children>.filmstrip-step{padding-left:8px}
|
|
1829
1854
|
|