@zhixuan92/multi-model-agent 3.0.0
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 +21 -0
- package/README.md +217 -0
- package/dist/cli/index.d.ts +61 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +252 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install-skill.d.ts +158 -0
- package/dist/cli/install-skill.d.ts.map +1 -0
- package/dist/cli/install-skill.js +425 -0
- package/dist/cli/install-skill.js.map +1 -0
- package/dist/cli/print-token.d.ts +18 -0
- package/dist/cli/print-token.d.ts.map +1 -0
- package/dist/cli/print-token.js +60 -0
- package/dist/cli/print-token.js.map +1 -0
- package/dist/cli/serve.d.ts +44 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +61 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/status.d.ts +49 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +155 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/http/async-dispatch.d.ts +32 -0
- package/dist/http/async-dispatch.d.ts.map +1 -0
- package/dist/http/async-dispatch.js +53 -0
- package/dist/http/async-dispatch.js.map +1 -0
- package/dist/http/auth.d.ts +26 -0
- package/dist/http/auth.d.ts.map +1 -0
- package/dist/http/auth.js +64 -0
- package/dist/http/auth.js.map +1 -0
- package/dist/http/cwd-validator.d.ts +11 -0
- package/dist/http/cwd-validator.d.ts.map +1 -0
- package/dist/http/cwd-validator.js +115 -0
- package/dist/http/cwd-validator.js.map +1 -0
- package/dist/http/errors.d.ts +4 -0
- package/dist/http/errors.d.ts.map +1 -0
- package/dist/http/errors.js +9 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/http/execution-context.d.ts +15 -0
- package/dist/http/execution-context.d.ts.map +1 -0
- package/dist/http/execution-context.js +35 -0
- package/dist/http/execution-context.js.map +1 -0
- package/dist/http/handler-deps.d.ts +16 -0
- package/dist/http/handler-deps.d.ts.map +1 -0
- package/dist/http/handler-deps.js +2 -0
- package/dist/http/handler-deps.js.map +1 -0
- package/dist/http/handlers/control/batch.d.ts +24 -0
- package/dist/http/handlers/control/batch.d.ts.map +1 -0
- package/dist/http/handlers/control/batch.js +81 -0
- package/dist/http/handlers/control/batch.js.map +1 -0
- package/dist/http/handlers/control/clarifications.d.ts +19 -0
- package/dist/http/handlers/control/clarifications.d.ts.map +1 -0
- package/dist/http/handlers/control/clarifications.js +58 -0
- package/dist/http/handlers/control/clarifications.js.map +1 -0
- package/dist/http/handlers/control/context-blocks.d.ts +22 -0
- package/dist/http/handlers/control/context-blocks.d.ts.map +1 -0
- package/dist/http/handlers/control/context-blocks.js +88 -0
- package/dist/http/handlers/control/context-blocks.js.map +1 -0
- package/dist/http/handlers/introspection/health.d.ts +13 -0
- package/dist/http/handlers/introspection/health.d.ts.map +1 -0
- package/dist/http/handlers/introspection/health.js +17 -0
- package/dist/http/handlers/introspection/health.js.map +1 -0
- package/dist/http/handlers/introspection/status.d.ts +26 -0
- package/dist/http/handlers/introspection/status.d.ts.map +1 -0
- package/dist/http/handlers/introspection/status.js +136 -0
- package/dist/http/handlers/introspection/status.js.map +1 -0
- package/dist/http/handlers/introspection/tools-list.d.ts +9 -0
- package/dist/http/handlers/introspection/tools-list.d.ts.map +1 -0
- package/dist/http/handlers/introspection/tools-list.js +28 -0
- package/dist/http/handlers/introspection/tools-list.js.map +1 -0
- package/dist/http/handlers/tools/audit.d.ts +4 -0
- package/dist/http/handlers/tools/audit.d.ts.map +1 -0
- package/dist/http/handlers/tools/audit.js +39 -0
- package/dist/http/handlers/tools/audit.js.map +1 -0
- package/dist/http/handlers/tools/debug.d.ts +4 -0
- package/dist/http/handlers/tools/debug.d.ts.map +1 -0
- package/dist/http/handlers/tools/debug.js +39 -0
- package/dist/http/handlers/tools/debug.js.map +1 -0
- package/dist/http/handlers/tools/delegate.d.ts +4 -0
- package/dist/http/handlers/tools/delegate.d.ts.map +1 -0
- package/dist/http/handlers/tools/delegate.js +57 -0
- package/dist/http/handlers/tools/delegate.js.map +1 -0
- package/dist/http/handlers/tools/execute-plan.d.ts +4 -0
- package/dist/http/handlers/tools/execute-plan.d.ts.map +1 -0
- package/dist/http/handlers/tools/execute-plan.js +39 -0
- package/dist/http/handlers/tools/execute-plan.js.map +1 -0
- package/dist/http/handlers/tools/retry.d.ts +4 -0
- package/dist/http/handlers/tools/retry.d.ts.map +1 -0
- package/dist/http/handlers/tools/retry.js +52 -0
- package/dist/http/handlers/tools/retry.js.map +1 -0
- package/dist/http/handlers/tools/review.d.ts +4 -0
- package/dist/http/handlers/tools/review.d.ts.map +1 -0
- package/dist/http/handlers/tools/review.js +39 -0
- package/dist/http/handlers/tools/review.js.map +1 -0
- package/dist/http/handlers/tools/verify.d.ts +4 -0
- package/dist/http/handlers/tools/verify.d.ts.map +1 -0
- package/dist/http/handlers/tools/verify.js +39 -0
- package/dist/http/handlers/tools/verify.js.map +1 -0
- package/dist/http/loopback.d.ts +17 -0
- package/dist/http/loopback.d.ts.map +1 -0
- package/dist/http/loopback.js +43 -0
- package/dist/http/loopback.js.map +1 -0
- package/dist/http/middleware/body-reader.d.ts +16 -0
- package/dist/http/middleware/body-reader.d.ts.map +1 -0
- package/dist/http/middleware/body-reader.js +44 -0
- package/dist/http/middleware/body-reader.js.map +1 -0
- package/dist/http/project-registry.d.ts +54 -0
- package/dist/http/project-registry.d.ts.map +1 -0
- package/dist/http/project-registry.js +132 -0
- package/dist/http/project-registry.js.map +1 -0
- package/dist/http/router.d.ts +14 -0
- package/dist/http/router.d.ts.map +1 -0
- package/dist/http/router.js +41 -0
- package/dist/http/router.js.map +1 -0
- package/dist/http/server.d.ts +16 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +235 -0
- package/dist/http/server.js.map +1 -0
- package/dist/http/types.d.ts +9 -0
- package/dist/http/types.d.ts.map +1 -0
- package/dist/http/types.js +2 -0
- package/dist/http/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/install/claude-code.d.ts +43 -0
- package/dist/install/claude-code.d.ts.map +1 -0
- package/dist/install/claude-code.js +65 -0
- package/dist/install/claude-code.js.map +1 -0
- package/dist/install/codex-cli.d.ts +39 -0
- package/dist/install/codex-cli.d.ts.map +1 -0
- package/dist/install/codex-cli.js +318 -0
- package/dist/install/codex-cli.js.map +1 -0
- package/dist/install/cursor.d.ts +72 -0
- package/dist/install/cursor.d.ts.map +1 -0
- package/dist/install/cursor.js +81 -0
- package/dist/install/cursor.js.map +1 -0
- package/dist/install/gemini-cli.d.ts +66 -0
- package/dist/install/gemini-cli.d.ts.map +1 -0
- package/dist/install/gemini-cli.js +111 -0
- package/dist/install/gemini-cli.js.map +1 -0
- package/dist/install/include-utils.d.ts +27 -0
- package/dist/install/include-utils.d.ts.map +1 -0
- package/dist/install/include-utils.js +90 -0
- package/dist/install/include-utils.js.map +1 -0
- package/dist/install/manifest.d.ts +90 -0
- package/dist/install/manifest.d.ts.map +1 -0
- package/dist/install/manifest.js +200 -0
- package/dist/install/manifest.js.map +1 -0
- package/dist/openapi.d.ts +15 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +314 -0
- package/dist/openapi.js.map +1 -0
- package/dist/skills/_shared/auth.md +32 -0
- package/dist/skills/_shared/error-handling.md +31 -0
- package/dist/skills/_shared/polling.md +40 -0
- package/dist/skills/_shared/response-shape.md +46 -0
- package/dist/skills/mma-audit/SKILL.md +55 -0
- package/dist/skills/mma-clarifications/SKILL.md +68 -0
- package/dist/skills/mma-context-blocks/SKILL.md +69 -0
- package/dist/skills/mma-debug/SKILL.md +59 -0
- package/dist/skills/mma-delegate/SKILL.md +63 -0
- package/dist/skills/mma-execute-plan/SKILL.md +63 -0
- package/dist/skills/mma-retry/SKILL.md +54 -0
- package/dist/skills/mma-review/SKILL.md +55 -0
- package/dist/skills/mma-verify/SKILL.md +57 -0
- package/dist/skills/multi-model-agent/SKILL.md +55 -0
- package/package.json +60 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as verify from '@zhixuan92/multi-model-agent-core/tool-schemas/verify';
|
|
2
|
+
import { executeVerify } from '@zhixuan92/multi-model-agent-core/executors/verify';
|
|
3
|
+
import { sendError, sendJson } from '../../errors.js';
|
|
4
|
+
import { asyncDispatch } from '../../async-dispatch.js';
|
|
5
|
+
export function buildVerifyHandler(deps) {
|
|
6
|
+
return async (_req, res, _params, ctx) => {
|
|
7
|
+
const parsed = verify.inputSchema.safeParse(ctx.body);
|
|
8
|
+
if (!parsed.success) {
|
|
9
|
+
sendError(res, 400, 'invalid_request', 'Request body validation failed', {
|
|
10
|
+
fieldErrors: parsed.error.flatten(),
|
|
11
|
+
});
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const input = parsed.data;
|
|
15
|
+
const cwd = ctx.cwd;
|
|
16
|
+
const reserveResult = deps.projectRegistry.reserveProject(cwd);
|
|
17
|
+
if (!reserveResult.ok) {
|
|
18
|
+
sendError(res, 503, reserveResult.error, reserveResult.message);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const pc = reserveResult.projectContext;
|
|
22
|
+
pc.lastActivityAt = Date.now();
|
|
23
|
+
deps.projectRegistry.cancelReservation(cwd);
|
|
24
|
+
const blockIds = input.contextBlockIds ?? [];
|
|
25
|
+
const { batchId, statusUrl } = asyncDispatch({
|
|
26
|
+
tool: 'verify',
|
|
27
|
+
projectCwd: cwd,
|
|
28
|
+
blockIds,
|
|
29
|
+
batchRegistry: deps.batchRegistry,
|
|
30
|
+
projectContext: pc,
|
|
31
|
+
deps,
|
|
32
|
+
executor: async (executionCtx) => {
|
|
33
|
+
return executeVerify(executionCtx, input);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
sendJson(res, 202, { batchId, statusUrl });
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../../src/http/handlers/tools/verify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,uDAAuD,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,oDAAoD,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAIxD,MAAM,UAAU,kBAAkB,CAAC,IAAiB;IAClD,OAAO,KAAK,EAAE,IAAqB,EAAE,GAAmB,EAAE,OAA+B,EAAE,GAAG,EAAE,EAAE;QAChG,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,iBAAiB,EAAE,gCAAgC,EAAE;gBACvE,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;aACpC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC;QAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,GAAI,CAAC;QAErB,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAC/D,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC;YACtB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;YAChE,OAAO;QACT,CAAC;QACD,MAAM,EAAE,GAAG,aAAa,CAAC,cAAc,CAAC;QACxC,EAAE,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;QAE5C,MAAM,QAAQ,GAAG,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC;QAC7C,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC;YAC3C,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,GAAG;YACf,QAAQ;YACR,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,cAAc,EAAE,EAAE;YAClB,IAAI;YACJ,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE;gBAC/B,OAAO,aAAa,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YAC5C,CAAC;SACF,CAAC,CAAC;QAEH,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true iff the given address string refers to the loopback interface.
|
|
3
|
+
* Accepts all forms Node's socket layer surfaces:
|
|
4
|
+
* - IPv4 loopback (any 127.0.0.0/8 address): 127.0.0.1, 127.0.1.1, etc.
|
|
5
|
+
* - IPv6 loopback: ::1
|
|
6
|
+
* - IPv4-mapped IPv6 loopback: ::ffff:127.0.0.1 (and other 127/8 mapped forms)
|
|
7
|
+
* - Hostname: localhost
|
|
8
|
+
*/
|
|
9
|
+
export declare function isLoopbackAddress(addr: string | undefined): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Returns true iff the request should be rejected because it came from a
|
|
12
|
+
* non-loopback address on a loopback-only endpoint.
|
|
13
|
+
*
|
|
14
|
+
* @param remoteAddress The socket remote address (req.socket?.remoteAddress)
|
|
15
|
+
*/
|
|
16
|
+
export declare function shouldRejectNonLoopback(remoteAddress: string | undefined): boolean;
|
|
17
|
+
//# sourceMappingURL=loopback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback.d.ts","sourceRoot":"","sources":["../../src/http/loopback.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAoBnE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAElF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as net from 'node:net';
|
|
2
|
+
/**
|
|
3
|
+
* Returns true iff the given address string refers to the loopback interface.
|
|
4
|
+
* Accepts all forms Node's socket layer surfaces:
|
|
5
|
+
* - IPv4 loopback (any 127.0.0.0/8 address): 127.0.0.1, 127.0.1.1, etc.
|
|
6
|
+
* - IPv6 loopback: ::1
|
|
7
|
+
* - IPv4-mapped IPv6 loopback: ::ffff:127.0.0.1 (and other 127/8 mapped forms)
|
|
8
|
+
* - Hostname: localhost
|
|
9
|
+
*/
|
|
10
|
+
export function isLoopbackAddress(addr) {
|
|
11
|
+
if (!addr)
|
|
12
|
+
return false;
|
|
13
|
+
if (addr === 'localhost')
|
|
14
|
+
return true;
|
|
15
|
+
// Strip any scope ID (fe80::1%lo0 style) — rare for loopback but cheap to handle.
|
|
16
|
+
const clean = addr.split('%')[0];
|
|
17
|
+
const kind = net.isIP(clean);
|
|
18
|
+
if (kind === 4) {
|
|
19
|
+
return clean.startsWith('127.');
|
|
20
|
+
}
|
|
21
|
+
if (kind === 6) {
|
|
22
|
+
if (clean === '::1')
|
|
23
|
+
return true;
|
|
24
|
+
// IPv4-mapped IPv6: ::ffff:A.B.C.D
|
|
25
|
+
const mappedPrefix = '::ffff:';
|
|
26
|
+
if (clean.toLowerCase().startsWith(mappedPrefix)) {
|
|
27
|
+
const v4 = clean.slice(mappedPrefix.length);
|
|
28
|
+
return net.isIPv4(v4) && v4.startsWith('127.');
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns true iff the request should be rejected because it came from a
|
|
36
|
+
* non-loopback address on a loopback-only endpoint.
|
|
37
|
+
*
|
|
38
|
+
* @param remoteAddress The socket remote address (req.socket?.remoteAddress)
|
|
39
|
+
*/
|
|
40
|
+
export function shouldRejectNonLoopback(remoteAddress) {
|
|
41
|
+
return !isLoopbackAddress(remoteAddress);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=loopback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../src/http/loopback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAwB;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACtC,kFAAkF;IAClF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACf,OAAO,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACf,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;QACjC,mCAAmC;QACnC,MAAM,YAAY,GAAG,SAAS,CAAC;QAC/B,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACjD,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC5C,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,aAAiC;IACvE,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
export type BodyReadResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
body: Buffer;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
reason: 'too_large';
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Reads the request body up to `maxBytes`.
|
|
11
|
+
* If the body exceeds `maxBytes`, drains remaining data and resolves with
|
|
12
|
+
* { ok: false, reason: 'too_large' } so the server can still send a 413 response
|
|
13
|
+
* before closing the connection.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readBody(req: IncomingMessage, maxBytes: number): Promise<BodyReadResult>;
|
|
16
|
+
//# sourceMappingURL=body-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-reader.d.ts","sourceRoot":"","sources":["../../../src/http/middleware/body-reader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAuCxF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the request body up to `maxBytes`.
|
|
3
|
+
* If the body exceeds `maxBytes`, drains remaining data and resolves with
|
|
4
|
+
* { ok: false, reason: 'too_large' } so the server can still send a 413 response
|
|
5
|
+
* before closing the connection.
|
|
6
|
+
*/
|
|
7
|
+
export function readBody(req, maxBytes) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const chunks = [];
|
|
10
|
+
let size = 0;
|
|
11
|
+
let settled = false;
|
|
12
|
+
let overflow = false;
|
|
13
|
+
function settle(result) {
|
|
14
|
+
if (settled)
|
|
15
|
+
return;
|
|
16
|
+
settled = true;
|
|
17
|
+
resolve(result);
|
|
18
|
+
}
|
|
19
|
+
req.on('data', (chunk) => {
|
|
20
|
+
if (overflow) {
|
|
21
|
+
// Drain remaining data silently after overflow is detected
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
size += chunk.length;
|
|
25
|
+
if (size > maxBytes) {
|
|
26
|
+
overflow = true;
|
|
27
|
+
// Don't push this chunk; signal overflow and continue draining
|
|
28
|
+
settle({ ok: false, reason: 'too_large' });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
chunks.push(chunk);
|
|
32
|
+
});
|
|
33
|
+
req.on('end', () => {
|
|
34
|
+
if (!overflow) {
|
|
35
|
+
settle({ ok: true, body: Buffer.concat(chunks) });
|
|
36
|
+
}
|
|
37
|
+
// If overflow, already settled — no-op
|
|
38
|
+
});
|
|
39
|
+
req.on('error', () => {
|
|
40
|
+
settle({ ok: false, reason: 'too_large' });
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=body-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-reader.js","sourceRoot":"","sources":["../../../src/http/middleware/body-reader.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAoB,EAAE,QAAgB;IAC7D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,SAAS,MAAM,CAAC,MAAsB;YACpC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC;QAED,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,IAAI,QAAQ,EAAE,CAAC;gBACb,2DAA2D;gBAC3D,OAAO;YACT,CAAC;YACD,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;YACrB,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;gBACpB,QAAQ,GAAG,IAAI,CAAC;gBAChB,+DAA+D;gBAC/D,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,uCAAuC;QACzC,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ProjectContext, BatchRegistry } from '@zhixuan92/multi-model-agent-core';
|
|
2
|
+
export type ReserveError = 'project_cap' | 'invalid_cwd' | 'missing_cwd' | 'cwd_not_dir' | 'forbidden_cwd';
|
|
3
|
+
export type ReserveResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
projectContext: ProjectContext;
|
|
6
|
+
created: boolean;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
error: ReserveError;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
export interface ProjectRegistryOptions {
|
|
13
|
+
cap: number;
|
|
14
|
+
idleEvictionMs: number;
|
|
15
|
+
evictionIntervalMs: number;
|
|
16
|
+
onProjectCreated?: (cwd: string) => void;
|
|
17
|
+
onProjectEvicted?: (cwd: string, idleMs: number) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare class ProjectRegistry {
|
|
20
|
+
private readonly map;
|
|
21
|
+
private readonly cap;
|
|
22
|
+
private readonly idleEvictionMs;
|
|
23
|
+
private readonly evictionIntervalMs;
|
|
24
|
+
private evictionTimer;
|
|
25
|
+
private readonly onProjectCreated?;
|
|
26
|
+
private readonly onProjectEvicted?;
|
|
27
|
+
constructor(options: ProjectRegistryOptions);
|
|
28
|
+
startEvictionTimer(): void;
|
|
29
|
+
stopEvictionTimer(): void;
|
|
30
|
+
/** Synchronous lookup-or-create with cap enforcement. Increments pendingReservations on success. */
|
|
31
|
+
reserveProject(cwd: string): ReserveResult;
|
|
32
|
+
/**
|
|
33
|
+
* Called from onsessioninitialized. Decrements pendingReservations and registers the session.
|
|
34
|
+
* `canonicalCwd` MUST be the `.cwd` value returned from a prior `reserveProject` call.
|
|
35
|
+
*/
|
|
36
|
+
attachSession(canonicalCwd: string, sessionId: string): void;
|
|
37
|
+
/** Detach a session. `canonicalCwd` must match `projectContext.cwd`. No-op if unknown. */
|
|
38
|
+
detachSession(canonicalCwd: string, sessionId: string): void;
|
|
39
|
+
/** Called if attachSession never fires. `canonicalCwd` must match `projectContext.cwd`. */
|
|
40
|
+
cancelReservation(canonicalCwd: string): void;
|
|
41
|
+
/** Look up a project by its canonical cwd. */
|
|
42
|
+
get(canonicalCwd: string): ProjectContext | undefined;
|
|
43
|
+
get size(): number;
|
|
44
|
+
entries(): IterableIterator<[string, ProjectContext]>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if the project context is idle and eligible for eviction.
|
|
47
|
+
* Gates on: no active HTTP requests, no active batches in the registry,
|
|
48
|
+
* no active sessions, no pending reservations, and idle for at least idleTimeoutMs.
|
|
49
|
+
*/
|
|
50
|
+
isIdleFor(pc: ProjectContext, now: number, idleTimeoutMs: number, batchRegistry: BatchRegistry): boolean;
|
|
51
|
+
evictIdle(batchRegistry?: BatchRegistry): void;
|
|
52
|
+
clear(): void;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=project-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-registry.d.ts","sourceRoot":"","sources":["../../src/http/project-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,cAAc,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAG7G,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,aAAa,GAAG,aAAa,GAAG,aAAa,GAAG,eAAe,CAAC;AAE3G,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAwB;IAC1D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAwC;gBAE9D,OAAO,EAAE,sBAAsB;IAQ3C,kBAAkB,IAAI,IAAI;IAM1B,iBAAiB,IAAI,IAAI;IAKzB,oGAAoG;IACpG,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAoB1C;;;OAGG;IACH,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAQ5D,0FAA0F;IAC1F,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAO5D,2FAA2F;IAC3F,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAO7C,8CAA8C;IAC9C,GAAG,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIrD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEA,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAItD;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,GAAG,OAAO;IAUxG,SAAS,CAAC,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI;IAwB9C,KAAK,IAAI,IAAI;CAQd"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { createProjectContext } from '@zhixuan92/multi-model-agent-core';
|
|
2
|
+
import { validateCwd } from './cwd-validator.js';
|
|
3
|
+
export class ProjectRegistry {
|
|
4
|
+
map = new Map();
|
|
5
|
+
cap;
|
|
6
|
+
idleEvictionMs;
|
|
7
|
+
evictionIntervalMs;
|
|
8
|
+
evictionTimer = null;
|
|
9
|
+
onProjectCreated;
|
|
10
|
+
onProjectEvicted;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.cap = options.cap;
|
|
13
|
+
this.idleEvictionMs = options.idleEvictionMs;
|
|
14
|
+
this.evictionIntervalMs = options.evictionIntervalMs;
|
|
15
|
+
this.onProjectCreated = options.onProjectCreated;
|
|
16
|
+
this.onProjectEvicted = options.onProjectEvicted;
|
|
17
|
+
}
|
|
18
|
+
startEvictionTimer() {
|
|
19
|
+
if (this.evictionTimer)
|
|
20
|
+
return;
|
|
21
|
+
this.evictionTimer = setInterval(() => this.evictIdle(), this.evictionIntervalMs);
|
|
22
|
+
this.evictionTimer.unref?.();
|
|
23
|
+
}
|
|
24
|
+
stopEvictionTimer() {
|
|
25
|
+
if (this.evictionTimer)
|
|
26
|
+
clearInterval(this.evictionTimer);
|
|
27
|
+
this.evictionTimer = null;
|
|
28
|
+
}
|
|
29
|
+
/** Synchronous lookup-or-create with cap enforcement. Increments pendingReservations on success. */
|
|
30
|
+
reserveProject(cwd) {
|
|
31
|
+
const v = validateCwd(cwd);
|
|
32
|
+
if (!v.ok)
|
|
33
|
+
return { ok: false, error: v.error, message: v.message };
|
|
34
|
+
const key = v.canonicalCwd;
|
|
35
|
+
const existing = this.map.get(key);
|
|
36
|
+
if (existing) {
|
|
37
|
+
existing.pendingReservations += 1;
|
|
38
|
+
existing.lastActivityAt = Date.now();
|
|
39
|
+
return { ok: true, projectContext: existing, created: false };
|
|
40
|
+
}
|
|
41
|
+
if (this.map.size >= this.cap) {
|
|
42
|
+
return { ok: false, error: 'project_cap', message: `server at ${this.cap} projects; close some connections and retry` };
|
|
43
|
+
}
|
|
44
|
+
const pc = createProjectContext(key);
|
|
45
|
+
pc.pendingReservations = 1;
|
|
46
|
+
this.map.set(key, pc);
|
|
47
|
+
this.onProjectCreated?.(key);
|
|
48
|
+
return { ok: true, projectContext: pc, created: true };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Called from onsessioninitialized. Decrements pendingReservations and registers the session.
|
|
52
|
+
* `canonicalCwd` MUST be the `.cwd` value returned from a prior `reserveProject` call.
|
|
53
|
+
*/
|
|
54
|
+
attachSession(canonicalCwd, sessionId) {
|
|
55
|
+
const pc = this.map.get(canonicalCwd);
|
|
56
|
+
if (!pc)
|
|
57
|
+
throw new Error(`attachSession: no project for ${canonicalCwd} (did you pass the raw cwd instead of projectContext.cwd?)`);
|
|
58
|
+
pc.activeSessions.add(sessionId);
|
|
59
|
+
if (pc.pendingReservations > 0)
|
|
60
|
+
pc.pendingReservations -= 1;
|
|
61
|
+
pc.lastActivityAt = Date.now();
|
|
62
|
+
}
|
|
63
|
+
/** Detach a session. `canonicalCwd` must match `projectContext.cwd`. No-op if unknown. */
|
|
64
|
+
detachSession(canonicalCwd, sessionId) {
|
|
65
|
+
const pc = this.map.get(canonicalCwd);
|
|
66
|
+
if (!pc)
|
|
67
|
+
return;
|
|
68
|
+
pc.activeSessions.delete(sessionId);
|
|
69
|
+
pc.lastActivityAt = Date.now();
|
|
70
|
+
}
|
|
71
|
+
/** Called if attachSession never fires. `canonicalCwd` must match `projectContext.cwd`. */
|
|
72
|
+
cancelReservation(canonicalCwd) {
|
|
73
|
+
const pc = this.map.get(canonicalCwd);
|
|
74
|
+
if (!pc)
|
|
75
|
+
return;
|
|
76
|
+
if (pc.pendingReservations > 0)
|
|
77
|
+
pc.pendingReservations -= 1;
|
|
78
|
+
pc.lastActivityAt = Date.now();
|
|
79
|
+
}
|
|
80
|
+
/** Look up a project by its canonical cwd. */
|
|
81
|
+
get(canonicalCwd) {
|
|
82
|
+
return this.map.get(canonicalCwd);
|
|
83
|
+
}
|
|
84
|
+
get size() {
|
|
85
|
+
return this.map.size;
|
|
86
|
+
}
|
|
87
|
+
*entries() {
|
|
88
|
+
yield* this.map.entries();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Returns true if the project context is idle and eligible for eviction.
|
|
92
|
+
* Gates on: no active HTTP requests, no active batches in the registry,
|
|
93
|
+
* no active sessions, no pending reservations, and idle for at least idleTimeoutMs.
|
|
94
|
+
*/
|
|
95
|
+
isIdleFor(pc, now, idleTimeoutMs, batchRegistry) {
|
|
96
|
+
return (pc.activeRequests === 0 &&
|
|
97
|
+
batchRegistry.countActiveForProject(pc.cwd) === 0 &&
|
|
98
|
+
pc.activeSessions.size === 0 &&
|
|
99
|
+
pc.pendingReservations === 0 &&
|
|
100
|
+
(now - pc.lastActivityAt) > idleTimeoutMs);
|
|
101
|
+
}
|
|
102
|
+
evictIdle(batchRegistry) {
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
const victims = [];
|
|
105
|
+
for (const [cwd, pc] of this.map.entries()) {
|
|
106
|
+
if (pc.activeSessions.size === 0 &&
|
|
107
|
+
pc.activeRequests === 0 &&
|
|
108
|
+
pc.pendingReservations === 0 &&
|
|
109
|
+
(batchRegistry === undefined || batchRegistry.countActiveForProject(cwd) === 0) &&
|
|
110
|
+
now - pc.lastActivityAt > this.idleEvictionMs) {
|
|
111
|
+
victims.push(cwd);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const cwd of victims) {
|
|
115
|
+
const pc = this.map.get(cwd);
|
|
116
|
+
pc.contextBlocks.clear();
|
|
117
|
+
pc.batchCache.clear();
|
|
118
|
+
pc.clarifications.clear();
|
|
119
|
+
this.map.delete(cwd);
|
|
120
|
+
this.onProjectEvicted?.(cwd, now - pc.lastActivityAt);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
clear() {
|
|
124
|
+
for (const pc of this.map.values()) {
|
|
125
|
+
pc.contextBlocks.clear();
|
|
126
|
+
pc.batchCache.clear();
|
|
127
|
+
pc.clarifications.clear();
|
|
128
|
+
}
|
|
129
|
+
this.map.clear();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=project-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-registry.js","sourceRoot":"","sources":["../../src/http/project-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { RequestContext } from './types.js';
|
|
3
|
+
export type RawHandler = (req: IncomingMessage, res: ServerResponse, params: Record<string, string>, ctx: RequestContext) => Promise<void> | void;
|
|
4
|
+
export declare class Router {
|
|
5
|
+
private routes;
|
|
6
|
+
register(method: string, path: string, handler: RawHandler): void;
|
|
7
|
+
match(method: string, url: string): {
|
|
8
|
+
handler: RawHandler;
|
|
9
|
+
params: Record<string, string>;
|
|
10
|
+
} | null;
|
|
11
|
+
/** Returns all HTTP methods registered for routes that match the given url pathname. */
|
|
12
|
+
methodsFor(url: string): string[];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/http/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAQlJ,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAA8C;IAE5D,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAWjE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI;IAalG,wFAAwF;IACxF,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;CAalC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class Router {
|
|
2
|
+
routes = new Map();
|
|
3
|
+
register(method, path, handler) {
|
|
4
|
+
const paramNames = [];
|
|
5
|
+
const regexStr = path.replace(/:(\w+)/g, (_, name) => {
|
|
6
|
+
paramNames.push(name);
|
|
7
|
+
return '([^/]+)';
|
|
8
|
+
});
|
|
9
|
+
const regex = new RegExp('^' + regexStr + '$');
|
|
10
|
+
if (!this.routes.has(method))
|
|
11
|
+
this.routes.set(method, new Map());
|
|
12
|
+
this.routes.get(method).set(path, { handler, paramNames, regex });
|
|
13
|
+
}
|
|
14
|
+
match(method, url) {
|
|
15
|
+
const pathname = url.split('?')[0];
|
|
16
|
+
for (const [, entry] of this.routes.get(method) ?? new Map()) {
|
|
17
|
+
const m = pathname.match(entry.regex);
|
|
18
|
+
if (m) {
|
|
19
|
+
const params = {};
|
|
20
|
+
entry.paramNames.forEach((n, i) => { params[n] = m[i + 1]; });
|
|
21
|
+
return { handler: entry.handler, params };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/** Returns all HTTP methods registered for routes that match the given url pathname. */
|
|
27
|
+
methodsFor(url) {
|
|
28
|
+
const pathname = url.split('?')[0];
|
|
29
|
+
const methods = [];
|
|
30
|
+
for (const [method, entries] of this.routes) {
|
|
31
|
+
for (const [, entry] of entries) {
|
|
32
|
+
if (entry.regex.test(pathname)) {
|
|
33
|
+
methods.push(method);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return methods;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/http/router.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,MAAM;IACT,MAAM,GAAG,IAAI,GAAG,EAAmC,CAAC;IAE5D,QAAQ,CAAC,MAAc,EAAE,IAAY,EAAE,OAAmB;QACxD,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,IAAY,EAAE,EAAE;YAC3D,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACjE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAc,EAAE,GAAW;QAC/B,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAsB,EAAE,CAAC;YACjF,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtC,IAAI,CAAC,EAAE,CAAC;gBACN,MAAM,MAAM,GAA2B,EAAE,CAAC;gBAC1C,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YAC5C,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wFAAwF;IACxF,UAAU,CAAC,GAAW;QACpB,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5C,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;gBAChC,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC/B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBACrB,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ServerConfig, BatchRegistry } from '@zhixuan92/multi-model-agent-core';
|
|
2
|
+
import type { ProjectRegistry } from './project-registry.js';
|
|
3
|
+
export interface RunningServer {
|
|
4
|
+
port: number;
|
|
5
|
+
/** The resolved address the server is bound to. Used by CLI to log the actual listen address. */
|
|
6
|
+
serverAddress: string | null;
|
|
7
|
+
stop(): Promise<void>;
|
|
8
|
+
/** Shared BatchRegistry — exposed for testing and introspection handlers. */
|
|
9
|
+
batchRegistry: BatchRegistry;
|
|
10
|
+
/** Shared ProjectRegistry — exposed for testing and introspection handlers. */
|
|
11
|
+
projectRegistry: ProjectRegistry;
|
|
12
|
+
/** Wall-clock ms when the server finished starting (Date.now()). Used for uptimeMs in /status. */
|
|
13
|
+
serverStartedAt: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function startServer(config: ServerConfig): Promise<RunningServer>;
|
|
16
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAQrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAiB7D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,6EAA6E;IAC7E,aAAa,EAAE,aAAa,CAAC;IAC7B,+EAA+E;IAC/E,eAAe,EAAE,eAAe,CAAC;IACjC,kGAAkG;IAClG,eAAe,EAAE,MAAM,CAAC;CACzB;AA4FD,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAmE9E"}
|