@rivus/gateway 0.16.6 → 0.18.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 +674 -21
- package/README.md +25 -0
- package/dist/bootstrap/herdr-feishu.d.ts +37 -0
- package/dist/bootstrap/herdr-feishu.js +853 -0
- package/dist/bootstrap/pi-feishu.d.ts +281 -16
- package/dist/bootstrap/pi-feishu.js +17315 -345
- package/dist/chunks/background-session-authority.js +2 -38
- package/dist/chunks/rivus-daemon-cli.d.ts +1115 -0
- package/dist/chunks/rivus-node-entrypoint.js +735 -847
- package/dist/chunks/{node-rivus-deployment-manifest.js → runtime-cache.js} +1118 -1476
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +1 -0
- package/dist/index.d.ts +65 -3
- package/dist/index.js +3 -1215
- package/dist/mcp.d.ts +7 -63
- package/dist/mcp.js +3 -250
- package/package.json +21 -13
- package/templates/a-share-briefing-analysis.mjs +3 -1
- package/templates/a-share-briefing-renderer.mjs +15 -5
- package/templates/a-share-index-evidence.mjs +9 -3
- package/templates/a-share-market-briefing.mjs +3 -1
- package/templates/a-share-overseas-evidence.mjs +12 -4
- package/templates/a-share-policy-evidence.mjs +30 -10
- package/templates/a-share-sector-evidence.mjs +3 -1
- package/templates/acp-stdio-proxy.mjs +10 -4
- package/templates/current-weather.mjs +36 -12
- package/templates/herdr-feishu.mjs +30 -0
- package/templates/html-drive-tools.mjs +36 -12
- package/templates/pi-feishu-deployment.bootstrap.ts +1 -3
- package/templates/pi-feishu.bootstrap.ts +1 -242
- package/templates/rivus-agents.plugin.mjs +3 -1
- package/dist/chunks/background-session-control-input.js +0 -45
- package/dist/chunks/background-session-service.d.ts +0 -390
- package/dist/chunks/index.d.ts +0 -4703
- package/dist/chunks/service.js +0 -12112
- package/templates/langfuse-drive-e2e.mjs +0 -175
package/dist/mcp.d.ts
CHANGED
|
@@ -1,41 +1,18 @@
|
|
|
1
|
-
import { M as BackgroundSessionAuthority, r as BackgroundSessionService } from "./chunks/background-session-service.js";
|
|
2
1
|
import { Readable, Writable } from "node:stream";
|
|
3
|
-
|
|
4
2
|
//#region src/bootstrap/mcp/rivus-mcp-entrypoint.d.ts
|
|
5
3
|
interface RunBackgroundSessionMcpServerEnv {
|
|
6
4
|
readonly fetch?: typeof fetch;
|
|
7
5
|
readonly input?: Readable;
|
|
8
6
|
readonly output?: Writable;
|
|
9
7
|
}
|
|
10
|
-
declare function runBackgroundSessionMcpServer(options?: RunBackgroundSessionMcpServerEnv): Promise<void>;
|
|
8
|
+
export declare function runBackgroundSessionMcpServer(options?: RunBackgroundSessionMcpServerEnv): Promise<void>;
|
|
11
9
|
//#endregion
|
|
12
10
|
//#region src/core/application/background-session/control/background-session-control.d.ts
|
|
13
11
|
type BackgroundSessionControlCommand = "start" | "wait" | "list" | "status" | "send" | "stop";
|
|
14
|
-
interface BackgroundSessionControlOrigin {
|
|
15
|
-
readonly allowedActorOpenIds: ReadonlyArray<string>;
|
|
16
|
-
readonly conversationId?: string;
|
|
17
|
-
readonly endpointId: string;
|
|
18
|
-
readonly tenantKey: string;
|
|
19
|
-
}
|
|
20
|
-
interface BackgroundSessionControlContext {
|
|
21
|
-
readonly agentId: string;
|
|
22
|
-
readonly authority: BackgroundSessionAuthority;
|
|
23
|
-
readonly origin: BackgroundSessionControlOrigin;
|
|
24
|
-
readonly policyEpoch: number;
|
|
25
|
-
readonly runId: string;
|
|
26
|
-
readonly sessionKey: string;
|
|
27
|
-
readonly sourceMessageId: string;
|
|
28
|
-
}
|
|
29
|
-
interface BackgroundSessionControl {
|
|
30
|
-
handle(command: BackgroundSessionControlCommand, context: BackgroundSessionControlContext, input: unknown): Promise<unknown>;
|
|
31
|
-
}
|
|
32
|
-
declare class BackgroundSessionControlError extends Error {
|
|
33
|
-
readonly name = "BackgroundSessionControlError";
|
|
34
|
-
}
|
|
35
12
|
//#endregion
|
|
36
13
|
//#region src/adapters/mcp/background-session/background-session-mcp-server.d.ts
|
|
37
|
-
declare const BACKGROUND_SESSION_MCP_SERVER_NAME = "rivus-background-sessions";
|
|
38
|
-
declare const BACKGROUND_SESSION_MCP_SERVER_VERSION = "1.0.0";
|
|
14
|
+
export declare const BACKGROUND_SESSION_MCP_SERVER_NAME = "rivus-background-sessions";
|
|
15
|
+
export declare const BACKGROUND_SESSION_MCP_SERVER_VERSION = "1.0.0";
|
|
39
16
|
interface BackgroundSessionMcpServerOptions {
|
|
40
17
|
readonly capability: string;
|
|
41
18
|
readonly controlUrl: string;
|
|
@@ -50,43 +27,10 @@ interface BackgroundSessionMcpServerOptions {
|
|
|
50
27
|
interface BackgroundSessionMcpServer {
|
|
51
28
|
run(): Promise<void>;
|
|
52
29
|
}
|
|
53
|
-
declare class BackgroundSessionMcpError extends Error {
|
|
30
|
+
export declare class BackgroundSessionMcpError extends Error {
|
|
54
31
|
readonly name = "BackgroundSessionMcpError";
|
|
55
32
|
}
|
|
56
|
-
declare function createBackgroundSessionMcpServer(options: BackgroundSessionMcpServerOptions): BackgroundSessionMcpServer;
|
|
57
|
-
//#endregion
|
|
58
|
-
//#region src/adapters/compatibility/background-session/control.d.ts
|
|
59
|
-
declare function createBackgroundSessionControl(options: {
|
|
60
|
-
readonly service: BackgroundSessionService;
|
|
61
|
-
}): BackgroundSessionControl;
|
|
62
|
-
declare function toControlContext(input: {
|
|
63
|
-
readonly agentId: string;
|
|
64
|
-
readonly authority: BackgroundSessionAuthority;
|
|
65
|
-
readonly origin: BackgroundSessionControlOrigin;
|
|
66
|
-
readonly policyEpoch: number;
|
|
67
|
-
readonly sessionKey: string;
|
|
68
|
-
readonly sourceMessageId?: string;
|
|
69
|
-
}): BackgroundSessionControlContext;
|
|
70
|
-
//#endregion
|
|
71
|
-
//#region src/adapters/http/background-session/background-session-control-http-server.d.ts
|
|
72
|
-
interface BackgroundSessionControlHttpServerOptions {
|
|
73
|
-
readonly control: BackgroundSessionControl;
|
|
74
|
-
readonly host?: string;
|
|
75
|
-
readonly port: number;
|
|
76
|
-
readonly status?: () => unknown;
|
|
77
|
-
readonly token: string;
|
|
78
|
-
}
|
|
79
|
-
interface BackgroundSessionControlHttpServer {
|
|
80
|
-
port(): number;
|
|
81
|
-
issueCapability(context: BackgroundSessionControlContext): string;
|
|
82
|
-
close(): Promise<void>;
|
|
83
|
-
start(): Promise<void>;
|
|
84
|
-
}
|
|
85
|
-
declare class BackgroundSessionControlHttpError extends Error {
|
|
86
|
-
readonly statusCode: number;
|
|
87
|
-
readonly name = "BackgroundSessionControlHttpError";
|
|
88
|
-
constructor(statusCode: number, message: string);
|
|
89
|
-
}
|
|
90
|
-
declare function createBackgroundSessionControlHttpServer(options: BackgroundSessionControlHttpServerOptions): BackgroundSessionControlHttpServer;
|
|
33
|
+
export declare function createBackgroundSessionMcpServer(options: BackgroundSessionMcpServerOptions): BackgroundSessionMcpServer;
|
|
91
34
|
//#endregion
|
|
92
|
-
export
|
|
35
|
+
export type { BackgroundSessionMcpServer, BackgroundSessionMcpServerOptions };
|
|
36
|
+
export {};
|
package/dist/mcp.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as readBackgroundSessionWaitInput, i as readBackgroundSessionString, n as readBackgroundSessionObject, r as readBackgroundSessionPhase, t as readBackgroundSessionInteger } from "./chunks/background-session-control-input.js";
|
|
3
|
-
import { createRandomId } from "@rivus/platform/identity";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
1
|
+
import { t as createBackgroundSessionToolContracts } from "./chunks/background-session-authority.js";
|
|
5
2
|
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { createServer } from "node:http";
|
|
7
3
|
//#region src/adapters/mcp/background-session/background-session-mcp-server.ts
|
|
8
4
|
const BACKGROUND_SESSION_MCP_SERVER_NAME = "rivus-background-sessions";
|
|
9
5
|
const BACKGROUND_SESSION_MCP_SERVER_VERSION = "1.0.0";
|
|
@@ -205,251 +201,8 @@ function optionalEnv(name) {
|
|
|
205
201
|
return process.env[name]?.trim() || void 0;
|
|
206
202
|
}
|
|
207
203
|
//#endregion
|
|
208
|
-
//#region src/core/application/background-session/control/background-session-control.ts
|
|
209
|
-
function createBackgroundSessionControl$1(options) {
|
|
210
|
-
return { handle: async (command, context, input) => {
|
|
211
|
-
const executionContext = {
|
|
212
|
-
agentId: context.agentId,
|
|
213
|
-
callId: `mcp-call:${options.createId()}`,
|
|
214
|
-
instanceId: `mcp:${context.agentId}`,
|
|
215
|
-
policyEpoch: context.policyEpoch,
|
|
216
|
-
runId: context.runId,
|
|
217
|
-
sessionKey: context.sessionKey,
|
|
218
|
-
sourceMessageId: context.sourceMessageId,
|
|
219
|
-
toolId: `background.${command}`,
|
|
220
|
-
toolVersion: "1.0.0",
|
|
221
|
-
origin: toToolOrigin(context.origin)
|
|
222
|
-
};
|
|
223
|
-
switch (command) {
|
|
224
|
-
case "start": {
|
|
225
|
-
const { displayName, prompt } = readObject(input, ["displayName", "prompt"]);
|
|
226
|
-
if (typeof prompt !== "string" || prompt.trim() === "") throw new BackgroundSessionControlError("background.start requires a non-empty prompt");
|
|
227
|
-
const sessionId = `bg-${options.createId()}`;
|
|
228
|
-
return options.service.start({
|
|
229
|
-
authority: {
|
|
230
|
-
...context.authority,
|
|
231
|
-
sessionKey: createBackgroundSessionKey(sessionId)
|
|
232
|
-
},
|
|
233
|
-
context: executionContext,
|
|
234
|
-
...displayName === void 0 ? {} : { displayName: readString(displayName, "displayName") },
|
|
235
|
-
prompt,
|
|
236
|
-
sessionId
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
case "wait": return options.service.wait({
|
|
240
|
-
context: executionContext,
|
|
241
|
-
...readBackgroundSessionWaitInput(input, (message) => new BackgroundSessionControlError(message))
|
|
242
|
-
});
|
|
243
|
-
case "list": {
|
|
244
|
-
const { limit, phase } = readObject(input, ["limit", "phase"]);
|
|
245
|
-
return options.service.list({
|
|
246
|
-
context: executionContext,
|
|
247
|
-
...limit === void 0 ? {} : { limit: readInteger(limit, "limit") },
|
|
248
|
-
...phase === void 0 ? {} : { phase: readPhase(phase) }
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
case "status": {
|
|
252
|
-
const { sessionId } = readObject(input, ["sessionId"]);
|
|
253
|
-
return options.service.status({
|
|
254
|
-
context: executionContext,
|
|
255
|
-
sessionId: readString(sessionId, "sessionId")
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
case "send": {
|
|
259
|
-
const { message, sessionId } = readObject(input, ["message", "sessionId"]);
|
|
260
|
-
return options.service.send({
|
|
261
|
-
context: executionContext,
|
|
262
|
-
message: readString(message, "message"),
|
|
263
|
-
sessionId: readString(sessionId, "sessionId")
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
case "stop": {
|
|
267
|
-
const { reason, sessionId } = readObject(input, ["reason", "sessionId"]);
|
|
268
|
-
return options.service.stop({
|
|
269
|
-
context: executionContext,
|
|
270
|
-
...reason === void 0 ? {} : { reason: readString(reason, "reason") },
|
|
271
|
-
sessionId: readString(sessionId, "sessionId")
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
} };
|
|
276
|
-
}
|
|
277
|
-
var BackgroundSessionControlError = class extends Error {
|
|
278
|
-
name = "BackgroundSessionControlError";
|
|
279
|
-
};
|
|
280
|
-
function toControlContext$1(input) {
|
|
281
|
-
return {
|
|
282
|
-
agentId: input.agentId,
|
|
283
|
-
authority: input.authority,
|
|
284
|
-
origin: {
|
|
285
|
-
allowedActorOpenIds: input.origin.allowedActorOpenIds,
|
|
286
|
-
...input.origin.conversationId === void 0 ? {} : { conversationId: input.origin.conversationId },
|
|
287
|
-
endpointId: input.origin.endpointId,
|
|
288
|
-
tenantKey: input.origin.tenantKey
|
|
289
|
-
},
|
|
290
|
-
policyEpoch: input.policyEpoch,
|
|
291
|
-
runId: `mcp:${input.createId()}`,
|
|
292
|
-
sessionKey: input.sessionKey,
|
|
293
|
-
sourceMessageId: input.sourceMessageId ?? `mcp:${input.createId()}`
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
function toToolOrigin(origin) {
|
|
297
|
-
return {
|
|
298
|
-
allowedActorOpenIds: origin.allowedActorOpenIds,
|
|
299
|
-
endpointId: origin.endpointId,
|
|
300
|
-
tenantKey: origin.tenantKey,
|
|
301
|
-
...origin.conversationId === void 0 ? {} : { conversationId: origin.conversationId }
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
function readObject(input, allowed) {
|
|
305
|
-
return readBackgroundSessionObject(input, allowed, (message) => new BackgroundSessionControlError(message));
|
|
306
|
-
}
|
|
307
|
-
function readString(value, name) {
|
|
308
|
-
return readBackgroundSessionString(value, name, (message) => new BackgroundSessionControlError(message));
|
|
309
|
-
}
|
|
310
|
-
function readInteger(value, name) {
|
|
311
|
-
return readBackgroundSessionInteger(value, name, (message) => new BackgroundSessionControlError(message));
|
|
312
|
-
}
|
|
313
|
-
function readPhase(value) {
|
|
314
|
-
return readBackgroundSessionPhase(value, (message) => new BackgroundSessionControlError(message));
|
|
315
|
-
}
|
|
316
|
-
//#endregion
|
|
317
|
-
//#region src/adapters/compatibility/background-session/control.ts
|
|
318
|
-
function createBackgroundSessionControl(options) {
|
|
319
|
-
return createBackgroundSessionControl$1({
|
|
320
|
-
createId: createRandomId,
|
|
321
|
-
service: options.service
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
function toControlContext(input) {
|
|
325
|
-
return toControlContext$1({
|
|
326
|
-
...input,
|
|
327
|
-
createId: createRandomId
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
//#endregion
|
|
331
|
-
//#region src/adapters/http/background-session/background-session-control-http-server.ts
|
|
332
|
-
var BackgroundSessionControlHttpError = class extends Error {
|
|
333
|
-
statusCode;
|
|
334
|
-
name = "BackgroundSessionControlHttpError";
|
|
335
|
-
constructor(statusCode, message) {
|
|
336
|
-
super(message);
|
|
337
|
-
this.statusCode = statusCode;
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
const COMMANDS = [
|
|
341
|
-
"start",
|
|
342
|
-
"wait",
|
|
343
|
-
"list",
|
|
344
|
-
"status",
|
|
345
|
-
"send",
|
|
346
|
-
"stop"
|
|
347
|
-
];
|
|
348
|
-
function createBackgroundSessionControlHttpServer(options) {
|
|
349
|
-
let server;
|
|
350
|
-
let boundPort = options.port;
|
|
351
|
-
const capabilities = /* @__PURE__ */ new Map();
|
|
352
|
-
const capabilitiesBySessionKey = /* @__PURE__ */ new Map();
|
|
353
|
-
const serverHandle = createServer((request, response) => {
|
|
354
|
-
(async () => {
|
|
355
|
-
try {
|
|
356
|
-
if (request.method === "GET" && request.url === "/background-sessions/status") {
|
|
357
|
-
if (request.headers.authorization !== `Bearer ${options.token}`) throw new BackgroundSessionControlHttpError(401, "unauthorized");
|
|
358
|
-
respond(response, 200, {
|
|
359
|
-
ok: true,
|
|
360
|
-
result: options.status?.() ?? null
|
|
361
|
-
});
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
if (request.method !== "POST" || request.url !== "/background-sessions") throw new BackgroundSessionControlHttpError(404, "not found");
|
|
365
|
-
if (request.headers.authorization !== `Bearer ${options.token}`) throw new BackgroundSessionControlHttpError(401, "unauthorized");
|
|
366
|
-
const envelope = parseEnvelope(await readBody(request, 64 * 1024));
|
|
367
|
-
const context = capabilities.get(envelope.capability);
|
|
368
|
-
if (!context) throw new BackgroundSessionControlHttpError(401, "unknown or expired capability");
|
|
369
|
-
const command = envelope.command;
|
|
370
|
-
if (!COMMANDS.includes(command)) throw new BackgroundSessionControlHttpError(400, `unsupported background session command: ${String(command)}`);
|
|
371
|
-
respond(response, 200, {
|
|
372
|
-
ok: true,
|
|
373
|
-
result: await options.control.handle(command, context, envelope.input)
|
|
374
|
-
});
|
|
375
|
-
} catch (error) {
|
|
376
|
-
respond(response, error instanceof BackgroundSessionControlHttpError ? error.statusCode : 500, {
|
|
377
|
-
error: {
|
|
378
|
-
message: error instanceof Error ? error.message : String(error),
|
|
379
|
-
name: error instanceof Error ? error.name : "Error"
|
|
380
|
-
},
|
|
381
|
-
ok: false
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
})();
|
|
385
|
-
});
|
|
386
|
-
serverHandle.on("error", () => void 0);
|
|
387
|
-
return {
|
|
388
|
-
port: () => boundPort,
|
|
389
|
-
close: async () => {
|
|
390
|
-
const active = server;
|
|
391
|
-
server = void 0;
|
|
392
|
-
capabilities.clear();
|
|
393
|
-
capabilitiesBySessionKey.clear();
|
|
394
|
-
if (active) await new Promise((resolve) => active.close(() => resolve()));
|
|
395
|
-
},
|
|
396
|
-
issueCapability: (context) => {
|
|
397
|
-
const existing = capabilitiesBySessionKey.get(context.sessionKey);
|
|
398
|
-
if (existing) return existing;
|
|
399
|
-
const capability = randomUUID();
|
|
400
|
-
capabilities.set(capability, context);
|
|
401
|
-
capabilitiesBySessionKey.set(context.sessionKey, capability);
|
|
402
|
-
return capability;
|
|
403
|
-
},
|
|
404
|
-
start: async () => {
|
|
405
|
-
if (server) return;
|
|
406
|
-
await new Promise((resolve, reject) => {
|
|
407
|
-
server = serverHandle;
|
|
408
|
-
serverHandle.listen(options.port, options.host ?? "127.0.0.1", () => {
|
|
409
|
-
const address = serverHandle.address();
|
|
410
|
-
if (address !== null && typeof address === "object") boundPort = address.port;
|
|
411
|
-
resolve();
|
|
412
|
-
});
|
|
413
|
-
serverHandle.once("error", (error) => {
|
|
414
|
-
server = void 0;
|
|
415
|
-
reject(error);
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
function respond(response, statusCode, body) {
|
|
422
|
-
response.writeHead(statusCode, { "content-type": "application/json" });
|
|
423
|
-
response.end(JSON.stringify(body));
|
|
424
|
-
}
|
|
425
|
-
function parseEnvelope(body) {
|
|
426
|
-
const parsed = JSON.parse(body);
|
|
427
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new BackgroundSessionControlHttpError(400, "invalid background session control envelope");
|
|
428
|
-
const record = parsed;
|
|
429
|
-
if (typeof record.command !== "string" || typeof record.capability !== "string") throw new BackgroundSessionControlHttpError(400, "background session control envelope requires command and capability");
|
|
430
|
-
return {
|
|
431
|
-
capability: record.capability,
|
|
432
|
-
command: record.command,
|
|
433
|
-
input: record.input
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function readBody(request, maxBytes) {
|
|
437
|
-
return new Promise((resolve, reject) => {
|
|
438
|
-
const chunks = [];
|
|
439
|
-
let total = 0;
|
|
440
|
-
request.on("data", (chunk) => {
|
|
441
|
-
total += chunk.length;
|
|
442
|
-
if (total > maxBytes) {
|
|
443
|
-
reject(new BackgroundSessionControlHttpError(413, "background session control request too large"));
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
chunks.push(chunk);
|
|
447
|
-
});
|
|
448
|
-
request.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
//#endregion
|
|
452
204
|
//#region src/mcp.ts
|
|
453
205
|
if (pathToFileURL(process.argv[1] ?? "").href === import.meta.url) await runBackgroundSessionMcpServer();
|
|
454
206
|
//#endregion
|
|
455
|
-
export { BACKGROUND_SESSION_MCP_SERVER_NAME, BACKGROUND_SESSION_MCP_SERVER_VERSION,
|
|
207
|
+
export { BACKGROUND_SESSION_MCP_SERVER_NAME, BACKGROUND_SESSION_MCP_SERVER_VERSION, BackgroundSessionMcpError, createBackgroundSessionMcpServer, runBackgroundSessionMcpServer };
|
|
208
|
+
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivus/gateway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Message gateway, channels, host binding, automation, and daemon assembly for Rivus.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "GPL-3.0-only",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": "^24.11.0"
|
|
9
9
|
},
|
|
@@ -35,7 +35,12 @@
|
|
|
35
35
|
"./cli": "./dist/cli.js",
|
|
36
36
|
"./plugin/starter": "./templates/rivus-starter.plugin.mjs",
|
|
37
37
|
"./skills/runtime-management/SKILL.md": "./skills/runtime-management/SKILL.md",
|
|
38
|
-
"./package.json": "./package.json"
|
|
38
|
+
"./package.json": "./package.json",
|
|
39
|
+
"./bootstrap/herdr-feishu": {
|
|
40
|
+
"types": "./dist/bootstrap/herdr-feishu.d.ts",
|
|
41
|
+
"import": "./dist/bootstrap/herdr-feishu.js",
|
|
42
|
+
"default": "./dist/bootstrap/herdr-feishu.js"
|
|
43
|
+
}
|
|
39
44
|
},
|
|
40
45
|
"files": [
|
|
41
46
|
"dist",
|
|
@@ -47,25 +52,28 @@
|
|
|
47
52
|
"LICENSE"
|
|
48
53
|
],
|
|
49
54
|
"dependencies": {
|
|
50
|
-
"@
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
+
"@rivus/platform": "0.18.0",
|
|
56
|
+
"@rivus/runtime": "0.18.0",
|
|
57
|
+
"@larksuiteoapi/node-sdk": "1.74.0",
|
|
58
|
+
"axios": "1.20.0",
|
|
59
|
+
"effect": "^4.0.0",
|
|
60
|
+
"marked": "18.0.14",
|
|
61
|
+
"@resvg/resvg-js": "2.6.2"
|
|
55
62
|
},
|
|
56
63
|
"publishConfig": {
|
|
57
64
|
"access": "public"
|
|
58
65
|
},
|
|
59
66
|
"devDependencies": {
|
|
60
|
-
"vite-plus": "0.
|
|
61
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@0.
|
|
67
|
+
"vite-plus": "1.0.0",
|
|
68
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.0.0",
|
|
62
69
|
"typescript": "6.0.3",
|
|
63
|
-
"@
|
|
64
|
-
"@
|
|
70
|
+
"@typescript/native": "npm:typescript@7.0.2",
|
|
71
|
+
"@types/node": "24.19.0",
|
|
72
|
+
"@vitest/coverage-v8": "5.0.1"
|
|
65
73
|
},
|
|
66
74
|
"scripts": {
|
|
67
75
|
"build": "node ../../scripts/generate-gateway-release-descriptor.mjs && vp pack",
|
|
68
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
76
|
+
"typecheck": "node node_modules/@typescript/native/bin/tsc -p tsconfig.json --noEmit",
|
|
69
77
|
"test": "vp test --run"
|
|
70
78
|
}
|
|
71
79
|
}
|
|
@@ -69,7 +69,9 @@ function analyzeOverseasStructure(evidence) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function analyzeSectorStructure(evidence) {
|
|
72
|
-
if (!evidence)
|
|
72
|
+
if (!evidence) {
|
|
73
|
+
return "行业数据不可用,不补写行业主线判断。";
|
|
74
|
+
}
|
|
73
75
|
const leaders = evidence.leaders
|
|
74
76
|
.slice(0, 2)
|
|
75
77
|
.map(({ name }) => name)
|
|
@@ -3,8 +3,12 @@ import { analyzeAShareBriefing } from "./a-share-briefing-analysis.mjs";
|
|
|
3
3
|
const TIME_ZONE = "Asia/Shanghai";
|
|
4
4
|
|
|
5
5
|
export function renderAShareMarketBriefing(input) {
|
|
6
|
-
if (input.status === "ready")
|
|
7
|
-
|
|
6
|
+
if (input.status === "ready") {
|
|
7
|
+
return renderReadyBriefing(input);
|
|
8
|
+
}
|
|
9
|
+
if (input.status === "snapshot-unavailable") {
|
|
10
|
+
return renderUnavailablePreMarket(input);
|
|
11
|
+
}
|
|
8
12
|
return renderUnavailablePostMarket(input);
|
|
9
13
|
}
|
|
10
14
|
|
|
@@ -128,7 +132,9 @@ function renderPreMarketBriefing({
|
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
function renderPolicyEvidence(evidence) {
|
|
131
|
-
if (!evidence)
|
|
135
|
+
if (!evidence) {
|
|
136
|
+
return ["- **官方资讯**:当前不可用,本次不补写政策或事件驱动。"];
|
|
137
|
+
}
|
|
132
138
|
const unavailable =
|
|
133
139
|
evidence.unavailableSources.length > 0
|
|
134
140
|
? [`- **官方资讯缺失**:${evidence.unavailableSources.join("、")}当前不可用;其余标题仍按计划发生时刻截断。`]
|
|
@@ -146,7 +152,9 @@ function renderPolicyEvidence(evidence) {
|
|
|
146
152
|
}
|
|
147
153
|
|
|
148
154
|
function renderSectorEvidence(evidence) {
|
|
149
|
-
if (!evidence)
|
|
155
|
+
if (!evidence) {
|
|
156
|
+
return ["- **行业数据**:当前不可用,本次不据此判断板块驱动。"];
|
|
157
|
+
}
|
|
150
158
|
return [
|
|
151
159
|
`- **领涨行业**:${formatSectorRanking(evidence.leaders)}`,
|
|
152
160
|
`- **领跌行业**:${formatSectorRanking(evidence.laggards)}`,
|
|
@@ -155,7 +163,9 @@ function renderSectorEvidence(evidence) {
|
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
function renderOverseasEvidence(evidence, direction) {
|
|
158
|
-
if (!evidence)
|
|
166
|
+
if (!evidence) {
|
|
167
|
+
return ["- **海外指数**:当前不可用,本次不据此判断外部风险背景。"];
|
|
168
|
+
}
|
|
159
169
|
return [
|
|
160
170
|
`- **隔夜美股方向**:${direction}`,
|
|
161
171
|
...evidence.quotes.map(
|
|
@@ -11,7 +11,9 @@ const INDEXES = Object.freeze([
|
|
|
11
11
|
export async function readAShareIndexEvidence() {
|
|
12
12
|
const quotes = await Promise.all(INDEXES.map(readQuote));
|
|
13
13
|
const marketDates = new Set(quotes.map(({ marketDate }) => marketDate));
|
|
14
|
-
if (marketDates.size !== 1)
|
|
14
|
+
if (marketDates.size !== 1) {
|
|
15
|
+
throw new Error("a-share market quote dates are inconsistent");
|
|
16
|
+
}
|
|
15
17
|
const asOf = quotes
|
|
16
18
|
.map(({ asOf }) => asOf)
|
|
17
19
|
.sort((left, right) => left.localeCompare(right))
|
|
@@ -35,7 +37,9 @@ async function readQuote(index) {
|
|
|
35
37
|
function parseQuote(index, body) {
|
|
36
38
|
const start = body.indexOf('"');
|
|
37
39
|
const end = body.lastIndexOf('"');
|
|
38
|
-
if (start < 0 || end <= start)
|
|
40
|
+
if (start < 0 || end <= start) {
|
|
41
|
+
throw new Error(`a-share market quote is invalid for ${index.symbol}`);
|
|
42
|
+
}
|
|
39
43
|
const fields = body.slice(start + 1, end).split("~");
|
|
40
44
|
const timestamp = fields[30];
|
|
41
45
|
if (!/^\d{14}$/.test(timestamp ?? "")) {
|
|
@@ -87,7 +91,9 @@ function readQuoteAmount(value, symbol) {
|
|
|
87
91
|
|
|
88
92
|
function readFiniteNumber(value, symbol, field) {
|
|
89
93
|
const number = Number(value);
|
|
90
|
-
if (!Number.isFinite(number))
|
|
94
|
+
if (!Number.isFinite(number)) {
|
|
95
|
+
throw new Error(`a-share market quote is missing ${field} for ${symbol}`);
|
|
96
|
+
}
|
|
91
97
|
return number;
|
|
92
98
|
}
|
|
93
99
|
|
|
@@ -9,7 +9,9 @@ export async function readAShareMarketBriefing(input) {
|
|
|
9
9
|
const { occurrence, session } = validateInput(input);
|
|
10
10
|
const occurrenceDate = dateInShanghai(new Date(occurrence));
|
|
11
11
|
const { asOf, marketDate, quotes, source } = await readAShareIndexEvidence();
|
|
12
|
-
if (marketDate > occurrenceDate)
|
|
12
|
+
if (marketDate > occurrenceDate) {
|
|
13
|
+
throw new Error("a-share market quote date is later than the scheduled occurrence");
|
|
14
|
+
}
|
|
13
15
|
const preMarketSnapshotUnavailable =
|
|
14
16
|
session === "pre-market" && Date.parse(asOf) > Date.parse(occurrence) + 5 * 60_000;
|
|
15
17
|
const status = preMarketSnapshotUnavailable
|
|
@@ -28,7 +28,9 @@ export async function readAShareOverseasEvidence({ occurrence }) {
|
|
|
28
28
|
throw new Error("a-share overseas evidence timestamps are inconsistent");
|
|
29
29
|
}
|
|
30
30
|
const age = providerClockTime(occurrence) - providerClockTime(asOf);
|
|
31
|
-
if (age < 0 || age > MAX_AGE_MS)
|
|
31
|
+
if (age < 0 || age > MAX_AGE_MS) {
|
|
32
|
+
throw new Error("a-share overseas evidence is stale for the occurrence");
|
|
33
|
+
}
|
|
32
34
|
return Object.freeze({ asOf, quotes: Object.freeze(quotes), source: SOURCE });
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -54,7 +56,9 @@ function providerClockTime(value) {
|
|
|
54
56
|
|
|
55
57
|
function parseQuote(index, body) {
|
|
56
58
|
const match = body.match(new RegExp(`(?:^|\\n)v_${index.symbol}="([^"]+)";`, "u"));
|
|
57
|
-
if (!match)
|
|
59
|
+
if (!match) {
|
|
60
|
+
throw new Error(`a-share overseas evidence is missing ${index.symbol}`);
|
|
61
|
+
}
|
|
58
62
|
const fields = match[1].split("~");
|
|
59
63
|
const providerTime = fields[30];
|
|
60
64
|
if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(providerTime ?? "")) {
|
|
@@ -63,7 +67,9 @@ function parseQuote(index, body) {
|
|
|
63
67
|
if (!isValidProviderTimestamp(providerTime)) {
|
|
64
68
|
throw new Error(`a-share overseas evidence has an invalid timestamp for ${index.symbol}`);
|
|
65
69
|
}
|
|
66
|
-
if (fields[35] !== "USD")
|
|
70
|
+
if (fields[35] !== "USD") {
|
|
71
|
+
throw new Error(`a-share overseas evidence has an unexpected currency for ${index.symbol}`);
|
|
72
|
+
}
|
|
67
73
|
return Object.freeze({
|
|
68
74
|
changePercent: readFiniteNumber(fields[32], index.symbol, "change percent"),
|
|
69
75
|
name: index.name,
|
|
@@ -81,6 +87,8 @@ function isValidProviderTimestamp(value) {
|
|
|
81
87
|
|
|
82
88
|
function readFiniteNumber(value, symbol, field) {
|
|
83
89
|
const number = Number(value);
|
|
84
|
-
if (!Number.isFinite(number))
|
|
90
|
+
if (!Number.isFinite(number)) {
|
|
91
|
+
throw new Error(`a-share overseas evidence is missing ${field} for ${symbol}`);
|
|
92
|
+
}
|
|
85
93
|
return number;
|
|
86
94
|
}
|
|
@@ -12,7 +12,9 @@ export async function readASharePolicyEvidence({ occurrence }) {
|
|
|
12
12
|
const available = settled.flatMap((result, index) =>
|
|
13
13
|
result.status === "fulfilled" ? [{ items: result.value, source: readers[index].source }] : []
|
|
14
14
|
);
|
|
15
|
-
if (available.length === 0)
|
|
15
|
+
if (available.length === 0) {
|
|
16
|
+
throw new Error("a-share policy evidence sources are unavailable");
|
|
17
|
+
}
|
|
16
18
|
const items = available
|
|
17
19
|
.flatMap(({ items }) => items)
|
|
18
20
|
.filter(({ publishedAt }) => {
|
|
@@ -41,9 +43,13 @@ async function readPbcHeadlines() {
|
|
|
41
43
|
for (const match of body.matchAll(pattern)) {
|
|
42
44
|
const href = readHtmlAttribute(match[1], "href");
|
|
43
45
|
const title = decodeHtml(readHtmlAttribute(match[1], "title"));
|
|
44
|
-
if (!href || !title || title.length > 200)
|
|
46
|
+
if (!href || !title || title.length > 200) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
45
49
|
const itemUrl = normalizeSourceUrl(href, url, "www.pbc.gov.cn");
|
|
46
|
-
if (!itemUrl)
|
|
50
|
+
if (!itemUrl) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
47
53
|
items.push(
|
|
48
54
|
Object.freeze({
|
|
49
55
|
publishedAt: readPbcPublishedAt(itemUrl, match[2]),
|
|
@@ -52,15 +58,21 @@ async function readPbcHeadlines() {
|
|
|
52
58
|
url: itemUrl
|
|
53
59
|
})
|
|
54
60
|
);
|
|
55
|
-
if (items.length === 2)
|
|
61
|
+
if (items.length === 2) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (items.length === 0) {
|
|
66
|
+
throw new Error("PBC policy evidence contains no valid headlines");
|
|
56
67
|
}
|
|
57
|
-
if (items.length === 0) throw new Error("PBC policy evidence contains no valid headlines");
|
|
58
68
|
return Object.freeze(items);
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
function readPbcPublishedAt(itemUrl, displayedDate) {
|
|
62
72
|
const timestamp = new URL(itemUrl).pathname.match(/\/(\d{14})\d*\/index\.html$/u)?.[1];
|
|
63
|
-
if (!timestamp)
|
|
73
|
+
if (!timestamp) {
|
|
74
|
+
return `${displayedDate}T23:59:59+08:00`;
|
|
75
|
+
}
|
|
64
76
|
return `${timestamp.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/u, "$1-$2-$3T$4:$5:$6")}+08:00`;
|
|
65
77
|
}
|
|
66
78
|
|
|
@@ -81,7 +93,9 @@ async function readCsrcHeadlines() {
|
|
|
81
93
|
throw new Error("CSRC policy evidence is not valid JSON", { cause });
|
|
82
94
|
}
|
|
83
95
|
const results = Array.isArray(payload?.data?.results) ? payload.data.results : undefined;
|
|
84
|
-
if (!results)
|
|
96
|
+
if (!results) {
|
|
97
|
+
throw new Error("CSRC policy evidence contains no result list");
|
|
98
|
+
}
|
|
85
99
|
const items = results
|
|
86
100
|
.flatMap((row) => {
|
|
87
101
|
if (
|
|
@@ -94,7 +108,9 @@ async function readCsrcHeadlines() {
|
|
|
94
108
|
return [];
|
|
95
109
|
}
|
|
96
110
|
const itemUrl = normalizeSourceUrl(row.url, url, "www.csrc.gov.cn");
|
|
97
|
-
if (!itemUrl)
|
|
111
|
+
if (!itemUrl) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
98
114
|
return [
|
|
99
115
|
Object.freeze({
|
|
100
116
|
publishedAt: `${row.publishedTimeStr.replace(" ", "T")}+08:00`,
|
|
@@ -105,7 +121,9 @@ async function readCsrcHeadlines() {
|
|
|
105
121
|
];
|
|
106
122
|
})
|
|
107
123
|
.slice(0, 2);
|
|
108
|
-
if (items.length === 0)
|
|
124
|
+
if (items.length === 0) {
|
|
125
|
+
throw new Error("CSRC policy evidence contains no valid headlines");
|
|
126
|
+
}
|
|
109
127
|
return Object.freeze(items);
|
|
110
128
|
}
|
|
111
129
|
|
|
@@ -131,7 +149,9 @@ function normalizeSourceUrl(value, baseUrl, expectedHostname) {
|
|
|
131
149
|
} catch {
|
|
132
150
|
return undefined;
|
|
133
151
|
}
|
|
134
|
-
if (url.protocol !== "https:" || url.hostname !== expectedHostname)
|
|
152
|
+
if (url.protocol !== "https:" || url.hostname !== expectedHostname) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
135
155
|
return url.href;
|
|
136
156
|
}
|
|
137
157
|
|
|
@@ -45,7 +45,9 @@ function parseSectorRanking(body) {
|
|
|
45
45
|
throw new Error("a-share sector evidence is not valid JSON", { cause });
|
|
46
46
|
}
|
|
47
47
|
const rows = payload?.rc === 0 && Array.isArray(payload?.data?.diff) ? payload.data.diff : undefined;
|
|
48
|
-
if (!rows || rows.length !== 3)
|
|
48
|
+
if (!rows || rows.length !== 3) {
|
|
49
|
+
throw new Error("a-share sector evidence must contain three ranked industries");
|
|
50
|
+
}
|
|
49
51
|
return Object.freeze(
|
|
50
52
|
rows.map((row) => {
|
|
51
53
|
if (
|