@farm.js/agent 0.0.0-stage → 0.1.0-beta.1
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/LICENSE +22 -0
- package/README.md +22 -2
- package/dist/config.d.ts +12 -0
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Farm.js Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @farm.js/agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Durable agents for Farm.js, served and built by [agent-unit](https://github.com/farming-labs/agent-unit)
|
|
4
|
+
|
|
5
|
+
See [Stability and Support](https://farmjs.dev/docs/stability) for what this package guarantees.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @farm.js/agent agent-unit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// farm.config.ts
|
|
13
|
+
import { agent } from "@farm.js/agent";
|
|
14
|
+
import { defineConfig } from "@farm.js/core";
|
|
15
|
+
|
|
16
|
+
export default defineConfig({
|
|
17
|
+
integrations: {
|
|
18
|
+
agent: agent({ storage: { driver: "redis", url: process.env.REDIS_URL } }),
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
See the [durable agents guide](https://farmjs.dev/docs/integrations/agent) for agents, the API, storage, and deploy targets.
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AgentUnitConfig } from 'agent-unit';
|
|
2
|
+
|
|
3
|
+
/** @internal Called by `agent()` as farm.config evaluates, in Farm's process and in the server. */
|
|
4
|
+
declare function registerAgentUnitConfig(config: AgentUnitConfig): void;
|
|
5
|
+
/**
|
|
6
|
+
* The agent-unit config of the app's `agent()` integration. agent-unit's engine reads it when it is
|
|
7
|
+
* first used: by then Farm's server has evaluated farm.config, so settings such as storage URLs and
|
|
8
|
+
* `authorize` come from where the server runs.
|
|
9
|
+
*/
|
|
10
|
+
declare function agentUnitConfig(): AgentUnitConfig;
|
|
11
|
+
|
|
12
|
+
export { agentUnitConfig as default, registerAgentUnitConfig };
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
var AGENT_UNIT_CONFIG = /* @__PURE__ */ Symbol.for("farm.agent.config");
|
|
3
|
+
function registerAgentUnitConfig(config) {
|
|
4
|
+
globalThis[AGENT_UNIT_CONFIG] = config;
|
|
5
|
+
}
|
|
6
|
+
function agentUnitConfig() {
|
|
7
|
+
const config = globalThis[AGENT_UNIT_CONFIG];
|
|
8
|
+
if (!config) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"@farm.js/agent: the server has not evaluated farm.config, so the agent() settings are missing. Mount agent() under `integrations` in farm.config."
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
return config;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
agentUnitConfig as default,
|
|
17
|
+
registerAgentUnitConfig
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import type { AgentUnitConfig } from \"agent-unit\";\n\nconst AGENT_UNIT_CONFIG = Symbol.for(\"farm.agent.config\");\n\ntype ConfigHolder = { [AGENT_UNIT_CONFIG]?: AgentUnitConfig };\n\n/** @internal Called by `agent()` as farm.config evaluates, in Farm's process and in the server. */\nexport function registerAgentUnitConfig(config: AgentUnitConfig): void {\n (globalThis as ConfigHolder)[AGENT_UNIT_CONFIG] = config;\n}\n\n/**\n * The agent-unit config of the app's `agent()` integration. agent-unit's engine reads it when it is\n * first used: by then Farm's server has evaluated farm.config, so settings such as storage URLs and\n * `authorize` come from where the server runs.\n */\nexport default function agentUnitConfig(): AgentUnitConfig {\n const config = (globalThis as ConfigHolder)[AGENT_UNIT_CONFIG];\n if (!config) {\n throw new Error(\n \"@farm.js/agent: the server has not evaluated farm.config, so the agent() settings are missing. Mount agent() under `integrations` in farm.config.\",\n );\n }\n return config;\n}\n"],"mappings":";AAEA,IAAM,oBAAoB,uBAAO,IAAI,mBAAmB;AAKjD,SAAS,wBAAwB,QAA+B;AACrE,EAAC,WAA4B,iBAAiB,IAAI;AACpD;AAOe,SAAR,kBAAoD;AACzD,QAAM,SAAU,WAA4B,iBAAiB;AAC7D,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as _farm_js_core from '@farm.js/core';
|
|
2
|
+
import { AgentFramework, AgentUnitConfig } from 'agent-unit';
|
|
3
|
+
export { AgentFramework } from 'agent-unit';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* agent-unit's settings, minus what Farm owns: the host (`deploy.target`), the server and the
|
|
7
|
+
* Nitro and Workers settings, which belong in farm.config.
|
|
8
|
+
*/
|
|
9
|
+
interface AgentOptions extends Omit<AgentUnitConfig, "server" | "preset" | "runtime" | "cloudflare" | "nitro"> {
|
|
10
|
+
/** Where the agent API is mounted, under `/api`. It owns every path under it. Default `/api/agent`. */
|
|
11
|
+
basePath?: string;
|
|
12
|
+
/** The folder of agent files, relative to the app root. Default `agents` in `srcDir`. */
|
|
13
|
+
agentsDir?: string;
|
|
14
|
+
}
|
|
15
|
+
interface AgentInstance {
|
|
16
|
+
readonly basePath: string;
|
|
17
|
+
readonly framework?: AgentFramework;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Durable agents in a Farm app: the agents in `src/agents`, written with the AI SDK, Mastra,
|
|
21
|
+
* LangGraph, the OpenAI Agents SDK or agent-unit's `defineAgent`, served under `basePath` in
|
|
22
|
+
* `farm dev` and built by agent-unit into every deployment target.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* integrations: { agent: agent({ framework: "ai-sdk", storage: { driver: "redis", url: process.env.REDIS_URL } }) }
|
|
26
|
+
*/
|
|
27
|
+
declare function agent(options?: AgentOptions): _farm_js_core.DefinedIntegration<{
|
|
28
|
+
category: "agent";
|
|
29
|
+
type: string;
|
|
30
|
+
instance: AgentInstance;
|
|
31
|
+
plugins: _farm_js_core.FarmPlugin<undefined, Record<string, never>, unknown, undefined, unknown, false, (_farm_js_core.RouteDefinition<`/${string}`, "POST", {
|
|
32
|
+
__types: {
|
|
33
|
+
body: unknown;
|
|
34
|
+
inputBody: never;
|
|
35
|
+
query: never;
|
|
36
|
+
inputQuery: never;
|
|
37
|
+
headers: never;
|
|
38
|
+
inputHeaders: never;
|
|
39
|
+
response: unknown;
|
|
40
|
+
errors: never;
|
|
41
|
+
};
|
|
42
|
+
__path?: string;
|
|
43
|
+
__method?: string;
|
|
44
|
+
__openapi?: _farm_js_core.EndpointOpenAPIMetadata;
|
|
45
|
+
__mcp?: _farm_js_core.EndpointMCPOptions;
|
|
46
|
+
} & ((options?: {
|
|
47
|
+
body?: undefined;
|
|
48
|
+
query?: undefined;
|
|
49
|
+
headers?: undefined;
|
|
50
|
+
} | undefined) => Promise<unknown>) & {
|
|
51
|
+
__types: {
|
|
52
|
+
params: {};
|
|
53
|
+
inputHeaders: never;
|
|
54
|
+
};
|
|
55
|
+
}> | _farm_js_core.RouteDefinition<`/${string}`, "GET", {
|
|
56
|
+
__types: {
|
|
57
|
+
body: unknown;
|
|
58
|
+
inputBody: never;
|
|
59
|
+
query: never;
|
|
60
|
+
inputQuery: never;
|
|
61
|
+
headers: never;
|
|
62
|
+
inputHeaders: never;
|
|
63
|
+
response: unknown;
|
|
64
|
+
errors: never;
|
|
65
|
+
};
|
|
66
|
+
__path?: string;
|
|
67
|
+
__method?: string;
|
|
68
|
+
__openapi?: _farm_js_core.EndpointOpenAPIMetadata;
|
|
69
|
+
__mcp?: _farm_js_core.EndpointMCPOptions;
|
|
70
|
+
} & ((options?: {
|
|
71
|
+
body?: undefined;
|
|
72
|
+
query?: undefined;
|
|
73
|
+
headers?: undefined;
|
|
74
|
+
} | undefined) => Promise<unknown>) & {
|
|
75
|
+
__types: {
|
|
76
|
+
params: {};
|
|
77
|
+
inputHeaders: never;
|
|
78
|
+
};
|
|
79
|
+
}> | _farm_js_core.RouteDefinition<`/${string}`, "DELETE", {
|
|
80
|
+
__types: {
|
|
81
|
+
body: unknown;
|
|
82
|
+
inputBody: never;
|
|
83
|
+
query: never;
|
|
84
|
+
inputQuery: never;
|
|
85
|
+
headers: never;
|
|
86
|
+
inputHeaders: never;
|
|
87
|
+
response: unknown;
|
|
88
|
+
errors: never;
|
|
89
|
+
};
|
|
90
|
+
__path?: string;
|
|
91
|
+
__method?: string;
|
|
92
|
+
__openapi?: _farm_js_core.EndpointOpenAPIMetadata;
|
|
93
|
+
__mcp?: _farm_js_core.EndpointMCPOptions;
|
|
94
|
+
} & ((options?: {
|
|
95
|
+
body?: undefined;
|
|
96
|
+
query?: undefined;
|
|
97
|
+
headers?: undefined;
|
|
98
|
+
} | undefined) => Promise<unknown>) & {
|
|
99
|
+
__types: {
|
|
100
|
+
params: {};
|
|
101
|
+
inputHeaders: never;
|
|
102
|
+
};
|
|
103
|
+
}>)[]>[];
|
|
104
|
+
}, _farm_js_core.FarmSchema | undefined>;
|
|
105
|
+
|
|
106
|
+
export { type AgentInstance, type AgentOptions, agent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { defineIntegration } from "@farm.js/core";
|
|
3
|
+
|
|
4
|
+
// src/config.ts
|
|
5
|
+
var AGENT_UNIT_CONFIG = /* @__PURE__ */ Symbol.for("farm.agent.config");
|
|
6
|
+
function registerAgentUnitConfig(config) {
|
|
7
|
+
globalThis[AGENT_UNIT_CONFIG] = config;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/plugin.ts
|
|
11
|
+
import { writeFileSync } from "fs";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { definePlugin } from "@farm.js/core/plugin";
|
|
15
|
+
var loadBuildOnly = (specifier) => import(
|
|
16
|
+
/* @vite-ignore */
|
|
17
|
+
specifier
|
|
18
|
+
);
|
|
19
|
+
var AGENT_SERVER = /* @__PURE__ */ Symbol.for("farm.agent.server");
|
|
20
|
+
var METHODS = ["get", "post", "delete"];
|
|
21
|
+
function isUnder(pathname, basePath) {
|
|
22
|
+
return basePath === "/" || pathname === basePath || pathname.startsWith(`${basePath}/`);
|
|
23
|
+
}
|
|
24
|
+
var SERVER_MODULE = `// Generated by @farm.js/agent. Do not edit.
|
|
25
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
26
|
+
import { getUnit } from "./runtime.mjs";
|
|
27
|
+
|
|
28
|
+
const hostWaitUntil = new AsyncLocalStorage();
|
|
29
|
+
|
|
30
|
+
globalThis[Symbol.for("farm.agent.server")] = {
|
|
31
|
+
handle(request) {
|
|
32
|
+
const waitUntil = hostWaitUntil.getStore();
|
|
33
|
+
return getUnit().handler(request, waitUntil ? { waitUntil } : {});
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default function farmAgentHost(event, next) {
|
|
38
|
+
const req = event.req;
|
|
39
|
+
return typeof req.waitUntil === "function"
|
|
40
|
+
? hostWaitUntil.run((promise) => req.waitUntil(promise), next)
|
|
41
|
+
: next();
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
function createAgentPlugin(config) {
|
|
45
|
+
const { basePath } = config;
|
|
46
|
+
let root;
|
|
47
|
+
let agentsDir = config.agentsDir ?? "agents";
|
|
48
|
+
let isDev = false;
|
|
49
|
+
let devHandler;
|
|
50
|
+
const buildConfig = () => ({ ...config, agentsDir });
|
|
51
|
+
const startDev = () => devHandler ??= loadBuildOnly("agent-unit/build").then(
|
|
52
|
+
({ createDevHandler }) => createDevHandler({ root: root ?? process.cwd(), config: buildConfig() })
|
|
53
|
+
);
|
|
54
|
+
const closeDev = async () => {
|
|
55
|
+
const pending = devHandler;
|
|
56
|
+
devHandler = void 0;
|
|
57
|
+
await (await pending?.catch(() => void 0))?.close();
|
|
58
|
+
};
|
|
59
|
+
const handle = async (request) => {
|
|
60
|
+
if (isDev) return (await startDev()).fetch(request);
|
|
61
|
+
const server = globalThis[AGENT_SERVER];
|
|
62
|
+
if (!server) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"@farm.js/agent: this server was built without the agents. Build it with `farm build`, which adds them."
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return server.handle(request);
|
|
68
|
+
};
|
|
69
|
+
return definePlugin({
|
|
70
|
+
name: "farm:agent",
|
|
71
|
+
configure(farmConfig, context) {
|
|
72
|
+
root = path.resolve(farmConfig.root ?? process.cwd());
|
|
73
|
+
agentsDir = config.agentsDir ?? path.posix.join(farmConfig.srcDir ?? "src", "agents");
|
|
74
|
+
isDev = context.isDev;
|
|
75
|
+
},
|
|
76
|
+
// Ordinary API routes, so a request passes Farm's middleware, runtime.before and integration
|
|
77
|
+
// guards before it reaches the agents, in development and in every production target.
|
|
78
|
+
routes: ({ route }) => METHODS.flatMap(
|
|
79
|
+
(method) => [basePath, `${basePath}/[...path]`].map(
|
|
80
|
+
(pattern) => route[method](pattern, { handler: (request) => handle(request) })
|
|
81
|
+
)
|
|
82
|
+
),
|
|
83
|
+
router: {
|
|
84
|
+
// agent-unit answers every path under basePath, so an app route there would never be reached.
|
|
85
|
+
discovered(route) {
|
|
86
|
+
if (route.kind !== "api" || !route.filePath || !isUnder(route.path, basePath)) return;
|
|
87
|
+
throw new Error(
|
|
88
|
+
`@farm.js/agent: ${route.filePath} is served at ${route.path}, which the agent API owns (basePath "${basePath}"). Move the route, or set another basePath.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
build: {
|
|
93
|
+
// agent-unit builds the agents into Farm's Nitro output for its preset: their framework
|
|
94
|
+
// adapters, storage, the sweep and `tasks` as scheduled tasks. Its own handler for basePath
|
|
95
|
+
// would answer before Farm's, so Farm's routes serve the API instead.
|
|
96
|
+
async configure(nitroConfig) {
|
|
97
|
+
const { agentUnit } = await loadBuildOnly("agent-unit/nitro");
|
|
98
|
+
const unit = agentUnit({
|
|
99
|
+
root: root ?? process.cwd(),
|
|
100
|
+
basePath,
|
|
101
|
+
config: buildConfig(),
|
|
102
|
+
// The module agent-unit's engine reads its config from in the built server.
|
|
103
|
+
configFile: fileURLToPath(new URL("./config.js", import.meta.url))
|
|
104
|
+
});
|
|
105
|
+
const module = {
|
|
106
|
+
name: unit.name,
|
|
107
|
+
async setup(nitro) {
|
|
108
|
+
const before = nitro.options.handlers.length;
|
|
109
|
+
await unit.setup(nitro);
|
|
110
|
+
const added = nitro.options.handlers.splice(before);
|
|
111
|
+
const entry = added.find((handler) => handler.handler.endsWith("entry.mjs"))?.handler;
|
|
112
|
+
if (!entry) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"@farm.js/agent: agent-unit's Nitro module did not mount its handler. Install the agent-unit version @farm.js/agent supports."
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
nitro.options.handlers.push(...added.filter((handler) => handler.handler !== entry));
|
|
118
|
+
const server = path.posix.join(
|
|
119
|
+
path.posix.dirname(entry.replaceAll("\\", "/")),
|
|
120
|
+
"farm-server.mjs"
|
|
121
|
+
);
|
|
122
|
+
writeFileSync(server, SERVER_MODULE);
|
|
123
|
+
nitro.options.handlers.push(
|
|
124
|
+
{ route: basePath, handler: server, middleware: true, lazy: false },
|
|
125
|
+
{ route: `${basePath}/**`, handler: server, middleware: true, lazy: false }
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
return { ...nitroConfig, modules: [...nitroConfig.modules ?? [], module] };
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
dev: {
|
|
133
|
+
server(_viteServer, { lifecycle }) {
|
|
134
|
+
void startDev().catch(() => {
|
|
135
|
+
});
|
|
136
|
+
lifecycle.onShutdown(closeDev);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
runtime: {
|
|
140
|
+
close: closeDev
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/index.ts
|
|
146
|
+
var DEFAULT_BASE_PATH = "/api/agent";
|
|
147
|
+
function normalizeBasePath(value) {
|
|
148
|
+
if (!value.startsWith("/")) {
|
|
149
|
+
throw new Error(`@farm.js/agent: basePath must start with "/", got "${value}".`);
|
|
150
|
+
}
|
|
151
|
+
const basePath = value.replace(/\/+$/, "");
|
|
152
|
+
if (basePath !== "/api" && !basePath.startsWith("/api/")) {
|
|
153
|
+
throw new Error(`@farm.js/agent: basePath must be under /api, got "${value}".`);
|
|
154
|
+
}
|
|
155
|
+
return basePath;
|
|
156
|
+
}
|
|
157
|
+
function agent(options = {}) {
|
|
158
|
+
const basePath = normalizeBasePath(options.basePath ?? DEFAULT_BASE_PATH);
|
|
159
|
+
const config = { ...options, basePath };
|
|
160
|
+
const instance = { basePath, framework: options.framework };
|
|
161
|
+
registerAgentUnitConfig(config);
|
|
162
|
+
return defineIntegration({
|
|
163
|
+
category: "agent",
|
|
164
|
+
type: "agent-unit",
|
|
165
|
+
instance,
|
|
166
|
+
plugins: [createAgentPlugin(config)]
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
export {
|
|
170
|
+
agent
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/plugin.ts"],"sourcesContent":["import { defineIntegration } from \"@farm.js/core\";\nimport type { AgentFramework, AgentUnitConfig } from \"agent-unit\";\n\nimport { registerAgentUnitConfig } from \"./config.js\";\nimport { createAgentPlugin } from \"./plugin.js\";\n\nexport type { AgentFramework };\n\n/**\n * agent-unit's settings, minus what Farm owns: the host (`deploy.target`), the server and the\n * Nitro and Workers settings, which belong in farm.config.\n */\nexport interface AgentOptions extends Omit<\n AgentUnitConfig,\n \"server\" | \"preset\" | \"runtime\" | \"cloudflare\" | \"nitro\"\n> {\n /** Where the agent API is mounted, under `/api`. It owns every path under it. Default `/api/agent`. */\n basePath?: string;\n /** The folder of agent files, relative to the app root. Default `agents` in `srcDir`. */\n agentsDir?: string;\n}\n\nexport interface AgentInstance {\n readonly basePath: string;\n readonly framework?: AgentFramework;\n}\n\nconst DEFAULT_BASE_PATH = \"/api/agent\";\n\nfunction normalizeBasePath(value: string): string {\n if (!value.startsWith(\"/\")) {\n throw new Error(`@farm.js/agent: basePath must start with \"/\", got \"${value}\".`);\n }\n const basePath = value.replace(/\\/+$/, \"\");\n // The agents are API routes, so Farm's middleware and guards run for them.\n if (basePath !== \"/api\" && !basePath.startsWith(\"/api/\")) {\n throw new Error(`@farm.js/agent: basePath must be under /api, got \"${value}\".`);\n }\n return basePath;\n}\n\n/**\n * Durable agents in a Farm app: the agents in `src/agents`, written with the AI SDK, Mastra,\n * LangGraph, the OpenAI Agents SDK or agent-unit's `defineAgent`, served under `basePath` in\n * `farm dev` and built by agent-unit into every deployment target.\n *\n * @example\n * integrations: { agent: agent({ framework: \"ai-sdk\", storage: { driver: \"redis\", url: process.env.REDIS_URL } }) }\n */\nexport function agent(options: AgentOptions = {}) {\n const basePath = normalizeBasePath(options.basePath ?? DEFAULT_BASE_PATH);\n const config: AgentUnitConfig & { basePath: string } = { ...options, basePath };\n const instance: AgentInstance = { basePath, framework: options.framework };\n // agent-unit's engine reads it from the server that evaluates farm.config (see ./config).\n registerAgentUnitConfig(config);\n\n return defineIntegration({\n category: \"agent\",\n type: \"agent-unit\",\n instance,\n plugins: [createAgentPlugin(config)],\n });\n}\n","import type { AgentUnitConfig } from \"agent-unit\";\n\nconst AGENT_UNIT_CONFIG = Symbol.for(\"farm.agent.config\");\n\ntype ConfigHolder = { [AGENT_UNIT_CONFIG]?: AgentUnitConfig };\n\n/** @internal Called by `agent()` as farm.config evaluates, in Farm's process and in the server. */\nexport function registerAgentUnitConfig(config: AgentUnitConfig): void {\n (globalThis as ConfigHolder)[AGENT_UNIT_CONFIG] = config;\n}\n\n/**\n * The agent-unit config of the app's `agent()` integration. agent-unit's engine reads it when it is\n * first used: by then Farm's server has evaluated farm.config, so settings such as storage URLs and\n * `authorize` come from where the server runs.\n */\nexport default function agentUnitConfig(): AgentUnitConfig {\n const config = (globalThis as ConfigHolder)[AGENT_UNIT_CONFIG];\n if (!config) {\n throw new Error(\n \"@farm.js/agent: the server has not evaluated farm.config, so the agent() settings are missing. Mount agent() under `integrations` in farm.config.\",\n );\n }\n return config;\n}\n","import { writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { definePlugin } from \"@farm.js/core/plugin\";\nimport type { AgentUnitConfig } from \"agent-unit\";\nimport type { DevHandler } from \"agent-unit/build\";\n\n// agent-unit's build and dev servers load only in Farm's own process. Farm bundles farm.config into\n// the production server, and a bundler follows literal imports, so these must not be literal.\nconst loadBuildOnly = <T>(specifier: string): Promise<T> => import(/* @vite-ignore */ specifier);\n\n/** Set by the built server's agent module (see SERVER_MODULE): agent-unit's engine. */\nconst AGENT_SERVER = Symbol.for(\"farm.agent.server\");\ntype AgentServer = { handle(request: Request): Promise<Response> };\ntype ServerHolder = { [AGENT_SERVER]?: AgentServer };\n\n// Every method agent-unit answers: the run API, MCP and the internal continue and sweep endpoints.\nconst METHODS = [\"get\", \"post\", \"delete\"] as const;\n\nexport function isUnder(pathname: string, basePath: string): boolean {\n return basePath === \"/\" || pathname === basePath || pathname.startsWith(`${basePath}/`);\n}\n\n// The built server's agent module, next to agent-unit's runtime. It hands the engine to the routes\n// below, and as Nitro middleware on the agent paths it passes on the host's waitUntil, which only\n// hosts that keep work alive after the response have, as agent-unit's own entry does.\nconst SERVER_MODULE = `// Generated by @farm.js/agent. Do not edit.\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { getUnit } from \"./runtime.mjs\";\n\nconst hostWaitUntil = new AsyncLocalStorage();\n\nglobalThis[Symbol.for(\"farm.agent.server\")] = {\n handle(request) {\n const waitUntil = hostWaitUntil.getStore();\n return getUnit().handler(request, waitUntil ? { waitUntil } : {});\n },\n};\n\nexport default function farmAgentHost(event, next) {\n const req = event.req;\n return typeof req.waitUntil === \"function\"\n ? hostWaitUntil.run((promise) => req.waitUntil(promise), next)\n : next();\n}\n`;\n\n/**\n * Serves the agents in `farm dev` and builds them into the production server, as API routes under\n * `basePath`: Farm's middleware, plugin guards and integration guards run for them as for any route.\n */\nexport function createAgentPlugin(config: AgentUnitConfig & { basePath: string }) {\n const { basePath } = config;\n // Set by `configure`, in Farm's process: farm.config also evaluates inside the server, where a\n // Worker has no working directory.\n let root: string | undefined;\n let agentsDir = config.agentsDir ?? \"agents\";\n let isDev = false;\n let devHandler: Promise<DevHandler> | undefined;\n\n const buildConfig = (): AgentUnitConfig => ({ ...config, agentsDir });\n const startDev = () =>\n (devHandler ??= loadBuildOnly<typeof import(\"agent-unit/build\")>(\"agent-unit/build\").then(\n ({ createDevHandler }) =>\n createDevHandler({ root: root ?? process.cwd(), config: buildConfig() }),\n ));\n const closeDev = async () => {\n const pending = devHandler;\n devHandler = undefined;\n await (await pending?.catch(() => undefined))?.close();\n };\n\n const handle = async (request: Request): Promise<Response> => {\n // A dev server runs the agents from source, kept across Vite restarts.\n if (isDev) return (await startDev()).fetch(request);\n const server = (globalThis as ServerHolder)[AGENT_SERVER];\n if (!server) {\n throw new Error(\n \"@farm.js/agent: this server was built without the agents. Build it with `farm build`, which adds them.\",\n );\n }\n return server.handle(request);\n };\n\n return definePlugin({\n name: \"farm:agent\",\n configure(farmConfig, context) {\n root = path.resolve(farmConfig.root ?? process.cwd());\n agentsDir = config.agentsDir ?? path.posix.join(farmConfig.srcDir ?? \"src\", \"agents\");\n isDev = context.isDev;\n },\n // Ordinary API routes, so a request passes Farm's middleware, runtime.before and integration\n // guards before it reaches the agents, in development and in every production target.\n routes: ({ route }) =>\n METHODS.flatMap((method) =>\n [basePath, `${basePath}/[...path]`].map((pattern) =>\n route[method](pattern, { handler: (request) => handle(request) }),\n ),\n ),\n router: {\n // agent-unit answers every path under basePath, so an app route there would never be reached.\n discovered(route) {\n if (route.kind !== \"api\" || !route.filePath || !isUnder(route.path, basePath)) return;\n throw new Error(\n `@farm.js/agent: ${route.filePath} is served at ${route.path}, which the agent API owns (basePath \"${basePath}\"). Move the route, or set another basePath.`,\n );\n },\n },\n build: {\n // agent-unit builds the agents into Farm's Nitro output for its preset: their framework\n // adapters, storage, the sweep and `tasks` as scheduled tasks. Its own handler for basePath\n // would answer before Farm's, so Farm's routes serve the API instead.\n async configure(nitroConfig) {\n const { agentUnit } =\n await loadBuildOnly<typeof import(\"agent-unit/nitro\")>(\"agent-unit/nitro\");\n const unit = agentUnit({\n root: root ?? process.cwd(),\n basePath,\n config: buildConfig(),\n // The module agent-unit's engine reads its config from in the built server.\n configFile: fileURLToPath(new URL(\"./config.js\", import.meta.url)),\n });\n const module: typeof unit = {\n name: unit.name,\n async setup(nitro) {\n const before = nitro.options.handlers.length;\n await unit.setup!(nitro);\n const added = nitro.options.handlers.splice(before);\n const entry = added.find((handler) => handler.handler.endsWith(\"entry.mjs\"))?.handler;\n if (!entry) {\n throw new Error(\n \"@farm.js/agent: agent-unit's Nitro module did not mount its handler. Install the agent-unit version @farm.js/agent supports.\",\n );\n }\n nitro.options.handlers.push(...added.filter((handler) => handler.handler !== entry));\n // Nitro writes handler paths into generated imports, so keep them in forward-slash\n // form; a Windows `D:\\…` path would lose its backslashes there.\n const server = path.posix.join(\n path.posix.dirname(entry.replaceAll(\"\\\\\", \"/\")),\n \"farm-server.mjs\",\n );\n writeFileSync(server, SERVER_MODULE);\n nitro.options.handlers.push(\n { route: basePath, handler: server, middleware: true, lazy: false },\n { route: `${basePath}/**`, handler: server, middleware: true, lazy: false },\n );\n },\n };\n return { ...nitroConfig, modules: [...(nitroConfig.modules ?? []), module] };\n },\n },\n dev: {\n server(_viteServer, { lifecycle }) {\n void startDev().catch(() => {});\n lifecycle.onShutdown(closeDev);\n },\n },\n runtime: {\n close: closeDev,\n },\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farm.js/agent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "Durable agents for Farm.js, served and built by agent-unit",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent-unit",
|
|
7
|
+
"agents",
|
|
8
|
+
"ai-sdk",
|
|
9
|
+
"farmjs",
|
|
10
|
+
"mastra"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/farming-labs/farm.js",
|
|
16
|
+
"directory": "packages/farm-agent"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./config": {
|
|
31
|
+
"types": "./dist/config.d.ts",
|
|
32
|
+
"import": "./dist/config.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"agent-unit": "^0.3.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^22.20.5",
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
|
+
"typescript": "^5.3.3",
|
|
45
|
+
"vite": "8.3.4",
|
|
46
|
+
"vitest": "5.0.5",
|
|
47
|
+
"@farm.js/core": "0.1.50"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@farm.js/core": ">=0.1.50 <0.2.0"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22.13.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsup",
|
|
57
|
+
"dev": "tsup --watch",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"type-check": "tsc --noEmit"
|
|
60
|
+
}
|
|
6
61
|
}
|