@treeseed/core 0.4.2 → 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.
- package/README.md +7 -1
- package/dist/api/agent-routes.d.ts +13 -0
- package/dist/api/agent-routes.js +402 -0
- package/dist/api/app.d.ts +5 -0
- package/dist/api/app.js +270 -0
- package/dist/api/auth/d1-database.d.ts +3 -0
- package/dist/api/auth/d1-database.js +24 -0
- package/dist/api/auth/d1-provider.d.ts +67 -0
- package/dist/api/auth/d1-provider.js +84 -0
- package/dist/api/auth/d1-store.d.ts +97 -0
- package/dist/api/auth/d1-store.js +631 -0
- package/dist/api/auth/memory-provider.d.ts +73 -0
- package/dist/api/auth/memory-provider.js +239 -0
- package/dist/api/auth/rbac.d.ts +22 -0
- package/dist/api/auth/rbac.js +158 -0
- package/dist/api/auth/tokens.d.ts +18 -0
- package/dist/api/auth/tokens.js +56 -0
- package/dist/api/config.d.ts +2 -0
- package/dist/api/config.js +65 -0
- package/dist/api/gateway.d.ts +5 -0
- package/dist/api/gateway.js +35 -0
- package/dist/api/http.d.ts +24 -0
- package/dist/api/http.js +44 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/index.js +18 -0
- package/dist/api/operations-routes.d.ts +6 -0
- package/dist/api/operations-routes.js +34 -0
- package/dist/api/operations.d.ts +3 -0
- package/dist/api/operations.js +26 -0
- package/dist/api/providers.d.ts +2 -0
- package/dist/api/providers.js +61 -0
- package/dist/api/railway.d.ts +45 -0
- package/dist/api/railway.js +69 -0
- package/dist/api/sdk-dispatch.d.ts +14 -0
- package/dist/api/sdk-dispatch.js +145 -0
- package/dist/api/sdk-routes.d.ts +10 -0
- package/dist/api/sdk-routes.js +25 -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 +193 -0
- package/dist/api/types.js +0 -0
- package/dist/api.d.ts +1 -0
- package/dist/api.js +1 -0
- package/dist/dev.d.ts +41 -0
- package/dist/dev.js +189 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +23 -1
- package/dist/platform-resources.d.ts +37 -0
- package/dist/platform-resources.js +133 -0
- package/dist/platform.d.ts +2 -0
- package/dist/platform.js +16 -0
- package/dist/plugin-default.d.ts +1 -0
- package/dist/plugin-default.js +4 -0
- package/dist/railway.d.ts +1 -0
- package/dist/railway.js +4 -0
- package/dist/scripts/build-dist.js +7 -0
- package/dist/scripts/dev-platform.js +24 -0
- package/dist/scripts/workspace-bootstrap.js +0 -1
- package/dist/site-resources.d.ts +1 -29
- package/dist/site-resources.js +7 -120
- package/dist/site.js +3 -1
- 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,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,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,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,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
|
+
};
|