@treeseed/agent 0.8.5
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/Dockerfile +7 -0
- package/README.md +198 -0
- package/dist/agent-runtime.d.ts +17 -0
- package/dist/agent-runtime.js +117 -0
- package/dist/agents/adapters/execution.d.ts +41 -0
- package/dist/agents/adapters/execution.js +73 -0
- package/dist/agents/adapters/mutations.d.ts +22 -0
- package/dist/agents/adapters/mutations.js +30 -0
- package/dist/agents/adapters/notification.d.ts +26 -0
- package/dist/agents/adapters/notification.js +46 -0
- package/dist/agents/adapters/repository.d.ts +28 -0
- package/dist/agents/adapters/repository.js +61 -0
- package/dist/agents/adapters/research.d.ts +26 -0
- package/dist/agents/adapters/research.js +59 -0
- package/dist/agents/adapters/verification.d.ts +36 -0
- package/dist/agents/adapters/verification.js +62 -0
- package/dist/agents/cli-tools.d.ts +1 -0
- package/dist/agents/cli-tools.js +5 -0
- package/dist/agents/cli.d.ts +15 -0
- package/dist/agents/cli.js +109 -0
- package/dist/agents/contracts/messages.d.ts +88 -0
- package/dist/agents/contracts/messages.js +138 -0
- package/dist/agents/contracts/run.d.ts +21 -0
- package/dist/agents/contracts/run.js +0 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.js +5 -0
- package/dist/agents/kernel/agent-kernel.d.ts +63 -0
- package/dist/agents/kernel/agent-kernel.js +291 -0
- package/dist/agents/kernel/trigger-resolver.d.ts +19 -0
- package/dist/agents/kernel/trigger-resolver.js +157 -0
- package/dist/agents/registry-helper.d.ts +4 -0
- package/dist/agents/registry-helper.js +14 -0
- package/dist/agents/registry.d.ts +6 -0
- package/dist/agents/registry.js +98 -0
- package/dist/agents/runtime-types.d.ts +118 -0
- package/dist/agents/runtime-types.js +0 -0
- package/dist/agents/spec-loader.d.ts +18 -0
- package/dist/agents/spec-loader.js +54 -0
- package/dist/agents/spec-normalizer.d.ts +2 -0
- package/dist/agents/spec-normalizer.js +327 -0
- package/dist/agents/spec-types.d.ts +64 -0
- package/dist/agents/spec-types.js +0 -0
- package/dist/agents/testing/agents-smoke.d.ts +1 -0
- package/dist/agents/testing/agents-smoke.js +32 -0
- package/dist/agents/testing/e2e-harness.d.ts +44 -0
- package/dist/agents/testing/e2e-harness.js +503 -0
- package/dist/api/agent-routes.d.ts +13 -0
- package/dist/api/agent-routes.js +327 -0
- package/dist/api/app.d.ts +8 -0
- package/dist/api/app.js +444 -0
- package/dist/api/auth/d1-database.d.ts +3 -0
- package/dist/api/auth/d1-database.js +20 -0
- package/dist/api/auth/d1-provider.d.ts +79 -0
- package/dist/api/auth/d1-provider.js +92 -0
- package/dist/api/auth/d1-store.d.ts +114 -0
- package/dist/api/auth/d1-store.js +895 -0
- package/dist/api/auth/memory-provider.d.ts +77 -0
- package/dist/api/auth/memory-provider.js +249 -0
- package/dist/api/auth/rbac.d.ts +22 -0
- package/dist/api/auth/rbac.js +162 -0
- package/dist/api/auth/tokens.d.ts +18 -0
- package/dist/api/auth/tokens.js +56 -0
- package/dist/api/capabilities.d.ts +9 -0
- package/dist/api/capabilities.js +33 -0
- package/dist/api/config.d.ts +2 -0
- package/dist/api/config.js +77 -0
- package/dist/api/http.d.ts +28 -0
- package/dist/api/http.js +51 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/index.js +20 -0
- package/dist/api/operations-routes.d.ts +11 -0
- package/dist/api/operations-routes.js +87 -0
- package/dist/api/operations.d.ts +3 -0
- package/dist/api/operations.js +26 -0
- package/dist/api/project-routes.d.ts +8 -0
- package/dist/api/project-routes.js +585 -0
- package/dist/api/providers.d.ts +2 -0
- package/dist/api/providers.js +62 -0
- package/dist/api/railway.d.ts +51 -0
- package/dist/api/railway.js +71 -0
- package/dist/api/sdk-dispatch.d.ts +5 -0
- package/dist/api/sdk-dispatch.js +13 -0
- package/dist/api/sdk-routes.d.ts +11 -0
- package/dist/api/sdk-routes.js +29 -0
- package/dist/api/server.d.ts +2 -0
- package/dist/api/server.js +10 -0
- package/dist/api/templates.d.ts +3 -0
- package/dist/api/templates.js +31 -0
- package/dist/api/types.d.ts +237 -0
- package/dist/api/types.js +0 -0
- package/dist/env.yaml +957 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +41 -0
- package/dist/scripts/assert-release-tag-version.d.ts +1 -0
- package/dist/scripts/assert-release-tag-version.js +20 -0
- package/dist/scripts/build-dist.d.ts +1 -0
- package/dist/scripts/build-dist.js +106 -0
- package/dist/scripts/package-tools.d.ts +1 -0
- package/dist/scripts/package-tools.js +7 -0
- package/dist/scripts/publish-package.d.ts +1 -0
- package/dist/scripts/publish-package.js +24 -0
- package/dist/scripts/release-verify.d.ts +1 -0
- package/dist/scripts/release-verify.js +152 -0
- package/dist/scripts/test-smoke.d.ts +1 -0
- package/dist/scripts/test-smoke.js +23 -0
- package/dist/scripts/treeseed-agent-api.d.ts +2 -0
- package/dist/scripts/treeseed-agent-api.js +25 -0
- package/dist/scripts/treeseed-agent-service.d.ts +2 -0
- package/dist/scripts/treeseed-agent-service.js +36 -0
- package/dist/scripts/treeseed-agents.d.ts +2 -0
- package/dist/scripts/treeseed-agents.js +13 -0
- package/dist/services/agents.d.ts +17 -0
- package/dist/services/agents.js +48 -0
- package/dist/services/common.d.ts +66 -0
- package/dist/services/common.js +212 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.js +19 -0
- package/dist/services/manager.d.ts +333 -0
- package/dist/services/manager.js +1368 -0
- package/dist/services/remote-runner.d.ts +30 -0
- package/dist/services/remote-runner.js +230 -0
- package/dist/services/workday-content.d.ts +53 -0
- package/dist/services/workday-content.js +190 -0
- package/dist/services/workday-manager.d.ts +391 -0
- package/dist/services/workday-manager.js +163 -0
- package/dist/services/workday-report.d.ts +238 -0
- package/dist/services/workday-report.js +17 -0
- package/dist/services/workday-start.d.ts +238 -0
- package/dist/services/workday-start.js +17 -0
- package/dist/services/worker-capacity.d.ts +58 -0
- package/dist/services/worker-capacity.js +208 -0
- package/dist/services/worker-pool-scaler.d.ts +27 -0
- package/dist/services/worker-pool-scaler.js +127 -0
- package/dist/services/worker.d.ts +19 -0
- package/dist/services/worker.js +436 -0
- package/dist/templates/github/deploy-processing.workflow.yml +119 -0
- package/package.json +136 -0
- package/templates/github/deploy-processing.workflow.yml +119 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
function parseInteger(value, fallback) {
|
|
4
|
+
if (!value) return fallback;
|
|
5
|
+
const parsed = Number.parseInt(value, 10);
|
|
6
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7
|
+
}
|
|
8
|
+
function resolveLocalWranglerConfigPath(repoRoot, env) {
|
|
9
|
+
const explicit = env.TREESEED_API_D1_WRANGLER_CONFIG?.trim() || env.TREESEED_LOCAL_WRANGLER_CONFIG?.trim();
|
|
10
|
+
if (explicit) return resolve(repoRoot, explicit);
|
|
11
|
+
const generated = resolve(repoRoot, ".treeseed", "generated", "environments", "local", "wrangler.toml");
|
|
12
|
+
return existsSync(generated) ? generated : void 0;
|
|
13
|
+
}
|
|
14
|
+
function normalizeUrl(value) {
|
|
15
|
+
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
16
|
+
}
|
|
17
|
+
function parseCsv(value) {
|
|
18
|
+
return (value ?? "").split(",").map((entry) => entry.trim().toLowerCase()).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
function resolveBaseUrl(env, host, port) {
|
|
21
|
+
if (env.TREESEED_API_BASE_URL?.trim()) {
|
|
22
|
+
return normalizeUrl(env.TREESEED_API_BASE_URL.trim());
|
|
23
|
+
}
|
|
24
|
+
if (env.RAILWAY_PUBLIC_DOMAIN?.trim()) {
|
|
25
|
+
return normalizeUrl(`https://${env.RAILWAY_PUBLIC_DOMAIN.trim()}`);
|
|
26
|
+
}
|
|
27
|
+
return normalizeUrl(`http://${host}:${port}`);
|
|
28
|
+
}
|
|
29
|
+
function resolveApiConfig(env = process.env) {
|
|
30
|
+
const host = env.HOST?.trim() || "0.0.0.0";
|
|
31
|
+
const port = parseInteger(env.PORT, 3e3);
|
|
32
|
+
const baseUrl = resolveBaseUrl(env, host === "0.0.0.0" ? "127.0.0.1" : host, port);
|
|
33
|
+
const issuer = normalizeUrl(env.TREESEED_API_ISSUER?.trim() || baseUrl);
|
|
34
|
+
const repoRoot = resolve(env.TREESEED_API_REPO_ROOT?.trim() || process.cwd());
|
|
35
|
+
return {
|
|
36
|
+
name: env.TREESEED_API_NAME?.trim() || "@treeseed/agent/api",
|
|
37
|
+
host,
|
|
38
|
+
port,
|
|
39
|
+
baseUrl,
|
|
40
|
+
issuer,
|
|
41
|
+
repoRoot,
|
|
42
|
+
projectId: env.TREESEED_PROJECT_ID?.trim() || "treeseed-project",
|
|
43
|
+
authSecret: env.TREESEED_API_AUTH_SECRET?.trim() || "treeseed-api-dev-secret",
|
|
44
|
+
projectApiKey: env.TREESEED_API_PROJECT_KEY?.trim() || void 0,
|
|
45
|
+
projectApiLabel: env.TREESEED_API_PROJECT_LABEL?.trim() || "Project API Key",
|
|
46
|
+
projectApiPermissions: parseCsv(env.TREESEED_API_PROJECT_KEY_PERMISSIONS).length > 0 ? parseCsv(env.TREESEED_API_PROJECT_KEY_PERMISSIONS) : ["sdk:execute:global", "agent:execute:global", "operations:execute:global"],
|
|
47
|
+
cloudflareAccountId: env.CLOUDFLARE_ACCOUNT_ID?.trim() || void 0,
|
|
48
|
+
cloudflareApiToken: env.CLOUDFLARE_API_TOKEN?.trim() || void 0,
|
|
49
|
+
d1DatabaseId: env.TREESEED_API_D1_DATABASE_ID?.trim() || void 0,
|
|
50
|
+
d1DatabaseName: env.TREESEED_API_D1_DATABASE_NAME?.trim() || env.SITE_DATA_DB?.trim() || void 0,
|
|
51
|
+
d1LocalPersistTo: env.TREESEED_API_D1_LOCAL_PERSIST_TO?.trim() || resolve(repoRoot, ".wrangler/state/v3/d1"),
|
|
52
|
+
d1WranglerConfigPath: resolveLocalWranglerConfigPath(repoRoot, env),
|
|
53
|
+
webServiceId: env.TREESEED_API_WEB_SERVICE_ID?.trim() || "web",
|
|
54
|
+
webServiceSecret: env.TREESEED_API_WEB_SERVICE_SECRET?.trim() || "treeseed-web-service-dev-secret",
|
|
55
|
+
webAssertionSecret: env.TREESEED_API_WEB_ASSERTION_SECRET?.trim() || env.TREESEED_API_AUTH_SECRET?.trim() || "treeseed-web-assertion-dev-secret",
|
|
56
|
+
webExchangeTtlSeconds: parseInteger(env.TREESEED_API_WEB_EXCHANGE_TTL, 300),
|
|
57
|
+
bootstrapAdminAllowlist: parseCsv(env.TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST),
|
|
58
|
+
accessTokenTtlSeconds: parseInteger(env.TREESEED_API_ACCESS_TOKEN_TTL, 900),
|
|
59
|
+
refreshTokenTtlSeconds: parseInteger(env.TREESEED_API_REFRESH_TOKEN_TTL, 7 * 24 * 60 * 60),
|
|
60
|
+
deviceCodeTtlSeconds: parseInteger(env.TREESEED_API_DEVICE_CODE_TTL, 10 * 60),
|
|
61
|
+
deviceCodePollIntervalSeconds: parseInteger(env.TREESEED_API_DEVICE_CODE_POLL_INTERVAL, 5),
|
|
62
|
+
templateCatalogPath: env.TREESEED_API_TEMPLATE_CATALOG_PATH?.trim() || void 0,
|
|
63
|
+
providers: {
|
|
64
|
+
auth: env.TREESEED_API_PROVIDER_AUTH?.trim() || "d1",
|
|
65
|
+
agents: {
|
|
66
|
+
execution: env.TREESEED_API_PROVIDER_AGENT_EXECUTION?.trim() || "stub",
|
|
67
|
+
queue: env.TREESEED_API_PROVIDER_AGENT_QUEUE?.trim() || "memory",
|
|
68
|
+
notification: env.TREESEED_API_PROVIDER_AGENT_NOTIFICATION?.trim() || "stub",
|
|
69
|
+
repository: env.TREESEED_API_PROVIDER_AGENT_REPOSITORY?.trim() || "stub",
|
|
70
|
+
verification: env.TREESEED_API_PROVIDER_AGENT_VERIFICATION?.trim() || "stub"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
resolveApiConfig
|
|
77
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { ApiPrincipal, ApiScope } from '@treeseed/sdk/remote';
|
|
3
|
+
import type { AppVariables } from './types.ts';
|
|
4
|
+
export type ApiContext = Context<{
|
|
5
|
+
Variables: AppVariables;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function jsonError(c: Context, status: number, error: string, details?: Record<string, unknown>): Response & import("hono").TypedResponse<{
|
|
8
|
+
ok: false;
|
|
9
|
+
error: string;
|
|
10
|
+
}, never, "json">;
|
|
11
|
+
export declare function bearerTokenFromRequest(request: Request): string | null;
|
|
12
|
+
export declare function hasScope(principal: ApiPrincipal | null, requiredScope: ApiScope): boolean;
|
|
13
|
+
export declare function requireScope(c: ApiContext, requiredScope: ApiScope): (Response & import("hono").TypedResponse<{
|
|
14
|
+
ok: false;
|
|
15
|
+
error: string;
|
|
16
|
+
}, never, "json">) | null;
|
|
17
|
+
export declare function requireAuthentication(c: ApiContext): (Response & import("hono").TypedResponse<{
|
|
18
|
+
ok: false;
|
|
19
|
+
error: string;
|
|
20
|
+
}, never, "json">) | null;
|
|
21
|
+
export declare function requireActorType(c: ApiContext, actorType: 'anonymous' | 'user' | 'service' | 'project', message?: string): (Response & import("hono").TypedResponse<{
|
|
22
|
+
ok: false;
|
|
23
|
+
error: string;
|
|
24
|
+
}, never, "json">) | null;
|
|
25
|
+
export declare function requirePermission(c: ApiContext, permission: string): (Response & import("hono").TypedResponse<{
|
|
26
|
+
ok: false;
|
|
27
|
+
error: string;
|
|
28
|
+
}, never, "json">) | null;
|
package/dist/api/http.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { permissionGranted } from "./auth/rbac.js";
|
|
2
|
+
function jsonError(c, status, error, details) {
|
|
3
|
+
return c.json({
|
|
4
|
+
ok: false,
|
|
5
|
+
error,
|
|
6
|
+
...details ?? {}
|
|
7
|
+
}, { status });
|
|
8
|
+
}
|
|
9
|
+
function bearerTokenFromRequest(request) {
|
|
10
|
+
const header = request.headers.get("authorization");
|
|
11
|
+
if (!header) return null;
|
|
12
|
+
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
13
|
+
return match?.[1] ?? null;
|
|
14
|
+
}
|
|
15
|
+
function hasScope(principal, requiredScope) {
|
|
16
|
+
return Boolean(principal && (principal.scopes.includes(requiredScope) || principal.scopes.includes("*")));
|
|
17
|
+
}
|
|
18
|
+
function requireScope(c, requiredScope) {
|
|
19
|
+
if (!hasScope(c.get("principal"), requiredScope)) {
|
|
20
|
+
return jsonError(c, 401, "Authentication required.", { requiredScope });
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function requireAuthentication(c) {
|
|
25
|
+
if (!c.get("principal")) {
|
|
26
|
+
return jsonError(c, 401, "Authentication required.");
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function requireActorType(c, actorType, message = "Trusted service authentication required.") {
|
|
31
|
+
if (c.get("actorType") !== actorType) {
|
|
32
|
+
return jsonError(c, 401, message);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function requirePermission(c, permission) {
|
|
37
|
+
const principal = c.get("principal");
|
|
38
|
+
if (!principal || !permissionGranted(c.get("permissionGrants"), permission)) {
|
|
39
|
+
return jsonError(c, 403, "Permission denied.", { permission });
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
bearerTokenFromRequest,
|
|
45
|
+
hasScope,
|
|
46
|
+
jsonError,
|
|
47
|
+
requireActorType,
|
|
48
|
+
requireAuthentication,
|
|
49
|
+
requirePermission,
|
|
50
|
+
requireScope
|
|
51
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { createTreeseedApiApp, createTreeseedApiRouter } from './app.ts';
|
|
2
|
+
export { resolveApiConfig } from './config.ts';
|
|
3
|
+
export { createRailwayTreeseedApiServer, createTreeseedNodeServer } from './railway.ts';
|
|
4
|
+
export { resolveApiRuntimeProviders } from './providers.ts';
|
|
5
|
+
export { resolveApiD1Database } from './auth/d1-database.ts';
|
|
6
|
+
export { loadTemplateCatalog } from './templates.ts';
|
|
7
|
+
export { MemoryDeviceCodeAuthProvider } from './auth/memory-provider.ts';
|
|
8
|
+
export { D1AuthProvider } from './auth/d1-provider.ts';
|
|
9
|
+
export type * from './types.ts';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createTreeseedApiApp, createTreeseedApiRouter } from "./app.js";
|
|
2
|
+
import { resolveApiConfig } from "./config.js";
|
|
3
|
+
import { createRailwayTreeseedApiServer, createTreeseedNodeServer } from "./railway.js";
|
|
4
|
+
import { resolveApiRuntimeProviders } from "./providers.js";
|
|
5
|
+
import { resolveApiD1Database } from "./auth/d1-database.js";
|
|
6
|
+
import { loadTemplateCatalog } from "./templates.js";
|
|
7
|
+
import { MemoryDeviceCodeAuthProvider } from "./auth/memory-provider.js";
|
|
8
|
+
import { D1AuthProvider } from "./auth/d1-provider.js";
|
|
9
|
+
export {
|
|
10
|
+
D1AuthProvider,
|
|
11
|
+
MemoryDeviceCodeAuthProvider,
|
|
12
|
+
createRailwayTreeseedApiServer,
|
|
13
|
+
createTreeseedApiApp,
|
|
14
|
+
createTreeseedApiRouter,
|
|
15
|
+
createTreeseedNodeServer,
|
|
16
|
+
loadTemplateCatalog,
|
|
17
|
+
resolveApiConfig,
|
|
18
|
+
resolveApiD1Database,
|
|
19
|
+
resolveApiRuntimeProviders
|
|
20
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { AgentSdk } from '@treeseed/sdk';
|
|
3
|
+
import { executeHttpWorkflowOperation } from './operations.ts';
|
|
4
|
+
import type { ApiConfig } from './types.ts';
|
|
5
|
+
export declare function registerOperationRoutes(app: Hono<any>, options: {
|
|
6
|
+
config: ApiConfig;
|
|
7
|
+
scope: string;
|
|
8
|
+
prefix?: string;
|
|
9
|
+
sdk?: AgentSdk;
|
|
10
|
+
executeOperation?: typeof executeHttpWorkflowOperation;
|
|
11
|
+
}): void;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { findDispatchCapability, findTreeseedOperation } from "@treeseed/sdk";
|
|
3
|
+
import { executeHttpWorkflowOperation, isHttpWorkflowOperationAllowed } from "./operations.js";
|
|
4
|
+
import { enqueueTaskFromSdk } from "../services/common.js";
|
|
5
|
+
import { enqueueTaskAndEnsureCapacity } from "../services/worker-capacity.js";
|
|
6
|
+
import { jsonError, requireScope } from "./http.js";
|
|
7
|
+
function registerOperationRoutes(app, options) {
|
|
8
|
+
const executeOperation = options.executeOperation ?? executeHttpWorkflowOperation;
|
|
9
|
+
const prefix = options.prefix ?? "";
|
|
10
|
+
function withPrefix(path) {
|
|
11
|
+
if (!prefix) return path;
|
|
12
|
+
return `${prefix}${path}`.replace(/\/{2,}/g, "/");
|
|
13
|
+
}
|
|
14
|
+
app.post(withPrefix("/operations/:operation"), async (c) => {
|
|
15
|
+
const unauthorized = requireScope(c, options.scope);
|
|
16
|
+
if (unauthorized) return unauthorized;
|
|
17
|
+
const requestedOperation = c.req.param("operation");
|
|
18
|
+
const resolvedOperation = findTreeseedOperation(requestedOperation);
|
|
19
|
+
if (!resolvedOperation) {
|
|
20
|
+
return jsonError(c, 400, `Unknown Treeseed operation "${requestedOperation}".`, {
|
|
21
|
+
operation: requestedOperation
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (!isHttpWorkflowOperationAllowed(resolvedOperation.name)) {
|
|
25
|
+
return jsonError(c, 400, `Workflow operation "${resolvedOperation.name}" is not supported over HTTP.`, {
|
|
26
|
+
operation: resolvedOperation.name
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const body = await c.req.json().catch(() => ({}));
|
|
30
|
+
try {
|
|
31
|
+
const capability = findDispatchCapability("workflow", resolvedOperation.name);
|
|
32
|
+
if (capability?.executionClass === "remote_job" && options.sdk) {
|
|
33
|
+
const input = body && typeof body.input === "object" ? body.input : {};
|
|
34
|
+
const idempotencyKey = typeof body.idempotencyKey === "string" && body.idempotencyKey.trim() ? body.idempotencyKey.trim() : `workflow:${resolvedOperation.name}:${crypto.createHash("sha256").update(JSON.stringify(input)).digest("hex")}`;
|
|
35
|
+
const created = await options.sdk.createTask({
|
|
36
|
+
workDayId: typeof body.workDayId === "string" ? body.workDayId : "",
|
|
37
|
+
agentId: "workflow-dispatch",
|
|
38
|
+
type: "workflow_dispatch",
|
|
39
|
+
priority: typeof body.priority === "number" ? body.priority : 75,
|
|
40
|
+
idempotencyKey,
|
|
41
|
+
payload: {
|
|
42
|
+
executionKind: "workflow_dispatch",
|
|
43
|
+
namespace: "workflow",
|
|
44
|
+
operation: resolvedOperation.name,
|
|
45
|
+
input,
|
|
46
|
+
requestedByType: c.get("actorType"),
|
|
47
|
+
requestedById: c.get("principal")?.id ?? null
|
|
48
|
+
},
|
|
49
|
+
actor: c.get("principal")?.id ?? "api"
|
|
50
|
+
});
|
|
51
|
+
if (!created.payload) {
|
|
52
|
+
return jsonError(c, 500, `Failed to create workflow task for "${resolvedOperation.name}".`, {
|
|
53
|
+
operation: resolvedOperation.name
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const capacity = await enqueueTaskAndEnsureCapacity(options.sdk, {
|
|
57
|
+
taskId: String(created.payload.id ?? ""),
|
|
58
|
+
actor: c.get("principal")?.id ?? "api",
|
|
59
|
+
priorityClass: "interactive",
|
|
60
|
+
projectId: options.config.projectId,
|
|
61
|
+
enqueueTask: enqueueTaskFromSdk
|
|
62
|
+
});
|
|
63
|
+
return c.json({
|
|
64
|
+
ok: true,
|
|
65
|
+
mode: "task",
|
|
66
|
+
operation: resolvedOperation.name,
|
|
67
|
+
payload: created.payload,
|
|
68
|
+
workerState: capacity.workerState,
|
|
69
|
+
capacity: {
|
|
70
|
+
desiredWorkers: capacity.desiredWorkers,
|
|
71
|
+
scaleApplied: capacity.scaleApplied,
|
|
72
|
+
reason: capacity.scaleReason
|
|
73
|
+
}
|
|
74
|
+
}, { status: 202 });
|
|
75
|
+
}
|
|
76
|
+
const result = await executeOperation(resolvedOperation.name, body);
|
|
77
|
+
return c.json(result, { status: result.ok ? 200 : 400 });
|
|
78
|
+
} catch (error) {
|
|
79
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
+
const status = /Unknown Treeseed operation|not supported over HTTP|confirmation required/i.test(message) ? 400 : 500;
|
|
81
|
+
return jsonError(c, status, message, { operation: resolvedOperation.name });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
registerOperationRoutes
|
|
87
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ApiWorkflowOperationResponse, WorkflowHttpOperationRequest } from './types.ts';
|
|
2
|
+
export declare function isHttpWorkflowOperationAllowed(operation: string): boolean;
|
|
3
|
+
export declare function executeHttpWorkflowOperation(operation: string, request: WorkflowHttpOperationRequest): Promise<ApiWorkflowOperationResponse>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TreeseedOperationsSdk } from "@treeseed/sdk";
|
|
2
|
+
const HTTP_BLOCKED_WORKFLOW_OPERATIONS = /* @__PURE__ */ new Set(["dev", "dev:watch"]);
|
|
3
|
+
function isHttpWorkflowOperationAllowed(operation) {
|
|
4
|
+
return !HTTP_BLOCKED_WORKFLOW_OPERATIONS.has(operation);
|
|
5
|
+
}
|
|
6
|
+
async function executeHttpWorkflowOperation(operation, request) {
|
|
7
|
+
if (!isHttpWorkflowOperationAllowed(operation)) {
|
|
8
|
+
throw new Error(`Workflow operation "${operation}" is not supported over HTTP.`);
|
|
9
|
+
}
|
|
10
|
+
const operations = new TreeseedOperationsSdk();
|
|
11
|
+
return operations.execute({
|
|
12
|
+
operationName: operation,
|
|
13
|
+
input: request.input ?? {}
|
|
14
|
+
}, {
|
|
15
|
+
cwd: request.cwd ?? process.cwd(),
|
|
16
|
+
env: {
|
|
17
|
+
...process.env,
|
|
18
|
+
...request.env ?? {}
|
|
19
|
+
},
|
|
20
|
+
transport: "api"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
executeHttpWorkflowOperation,
|
|
25
|
+
isHttpWorkflowOperationAllowed
|
|
26
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { AgentSdk } from '@treeseed/sdk';
|
|
3
|
+
import type { ApiConfig } from './types.ts';
|
|
4
|
+
export declare function registerProjectRoutes(app: Hono<any>, options: {
|
|
5
|
+
config: ApiConfig;
|
|
6
|
+
sharedSdk: AgentSdk;
|
|
7
|
+
prefix?: string;
|
|
8
|
+
}): void;
|