@treeseed/core 0.4.1 → 0.4.4

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 (64) hide show
  1. package/README.md +7 -1
  2. package/dist/api/agent-routes.d.ts +13 -0
  3. package/dist/api/agent-routes.js +402 -0
  4. package/dist/api/app.d.ts +5 -0
  5. package/dist/api/app.js +270 -0
  6. package/dist/api/auth/d1-database.d.ts +3 -0
  7. package/dist/api/auth/d1-database.js +24 -0
  8. package/dist/api/auth/d1-provider.d.ts +67 -0
  9. package/dist/api/auth/d1-provider.js +84 -0
  10. package/dist/api/auth/d1-store.d.ts +97 -0
  11. package/dist/api/auth/d1-store.js +631 -0
  12. package/dist/api/auth/memory-provider.d.ts +73 -0
  13. package/dist/api/auth/memory-provider.js +239 -0
  14. package/dist/api/auth/rbac.d.ts +22 -0
  15. package/dist/api/auth/rbac.js +158 -0
  16. package/dist/api/auth/tokens.d.ts +18 -0
  17. package/dist/api/auth/tokens.js +56 -0
  18. package/dist/api/config.d.ts +2 -0
  19. package/dist/api/config.js +65 -0
  20. package/dist/api/gateway.d.ts +5 -0
  21. package/dist/api/gateway.js +35 -0
  22. package/dist/api/http.d.ts +24 -0
  23. package/dist/api/http.js +44 -0
  24. package/dist/api/index.d.ts +9 -0
  25. package/dist/api/index.js +18 -0
  26. package/dist/api/operations-routes.d.ts +6 -0
  27. package/dist/api/operations-routes.js +34 -0
  28. package/dist/api/operations.d.ts +3 -0
  29. package/dist/api/operations.js +26 -0
  30. package/dist/api/providers.d.ts +2 -0
  31. package/dist/api/providers.js +61 -0
  32. package/dist/api/railway.d.ts +45 -0
  33. package/dist/api/railway.js +69 -0
  34. package/dist/api/sdk-dispatch.d.ts +14 -0
  35. package/dist/api/sdk-dispatch.js +145 -0
  36. package/dist/api/sdk-routes.d.ts +10 -0
  37. package/dist/api/sdk-routes.js +25 -0
  38. package/dist/api/server.d.ts +2 -0
  39. package/dist/api/server.js +10 -0
  40. package/dist/api/templates.d.ts +3 -0
  41. package/dist/api/templates.js +31 -0
  42. package/dist/api/types.d.ts +193 -0
  43. package/dist/api/types.js +0 -0
  44. package/dist/api.d.ts +1 -0
  45. package/dist/api.js +1 -0
  46. package/dist/dev.d.ts +41 -0
  47. package/dist/dev.js +189 -0
  48. package/dist/index.d.ts +9 -0
  49. package/dist/index.js +23 -1
  50. package/dist/platform-resources.d.ts +37 -0
  51. package/dist/platform-resources.js +133 -0
  52. package/dist/platform.d.ts +2 -0
  53. package/dist/platform.js +16 -0
  54. package/dist/plugin-default.d.ts +1 -0
  55. package/dist/plugin-default.js +4 -0
  56. package/dist/railway.d.ts +1 -0
  57. package/dist/railway.js +4 -0
  58. package/dist/scripts/build-dist.js +7 -0
  59. package/dist/scripts/dev-platform.js +24 -0
  60. package/dist/scripts/workspace-bootstrap.js +24 -10
  61. package/dist/site-resources.d.ts +1 -29
  62. package/dist/site-resources.js +7 -120
  63. package/dist/site.js +3 -1
  64. package/package.json +37 -3
