akanjs 3.0.0-alpha.21 → 3.0.0-alpha.23
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/base/baseEnv.ts +1 -1
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/package.json +1 -1
- package/server/akanOption.ts +6 -10
- package/server/akanServer.ts +16 -14
- package/server/di/diLifecycle.ts +4 -4
- package/server/mcp/McpDispatcher.ts +2 -2
- package/server/mcp/McpRouter.ts +6 -4
- package/server/resolver/signal.resolver.ts +2 -2
- package/service/injectInfo.ts +8 -3
- package/service/predefinedAdaptor/database.adaptor.ts +24 -22
- package/service/predefinedAdaptor/queue.adaptor.ts +5 -2
- package/service/predefinedAdaptor/solidQueue.adaptor.ts +6 -2
- package/service/predefinedAdaptor/solidSqlite.ts +11 -9
- package/signal/agent.signal.ts +1 -5
- package/signal/guards.ts +3 -11
- package/signal/middleware.ts +3 -3
- package/signal/signalContext.ts +3 -3
- package/test/index.ts +1 -1
- package/test/signalTestRuntime.ts +5 -5
- package/test/testServer.ts +8 -5
- package/types/base/baseEnv.d.ts +1 -1
- package/types/server/akanOption.d.ts +7 -7
- package/types/server/akanServer.d.ts +4 -7
- package/types/server/di/diLifecycle.d.ts +2 -2
- package/types/server/mcp/McpDispatcher.d.ts +2 -2
- package/types/server/mcp/McpRouter.d.ts +3 -2
- package/types/server/resolver/signal.resolver.d.ts +2 -2
- package/types/service/injectInfo.d.ts +2 -2
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +2 -2
- package/types/service/predefinedAdaptor/queue.adaptor.d.ts +3 -2
- package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +6 -2
- package/types/service/predefinedAdaptor/solidSqlite.d.ts +2 -2
- package/types/signal/agent.signal.d.ts +1 -6
- package/types/signal/guards.d.ts +2 -4
- package/types/signal/middleware.d.ts +7 -7
- package/types/signal/signalContext.d.ts +2 -2
- package/types/test/index.d.ts +1 -1
- package/types/test/signalTestRuntime.d.ts +2 -3
- package/types/test/testServer.d.ts +6 -4
package/base/baseEnv.ts
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/server/akanOption.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface AdaptorOverride {
|
|
|
14
14
|
* App/library server option builder: use objects, signal middleware, adaptor overrides, web proxies, and the
|
|
15
15
|
* server settings an app owns — MCP, the agent relay's access policy, and the LLM the relay speaks to.
|
|
16
16
|
*/
|
|
17
|
-
export class AkanOption<Env =
|
|
17
|
+
export class AkanOption<Env extends BackendEnv = BackendEnv> {
|
|
18
18
|
readonly #getUses: ((env: Env) => Record<string, PromiseOrObject<unknown>>)[];
|
|
19
19
|
readonly #middlewares: MiddlewareCls[] = [];
|
|
20
20
|
readonly #adaptorOverrides: AdaptorOverride[] = [];
|
|
@@ -25,11 +25,7 @@ export class AkanOption<Env = unknown> {
|
|
|
25
25
|
constructor() {
|
|
26
26
|
this.#getUses = [];
|
|
27
27
|
}
|
|
28
|
-
use(
|
|
29
|
-
fnOrObject:
|
|
30
|
-
| ((env: Env & BackendEnv) => Record<string, PromiseOrObject<unknown>>)
|
|
31
|
-
| Record<string, PromiseOrObject<unknown>>,
|
|
32
|
-
) {
|
|
28
|
+
use(fnOrObject: ((env: Env) => Record<string, PromiseOrObject<unknown>>) | Record<string, PromiseOrObject<unknown>>) {
|
|
33
29
|
if (typeof fnOrObject === "function")
|
|
34
30
|
this.#getUses.push(fnOrObject as (env: Env) => Record<string, PromiseOrObject<unknown>>);
|
|
35
31
|
else this.#getUses.push(() => fnOrObject);
|
|
@@ -58,8 +54,8 @@ export class AkanOption<Env = unknown> {
|
|
|
58
54
|
return this;
|
|
59
55
|
}
|
|
60
56
|
/**
|
|
61
|
-
* Who may spend the LLM key through the `runAgentTurn` relay
|
|
62
|
-
*
|
|
57
|
+
* Who may spend the LLM key through the `runAgentTurn` relay. With no policy the call is refused — the same
|
|
58
|
+
* answer `None` gives — because the framework has no account model to gate on. `null` clears the policy.
|
|
63
59
|
*/
|
|
64
60
|
setAgentAccess(policy: AgentRelayPolicy | null) {
|
|
65
61
|
this.#agentAccess = policy;
|
|
@@ -71,7 +67,7 @@ export class AkanOption<Env = unknown> {
|
|
|
71
67
|
else this.#getLlms.push(() => llmOrFn);
|
|
72
68
|
return this;
|
|
73
69
|
}
|
|
74
|
-
getUses(env: Env
|
|
70
|
+
getUses(env: Env): Record<string, PromiseOrObject<unknown>> {
|
|
75
71
|
const uses = this.#getUses.map((fn) => fn(env));
|
|
76
72
|
return Object.assign({}, ...uses);
|
|
77
73
|
}
|
|
@@ -90,7 +86,7 @@ export class AkanOption<Env = unknown> {
|
|
|
90
86
|
getAgentAccess(): AgentRelayPolicy | null | undefined {
|
|
91
87
|
return this.#agentAccess;
|
|
92
88
|
}
|
|
93
|
-
getLlm(env: Env
|
|
89
|
+
getLlm(env: Env): LlmOption {
|
|
94
90
|
return Object.assign({}, ...this.#getLlms.map((fn) => fn(env)));
|
|
95
91
|
}
|
|
96
92
|
}
|
package/server/akanServer.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BaseEnv, getEnv } from "akanjs/base";
|
|
1
|
+
import { type BackendEnv, type BaseEnv, getEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import { DictionaryLookup } from "akanjs/dictionary";
|
|
4
4
|
import type {
|
|
@@ -31,7 +31,7 @@ import type { HttpRoutes, SignalRoutes, WebsocketRoutes } from "./types";
|
|
|
31
31
|
import type { WebRouter } from "./webRouter";
|
|
32
32
|
|
|
33
33
|
export interface AkanServerProps extends AkanLibProps {
|
|
34
|
-
env?:
|
|
34
|
+
env?: BackendEnv;
|
|
35
35
|
prefix?: string;
|
|
36
36
|
websocketPrefix?: string;
|
|
37
37
|
openapi?: boolean;
|
|
@@ -113,7 +113,7 @@ export class AkanServer {
|
|
|
113
113
|
readonly logger: Logger;
|
|
114
114
|
readonly name: string;
|
|
115
115
|
readonly libs: AkanLib[];
|
|
116
|
-
readonly env:
|
|
116
|
+
readonly env: BackendEnv;
|
|
117
117
|
prefix = "/api";
|
|
118
118
|
websocketPrefix = "/ws";
|
|
119
119
|
openapi = AkanServer.#isOpenApiEnvEnabled();
|
|
@@ -129,7 +129,7 @@ export class AkanServer {
|
|
|
129
129
|
#metricsTimer: Timer | null = null;
|
|
130
130
|
constructor(
|
|
131
131
|
name = "AkanServer",
|
|
132
|
-
env = {},
|
|
132
|
+
env: BackendEnv = {},
|
|
133
133
|
serverMode: "federation" | "batch" | "all" = (process.env.SERVER_MODE as
|
|
134
134
|
| "federation"
|
|
135
135
|
| "batch"
|
|
@@ -141,7 +141,7 @@ export class AkanServer {
|
|
|
141
141
|
this.name = name;
|
|
142
142
|
this.logger = new Logger(name);
|
|
143
143
|
this.libs = libs;
|
|
144
|
-
this.env =
|
|
144
|
+
this.env = { ...env };
|
|
145
145
|
this.openapi = options?.openapi ?? this.openapi;
|
|
146
146
|
|
|
147
147
|
libs.forEach((lib) => {
|
|
@@ -217,17 +217,18 @@ export class AkanServer {
|
|
|
217
217
|
|
|
218
218
|
inspectConsole(): AkanServerConsoleInfo {
|
|
219
219
|
this.#assertCanGet();
|
|
220
|
+
const env = getEnv();
|
|
220
221
|
return {
|
|
221
222
|
name: this.name,
|
|
222
223
|
status: this.status,
|
|
223
224
|
serverMode: this.serverMode,
|
|
224
225
|
env: {
|
|
225
|
-
appName:
|
|
226
|
-
environment:
|
|
227
|
-
operationMode:
|
|
228
|
-
repoName:
|
|
229
|
-
serveDomain:
|
|
230
|
-
databaseMode:
|
|
226
|
+
appName: env.appName,
|
|
227
|
+
environment: env.environment,
|
|
228
|
+
operationMode: env.operationMode,
|
|
229
|
+
repoName: env.repoName,
|
|
230
|
+
serveDomain: env.serveDomain,
|
|
231
|
+
databaseMode: env.databaseMode,
|
|
231
232
|
},
|
|
232
233
|
services: [...this.#di.registry.serviceCls.keys()].sort((a, b) => a.localeCompare(b)),
|
|
233
234
|
signals: [...this.#di.registry.serverSignalCls.keys()].sort((a, b) => a.localeCompare(b)),
|
|
@@ -514,13 +515,14 @@ export class AkanServer {
|
|
|
514
515
|
}
|
|
515
516
|
|
|
516
517
|
#createBuiltinRoutes(): HttpRoutes {
|
|
518
|
+
const { appName } = getEnv();
|
|
517
519
|
const openapiRoutes: HttpRoutes = this.openapi
|
|
518
520
|
? {
|
|
519
521
|
"/openapi.json": {
|
|
520
522
|
GET: () =>
|
|
521
523
|
Response.json(
|
|
522
524
|
createOpenApiDocument(FetchSerializer.serializeRegistry(this.#di.live).signal, {
|
|
523
|
-
title: `${
|
|
525
|
+
title: `${appName} API`,
|
|
524
526
|
version: "0.0.0",
|
|
525
527
|
servers: this.#getOpenApiServers(),
|
|
526
528
|
resolveDescription: AkanServer.#createDescriptionResolver(),
|
|
@@ -537,7 +539,7 @@ export class AkanServer {
|
|
|
537
539
|
env: this.env,
|
|
538
540
|
live: this.#di.live,
|
|
539
541
|
middleware: new Map(this.#di.modules.middleware),
|
|
540
|
-
instructions: `Domain tools for the ${
|
|
542
|
+
instructions: `Domain tools for the ${appName} app.`,
|
|
541
543
|
...this.mcpOption,
|
|
542
544
|
readOnly: this.mcpReadOnly,
|
|
543
545
|
auth: this.mcpAuth,
|
|
@@ -549,7 +551,7 @@ export class AkanServer {
|
|
|
549
551
|
|
|
550
552
|
const devtoolsRoutes = new DevtoolsRouter({
|
|
551
553
|
di: this.#di,
|
|
552
|
-
env:
|
|
554
|
+
env: getEnv(),
|
|
553
555
|
name: this.name,
|
|
554
556
|
serverMode: this.serverMode,
|
|
555
557
|
prefix: this.prefix,
|
package/server/di/diLifecycle.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type BackendEnv, getEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import {
|
|
4
4
|
type Adaptor,
|
|
@@ -53,7 +53,7 @@ export class DiLifecycle {
|
|
|
53
53
|
adaptorStages: [] as string[][],
|
|
54
54
|
serviceStages: [] as string[][],
|
|
55
55
|
};
|
|
56
|
-
readonly #env:
|
|
56
|
+
readonly #env: BackendEnv;
|
|
57
57
|
readonly #libs: AkanLib[];
|
|
58
58
|
readonly #database = new Map<string, DatabaseModule>();
|
|
59
59
|
readonly #service = new Map<string, ServiceModule>();
|
|
@@ -88,10 +88,10 @@ export class DiLifecycle {
|
|
|
88
88
|
return !names.some((name) => process.env[name] === "false" || process.env[name] === "0");
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
constructor(env:
|
|
91
|
+
constructor(env: BackendEnv, serverMode: "federation" | "batch" | "all", ...libs: AkanLib[]) {
|
|
92
92
|
this.#env = env;
|
|
93
93
|
|
|
94
|
-
this.#predefinedAdaptor = { ...getPredefinedAdaptor(
|
|
94
|
+
this.#predefinedAdaptor = { ...getPredefinedAdaptor(getEnv().databaseMode ?? "single") };
|
|
95
95
|
this.#libs = libs;
|
|
96
96
|
this.#service.set("base", {
|
|
97
97
|
service: srv.base,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BackendEnv, ENDPOINT_META } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import { DictionaryLookup } from "akanjs/dictionary";
|
|
4
4
|
import { NoDocumentError } from "akanjs/document";
|
|
@@ -37,7 +37,7 @@ export class McpPromptError extends Error {
|
|
|
37
37
|
|
|
38
38
|
interface McpDispatcherProps {
|
|
39
39
|
registry: InjectRegistry;
|
|
40
|
-
env:
|
|
40
|
+
env: BackendEnv;
|
|
41
41
|
live: LiveRegistry;
|
|
42
42
|
middleware: Map<string, MiddlewareCls>;
|
|
43
43
|
/** The one language error text is resolved in, matching the catalogue the client was handed. */
|
package/server/mcp/McpRouter.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type BackendEnv, getEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import { DictionaryLookup } from "akanjs/dictionary";
|
|
4
4
|
import type { InjectRegistry, LiveRegistry } from "akanjs/service";
|
|
@@ -25,7 +25,8 @@ import { McpEventStream } from "./McpEventStream";
|
|
|
25
25
|
|
|
26
26
|
export interface McpRouterProps {
|
|
27
27
|
registry: InjectRegistry;
|
|
28
|
-
|
|
28
|
+
/** Spread into every `SignalContext` this router's dispatcher builds; middleware reads it. */
|
|
29
|
+
env: BackendEnv;
|
|
29
30
|
live: LiveRegistry;
|
|
30
31
|
middleware: Map<string, MiddlewareCls>;
|
|
31
32
|
path?: string;
|
|
@@ -143,7 +144,7 @@ export class McpRouter {
|
|
|
143
144
|
McpRouter.logger.warn(
|
|
144
145
|
"MCP is enabled but published nothing. Every candidate was refused; see the reasons below.",
|
|
145
146
|
);
|
|
146
|
-
for (const { key, reason } of refusals) McpRouter.logger.
|
|
147
|
+
for (const { key, reason } of refusals) McpRouter.logger.verbose(`MCP did not expose "${key}": ${reason}`);
|
|
147
148
|
for (const { key, reason } of undescribed)
|
|
148
149
|
McpRouter.logger.warn(`MCP exposed "${key}" with no description: ${reason}`);
|
|
149
150
|
} catch (error) {
|
|
@@ -461,7 +462,8 @@ export class McpRouter {
|
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
#serverInfo() {
|
|
464
|
-
|
|
465
|
+
const env = getEnv();
|
|
466
|
+
return { name: `${env.appName}-mcp`, version: this.#props.version ?? "0.0.0" };
|
|
465
467
|
}
|
|
466
468
|
|
|
467
469
|
#result(call: McpCall, result: object, cache?: McpCacheHint) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
2
|
+
type BackendEnv,
|
|
3
3
|
type Cls,
|
|
4
4
|
ENDPOINT_META,
|
|
5
5
|
FIELD_META,
|
|
@@ -316,7 +316,7 @@ export class SignalResolver {
|
|
|
316
316
|
env,
|
|
317
317
|
live,
|
|
318
318
|
middleware,
|
|
319
|
-
}: { registry: InjectRegistry; env:
|
|
319
|
+
}: { registry: InjectRegistry; env: BackendEnv; live: LiveRegistry; middleware: Map<string, MiddlewareCls> },
|
|
320
320
|
): SignalRoutes {
|
|
321
321
|
const endpointMeta = endpointCls[ENDPOINT_META] as { [key: string]: EndpointInfo };
|
|
322
322
|
const routes: HttpRoutes = {};
|
package/service/injectInfo.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BackendEnv, type Cls, INJECT_META } from "akanjs/base";
|
|
2
2
|
import {
|
|
3
3
|
type ConstantFieldTypeInput,
|
|
4
4
|
ConstantRegistry,
|
|
@@ -119,7 +119,7 @@ export class InjectInfo<
|
|
|
119
119
|
instance: Adaptor | Service,
|
|
120
120
|
applyCls: AdaptorCls | ServiceCls,
|
|
121
121
|
registry: InjectRegistry,
|
|
122
|
-
env:
|
|
122
|
+
env: BackendEnv,
|
|
123
123
|
) {
|
|
124
124
|
const injectMap = applyCls[INJECT_META] as Record<string, InjectInfo>;
|
|
125
125
|
await Promise.all(
|
|
@@ -240,7 +240,12 @@ export class InjectInfo<
|
|
|
240
240
|
const value = await injectInfo.generateFactory(depInstance);
|
|
241
241
|
Object.defineProperty(instance, propKey, { value, writable: false, enumerable: true });
|
|
242
242
|
}
|
|
243
|
-
static async #injectEnv(
|
|
243
|
+
static async #injectEnv(
|
|
244
|
+
instance: Adaptor | Service,
|
|
245
|
+
propKey: string,
|
|
246
|
+
injectInfo: InjectInfo<"env">,
|
|
247
|
+
env: BackendEnv,
|
|
248
|
+
) {
|
|
244
249
|
const value = await injectInfo.generateFactory(env);
|
|
245
250
|
Object.defineProperty(instance, propKey, { value, writable: false, enumerable: true });
|
|
246
251
|
}
|
|
@@ -3,7 +3,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
3
3
|
import { mkdir } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import type { InArgs, InValue, Client as LibsqlClient } from "@libsql/client";
|
|
6
|
-
import {
|
|
6
|
+
import { DEFAULT_VALUE, dayjs, FIELD_META, getEnv, type PromiseOrObject } from "akanjs/base";
|
|
7
7
|
import { type ConstantModel, getDefault } from "akanjs/constant";
|
|
8
8
|
import {
|
|
9
9
|
createDocumentId,
|
|
@@ -147,7 +147,7 @@ export interface DatabaseAdaptor {
|
|
|
147
147
|
getSearchIndex(): SearchIndex | null;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
interface SqliteEnv
|
|
150
|
+
interface SqliteEnv {
|
|
151
151
|
workspaceRoot?: string;
|
|
152
152
|
database?: DatabaseConfig;
|
|
153
153
|
}
|
|
@@ -1759,22 +1759,23 @@ export class SqliteDatabase
|
|
|
1759
1759
|
extends adapt("sqliteDatabase", ({ env, plug }) => ({
|
|
1760
1760
|
scheduler: plug(ScheduleAdaptorRole),
|
|
1761
1761
|
config: env((env: SqliteEnv) => {
|
|
1762
|
-
const
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1762
|
+
const defaultFile = () => {
|
|
1763
|
+
const { appName, environment, operationMode } = getEnv();
|
|
1764
|
+
return resolveDefaultSqliteFile({
|
|
1765
|
+
appName,
|
|
1766
|
+
fileName: `${appName}-${environment}.db`,
|
|
1767
|
+
isProduction: process.env.NODE_ENV === "production",
|
|
1768
|
+
operationMode,
|
|
1769
|
+
workspaceRoot: env.workspaceRoot,
|
|
1770
|
+
});
|
|
1771
|
+
};
|
|
1771
1772
|
return {
|
|
1772
1773
|
journalMode: "WAL",
|
|
1773
1774
|
busyTimeoutMs: 5000,
|
|
1774
1775
|
synchronous: "NORMAL",
|
|
1775
1776
|
foreignKeys: true,
|
|
1776
1777
|
...env.database?.sqlite,
|
|
1777
|
-
filePath: env.database?.sqlite?.filePath ?? process.env.SQLITE_DATABASE_PATH ?? defaultFile,
|
|
1778
|
+
filePath: env.database?.sqlite?.filePath ?? process.env.SQLITE_DATABASE_PATH ?? defaultFile(),
|
|
1778
1779
|
search: {
|
|
1779
1780
|
enabled: env.database?.search?.enabled ?? parseSearchEnabled(process.env.AKAN_SEARCH_ENABLED),
|
|
1780
1781
|
tokenizer: env.database?.search?.tokenizer ?? process.env.AKAN_SEARCH_TOKENIZER ?? DEFAULT_TOKENIZER,
|
|
@@ -1892,21 +1893,22 @@ export class LibsqlDatabase
|
|
|
1892
1893
|
extends adapt("libsqlDatabase", ({ env, plug }) => ({
|
|
1893
1894
|
scheduler: plug(ScheduleAdaptorRole),
|
|
1894
1895
|
config: env((env: SqliteEnv) => {
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1896
|
+
const defaultFile = () => {
|
|
1897
|
+
const { appName, environment, operationMode } = getEnv();
|
|
1898
|
+
return resolveDefaultSqliteFile({
|
|
1899
|
+
appName,
|
|
1900
|
+
fileName: `${appName}-${environment}.db`,
|
|
1901
|
+
isProduction: process.env.NODE_ENV === "production",
|
|
1902
|
+
operationMode,
|
|
1903
|
+
workspaceRoot: env.workspaceRoot,
|
|
1904
|
+
});
|
|
1905
|
+
};
|
|
1904
1906
|
return {
|
|
1905
1907
|
url:
|
|
1906
1908
|
env.database?.libsql?.url ??
|
|
1907
1909
|
process.env.LIBSQL_URL ??
|
|
1908
1910
|
process.env.LIBSQL_URI ??
|
|
1909
|
-
`file:${env.database?.sqlite?.filePath ?? process.env.SQLITE_DATABASE_PATH ?? defaultFile}`,
|
|
1911
|
+
`file:${env.database?.sqlite?.filePath ?? process.env.SQLITE_DATABASE_PATH ?? defaultFile()}`,
|
|
1910
1912
|
authToken: env.database?.libsql?.authToken ?? process.env.LIBSQL_AUTH_TOKEN,
|
|
1911
1913
|
search: {
|
|
1912
1914
|
enabled: env.database?.search?.enabled ?? parseSearchEnabled(process.env.AKAN_SEARCH_ENABLED),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getEnv } from "akanjs/base";
|
|
2
2
|
import { adapt } from "../adapt";
|
|
3
3
|
import type { AkanJob, AkanJobOptions, AkanWorker } from "../ipcTypes";
|
|
4
4
|
import { RedisCache } from "./cache.adaptor";
|
|
@@ -30,7 +30,10 @@ export interface QueueAdaptor {
|
|
|
30
30
|
export class BullQueue
|
|
31
31
|
extends adapt("bullQueue", ({ plug, env }) => ({
|
|
32
32
|
redis: plug(RedisCache, (redisCache) => redisCache.getClient()),
|
|
33
|
-
prefix: env((
|
|
33
|
+
prefix: env(() => {
|
|
34
|
+
const { repoName, appName, environment, operationMode } = getEnv();
|
|
35
|
+
return `queue-${repoName}-${appName}-${environment}-${operationMode}`;
|
|
36
|
+
}),
|
|
34
37
|
}))
|
|
35
38
|
implements QueueAdaptor
|
|
36
39
|
{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Database, Statement } from "bun:sqlite";
|
|
2
|
+
import { getEnv } from "akanjs/base";
|
|
2
3
|
import { adapt } from "../adapt";
|
|
3
4
|
import { type AkanJob, type AkanJobOptions, type AkanWorker, sendAkanIpc } from "../ipcTypes";
|
|
4
5
|
import type { QueueAdaptor } from "./queue.adaptor";
|
|
@@ -56,8 +57,11 @@ class SolidWorker implements AkanWorker {
|
|
|
56
57
|
export class SolidQueue
|
|
57
58
|
extends adapt("solidQueue", ({ env }) => ({
|
|
58
59
|
config: env((env: SolidEnv) => getSolidConfig(env)),
|
|
59
|
-
queueName: env((
|
|
60
|
-
|
|
60
|
+
queueName: env(() => {
|
|
61
|
+
const { repoName, appName, environment, operationMode } = getEnv();
|
|
62
|
+
return `queue-${repoName}-${appName}-${environment}-${operationMode}`;
|
|
63
|
+
}),
|
|
64
|
+
workerId: env(() => `${getEnv().appName}-${process.env.AKAN_REPLICA_IDX ?? "0"}-${process.pid}`),
|
|
61
65
|
}))
|
|
62
66
|
implements QueueAdaptor
|
|
63
67
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
import { mkdir } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import
|
|
4
|
+
import { type Dayjs, getEnv } from "akanjs/base";
|
|
5
5
|
import { resolveDefaultSqliteFile } from "./sqlitePath";
|
|
6
6
|
|
|
7
7
|
export interface SolidConfig {
|
|
@@ -14,7 +14,7 @@ export interface SolidConfig {
|
|
|
14
14
|
queueLeaseMs?: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export interface SolidEnv
|
|
17
|
+
export interface SolidEnv {
|
|
18
18
|
workspaceRoot?: string;
|
|
19
19
|
solid?: SolidConfig;
|
|
20
20
|
}
|
|
@@ -23,18 +23,20 @@ export type SolidValueType = "string" | "number" | "buffer" | "json";
|
|
|
23
23
|
|
|
24
24
|
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
const appName
|
|
28
|
-
|
|
29
|
-
const defaultFile = resolveDefaultSqliteFile({
|
|
26
|
+
const defaultSolidFile = (workspaceRoot?: string) => {
|
|
27
|
+
const { appName, environment, operationMode } = getEnv();
|
|
28
|
+
return resolveDefaultSqliteFile({
|
|
30
29
|
appName,
|
|
31
30
|
fileName: `${appName}-${environment}_solid.db`,
|
|
32
31
|
isProduction: process.env.NODE_ENV === "production",
|
|
33
|
-
operationMode
|
|
34
|
-
workspaceRoot
|
|
32
|
+
operationMode,
|
|
33
|
+
workspaceRoot,
|
|
35
34
|
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const getSolidConfig = (env: SolidEnv): Required<SolidConfig> => {
|
|
36
38
|
return {
|
|
37
|
-
filePath: env.solid?.filePath ?? process.env.AKAN_SOLID_DB_PATH ??
|
|
39
|
+
filePath: env.solid?.filePath ?? process.env.AKAN_SOLID_DB_PATH ?? defaultSolidFile(env.workspaceRoot),
|
|
38
40
|
journalMode: env.solid?.journalMode ?? "WAL",
|
|
39
41
|
busyTimeoutMs: env.solid?.busyTimeoutMs ?? 5000,
|
|
40
42
|
synchronous: env.solid?.synchronous ?? "NORMAL",
|
package/signal/agent.signal.ts
CHANGED
|
@@ -10,11 +10,7 @@ import { Req } from "./internalArg";
|
|
|
10
10
|
import { serverSignal } from "./serverSignal";
|
|
11
11
|
import { SignalRegistry } from "./signalRegistry";
|
|
12
12
|
|
|
13
|
-
export class AgentInternal extends internal(srv.agent, (
|
|
14
|
-
warnOpenRelay: initialize().exec(() => {
|
|
15
|
-
AgentRelayAccess.warnIfOpen();
|
|
16
|
-
}),
|
|
17
|
-
})) {}
|
|
13
|
+
export class AgentInternal extends internal(srv.agent, () => ({})) {}
|
|
18
14
|
|
|
19
15
|
export class AgentEndpoint extends endpoint(srv.agent, ({ mutation }) => ({
|
|
20
16
|
|
package/signal/guards.ts
CHANGED
|
@@ -24,8 +24,8 @@ export type AgentRelayPolicy = (context: SignalContext) => boolean | Promise<boo
|
|
|
24
24
|
* Gate for the `runAgentTurn` relay. Every tool runs in the caller's own browser session, so the LLM key is the
|
|
25
25
|
* one thing this endpoint spends — ungated, any visitor can bill the app's provider through fetch alone.
|
|
26
26
|
*
|
|
27
|
-
* The framework has no account model to gate on, so with no policy registered it
|
|
28
|
-
*
|
|
27
|
+
* The framework has no account model to gate on, so with no policy registered it refuses every call — the same
|
|
28
|
+
* answer `None` gives. An app opens it at boot, e.g.
|
|
29
29
|
* `AgentRelayAccess.use((context) => !!context.get("account"))`. The policy is the app's; the framework cannot know it.
|
|
30
30
|
*/
|
|
31
31
|
export class AgentRelayAccess implements Guard {
|
|
@@ -44,17 +44,9 @@ export class AgentRelayAccess implements Guard {
|
|
|
44
44
|
return !!AgentRelayAccess.#policy;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/** Runs at boot, after the app entry has evaluated — a module-scope check would fire before `use()` could. */
|
|
48
|
-
static warnIfOpen() {
|
|
49
|
-
if (AgentRelayAccess.#policy) return;
|
|
50
|
-
AgentRelayAccess.#logger.warn(
|
|
51
|
-
'runAgentTurn is open to every caller — anyone can spend the LLM key. Register a policy at boot: AgentRelayAccess.use((context) => !!context.get("account")), or set AKAN_AGENT=false.',
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
47
|
async canPass(context: SignalContext): Promise<boolean> {
|
|
56
48
|
const policy = AgentRelayAccess.#policy;
|
|
57
|
-
if (!policy) return
|
|
49
|
+
if (!policy) return false;
|
|
58
50
|
try {
|
|
59
51
|
return await policy(context);
|
|
60
52
|
} catch (error) {
|
package/signal/middleware.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BackendEnv, Cls, PromiseOrObject } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import { type CacheAdaptor, CacheAdaptorRole } from "akanjs/service";
|
|
4
4
|
import dayjs from "dayjs";
|
|
5
5
|
import type { SignalContext } from "./signalContext";
|
|
6
6
|
import { traceCache } from "./trace";
|
|
7
7
|
|
|
8
|
-
export interface Middleware<Env extends
|
|
8
|
+
export interface Middleware<Env extends BackendEnv = BackendEnv> {
|
|
9
9
|
use(env: Env): PromiseOrObject<(context: SignalContext, next: () => Promise<unknown>) => PromiseOrObject<unknown>>;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -14,7 +14,7 @@ export type MiddlewareCls = Cls<Middleware, { readonly refName: string }>;
|
|
|
14
14
|
export const middleware = (refName: string) => {
|
|
15
15
|
return class Middleware {
|
|
16
16
|
static refName = refName;
|
|
17
|
-
async use(env:
|
|
17
|
+
async use(env: BackendEnv) {
|
|
18
18
|
return async (context: SignalContext, next: () => Promise<unknown>) => {
|
|
19
19
|
return await next();
|
|
20
20
|
};
|
package/signal/signalContext.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
2
|
+
type BackendEnv,
|
|
3
3
|
type Cls,
|
|
4
4
|
FIELD_META,
|
|
5
5
|
INTERNAL_META,
|
|
@@ -52,7 +52,7 @@ const isExceptionLike = (error: unknown): error is ExceptionLike => {
|
|
|
52
52
|
|
|
53
53
|
export class SignalContext<
|
|
54
54
|
Ctx extends HttpExecutionContext | WebSocketExecutionContext = HttpExecutionContext | WebSocketExecutionContext,
|
|
55
|
-
Env extends
|
|
55
|
+
Env extends BackendEnv = BackendEnv,
|
|
56
56
|
> {
|
|
57
57
|
key: string;
|
|
58
58
|
transport: SignalTransportType;
|
|
@@ -215,7 +215,7 @@ export class SignalContext<
|
|
|
215
215
|
* call for every registered middleware — and `Logging` is registered by default.
|
|
216
216
|
*/
|
|
217
217
|
static #middlewareHandlers = new WeakMap<MiddlewareCls, WeakMap<object, Promise<MiddlewareHandler>>>();
|
|
218
|
-
static #getMiddlewareHandler(MiddlewareCls: MiddlewareCls, env:
|
|
218
|
+
static #getMiddlewareHandler(MiddlewareCls: MiddlewareCls, env: BackendEnv): Promise<MiddlewareHandler> {
|
|
219
219
|
const byEnv =
|
|
220
220
|
SignalContext.#middlewareHandlers.get(MiddlewareCls) ?? new WeakMap<object, Promise<MiddlewareHandler>>();
|
|
221
221
|
SignalContext.#middlewareHandlers.set(MiddlewareCls, byEnv);
|
package/test/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { BackendEnv } from "akanjs/base";
|
|
1
|
+
import type { BackendEnv, BaseEnv } from "akanjs/base";
|
|
2
2
|
import type { FetchProxy } from "akanjs/fetch";
|
|
3
3
|
import type { AkanLib } from "akanjs/server";
|
|
4
|
-
import { TestServer, type TestServerOptions } from "./testServer";
|
|
4
|
+
import { type TestEnv, TestServer, type TestServerOptions } from "./testServer";
|
|
5
5
|
|
|
6
6
|
export interface SignalTestTarget {
|
|
7
7
|
type: "app" | "lib";
|
|
8
8
|
name: string;
|
|
9
|
-
env:
|
|
9
|
+
env: TestEnv;
|
|
10
10
|
fetch: FetchProxy;
|
|
11
11
|
libs: AkanLib[];
|
|
12
12
|
}
|
|
@@ -87,7 +87,7 @@ export const setupSignalTestTarget = async <Fetch = FetchProxy>(
|
|
|
87
87
|
pendingContext = (async () => {
|
|
88
88
|
terminatingContext = undefined;
|
|
89
89
|
const resolvedOptions = { ...configuredOptions, ...options };
|
|
90
|
-
const env:
|
|
90
|
+
const env: BaseEnv = {
|
|
91
91
|
repoName: process.env.AKAN_PUBLIC_REPO_NAME ?? "akanjs",
|
|
92
92
|
serveDomain: process.env.AKAN_PUBLIC_SERVE_DOMAIN ?? "akanjs.com",
|
|
93
93
|
appName: name,
|
|
@@ -103,7 +103,7 @@ export const setupSignalTestTarget = async <Fetch = FetchProxy>(
|
|
|
103
103
|
const target: SignalTestTarget = {
|
|
104
104
|
type,
|
|
105
105
|
name,
|
|
106
|
-
env: targetModule.env,
|
|
106
|
+
env: { ...env, ...targetModule.env },
|
|
107
107
|
fetch: targetModule.fetch,
|
|
108
108
|
libs: [...dependencyModules.map((mod) => mod.lib), targetModule.lib],
|
|
109
109
|
};
|
package/test/testServer.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import type { BackendEnv } from "akanjs/base";
|
|
4
|
+
import type { BackendEnv, BaseEnv } from "akanjs/base";
|
|
5
5
|
import { Logger, sleep } from "akanjs/common";
|
|
6
6
|
import { type AkanLib, AkanServer } from "akanjs/server";
|
|
7
7
|
|
|
@@ -12,6 +12,9 @@ const MAX_ACTIVATION_TIME = 30000;
|
|
|
12
12
|
|
|
13
13
|
type TestDatabaseMode = "memory" | "tempFile";
|
|
14
14
|
|
|
15
|
+
/** `BackendEnv` carries server options only, so a harness that also stamps `process.env` needs the identity beside it. */
|
|
16
|
+
export type TestEnv = BaseEnv & BackendEnv;
|
|
17
|
+
|
|
15
18
|
export interface TestServerOptions {
|
|
16
19
|
workerId?: number;
|
|
17
20
|
port?: number;
|
|
@@ -44,7 +47,7 @@ const resolveWorkerId = (workerId?: number) => {
|
|
|
44
47
|
export class TestServer {
|
|
45
48
|
readonly #logger = new Logger("TestServer");
|
|
46
49
|
readonly #libs: AkanLib[];
|
|
47
|
-
readonly #env:
|
|
50
|
+
readonly #env: TestEnv;
|
|
48
51
|
readonly #databaseMode: TestDatabaseMode;
|
|
49
52
|
readonly #serverMode: "federation" | "batch" | "all";
|
|
50
53
|
readonly #listen: boolean;
|
|
@@ -55,7 +58,7 @@ export class TestServer {
|
|
|
55
58
|
#startAt = Date.now();
|
|
56
59
|
#server?: AkanServer;
|
|
57
60
|
#tempDir?: string;
|
|
58
|
-
static initClient(env:
|
|
61
|
+
static initClient(env: BaseEnv, workerId?: number) {
|
|
59
62
|
TestServer.applyProcessEnv(env, {
|
|
60
63
|
workerId,
|
|
61
64
|
port: TEST_LISTEN_PORT_BASE + resolveWorkerId(workerId),
|
|
@@ -63,7 +66,7 @@ export class TestServer {
|
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
static applyProcessEnv(
|
|
66
|
-
env:
|
|
69
|
+
env: BaseEnv,
|
|
67
70
|
{
|
|
68
71
|
workerId,
|
|
69
72
|
port,
|
|
@@ -82,7 +85,7 @@ export class TestServer {
|
|
|
82
85
|
process.env.AKAN_PUBLIC_SERVER_PORT = String(resolvedPort);
|
|
83
86
|
process.env.SERVER_HTTP_PROTOCOL = "http:";
|
|
84
87
|
}
|
|
85
|
-
constructor(env:
|
|
88
|
+
constructor(env: TestEnv, libs: AkanLib | AkanLib[], options: TestServerOptions = {}) {
|
|
86
89
|
this.workerId = resolveWorkerId(options.workerId);
|
|
87
90
|
this.#port = options.port ?? TEST_LISTEN_PORT_BASE + this.workerId;
|
|
88
91
|
this.#env = { ...env };
|
package/types/base/baseEnv.d.ts
CHANGED
|
@@ -11,10 +11,10 @@ export interface AdaptorOverride {
|
|
|
11
11
|
* App/library server option builder: use objects, signal middleware, adaptor overrides, web proxies, and the
|
|
12
12
|
* server settings an app owns — MCP, the agent relay's access policy, and the LLM the relay speaks to.
|
|
13
13
|
*/
|
|
14
|
-
export declare class AkanOption<Env =
|
|
14
|
+
export declare class AkanOption<Env extends BackendEnv = BackendEnv> {
|
|
15
15
|
#private;
|
|
16
16
|
constructor();
|
|
17
|
-
use(fnOrObject: ((env: Env
|
|
17
|
+
use(fnOrObject: ((env: Env) => Record<string, PromiseOrObject<unknown>>) | Record<string, PromiseOrObject<unknown>>): this;
|
|
18
18
|
applyMiddleware(...middlewares: MiddlewareCls[]): this;
|
|
19
19
|
/** Rebinds a predefined adaptor role (e.g. `LlmAdaptorRole`) to the app's own implementation. Last writer wins. */
|
|
20
20
|
applyAdaptor<T extends Adaptor>(role: AdaptorCls<T>, adaptor: AdaptorCls<T>): this;
|
|
@@ -26,18 +26,18 @@ export declare class AkanOption<Env = unknown> {
|
|
|
26
26
|
*/
|
|
27
27
|
setMcp(mcp?: boolean | McpServerOption): this;
|
|
28
28
|
/**
|
|
29
|
-
* Who may spend the LLM key through the `runAgentTurn` relay
|
|
30
|
-
*
|
|
29
|
+
* Who may spend the LLM key through the `runAgentTurn` relay. With no policy the call is refused — the same
|
|
30
|
+
* answer `None` gives — because the framework has no account model to gate on. `null` clears the policy.
|
|
31
31
|
*/
|
|
32
32
|
setAgentAccess(policy: AgentRelayPolicy | null): this;
|
|
33
33
|
/** Settings for whichever adaptor fills `LlmAdaptorRole`, injected into it as the `llmOption` use. */
|
|
34
34
|
setLlm(llmOrFn: LlmOption | ((env: Env) => LlmOption)): this;
|
|
35
|
-
getUses(env: Env
|
|
35
|
+
getUses(env: Env): Record<string, PromiseOrObject<unknown>>;
|
|
36
36
|
getMiddlewares(): MiddlewareCls[];
|
|
37
37
|
getAdaptorOverrides(): AdaptorOverride[];
|
|
38
38
|
getWebProxies(): WebProxyRegistration[];
|
|
39
39
|
getMcp(): boolean | McpServerOption | undefined;
|
|
40
40
|
getAgentAccess(): AgentRelayPolicy | null | undefined;
|
|
41
|
-
getLlm(env: Env
|
|
41
|
+
getLlm(env: Env): LlmOption;
|
|
42
42
|
}
|
|
43
|
-
export declare function createDefaultAkanOption(): AkanOption<
|
|
43
|
+
export declare function createDefaultAkanOption(): AkanOption<BackendEnv>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type BaseEnv } from "akanjs/base";
|
|
1
|
+
import { type BackendEnv, type BaseEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import type { Adaptor, AdaptorCls, DatabaseConfig, Service, ServiceCls, SolidConfig } from "akanjs/service";
|
|
4
4
|
import type { ServerSignal, ServerSignalCls } from "akanjs/signal";
|
|
5
5
|
import type { AkanLib, AkanLibProps } from "./akanLib.d.ts";
|
|
6
6
|
import { type McpAuthOption } from "./mcp.d.ts";
|
|
7
7
|
export interface AkanServerProps extends AkanLibProps {
|
|
8
|
-
env?:
|
|
8
|
+
env?: BackendEnv;
|
|
9
9
|
prefix?: string;
|
|
10
10
|
websocketPrefix?: string;
|
|
11
11
|
openapi?: boolean;
|
|
@@ -68,10 +68,7 @@ export declare class AkanServer {
|
|
|
68
68
|
readonly logger: Logger;
|
|
69
69
|
readonly name: string;
|
|
70
70
|
readonly libs: AkanLib[];
|
|
71
|
-
readonly env:
|
|
72
|
-
database?: DatabaseConfig;
|
|
73
|
-
solid?: SolidConfig;
|
|
74
|
-
};
|
|
71
|
+
readonly env: BackendEnv;
|
|
75
72
|
prefix: string;
|
|
76
73
|
websocketPrefix: string;
|
|
77
74
|
openapi: boolean;
|
|
@@ -81,7 +78,7 @@ export declare class AkanServer {
|
|
|
81
78
|
mcpOption: Omit<McpServerOption, "enabled" | "readOnly" | "auth">;
|
|
82
79
|
serverMode: "federation" | "batch" | "all";
|
|
83
80
|
shutdownTimeoutMs: number;
|
|
84
|
-
constructor(name?: string, env?:
|
|
81
|
+
constructor(name?: string, env?: BackendEnv, serverMode?: "federation" | "batch" | "all", ...libsOrOptions: (AkanLib | AkanServerOptions)[]);
|
|
85
82
|
setPrefix(prefix: string): this;
|
|
86
83
|
setWebsocketPrefix(websocketPrefix: string): this;
|
|
87
84
|
setOpenApi(openapi?: boolean): this;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type BackendEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import { type Adaptor, type AdaptorCls, type Service, type ServiceCls, type WebsocketAdaptor } from "akanjs/service";
|
|
4
4
|
import { type MiddlewareCls } from "../../signal/middleware.d.ts";
|
|
@@ -31,7 +31,7 @@ export declare class DiLifecycle {
|
|
|
31
31
|
adaptor: ReadonlyMap<string, AdaptorCls>;
|
|
32
32
|
middleware: ReadonlyMap<string, MiddlewareCls>;
|
|
33
33
|
};
|
|
34
|
-
constructor(env:
|
|
34
|
+
constructor(env: BackendEnv, serverMode: "federation" | "batch" | "all", ...libs: AkanLib[]);
|
|
35
35
|
/** Run every init stage in dependency order and collect the generated routes. */
|
|
36
36
|
initializeAll(): Promise<SignalRoutes>;
|
|
37
37
|
destroyAll(): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BackendEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import type { InjectRegistry, LiveRegistry } from "akanjs/service";
|
|
4
4
|
import { type McpExposedEndpoint, type McpToolResult, type PromptMessage } from "../../signal/mcp.d.ts";
|
|
@@ -19,7 +19,7 @@ export declare class McpPromptError extends Error {
|
|
|
19
19
|
}
|
|
20
20
|
interface McpDispatcherProps {
|
|
21
21
|
registry: InjectRegistry;
|
|
22
|
-
env:
|
|
22
|
+
env: BackendEnv;
|
|
23
23
|
live: LiveRegistry;
|
|
24
24
|
middleware: Map<string, MiddlewareCls>;
|
|
25
25
|
/** The one language error text is resolved in, matching the catalogue the client was handed. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type BackendEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import type { InjectRegistry, LiveRegistry } from "akanjs/service";
|
|
4
4
|
import type { MiddlewareCls } from "../../signal/middleware.d.ts";
|
|
@@ -6,7 +6,8 @@ import type { HttpRoutes } from "../types.d.ts";
|
|
|
6
6
|
import { type McpAuthOption } from "./McpAuth.d.ts";
|
|
7
7
|
export interface McpRouterProps {
|
|
8
8
|
registry: InjectRegistry;
|
|
9
|
-
|
|
9
|
+
/** Spread into every `SignalContext` this router's dispatcher builds; middleware reads it. */
|
|
10
|
+
env: BackendEnv;
|
|
10
11
|
live: LiveRegistry;
|
|
11
12
|
middleware: Map<string, MiddlewareCls>;
|
|
12
13
|
path?: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BackendEnv } from "akanjs/base";
|
|
2
2
|
import { Logger } from "akanjs/common";
|
|
3
3
|
import { type InjectRegistry, type LiveRegistry, type WebsocketAdaptor } from "akanjs/service";
|
|
4
4
|
import { type Endpoint, type EndpointCls } from "../../signal/endpoint.d.ts";
|
|
@@ -26,7 +26,7 @@ export declare class SignalResolver {
|
|
|
26
26
|
static resolveSlice(sliceCls: SliceCls): EndpointCls;
|
|
27
27
|
static resolveEndpoint(endpointCls: EndpointCls, endpoint: Endpoint, { registry, env, live, middleware, }: {
|
|
28
28
|
registry: InjectRegistry;
|
|
29
|
-
env:
|
|
29
|
+
env: BackendEnv;
|
|
30
30
|
live: LiveRegistry;
|
|
31
31
|
middleware: Map<string, MiddlewareCls>;
|
|
32
32
|
}): SignalRoutes;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BackendEnv } from "akanjs/base";
|
|
2
2
|
import { type ConstantFieldTypeInput, type FieldToValue, type PlainTypeToFieldType } from "akanjs/constant";
|
|
3
3
|
import type { DatabaseModel } from "akanjs/document";
|
|
4
4
|
import type { Endpoint, EndpointCls, Internal, InternalCls, ServerSignal, ServerSignalCls, SliceCls } from "akanjs/signal";
|
|
@@ -60,7 +60,7 @@ export declare class InjectInfo<Type extends InjectType = any, ReturnType = any,
|
|
|
60
60
|
readonly cacheOption?: CacheSetOptions;
|
|
61
61
|
readonly parentRefName: string;
|
|
62
62
|
constructor(type: Type, options: InjectBuilderOptions<ReturnType>);
|
|
63
|
-
static resolveInjection(instance: Adaptor | Service, applyCls: AdaptorCls | ServiceCls, registry: InjectRegistry, env:
|
|
63
|
+
static resolveInjection(instance: Adaptor | Service, applyCls: AdaptorCls | ServiceCls, registry: InjectRegistry, env: BackendEnv): Promise<void>;
|
|
64
64
|
}
|
|
65
65
|
type GetFieldValue<ValueRef, ExplicitType, MapValue = never> = unknown extends ExplicitType ? FieldToValue<ValueRef, MapValue> : ExplicitType;
|
|
66
66
|
export declare const injectionBuilder: (parentRefName: string) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
import type { Client as LibsqlClient } from "@libsql/client";
|
|
3
|
-
import { type
|
|
3
|
+
import { type PromiseOrObject } from "akanjs/base";
|
|
4
4
|
import { type ConstantModel } from "akanjs/constant";
|
|
5
5
|
import { type DatabaseModel, type DocumentQuery, type DocumentSchema, type DocumentUpdate, type DocumentUpdateInput, type DocumentUpdateOperator, type DocumentUpdateOptions, type SchemaOf } from "akanjs/document";
|
|
6
6
|
import type { Sql } from "postgres";
|
|
@@ -120,7 +120,7 @@ export interface DatabaseAdaptor {
|
|
|
120
120
|
transaction<T>(fn: () => PromiseOrObject<T>): Promise<T>;
|
|
121
121
|
getSearchIndex(): SearchIndex | null;
|
|
122
122
|
}
|
|
123
|
-
interface SqliteEnv
|
|
123
|
+
interface SqliteEnv {
|
|
124
124
|
workspaceRoot?: string;
|
|
125
125
|
database?: DatabaseConfig;
|
|
126
126
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { BaseEnv } from "akanjs/base";
|
|
2
1
|
import type { AkanJob, AkanJobOptions, AkanWorker } from "../ipcTypes.d.ts";
|
|
3
2
|
type QueueLike = {
|
|
4
3
|
add(name: string, args: unknown[], options?: AkanJobOptions): Promise<unknown>;
|
|
@@ -10,7 +9,9 @@ export interface QueueAdaptor {
|
|
|
10
9
|
}
|
|
11
10
|
declare const BullQueue_base: import("..").AdaptorCls<{}, {
|
|
12
11
|
redis: import("..").InjectInfo<"plug", import("ioredis").default, never, never>;
|
|
13
|
-
prefix: import("..").InjectInfo<"env", string,
|
|
12
|
+
prefix: import("..").InjectInfo<"env", string, {
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}, never>;
|
|
14
15
|
}>;
|
|
15
16
|
export declare class BullQueue extends BullQueue_base implements QueueAdaptor {
|
|
16
17
|
#private;
|
|
@@ -3,8 +3,12 @@ import type { QueueAdaptor } from "./queue.adaptor";
|
|
|
3
3
|
import { type SolidConfig, type SolidEnv } from "./solidSqlite.d.ts";
|
|
4
4
|
declare const SolidQueue_base: import("..").AdaptorCls<{}, {
|
|
5
5
|
config: import("..").InjectInfo<"env", Required<SolidConfig>, SolidEnv, never>;
|
|
6
|
-
queueName: import("..").InjectInfo<"env", string,
|
|
7
|
-
|
|
6
|
+
queueName: import("..").InjectInfo<"env", string, {
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}, never>;
|
|
9
|
+
workerId: import("..").InjectInfo<"env", string, {
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}, never>;
|
|
8
12
|
}>;
|
|
9
13
|
export declare class SolidQueue extends SolidQueue_base implements QueueAdaptor {
|
|
10
14
|
#private;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
-
import
|
|
2
|
+
import { type Dayjs } from "akanjs/base";
|
|
3
3
|
export interface SolidConfig {
|
|
4
4
|
filePath?: string;
|
|
5
5
|
journalMode?: "WAL" | "DELETE" | "TRUNCATE" | "PERSIST" | "MEMORY" | "OFF";
|
|
@@ -9,7 +9,7 @@ export interface SolidConfig {
|
|
|
9
9
|
queuePollIntervalMs?: number;
|
|
10
10
|
queueLeaseMs?: number;
|
|
11
11
|
}
|
|
12
|
-
export interface SolidEnv
|
|
12
|
+
export interface SolidEnv {
|
|
13
13
|
workspaceRoot?: string;
|
|
14
14
|
solid?: SolidConfig;
|
|
15
15
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import { Any } from "akanjs/base";
|
|
2
1
|
import { AgentTurn } from "./agentTurn.d.ts";
|
|
3
2
|
declare const AgentInternal_base: import("./internal.d.ts").InternalCls<import("akanjs/service").ServiceModel<typeof import("akanjs/service").AgentService, any, any, {
|
|
4
3
|
agentService: import("akanjs/service").AgentService;
|
|
5
|
-
}>, {
|
|
6
|
-
warnOpenRelay: import("./internalInfo.d.ts").InternalInfo<"init", {
|
|
7
|
-
agentService: import("akanjs/service").AgentService;
|
|
8
|
-
}, [], [], [], typeof Any, void, false>;
|
|
9
|
-
}>;
|
|
4
|
+
}>, {}>;
|
|
10
5
|
export declare class AgentInternal extends AgentInternal_base {
|
|
11
6
|
}
|
|
12
7
|
declare const AgentEndpoint_base: import("./endpoint.d.ts").EndpointCls<import("akanjs/service").ServiceModel<typeof import("akanjs/service").AgentService, any, any, {
|
package/types/signal/guards.d.ts
CHANGED
|
@@ -15,8 +15,8 @@ export type AgentRelayPolicy = (context: SignalContext) => boolean | Promise<boo
|
|
|
15
15
|
* Gate for the `runAgentTurn` relay. Every tool runs in the caller's own browser session, so the LLM key is the
|
|
16
16
|
* one thing this endpoint spends — ungated, any visitor can bill the app's provider through fetch alone.
|
|
17
17
|
*
|
|
18
|
-
* The framework has no account model to gate on, so with no policy registered it
|
|
19
|
-
*
|
|
18
|
+
* The framework has no account model to gate on, so with no policy registered it refuses every call — the same
|
|
19
|
+
* answer `None` gives. An app opens it at boot, e.g.
|
|
20
20
|
* `AgentRelayAccess.use((context) => !!context.get("account"))`. The policy is the app's; the framework cannot know it.
|
|
21
21
|
*/
|
|
22
22
|
export declare class AgentRelayAccess implements Guard {
|
|
@@ -25,7 +25,5 @@ export declare class AgentRelayAccess implements Guard {
|
|
|
25
25
|
static scope: GuardScope;
|
|
26
26
|
static use(policy: AgentRelayPolicy | null): void;
|
|
27
27
|
static get hasPolicy(): boolean;
|
|
28
|
-
/** Runs at boot, after the app entry has evaluated — a module-scope check would fire before `use()` could. */
|
|
29
|
-
static warnIfOpen(): void;
|
|
30
28
|
canPass(context: SignalContext): Promise<boolean>;
|
|
31
29
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BackendEnv, Cls, PromiseOrObject } from "akanjs/base";
|
|
2
2
|
import type { SignalContext } from "./signalContext.d.ts";
|
|
3
|
-
export interface Middleware<Env extends
|
|
3
|
+
export interface Middleware<Env extends BackendEnv = BackendEnv> {
|
|
4
4
|
use(env: Env): PromiseOrObject<(context: SignalContext, next: () => Promise<unknown>) => PromiseOrObject<unknown>>;
|
|
5
5
|
}
|
|
6
6
|
export type MiddlewareCls = Cls<Middleware, {
|
|
@@ -8,13 +8,13 @@ export type MiddlewareCls = Cls<Middleware, {
|
|
|
8
8
|
}>;
|
|
9
9
|
export declare const middleware: (refName: string) => {
|
|
10
10
|
new (): {
|
|
11
|
-
use(env:
|
|
11
|
+
use(env: BackendEnv): Promise<(context: SignalContext, next: () => Promise<unknown>) => Promise<unknown>>;
|
|
12
12
|
};
|
|
13
13
|
refName: string;
|
|
14
14
|
};
|
|
15
15
|
declare const Logging_base: {
|
|
16
16
|
new (): {
|
|
17
|
-
use(env:
|
|
17
|
+
use(env: BackendEnv): Promise<(context: SignalContext, next: () => Promise<unknown>) => Promise<unknown>>;
|
|
18
18
|
};
|
|
19
19
|
refName: string;
|
|
20
20
|
};
|
|
@@ -23,7 +23,7 @@ export declare class Logging extends Logging_base {
|
|
|
23
23
|
}
|
|
24
24
|
declare const Cache_base: {
|
|
25
25
|
new (): {
|
|
26
|
-
use(env:
|
|
26
|
+
use(env: BackendEnv): Promise<(context: SignalContext, next: () => Promise<unknown>) => Promise<unknown>>;
|
|
27
27
|
};
|
|
28
28
|
refName: string;
|
|
29
29
|
};
|
|
@@ -32,7 +32,7 @@ export declare class Cache extends Cache_base {
|
|
|
32
32
|
}
|
|
33
33
|
declare const Timeout_base: {
|
|
34
34
|
new (): {
|
|
35
|
-
use(env:
|
|
35
|
+
use(env: BackendEnv): Promise<(context: SignalContext, next: () => Promise<unknown>) => Promise<unknown>>;
|
|
36
36
|
};
|
|
37
37
|
refName: string;
|
|
38
38
|
};
|
|
@@ -41,7 +41,7 @@ export declare class Timeout extends Timeout_base {
|
|
|
41
41
|
}
|
|
42
42
|
declare const Retry_base: {
|
|
43
43
|
new (): {
|
|
44
|
-
use(env:
|
|
44
|
+
use(env: BackendEnv): Promise<(context: SignalContext, next: () => Promise<unknown>) => Promise<unknown>>;
|
|
45
45
|
};
|
|
46
46
|
refName: string;
|
|
47
47
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BackendEnv, type PromiseOrObject } from "akanjs/base";
|
|
2
2
|
import { type ConstantFieldTypeInput } from "akanjs/constant";
|
|
3
3
|
import type { Adaptor, AdaptorCls, DatabaseService, InjectRegistry, LiveRegistry } from "akanjs/service";
|
|
4
4
|
import type { Internal, InternalInfo, MiddlewareCls } from ".";
|
|
@@ -11,7 +11,7 @@ interface WebSocketRequest {
|
|
|
11
11
|
eventType: WebSocketEventType;
|
|
12
12
|
}
|
|
13
13
|
type RuntimeRecord = Record<string, unknown>;
|
|
14
|
-
export declare class SignalContext<Ctx extends HttpExecutionContext | WebSocketExecutionContext = HttpExecutionContext | WebSocketExecutionContext, Env extends
|
|
14
|
+
export declare class SignalContext<Ctx extends HttpExecutionContext | WebSocketExecutionContext = HttpExecutionContext | WebSocketExecutionContext, Env extends BackendEnv = BackendEnv> {
|
|
15
15
|
#private;
|
|
16
16
|
key: string;
|
|
17
17
|
transport: SignalTransportType;
|
package/types/test/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { sample } from "./sample.d.ts";
|
|
2
2
|
export { sampleOf } from "./sampleOf.d.ts";
|
|
3
3
|
export { configureSignalTest, getOrSetupSignalTestContext, getOrSetupSignalTestFetch, getSignalTestContext, getSignalTestFetch, hasSignalTestContext, type SignalTestContext, type SignalTestOptions, type SignalTestTarget, setupSignalTestTarget, terminateSignalTestContext, } from "./signalTestRuntime.d.ts";
|
|
4
|
-
export { TestServer, type TestServerOptions } from "./testServer.d.ts";
|
|
4
|
+
export { type TestEnv, TestServer, type TestServerOptions } from "./testServer.d.ts";
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import type { BackendEnv } from "akanjs/base";
|
|
2
1
|
import type { FetchProxy } from "akanjs/fetch";
|
|
3
2
|
import type { AkanLib } from "akanjs/server";
|
|
4
|
-
import { type TestServerOptions } from "./testServer.d.ts";
|
|
3
|
+
import { type TestEnv, type TestServerOptions } from "./testServer.d.ts";
|
|
5
4
|
export interface SignalTestTarget {
|
|
6
5
|
type: "app" | "lib";
|
|
7
6
|
name: string;
|
|
8
|
-
env:
|
|
7
|
+
env: TestEnv;
|
|
9
8
|
fetch: FetchProxy;
|
|
10
9
|
libs: AkanLib[];
|
|
11
10
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { BackendEnv } from "akanjs/base";
|
|
1
|
+
import type { BackendEnv, BaseEnv } from "akanjs/base";
|
|
2
2
|
import { type AkanLib } from "akanjs/server";
|
|
3
3
|
type TestDatabaseMode = "memory" | "tempFile";
|
|
4
|
+
/** `BackendEnv` carries server options only, so a harness that also stamps `process.env` needs the identity beside it. */
|
|
5
|
+
export type TestEnv = BaseEnv & BackendEnv;
|
|
4
6
|
export interface TestServerOptions {
|
|
5
7
|
workerId?: number;
|
|
6
8
|
port?: number;
|
|
@@ -12,13 +14,13 @@ export interface TestServerOptions {
|
|
|
12
14
|
export declare class TestServer {
|
|
13
15
|
#private;
|
|
14
16
|
workerId: number;
|
|
15
|
-
static initClient(env:
|
|
16
|
-
static applyProcessEnv(env:
|
|
17
|
+
static initClient(env: BaseEnv, workerId?: number): void;
|
|
18
|
+
static applyProcessEnv(env: BaseEnv, { workerId, port, serverMode, }?: {
|
|
17
19
|
workerId?: number;
|
|
18
20
|
port?: number;
|
|
19
21
|
serverMode?: "federation" | "batch" | "all";
|
|
20
22
|
}): void;
|
|
21
|
-
constructor(env:
|
|
23
|
+
constructor(env: TestEnv, libs: AkanLib | AkanLib[], options?: TestServerOptions);
|
|
22
24
|
init(): Promise<void>;
|
|
23
25
|
cleanup(): Promise<void>;
|
|
24
26
|
terminate(): Promise<void>;
|