@treeseed/sdk 0.9.0 → 0.10.6

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.
Files changed (76) hide show
  1. package/dist/api/app.d.ts +8 -0
  2. package/dist/api/app.js +404 -0
  3. package/dist/api/auth/d1-database.d.ts +3 -0
  4. package/dist/api/auth/d1-database.js +20 -0
  5. package/dist/api/auth/d1-provider.d.ts +79 -0
  6. package/dist/api/auth/d1-provider.js +92 -0
  7. package/dist/api/auth/d1-store.d.ts +114 -0
  8. package/dist/api/auth/d1-store.js +902 -0
  9. package/dist/api/auth/memory-provider.d.ts +77 -0
  10. package/dist/api/auth/memory-provider.js +256 -0
  11. package/dist/api/auth/rbac.d.ts +22 -0
  12. package/dist/api/auth/rbac.js +162 -0
  13. package/dist/api/auth/tokens.d.ts +18 -0
  14. package/dist/api/auth/tokens.js +56 -0
  15. package/dist/api/config.d.ts +2 -0
  16. package/dist/api/config.js +118 -0
  17. package/dist/api/http.d.ts +28 -0
  18. package/dist/api/http.js +51 -0
  19. package/dist/api/index.d.ts +10 -0
  20. package/dist/api/index.js +27 -0
  21. package/dist/api/operations-routes.d.ts +11 -0
  22. package/dist/api/operations-routes.js +39 -0
  23. package/dist/api/operations.d.ts +3 -0
  24. package/dist/api/operations.js +26 -0
  25. package/dist/api/providers.d.ts +2 -0
  26. package/dist/api/providers.js +68 -0
  27. package/dist/api/railway.d.ts +52 -0
  28. package/dist/api/railway.js +71 -0
  29. package/dist/api/sdk-dispatch.d.ts +6 -0
  30. package/dist/api/sdk-dispatch.js +14 -0
  31. package/dist/api/sdk-routes.d.ts +11 -0
  32. package/dist/api/sdk-routes.js +29 -0
  33. package/dist/api/templates.d.ts +3 -0
  34. package/dist/api/templates.js +31 -0
  35. package/dist/api/types.d.ts +232 -0
  36. package/dist/api/types.js +0 -0
  37. package/dist/capacity-provider.d.ts +383 -0
  38. package/dist/capacity-provider.js +535 -0
  39. package/dist/capacity.d.ts +2 -35
  40. package/dist/control-plane-client.d.ts +8 -3
  41. package/dist/control-plane-client.js +12 -1
  42. package/dist/dispatch.js +0 -1
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.js +40 -0
  45. package/dist/market-client.d.ts +1 -5
  46. package/dist/market-client.js +2 -8
  47. package/dist/operations/providers/default.js +0 -9
  48. package/dist/operations/services/config-runtime.d.ts +2 -2
  49. package/dist/operations/services/config-runtime.js +55 -3
  50. package/dist/operations/services/github-automation.d.ts +10 -15
  51. package/dist/operations/services/github-automation.js +3 -35
  52. package/dist/operations/services/hosting-audit.d.ts +1 -1
  53. package/dist/operations/services/hosting-audit.js +3 -27
  54. package/dist/operations/services/hub-launch.d.ts +0 -1
  55. package/dist/operations/services/hub-launch.js +1 -2
  56. package/dist/operations/services/hub-provider-launch.d.ts +0 -15
  57. package/dist/operations/services/hub-provider-launch.js +5 -41
  58. package/dist/operations/services/package-reference-policy.d.ts +1 -0
  59. package/dist/operations/services/package-reference-policy.js +10 -2
  60. package/dist/operations/services/project-platform.d.ts +9 -9
  61. package/dist/operations/services/project-platform.js +6 -17
  62. package/dist/operations/services/release-candidate.js +19 -3
  63. package/dist/operations-registry.js +1 -3
  64. package/dist/platform/contracts.d.ts +2 -2
  65. package/dist/project-workflow.d.ts +0 -3
  66. package/dist/scripts/publish-package.js +5 -1
  67. package/dist/scripts/tenant-workflow-action.js +3 -3
  68. package/dist/scripts/workflow-commands.test.js +3 -6
  69. package/dist/sdk-types.d.ts +33 -1
  70. package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +1 -4
  71. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/api/server.js +1 -1
  72. package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +1 -17
  73. package/dist/workflow/operations.js +26 -8
  74. package/package.json +14 -1
  75. package/templates/github/hosted-project.workflow.yml +0 -1
  76. package/templates/github/deploy-processing.workflow.yml +0 -123