@@ -0,0 +1,9 @@
1
+ export { createTreeseedApiApp } from './app.ts';
2
+ export { createTreeseedGatewayApp } from './gateway.ts';
3
+ export { resolveApiConfig } from './config.ts';
4
+ export { createRailwayTreeseedApiServer } from './railway.ts';
5
+ export { resolveApiRuntimeProviders } from './providers.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,18 @@
1
+ import { createTreeseedApiApp } from "./app.js";
2
+ import { createTreeseedGatewayApp } from "./gateway.js";
3
+ import { resolveApiConfig } from "./config.js";
4
+ import { createRailwayTreeseedApiServer } from "./railway.js";
5
+ import { resolveApiRuntimeProviders } from "./providers.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
+ createTreeseedGatewayApp,
15
+ loadTemplateCatalog,
16
+ resolveApiConfig,
17
+ resolveApiRuntimeProviders
18
+ };
@@ -0,0 +1,6 @@
1
+ import type { Hono } from 'hono';
2
+ import { executeHttpWorkflowOperation } from './operations.ts';
3
+ export declare function registerOperationRoutes(app: Hono<any>, options: {
4
+ scope: string;
5
+ executeOperation?: typeof executeHttpWorkflowOperation;
6
+ }): void;
@@ -0,0 +1,34 @@
1
+ import { findTreeseedOperation } from "@treeseed/sdk";
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
+ app.post("/operations/:operation", async (c) => {
7
+ const unauthorized = requireScope(c, options.scope);
8
+ if (unauthorized) return unauthorized;
9
+ const requestedOperation = c.req.param("operation");
10
+ const resolvedOperation = findTreeseedOperation(requestedOperation);
11
+ if (!resolvedOperation) {
12
+ return jsonError(c, 400, `Unknown Treeseed operation "${requestedOperation}".`, {
13
+ operation: requestedOperation
14
+ });
15
+ }
16
+ if (!isHttpWorkflowOperationAllowed(resolvedOperation.name)) {
17
+ return jsonError(c, 400, `Workflow operation "${resolvedOperation.name}" is not supported over HTTP.`, {
18
+ operation: resolvedOperation.name
19
+ });
20
+ }
21
+ const body = await c.req.json().catch(() => ({}));
22
+ try {
23
+ const result = await executeOperation(resolvedOperation.name, body);
24
+ return c.json(result, { status: result.ok ? 200 : 400 });
25
+ } catch (error) {
26
+ const message = error instanceof Error ? error.message : String(error);
27
+ const status = /Unknown Treeseed operation|not supported over HTTP|confirmation required/i.test(message) ? 400 : 500;
28
+ return jsonError(c, status, message, { operation: resolvedOperation.name });
29
+ }
30
+ });
31
+ }
32
+ export {
33
+ registerOperationRoutes
34
+ };
@@ -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,2 @@
1
+ import type { ApiConfig, ApiRuntimeProviders, ResolvedApiRuntimeProviders } from './types.ts';
2
+ export declare function resolveApiRuntimeProviders(config: ApiConfig, overrides?: ApiRuntimeProviders): ResolvedApiRuntimeProviders;
@@ -0,0 +1,61 @@
1
+ import { MemoryDeviceCodeAuthProvider } from "./auth/memory-provider.js";
2
+ import { D1AuthProvider } from "./auth/d1-provider.js";
3
+ function addProviders(target, incoming, label) {
4
+ for (const [id, value] of Object.entries(incoming ?? {})) {
5
+ if (target.has(id)) {
6
+ throw new Error(`Treeseed API runtime found duplicate ${label} provider "${id}".`);
7
+ }
8
+ target.set(id, value);
9
+ }
10
+ }
11
+ function resolveSelectedProvider(registry, selectedId, label) {
12
+ const selected = registry.get(selectedId);
13
+ if (!selected) {
14
+ throw new Error(`Treeseed API runtime could not resolve ${label} provider "${selectedId}".`);
15
+ }
16
+ return selected;
17
+ }
18
+ function resolveApiRuntimeProviders(config, overrides = {}) {
19
+ const authRegistry = /* @__PURE__ */ new Map();
20
+ const agentExecution = /* @__PURE__ */ new Map();
21
+ const agentQueue = /* @__PURE__ */ new Map();
22
+ const agentNotification = /* @__PURE__ */ new Map();
23
+ const agentRepository = /* @__PURE__ */ new Map();
24
+ const agentVerification = /* @__PURE__ */ new Map();
25
+ addProviders(authRegistry, {
26
+ memory: ({ config: runtimeConfig }) => new MemoryDeviceCodeAuthProvider(runtimeConfig),
27
+ d1: ({ config: runtimeConfig }) => new D1AuthProvider(runtimeConfig)
28
+ }, "auth");
29
+ addProviders(authRegistry, overrides.auth, "auth");
30
+ addProviders(agentExecution, { stub: { id: "stub" } }, "agent execution");
31
+ addProviders(agentQueue, { memory: { id: "memory" } }, "agent queue");
32
+ addProviders(agentNotification, { stub: { id: "stub" } }, "agent notification");
33
+ addProviders(agentRepository, { stub: { id: "stub" } }, "agent repository");
34
+ addProviders(agentVerification, { stub: { id: "stub" } }, "agent verification");
35
+ addProviders(agentExecution, overrides.agentExecution, "agent execution");
36
+ addProviders(agentQueue, overrides.agentQueue, "agent queue");
37
+ addProviders(agentNotification, overrides.agentNotification, "agent notification");
38
+ addProviders(agentRepository, overrides.agentRepository, "agent repository");
39
+ addProviders(agentVerification, overrides.agentVerification, "agent verification");
40
+ const authFactory = resolveSelectedProvider(authRegistry, config.providers.auth, "auth");
41
+ resolveSelectedProvider(agentExecution, config.providers.agents.execution, "agent execution");
42
+ resolveSelectedProvider(agentQueue, config.providers.agents.queue, "agent queue");
43
+ resolveSelectedProvider(agentNotification, config.providers.agents.notification, "agent notification");
44
+ resolveSelectedProvider(agentRepository, config.providers.agents.repository, "agent repository");
45
+ resolveSelectedProvider(agentVerification, config.providers.agents.verification, "agent verification");
46
+ return {
47
+ auth: authFactory({ config }),
48
+ registries: {
49
+ auth: authRegistry,
50
+ agentExecution,
51
+ agentQueue,
52
+ agentNotification,
53
+ agentRepository,
54
+ agentVerification
55
+ },
56
+ selections: config.providers
57
+ };
58
+ }
59
+ export {
60
+ resolveApiRuntimeProviders
61
+ };
@@ -0,0 +1,45 @@
1
+ import { type Server } from 'node:http';
2
+ import type { Hono } from 'hono';
3
+ import type { ApiServerOptions } from './types.ts';
4
+ export declare function createRailwayTreeseedApiServer(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
+ issuer: string;
24
+ repoRoot: string;
25
+ authSecret: string;
26
+ cloudflareAccountId?: string;
27
+ cloudflareApiToken?: string;
28
+ d1DatabaseId?: string;
29
+ d1DatabaseName?: string;
30
+ d1LocalPersistTo?: string;
31
+ webServiceId: string;
32
+ webServiceSecret: string;
33
+ webAssertionSecret: string;
34
+ webExchangeTtlSeconds: number;
35
+ bootstrapAdminAllowlist: string[];
36
+ accessTokenTtlSeconds: number;
37
+ refreshTokenTtlSeconds: number;
38
+ deviceCodeTtlSeconds: number;
39
+ deviceCodePollIntervalSeconds: number;
40
+ templateCatalogPath?: string;
41
+ };
42
+ server: Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>;
43
+ url: string;
44
+ close(): Promise<void>;
45
+ }>;
@@ -0,0 +1,69 @@
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 createRailwayTreeseedApiServer(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
+ export {
68
+ createRailwayTreeseedApiServer
69
+ };
@@ -0,0 +1,14 @@
1
+ import type { AgentSdk, RemoteSdkOperationRequest } from '@treeseed/sdk';
2
+ import type { ApiConfig } from './types.ts';
3
+ type JsonRecord = Record<string, unknown>;
4
+ type SdkOperationHandler = (sdk: AgentSdk, input: JsonRecord) => Promise<unknown> | unknown;
5
+ interface SdkOperationSpec {
6
+ name: string;
7
+ aliases?: string[];
8
+ handler: SdkOperationHandler;
9
+ }
10
+ export declare function listSdkOperationNames(): string[];
11
+ export declare function findSdkOperation(name: string): SdkOperationSpec;
12
+ export declare function resolveSdkInstance(sharedSdk: AgentSdk | undefined, config: ApiConfig, request: RemoteSdkOperationRequest): AgentSdk;
13
+ export declare function executeSdkOperation(sdk: AgentSdk, operationName: string, input: JsonRecord): Promise<unknown>;
14
+ export {};
@@ -0,0 +1,145 @@
1
+ import { AgentSdk as AgentSdkClass } from "@treeseed/sdk";
2
+ function passthrough(methodName) {
3
+ return (sdk, input) => sdk[methodName](input);
4
+ }
5
+ const SDK_OPERATION_SPECS = [
6
+ { name: "get", handler: passthrough("get") },
7
+ { name: "read", handler: passthrough("read") },
8
+ { name: "search", handler: passthrough("search") },
9
+ { name: "follow", handler: passthrough("follow") },
10
+ { name: "pick", handler: passthrough("pick") },
11
+ { name: "create", handler: passthrough("create") },
12
+ { name: "update", handler: passthrough("update") },
13
+ { name: "claimMessage", aliases: ["claim-message"], handler: passthrough("claimMessage") },
14
+ { name: "ackMessage", aliases: ["ack-message"], handler: passthrough("ackMessage") },
15
+ { name: "createMessage", aliases: ["create-message"], handler: passthrough("createMessage") },
16
+ { name: "recordRun", aliases: ["record-run"], handler: passthrough("recordRun") },
17
+ { name: "getCursor", aliases: ["get-cursor"], handler: passthrough("getCursor") },
18
+ { name: "upsertCursor", aliases: ["upsert-cursor"], handler: passthrough("upsertCursor") },
19
+ { name: "releaseLease", aliases: ["release-lease"], handler: passthrough("releaseLease") },
20
+ {
21
+ name: "releaseAllLeases",
22
+ aliases: ["release-all-leases"],
23
+ handler: (sdk) => sdk.releaseAllLeases()
24
+ },
25
+ { name: "startWorkDay", aliases: ["start-work-day"], handler: passthrough("startWorkDay") },
26
+ { name: "closeWorkDay", aliases: ["close-work-day"], handler: passthrough("closeWorkDay") },
27
+ { name: "createTask", aliases: ["create-task"], handler: passthrough("createTask") },
28
+ { name: "claimTask", aliases: ["claim-task"], handler: passthrough("claimTask") },
29
+ {
30
+ name: "recordTaskProgress",
31
+ aliases: ["record-task-progress"],
32
+ handler: passthrough("recordTaskProgress")
33
+ },
34
+ { name: "completeTask", aliases: ["complete-task"], handler: passthrough("completeTask") },
35
+ { name: "failTask", aliases: ["fail-task"], handler: passthrough("failTask") },
36
+ { name: "appendTaskEvent", aliases: ["append-task-event"], handler: passthrough("appendTaskEvent") },
37
+ { name: "searchTasks", aliases: ["search-tasks"], handler: passthrough("searchTasks") },
38
+ {
39
+ name: "getManagerContext",
40
+ aliases: ["get-manager-context"],
41
+ handler: (sdk, input) => sdk.getManagerContext(String(input.taskId ?? input.id ?? ""))
42
+ },
43
+ { name: "createReport", aliases: ["create-report"], handler: passthrough("createReport") },
44
+ {
45
+ name: "listAgentSpecs",
46
+ aliases: ["list-agent-specs"],
47
+ handler: (sdk, input) => sdk.listAgentSpecs(input)
48
+ },
49
+ { name: "refreshGraph", aliases: ["refresh-graph"], handler: passthrough("refreshGraph") },
50
+ {
51
+ name: "searchFiles",
52
+ aliases: ["search-files"],
53
+ handler: (sdk, input) => sdk.searchFiles(String(input.query ?? ""), input.options)
54
+ },
55
+ {
56
+ name: "searchSections",
57
+ aliases: ["search-sections"],
58
+ handler: (sdk, input) => sdk.searchSections(String(input.query ?? ""), input.options)
59
+ },
60
+ {
61
+ name: "searchEntities",
62
+ aliases: ["search-entities"],
63
+ handler: (sdk, input) => sdk.searchEntities(String(input.query ?? ""), input.options)
64
+ },
65
+ {
66
+ name: "getGraphNode",
67
+ aliases: ["get-graph-node"],
68
+ handler: (sdk, input) => sdk.getGraphNode(String(input.id ?? ""))
69
+ },
70
+ {
71
+ name: "getNeighbors",
72
+ aliases: ["get-neighbors"],
73
+ handler: (sdk, input) => sdk.getNeighbors(String(input.id ?? ""), input.options)
74
+ },
75
+ {
76
+ name: "followReferences",
77
+ aliases: ["follow-references"],
78
+ handler: (sdk, input) => sdk.followReferences(String(input.id ?? ""), input.options)
79
+ },
80
+ {
81
+ name: "getBacklinks",
82
+ aliases: ["get-backlinks"],
83
+ handler: (sdk, input) => sdk.getBacklinks(String(input.id ?? ""), input.options)
84
+ },
85
+ {
86
+ name: "getRelated",
87
+ aliases: ["get-related"],
88
+ handler: (sdk, input) => sdk.getRelated(String(input.id ?? ""), input.options)
89
+ },
90
+ {
91
+ name: "getSubgraph",
92
+ aliases: ["get-subgraph"],
93
+ handler: (sdk, input) => sdk.getSubgraph(Array.isArray(input.seedIds) ? input.seedIds.map(String) : [], input.options)
94
+ },
95
+ { name: "resolveSeeds", aliases: ["resolve-seeds"], handler: passthrough("resolveSeeds") },
96
+ { name: "queryGraph", aliases: ["query-graph"], handler: passthrough("queryGraph") },
97
+ { name: "buildContextPack", aliases: ["build-context-pack"], handler: passthrough("buildContextPack") },
98
+ {
99
+ name: "parseGraphDsl",
100
+ aliases: ["parse-graph-dsl"],
101
+ handler: (sdk, input) => sdk.parseGraphDsl(String(input.source ?? input.query ?? ""))
102
+ },
103
+ {
104
+ name: "resolveReference",
105
+ aliases: ["resolve-reference"],
106
+ handler: (sdk, input) => sdk.resolveReference(String(input.reference ?? ""), input.options)
107
+ },
108
+ {
109
+ name: "explainReferenceChain",
110
+ aliases: ["explain-reference-chain"],
111
+ handler: (sdk, input) => sdk.explainReferenceChain(String(input.fromId ?? ""), String(input.toId ?? ""))
112
+ }
113
+ ];
114
+ const SDK_OPERATION_INDEX = /* @__PURE__ */ new Map();
115
+ for (const spec of SDK_OPERATION_SPECS) {
116
+ SDK_OPERATION_INDEX.set(spec.name, spec);
117
+ for (const alias of spec.aliases ?? []) {
118
+ SDK_OPERATION_INDEX.set(alias, spec);
119
+ }
120
+ }
121
+ function listSdkOperationNames() {
122
+ return [...new Set(SDK_OPERATION_SPECS.map((entry) => entry.name))];
123
+ }
124
+ function findSdkOperation(name) {
125
+ return SDK_OPERATION_INDEX.get(name) ?? null;
126
+ }
127
+ function resolveSdkInstance(sharedSdk, config, request) {
128
+ if (!request.repoRoot || request.repoRoot === config.repoRoot) {
129
+ return sharedSdk ?? new AgentSdkClass({ repoRoot: config.repoRoot });
130
+ }
131
+ return new AgentSdkClass({ repoRoot: request.repoRoot });
132
+ }
133
+ async function executeSdkOperation(sdk, operationName, input) {
134
+ const spec = findSdkOperation(operationName);
135
+ if (!spec) {
136
+ throw new Error(`Unknown SDK operation "${operationName}".`);
137
+ }
138
+ return await spec.handler(sdk, input);
139
+ }
140
+ export {
141
+ executeSdkOperation,
142
+ findSdkOperation,
143
+ listSdkOperationNames,
144
+ resolveSdkInstance
145
+ };
@@ -0,0 +1,10 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AgentSdk } from '@treeseed/sdk';
3
+ import type { ApiConfig } from './types.ts';
4
+ interface RegisterSdkRoutesOptions {
5
+ config: ApiConfig;
6
+ sharedSdk?: AgentSdk;
7
+ scope: string;
8
+ }
9
+ export declare function registerSdkRoutes(app: Hono<any>, options: RegisterSdkRoutesOptions): void;
10
+ export {};
@@ -0,0 +1,25 @@
1
+ import { executeSdkOperation, resolveSdkInstance } from "./sdk-dispatch.js";
2
+ import { jsonError, requireScope } from "./http.js";
3
+ function registerSdkRoutes(app, options) {
4
+ app.post("/sdk/:operation", async (c) => {
5
+ const unauthorized = requireScope(c, options.scope);
6
+ if (unauthorized) return unauthorized;
7
+ const operation = c.req.param("operation");
8
+ const body = await c.req.json().catch(() => ({}));
9
+ try {
10
+ const result = await executeSdkOperation(
11
+ resolveSdkInstance(options.sharedSdk, options.config, body),
12
+ operation,
13
+ body.input ?? {}
14
+ );
15
+ return c.json(result);
16
+ } catch (error) {
17
+ const message = error instanceof Error ? error.message : String(error);
18
+ const status = /Unknown SDK operation/.test(message) ? 400 : 500;
19
+ return jsonError(c, status, message, { operation });
20
+ }
21
+ });
22
+ }
23
+ export {
24
+ registerSdkRoutes
25
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { createRailwayTreeseedApiServer } from "./railway.js";
4
+ const currentFile = fileURLToPath(import.meta.url);
5
+ const entryFile = process.argv[1] ?? "";
6
+ if (entryFile === currentFile) {
7
+ const instance = await createRailwayTreeseedApiServer();
8
+ process.stdout.write(`Treeseed API listening on ${instance.url}
9
+ `);
10
+ }
@@ -0,0 +1,3 @@
1
+ import type { SdkTemplateCatalogResponse } from '@treeseed/sdk/types';
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 "@treeseed/sdk/template-catalog";
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
+ };