@@ -0,0 +1,118 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ const LOCAL_DEV_AUTH_TTL_SECONDS = 365 * 24 * 60 * 60;
4
+ const DEFAULT_AUTH_TTL_SECONDS = 15 * 60;
5
+ const DEFAULT_REFRESH_TTL_SECONDS = 7 * 24 * 60 * 60;
6
+ function parseInteger(value, fallback) {
7
+ if (!value) return fallback;
8
+ const parsed = Number.parseInt(value, 10);
9
+ return Number.isFinite(parsed) ? parsed : fallback;
10
+ }
11
+ function resolveLocalWranglerConfigPath(repoRoot, env) {
12
+ const explicit = env.TREESEED_API_D1_WRANGLER_CONFIG?.trim() || env.TREESEED_LOCAL_WRANGLER_CONFIG?.trim();
13
+ if (explicit) return resolve(repoRoot, explicit);
14
+ const generated = resolve(repoRoot, ".treeseed", "generated", "environments", "local", "wrangler.toml");
15
+ return existsSync(generated) ? generated : void 0;
16
+ }
17
+ function normalizeUrl(value) {
18
+ return value.endsWith("/") ? value.slice(0, -1) : value;
19
+ }
20
+ function isLoopbackUrl(value) {
21
+ try {
22
+ const url = new URL(value);
23
+ return url.hostname === "127.0.0.1" || url.hostname === "localhost";
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ function parseCsv(value) {
29
+ return (value ?? "").split(",").map((entry) => entry.trim().toLowerCase()).filter(Boolean);
30
+ }
31
+ function resolveBaseUrl(env, host, port) {
32
+ if (env.TREESEED_API_BASE_URL?.trim()) {
33
+ return normalizeUrl(env.TREESEED_API_BASE_URL.trim());
34
+ }
35
+ if (env.RAILWAY_PUBLIC_DOMAIN?.trim()) {
36
+ return normalizeUrl(`https://${env.RAILWAY_PUBLIC_DOMAIN.trim()}`);
37
+ }
38
+ return normalizeUrl(`http://${host}:${port}`);
39
+ }
40
+ function resolveAuthApprovalBaseUrl(env, baseUrl) {
41
+ const explicit = env.TREESEED_API_AUTH_APPROVAL_BASE_URL?.trim() || env.TREESEED_SITE_URL?.trim() || env.BETTER_AUTH_URL?.trim();
42
+ const explicitIsLoopback = explicit ? isLoopbackUrl(explicit) : false;
43
+ try {
44
+ const url = new URL(baseUrl);
45
+ const isLocalApi = url.hostname === "127.0.0.1" || url.hostname === "localhost";
46
+ if (!isLocalApi && explicit && explicitIsLoopback) {
47
+ throw new Error(`Refusing loopback device approval URL "${explicit}" for remote API "${baseUrl}".`);
48
+ }
49
+ if (explicit) {
50
+ return normalizeUrl(explicit);
51
+ }
52
+ if (isLocalApi && url.port === "3000") {
53
+ url.port = "4321";
54
+ return normalizeUrl(url.toString());
55
+ }
56
+ } catch (error) {
57
+ if (error instanceof Error && error.message.includes("Refusing loopback device approval URL")) {
58
+ throw error;
59
+ }
60
+ if (explicit && /^https?:\/\//u.test(explicit)) {
61
+ throw new Error(`Invalid device approval URL configuration for API "${baseUrl}".`);
62
+ }
63
+ }
64
+ return baseUrl;
65
+ }
66
+ function resolveApiConfig(env = process.env) {
67
+ const host = env.HOST?.trim() || "0.0.0.0";
68
+ const port = parseInteger(env.PORT, 3e3);
69
+ const baseUrl = resolveBaseUrl(env, host === "0.0.0.0" ? "127.0.0.1" : host, port);
70
+ const issuer = normalizeUrl(env.TREESEED_API_ISSUER?.trim() || baseUrl);
71
+ const repoRoot = resolve(env.TREESEED_API_REPO_ROOT?.trim() || process.cwd());
72
+ const localDevAuth = env.TREESEED_LOCAL_DEV_MODE === "cloudflare" || isLoopbackUrl(baseUrl);
73
+ const defaultAccessTokenTtl = localDevAuth ? LOCAL_DEV_AUTH_TTL_SECONDS : DEFAULT_AUTH_TTL_SECONDS;
74
+ const defaultRefreshTokenTtl = localDevAuth ? LOCAL_DEV_AUTH_TTL_SECONDS : DEFAULT_REFRESH_TTL_SECONDS;
75
+ return {
76
+ name: env.TREESEED_API_NAME?.trim() || "@treeseed/sdk/api",
77
+ host,
78
+ port,
79
+ baseUrl,
80
+ authApprovalBaseUrl: resolveAuthApprovalBaseUrl(env, baseUrl),
81
+ issuer,
82
+ repoRoot,
83
+ projectId: env.TREESEED_PROJECT_ID?.trim() || "treeseed-project",
84
+ authSecret: env.TREESEED_API_AUTH_SECRET?.trim() || "treeseed-api-dev-secret",
85
+ projectApiKey: env.TREESEED_API_PROJECT_KEY?.trim() || void 0,
86
+ projectApiLabel: env.TREESEED_API_PROJECT_LABEL?.trim() || "Project API Key",
87
+ 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"],
88
+ cloudflareAccountId: env.CLOUDFLARE_ACCOUNT_ID?.trim() || void 0,
89
+ cloudflareApiToken: env.CLOUDFLARE_API_TOKEN?.trim() || void 0,
90
+ d1DatabaseId: env.TREESEED_API_D1_DATABASE_ID?.trim() || void 0,
91
+ d1DatabaseName: env.TREESEED_API_D1_DATABASE_NAME?.trim() || env.SITE_DATA_DB?.trim() || void 0,
92
+ d1LocalPersistTo: env.TREESEED_API_D1_LOCAL_PERSIST_TO?.trim() || resolve(repoRoot, ".wrangler/state/v3/d1"),
93
+ d1WranglerConfigPath: resolveLocalWranglerConfigPath(repoRoot, env),
94
+ webServiceId: env.TREESEED_API_WEB_SERVICE_ID?.trim() || "web",
95
+ webServiceSecret: env.TREESEED_API_WEB_SERVICE_SECRET?.trim() || "treeseed-web-service-dev-secret",
96
+ webAssertionSecret: env.TREESEED_API_WEB_ASSERTION_SECRET?.trim() || env.TREESEED_API_AUTH_SECRET?.trim() || "treeseed-web-assertion-dev-secret",
97
+ webExchangeTtlSeconds: parseInteger(env.TREESEED_API_WEB_EXCHANGE_TTL, 300),
98
+ bootstrapAdminAllowlist: parseCsv(env.TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST),
99
+ accessTokenTtlSeconds: parseInteger(env.TREESEED_API_ACCESS_TOKEN_TTL, defaultAccessTokenTtl),
100
+ refreshTokenTtlSeconds: parseInteger(env.TREESEED_API_REFRESH_TOKEN_TTL, defaultRefreshTokenTtl),
101
+ deviceCodeTtlSeconds: parseInteger(env.TREESEED_API_DEVICE_CODE_TTL, 10 * 60),
102
+ deviceCodePollIntervalSeconds: parseInteger(env.TREESEED_API_DEVICE_CODE_POLL_INTERVAL, 5),
103
+ templateCatalogPath: env.TREESEED_API_TEMPLATE_CATALOG_PATH?.trim() || void 0,
104
+ providers: {
105
+ auth: env.TREESEED_API_PROVIDER_AUTH?.trim() || "d1",
106
+ agents: {
107
+ execution: env.TREESEED_API_PROVIDER_AGENT_EXECUTION?.trim() || "stub",
108
+ queue: env.TREESEED_API_PROVIDER_AGENT_QUEUE?.trim() || "memory",
109
+ notification: env.TREESEED_API_PROVIDER_AGENT_NOTIFICATION?.trim() || "stub",
110
+ repository: env.TREESEED_API_PROVIDER_AGENT_REPOSITORY?.trim() || "stub",
111
+ verification: env.TREESEED_API_PROVIDER_AGENT_VERIFICATION?.trim() || "stub"
112
+ }
113
+ }
114
+ };
115
+ }
116
+ export {
117
+ resolveApiConfig
118
+ };
@@ -0,0 +1,28 @@
1
+ import type { Context } from 'hono';
2
+ import type { ApiPrincipal, ApiScope } from '../remote.ts';
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;
@@ -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,10 @@
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 { bearerTokenFromRequest, jsonError, requireActorType, requireAuthentication, requirePermission, requireScope } from './http.ts';
10
+ export type * from './types.ts';
@@ -0,0 +1,27 @@
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
+ import { bearerTokenFromRequest, jsonError, requireActorType, requireAuthentication, requirePermission, requireScope } from "./http.js";
10
+ export {
11
+ D1AuthProvider,
12
+ MemoryDeviceCodeAuthProvider,
13
+ bearerTokenFromRequest,
14
+ createRailwayTreeseedApiServer,
15
+ createTreeseedApiApp,
16
+ createTreeseedApiRouter,
17
+ createTreeseedNodeServer,
18
+ jsonError,
19
+ loadTemplateCatalog,
20
+ requireActorType,
21
+ requireAuthentication,
22
+ requirePermission,
23
+ requireScope,
24
+ resolveApiConfig,
25
+ resolveApiD1Database,
26
+ resolveApiRuntimeProviders
27
+ };
@@ -0,0 +1,11 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AgentSdk } from '../sdk.ts';
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,39 @@
1
+ import { findTreeseedOperation } from "../operations-registry.js";
2
+ import { executeHttpWorkflowOperation, isHttpWorkflowOperationAllowed } from "./operations.js";
3
+ import { jsonError, requireScope } from "./http.js";
4
+ function registerOperationRoutes(app, options) {
5
+ const executeOperation = options.executeOperation ?? executeHttpWorkflowOperation;
6
+ const prefix = options.prefix ?? "";
7
+ function withPrefix(path) {
8
+ if (!prefix) return path;
9
+ return `${prefix}${path}`.replace(/\/{2,}/g, "/");
10
+ }
11
+ app.post(withPrefix("/operations/:operation"), async (c) => {
12
+ const unauthorized = requireScope(c, options.scope);
13
+ if (unauthorized) return unauthorized;
14
+ const requestedOperation = c.req.param("operation");
15
+ const resolvedOperation = findTreeseedOperation(requestedOperation);
16
+ if (!resolvedOperation) {
17
+ return jsonError(c, 400, `Unknown Treeseed operation "${requestedOperation}".`, {
18
+ operation: requestedOperation
19
+ });
20
+ }
21
+ if (!isHttpWorkflowOperationAllowed(resolvedOperation.name)) {
22
+ return jsonError(c, 400, `Workflow operation "${resolvedOperation.name}" is not supported over HTTP.`, {
23
+ operation: resolvedOperation.name
24
+ });
25
+ }
26
+ const body = await c.req.json().catch(() => ({}));
27
+ try {
28
+ const result = await executeOperation(resolvedOperation.name, body);
29
+ return c.json(result, { status: result.ok ? 200 : 400 });
30
+ } catch (error) {
31
+ const message = error instanceof Error ? error.message : String(error);
32
+ const status = /Unknown Treeseed operation|not supported over HTTP|confirmation required/i.test(message) ? 400 : 500;
33
+ return jsonError(c, status, message, { operation: resolvedOperation.name });
34
+ }
35
+ });
36
+ }
37
+ export {
38
+ registerOperationRoutes
39
+ };
@@ -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 "../operations.js";
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,2 @@
1
+ import type { ApiConfig, ApiRuntimeProviders, ResolvedApiRuntimeProviders } from './types.ts';
2
+ export declare function resolveApiRuntimeProviders(config: ApiConfig, overrides?: ApiRuntimeProviders): ResolvedApiRuntimeProviders;
@@ -0,0 +1,68 @@
1
+ import { MemoryDeviceCodeAuthProvider } from "./auth/memory-provider.js";
2
+ import { D1AuthProvider } from "./auth/d1-provider.js";
3
+ import { resolveApiD1Database } from "./auth/d1-database.js";
4
+ function addProviders(target, incoming, label) {
5
+ for (const [id, value] of Object.entries(incoming ?? {})) {
6
+ if (target.has(id)) {
7
+ throw new Error(`Treeseed API runtime found duplicate ${label} provider "${id}".`);
8
+ }
9
+ target.set(id, value);
10
+ }
11
+ }
12
+ function resolveSelectedProvider(registry, selectedId, label) {
13
+ const selected = registry.get(selectedId);
14
+ if (!selected) {
15
+ throw new Error(`Treeseed API runtime could not resolve ${label} provider "${selectedId}".`);
16
+ }
17
+ return selected;
18
+ }
19
+ function resolveApiRuntimeProviders(config, overrides = {}) {
20
+ const authRegistry = /* @__PURE__ */ new Map();
21
+ const agentExecution = /* @__PURE__ */ new Map();
22
+ const agentQueue = /* @__PURE__ */ new Map();
23
+ const agentNotification = /* @__PURE__ */ new Map();
24
+ const agentRepository = /* @__PURE__ */ new Map();
25
+ const agentVerification = /* @__PURE__ */ new Map();
26
+ addProviders(authRegistry, {
27
+ memory: ({ config: runtimeConfig }) => new MemoryDeviceCodeAuthProvider({
28
+ ...runtimeConfig,
29
+ baseUrl: runtimeConfig.authApprovalBaseUrl ?? runtimeConfig.baseUrl
30
+ }),
31
+ d1: ({ config: runtimeConfig }) => new D1AuthProvider({
32
+ ...runtimeConfig,
33
+ baseUrl: runtimeConfig.authApprovalBaseUrl ?? runtimeConfig.baseUrl
34
+ }, { db: resolveApiD1Database(runtimeConfig) })
35
+ }, "auth");
36
+ addProviders(authRegistry, overrides.auth, "auth");
37
+ addProviders(agentExecution, { stub: { id: "stub" } }, "agent execution");
38
+ addProviders(agentQueue, { memory: { id: "memory" } }, "agent queue");
39
+ addProviders(agentNotification, { stub: { id: "stub" } }, "agent notification");
40
+ addProviders(agentRepository, { stub: { id: "stub" } }, "agent repository");
41
+ addProviders(agentVerification, { stub: { id: "stub" } }, "agent verification");
42
+ addProviders(agentExecution, overrides.agentExecution, "agent execution");
43
+ addProviders(agentQueue, overrides.agentQueue, "agent queue");
44
+ addProviders(agentNotification, overrides.agentNotification, "agent notification");
45
+ addProviders(agentRepository, overrides.agentRepository, "agent repository");
46
+ addProviders(agentVerification, overrides.agentVerification, "agent verification");
47
+ const authFactory = resolveSelectedProvider(authRegistry, config.providers.auth, "auth");
48
+ resolveSelectedProvider(agentExecution, config.providers.agents.execution, "agent execution");
49
+ resolveSelectedProvider(agentQueue, config.providers.agents.queue, "agent queue");
50
+ resolveSelectedProvider(agentNotification, config.providers.agents.notification, "agent notification");
51
+ resolveSelectedProvider(agentRepository, config.providers.agents.repository, "agent repository");
52
+ resolveSelectedProvider(agentVerification, config.providers.agents.verification, "agent verification");
53
+ return {
54
+ auth: authFactory({ config }),
55
+ registries: {
56
+ auth: authRegistry,
57
+ agentExecution,
58
+ agentQueue,
59
+ agentNotification,
60
+ agentRepository,
61
+ agentVerification
62
+ },
63
+ selections: config.providers
64
+ };
65
+ }
66
+ export {
67
+ resolveApiRuntimeProviders
68
+ };
@@ -0,0 +1,52 @@
1
+ import { type Server } from 'node:http';
2
+ import type { Hono } from 'hono';
3
+ import type { ApiServerOptions } from './types.ts';
4
+ export declare function createTreeseedNodeServer(options?: ApiServerOptions): Promise<{
5
+ app: Hono<{
6
+ Variables: import("./types.ts").AppVariables;
7
+ }, import("hono/types").BlankSchema, "/">;
8
+ config: {
9
+ providers: {
10
+ agents: {
11
+ execution: string;
12
+ queue: string;
13
+ notification: string;
14
+ repository: string;
15
+ verification: string;
16
+ };
17
+ auth: string;
18
+ };
19
+ name: string;
20
+ host: string;
21
+ port: number;
22
+ baseUrl: string;
23
+ authApprovalBaseUrl?: string;
24
+ issuer: string;
25
+ repoRoot: string;
26
+ projectId: string;
27
+ authSecret: string;
28
+ projectApiKey?: string;
29
+ projectApiLabel: string;
30
+ projectApiPermissions: string[];
31
+ cloudflareAccountId?: string;
32
+ cloudflareApiToken?: string;
33
+ d1DatabaseId?: string;
34
+ d1DatabaseName?: string;
35
+ d1LocalPersistTo?: string;
36
+ d1WranglerConfigPath?: string;
37
+ webServiceId: string;
38
+ webServiceSecret: string;
39
+ webAssertionSecret: string;
40
+ webExchangeTtlSeconds: number;
41
+ bootstrapAdminAllowlist: string[];
42
+ accessTokenTtlSeconds: number;
43
+ refreshTokenTtlSeconds: number;
44
+ deviceCodeTtlSeconds: number;
45
+ deviceCodePollIntervalSeconds: number;
46
+ templateCatalogPath?: string;
47
+ };
48
+ server: Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
49
+ url: string;
50
+ close(): Promise<void>;
51
+ }>;
52
+ export declare const createRailwayTreeseedApiServer: typeof createTreeseedNodeServer;
@@ -0,0 +1,71 @@
1
+ import { createServer } from "node:http";
2
+ import { Readable } from "node:stream";
3
+ import { createTreeseedApiApp } from "./app.js";
4
+ import { resolveApiConfig } from "./config.js";
5
+ function hasRequestBody(method) {
6
+ return method !== "GET" && method !== "HEAD";
7
+ }
8
+ async function honoNodeHandler(app, request, response) {
9
+ const req = request;
10
+ const res = response;
11
+ const origin = req.headers.host ? `http://${req.headers.host}` : "http://127.0.0.1";
12
+ const url = new URL(req.url ?? "/", origin);
13
+ const webRequest = new Request(url, {
14
+ method: req.method,
15
+ headers: req.headers,
16
+ body: hasRequestBody(req.method) ? req : void 0,
17
+ duplex: "half"
18
+ });
19
+ const webResponse = await app.fetch(webRequest);
20
+ res.statusCode = webResponse.status;
21
+ webResponse.headers.forEach((value, key) => {
22
+ res.setHeader(key, value);
23
+ });
24
+ if (!webResponse.body) {
25
+ res.end();
26
+ return;
27
+ }
28
+ Readable.fromWeb(webResponse.body).pipe(res);
29
+ }
30
+ async function createTreeseedNodeServer(options = {}) {
31
+ const config = {
32
+ ...resolveApiConfig(),
33
+ ...options.config ?? {},
34
+ providers: {
35
+ ...resolveApiConfig().providers,
36
+ ...options.config?.providers ?? {},
37
+ agents: {
38
+ ...resolveApiConfig().providers.agents,
39
+ ...options.config?.providers?.agents ?? {}
40
+ }
41
+ }
42
+ };
43
+ const app = createTreeseedApiApp({
44
+ ...options,
45
+ config
46
+ });
47
+ const server = createServer((req, res) => {
48
+ void honoNodeHandler(app, req, res);
49
+ });
50
+ await new Promise((resolvePromise) => {
51
+ server.listen(config.port, config.host, () => resolvePromise());
52
+ });
53
+ const address = server.address();
54
+ const resolvedUrl = address ? `${config.baseUrl.startsWith("http") ? config.baseUrl : `http://${address.address}:${address.port}`}` : config.baseUrl;
55
+ return {
56
+ app,
57
+ config,
58
+ server,
59
+ url: resolvedUrl,
60
+ async close() {
61
+ await new Promise((resolvePromise, rejectPromise) => {
62
+ server.close((error) => error ? rejectPromise(error) : resolvePromise());
63
+ });
64
+ }
65
+ };
66
+ }
67
+ const createRailwayTreeseedApiServer = createTreeseedNodeServer;
68
+ export {
69
+ createRailwayTreeseedApiServer,
70
+ createTreeseedNodeServer
71
+ };
@@ -0,0 +1,6 @@
1
+ import type { RemoteSdkOperationRequest } from '../remote.ts';
2
+ import type { AgentSdk } from '../sdk.ts';
3
+ import { executeSdkOperation, findSdkOperation, listSdkOperationNames } from '../sdk-dispatch.ts';
4
+ import type { ApiConfig } from './types.ts';
5
+ export { executeSdkOperation, findSdkOperation, listSdkOperationNames, };
6
+ export declare function resolveSdkInstance(sharedSdk: AgentSdk | undefined, config: ApiConfig, request: RemoteSdkOperationRequest): AgentSdk;
@@ -0,0 +1,14 @@
1
+ import { AgentSdk as AgentSdkClass } from "../sdk.js";
2
+ import { executeSdkOperation, findSdkOperation, listSdkOperationNames } from "../sdk-dispatch.js";
3
+ function resolveSdkInstance(sharedSdk, config, request) {
4
+ if (!request.repoRoot || request.repoRoot === config.repoRoot) {
5
+ return sharedSdk ?? new AgentSdkClass({ repoRoot: config.repoRoot });
6
+ }
7
+ return new AgentSdkClass({ repoRoot: request.repoRoot });
8
+ }
9
+ export {
10
+ executeSdkOperation,
11
+ findSdkOperation,
12
+ listSdkOperationNames,
13
+ resolveSdkInstance
14
+ };
@@ -0,0 +1,11 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AgentSdk } from '../sdk.ts';
3
+ import type { ApiConfig } from './types.ts';
4
+ interface RegisterSdkRoutesOptions {
5
+ config: ApiConfig;
6
+ sharedSdk?: AgentSdk;
7
+ scope: string;
8
+ prefix?: string;
9
+ }
10
+ export declare function registerSdkRoutes(app: Hono<any>, options: RegisterSdkRoutesOptions): void;
11
+ export {};
@@ -0,0 +1,29 @@
1
+ import { executeSdkOperation, resolveSdkInstance } from "./sdk-dispatch.js";
2
+ import { jsonError, requireScope } from "./http.js";
3
+ function withPrefix(prefix, path) {
4
+ if (!prefix) return path;
5
+ return `${prefix}${path}`.replace(/\/{2,}/g, "/");
6
+ }
7
+ function registerSdkRoutes(app, options) {
8
+ app.post(withPrefix(options.prefix ?? "", "/sdk/:operation"), async (c) => {
9
+ const unauthorized = requireScope(c, options.scope);
10
+ if (unauthorized) return unauthorized;
11
+ const operation = c.req.param("operation");
12
+ const body = await c.req.json().catch(() => ({}));
13
+ try {
14
+ const result = await executeSdkOperation(
15
+ resolveSdkInstance(options.sharedSdk, options.config, body),
16
+ operation,
17
+ body.input ?? {}
18
+ );
19
+ return c.json(result);
20
+ } catch (error) {
21
+ const message = error instanceof Error ? error.message : String(error);
22
+ const status = /Unknown SDK operation/.test(message) ? 400 : 500;
23
+ return jsonError(c, status, message, { operation });
24
+ }
25
+ });
26
+ }
27
+ export {
28
+ registerSdkRoutes
29
+ };
@@ -0,0 +1,3 @@
1
+ import type { SdkTemplateCatalogResponse } from '../sdk-types.ts';
2
+ import type { ApiConfig } from './types.ts';
3
+ export declare function loadTemplateCatalog(config: ApiConfig): SdkTemplateCatalogResponse;
@@ -0,0 +1,31 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, resolve } from "node:path";
4
+ import { parseTemplateCatalogResponse } from "../template-catalog.js";
5
+ const require2 = createRequire(import.meta.url);
6
+ function resolveSdkPackageRoot() {
7
+ const exportedEntrypoint = require2.resolve("@treeseed/sdk");
8
+ const distRoot = dirname(exportedEntrypoint);
9
+ const packageRoot = resolve(distRoot, "..");
10
+ return packageRoot;
11
+ }
12
+ function resolveDefaultCatalogPath() {
13
+ const sdkRoot = resolveSdkPackageRoot();
14
+ const candidates = [
15
+ resolve(sdkRoot, "dist", "treeseed", "template-catalog", "catalog.fixture.json"),
16
+ resolve(sdkRoot, "src", "treeseed", "template-catalog", "catalog.fixture.json")
17
+ ];
18
+ for (const candidate of candidates) {
19
+ if (existsSync(candidate)) {
20
+ return candidate;
21
+ }
22
+ }
23
+ throw new Error("Unable to resolve the bundled Treeseed template catalog fixture.");
24
+ }
25
+ function loadTemplateCatalog(config) {
26
+ const catalogPath = config.templateCatalogPath ?? resolveDefaultCatalogPath();
27
+ return parseTemplateCatalogResponse(JSON.parse(readFileSync(catalogPath, "utf8")));
28
+ }
29
+ export {
30
+ loadTemplateCatalog
31
+ };