@saleso.innovations/bridge 0.1.15 → 0.1.17
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/INTEGRATION.md +11 -0
- package/README.md +8 -1
- package/dist/cli.js +25 -0
- package/dist/client.d.ts +3 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -2
- package/dist/convexRelay.d.ts +24 -0
- package/dist/convexRelay.d.ts.map +1 -0
- package/dist/convexRelay.js +70 -0
- package/dist/cronBackfill.d.ts +16 -0
- package/dist/cronBackfill.d.ts.map +1 -0
- package/dist/cronBackfill.js +14 -0
- package/dist/cronWatcher.d.ts +14 -0
- package/dist/cronWatcher.d.ts.map +1 -1
- package/dist/cronWatcher.js +85 -41
- package/dist/hermesCommands.d.ts +1 -1
- package/dist/hermesCommands.d.ts.map +1 -1
- package/dist/hermesCommands.js +41 -0
- package/dist/hermesFileCommands.d.ts +19 -0
- package/dist/hermesFileCommands.d.ts.map +1 -0
- package/dist/hermesFileCommands.js +61 -0
- package/dist/hermesFiles.d.ts +60 -0
- package/dist/hermesFiles.d.ts.map +1 -0
- package/dist/hermesFiles.js +266 -0
- package/dist/hermesForwarder.d.ts +4 -1
- package/dist/hermesForwarder.d.ts.map +1 -1
- package/dist/hermesForwarder.js +80 -3
- package/dist/hermesSessionDb.d.ts +21 -0
- package/dist/hermesSessionDb.d.ts.map +1 -0
- package/dist/hermesSessionDb.js +174 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/runtimeVersion.d.ts +14 -0
- package/dist/runtimeVersion.d.ts.map +1 -0
- package/dist/runtimeVersion.js +105 -0
- package/dist/skillsList.d.ts +9 -0
- package/dist/skillsList.d.ts.map +1 -0
- package/dist/skillsList.js +187 -0
- package/package.json +14 -3
package/INTEGRATION.md
CHANGED
|
@@ -109,6 +109,17 @@ Codes expire after 10 minutes. Each code links **one** Hermes instance to **one*
|
|
|
109
109
|
|
|
110
110
|
After first pairing, credentials are saved to `~/.cleos/agent.json`. Delivered cron files are tracked in `~/.cleos/cron-delivered.json` so restarts do not duplicate Jobs. Use `reconnectHermesAgent()` on restart — no new pairing code required.
|
|
111
111
|
|
|
112
|
+
### Recover missed cron results
|
|
113
|
+
|
|
114
|
+
If cron results reached Telegram but not Cleos (for example after a bridge restart), re-deliver pending output files:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cleos-bridge cron-backfill --list # inspect pending files
|
|
118
|
+
cleos-bridge cron-backfill --reset # clear delivery index and re-send all output files
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Keep `cleos-bridge start` running so new cron runs are forwarded automatically.
|
|
122
|
+
|
|
112
123
|
## Self-hosting Cleos
|
|
113
124
|
|
|
114
125
|
Set `CLEOS_CONVEX_SITE_URL` in the Hermes build to your Convex `.site` URL instead of the default.
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ This installs the latest package, refreshes the CLI symlink, and restarts `cleos
|
|
|
34
34
|
To pin a specific release (e.g. after a Cleos update):
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
curl -fsSL https://amicable-elephant-407.convex.site/update-bridge.sh | bash -s -- 0.1.
|
|
37
|
+
curl -fsSL https://amicable-elephant-407.convex.site/update-bridge.sh | bash -s -- 0.1.17
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Manual usage
|
|
@@ -58,6 +58,13 @@ Credentials are saved to `~/.cleos/agent.json`.
|
|
|
58
58
|
|
|
59
59
|
The daemon watches `~/.hermes/cron/output/` and forwards new cron results to the Cleos Jobs tab automatically.
|
|
60
60
|
|
|
61
|
+
To recover missed cron output (for example after a bridge restart):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cleos-bridge cron-backfill --list
|
|
65
|
+
cleos-bridge cron-backfill --reset
|
|
66
|
+
```
|
|
67
|
+
|
|
61
68
|
## Hermes API
|
|
62
69
|
|
|
63
70
|
By default the bridge forwards chat to Hermes at `http://127.0.0.1:8642/v1/chat/completions`.
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { prepareHermesForBridge } from "./ensureHermesApi.js";
|
|
|
8
8
|
import { createHermesMessageHandler } from "./hermesForwarder.js";
|
|
9
9
|
import { convexSiteUrlFromEnv } from "./resolve.js";
|
|
10
10
|
import { normalizePairingCode } from "./normalizePairingCode.js";
|
|
11
|
+
import { backfillCronDeliveries, describeCronDeliveryState } from "./cronBackfill.js";
|
|
11
12
|
async function main() {
|
|
12
13
|
const [, , command, codeArg] = process.argv;
|
|
13
14
|
const onUserMessage = createHermesMessageHandler();
|
|
@@ -70,6 +71,28 @@ async function main() {
|
|
|
70
71
|
runBridgeUpdate({ tag: codeArg || "latest" });
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
74
|
+
if (command === "cron-backfill") {
|
|
75
|
+
const reset = process.argv.includes("--reset");
|
|
76
|
+
const listOnly = process.argv.includes("--list");
|
|
77
|
+
if (listOnly) {
|
|
78
|
+
const state = describeCronDeliveryState();
|
|
79
|
+
console.log(JSON.stringify({ event: "cleos-bridge.cron-backfill.list", ...state }, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const session = await reconnectHermesAgent({ onUserMessage });
|
|
83
|
+
try {
|
|
84
|
+
const summary = await backfillCronDeliveries(session, { reset });
|
|
85
|
+
console.log(JSON.stringify({ event: "cleos-bridge.cron-backfill.complete", ...summary }, null, 2));
|
|
86
|
+
if (summary.remainingCount > 0) {
|
|
87
|
+
console.error(`${summary.remainingCount} cron file(s) still pending. Keep cleos-bridge running or retry when relay is connected.`);
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
session.close();
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
73
96
|
printUsage();
|
|
74
97
|
process.exit(1);
|
|
75
98
|
}
|
|
@@ -80,6 +103,8 @@ function printUsage() {
|
|
|
80
103
|
console.error(" cleos-bridge start [CODE] Run as a reconnecting daemon (systemd)");
|
|
81
104
|
console.error(" cleos-bridge reconnect Reconnect once using saved credentials");
|
|
82
105
|
console.error(" cleos-bridge update [TAG] Update package and restart systemd service");
|
|
106
|
+
console.error(" cleos-bridge cron-backfill [--reset] [--list]");
|
|
107
|
+
console.error(" Re-deliver pending cron output files to Cleos");
|
|
83
108
|
}
|
|
84
109
|
async function waitForExit(session, onStart) {
|
|
85
110
|
const stop = onStart?.();
|
package/dist/client.d.ts
CHANGED
|
@@ -18,7 +18,8 @@ export type UserMessageAttachment = {
|
|
|
18
18
|
export type UserMessageMeta = {
|
|
19
19
|
agentId: string;
|
|
20
20
|
conversationId: string;
|
|
21
|
-
|
|
21
|
+
clientMessageId: string;
|
|
22
|
+
replyMessageId: string;
|
|
22
23
|
attachments?: UserMessageAttachment[];
|
|
23
24
|
};
|
|
24
25
|
export type AgentActivityPayload = {
|
|
@@ -36,7 +37,7 @@ export type AgentFailedPayload = {
|
|
|
36
37
|
export type AgentReply = {
|
|
37
38
|
delta: (text: string, sequence: number) => void;
|
|
38
39
|
activity: (activity: AgentActivityPayload) => void;
|
|
39
|
-
complete: (text: string, sequence: number) => void;
|
|
40
|
+
complete: (text: string, sequence: number, attachments?: UserMessageAttachment[]) => void;
|
|
40
41
|
failed: (failure: AgentFailedPayload) => void;
|
|
41
42
|
};
|
|
42
43
|
export type CronDeliveryMeta = {
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAYhG,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACrG,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -72,7 +72,7 @@ function createReplySender(ws, agentId, conversationId, messageId) {
|
|
|
72
72
|
sequence: activity.sequence,
|
|
73
73
|
}));
|
|
74
74
|
},
|
|
75
|
-
complete(text, sequence) {
|
|
75
|
+
complete(text, sequence, attachments) {
|
|
76
76
|
ws.send(JSON.stringify({
|
|
77
77
|
type: "agent.message",
|
|
78
78
|
agentId,
|
|
@@ -81,6 +81,7 @@ function createReplySender(ws, agentId, conversationId, messageId) {
|
|
|
81
81
|
content: text,
|
|
82
82
|
sequence,
|
|
83
83
|
final: true,
|
|
84
|
+
...(attachments && attachments.length > 0 ? { attachments } : {}),
|
|
84
85
|
}));
|
|
85
86
|
},
|
|
86
87
|
failed(failure) {
|
|
@@ -195,11 +196,14 @@ async function openAgentConnection(options) {
|
|
|
195
196
|
rememberConversationId(conversationId);
|
|
196
197
|
}
|
|
197
198
|
const attachments = parseUserMessageAttachments(envelope.attachments);
|
|
199
|
+
const clientMessageId = typeof envelope.clientMessageId === "string" && envelope.clientMessageId.trim().length > 0
|
|
200
|
+
? envelope.clientMessageId.trim()
|
|
201
|
+
: randomUUID();
|
|
198
202
|
const replyMessageId = randomUUID();
|
|
199
203
|
const reply = createReplySender(ws, agentId, conversationId, replyMessageId);
|
|
200
204
|
if (options.onUserMessage) {
|
|
201
205
|
try {
|
|
202
|
-
await options.onUserMessage(content, { agentId, conversationId,
|
|
206
|
+
await options.onUserMessage(content, { agentId, conversationId, clientMessageId, replyMessageId, attachments }, reply);
|
|
203
207
|
}
|
|
204
208
|
catch (error) {
|
|
205
209
|
const { message, code } = userSafeHermesError(error);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type ConvexUploadedFile = {
|
|
2
|
+
storageId: string;
|
|
3
|
+
url: string | null;
|
|
4
|
+
mimeType: string;
|
|
5
|
+
fileName: string;
|
|
6
|
+
size: number;
|
|
7
|
+
};
|
|
8
|
+
export type HermesMessageLink = {
|
|
9
|
+
clientMessageId?: string;
|
|
10
|
+
externalMessageId?: string;
|
|
11
|
+
hermesMessageId: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function linkHermesMessageIds(args: {
|
|
14
|
+
agentId: string;
|
|
15
|
+
conversationId: string;
|
|
16
|
+
links: HermesMessageLink[];
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
export declare function uploadFileToConvex(args: {
|
|
19
|
+
agentId: string;
|
|
20
|
+
fileName: string;
|
|
21
|
+
mimeType: string;
|
|
22
|
+
contentBase64: string;
|
|
23
|
+
}): Promise<ConvexUploadedFile>;
|
|
24
|
+
//# sourceMappingURL=convexRelay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convexRelay.d.ts","sourceRoot":"","sources":["../src/convexRelay.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BhB;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA4C9B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { loadCredentials } from "./credentials.js";
|
|
2
|
+
import { MAX_RELAY_UPLOAD_BYTES } from "./hermesFiles.js";
|
|
3
|
+
import { convexSiteUrlFromEnv } from "./resolve.js";
|
|
4
|
+
export async function linkHermesMessageIds(args) {
|
|
5
|
+
if (args.links.length === 0)
|
|
6
|
+
return;
|
|
7
|
+
const credentials = loadCredentials();
|
|
8
|
+
const convexSiteUrl = credentials?.convexSiteUrl ?? convexSiteUrlFromEnv();
|
|
9
|
+
const relaySecret = process.env.CLEOS_RELAY_SECRET?.trim() || "dev-relay-secret";
|
|
10
|
+
const response = await fetch(`${convexSiteUrl.replace(/\/$/, "")}/relay/messages`, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: {
|
|
13
|
+
"content-type": "application/json",
|
|
14
|
+
"x-cleos-relay-secret": relaySecret,
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify({
|
|
17
|
+
envelope: {
|
|
18
|
+
type: "hermes.link",
|
|
19
|
+
agentId: args.agentId,
|
|
20
|
+
conversationId: args.conversationId,
|
|
21
|
+
links: args.links,
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const text = await response.text();
|
|
27
|
+
throw new Error(`Failed to link Hermes message IDs (${response.status}): ${text}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function uploadFileToConvex(args) {
|
|
31
|
+
if (args.contentBase64.length === 0) {
|
|
32
|
+
throw new Error("contentBase64 is required");
|
|
33
|
+
}
|
|
34
|
+
const byteLength = Buffer.byteLength(args.contentBase64, "base64");
|
|
35
|
+
if (byteLength > MAX_RELAY_UPLOAD_BYTES) {
|
|
36
|
+
throw new Error(`File exceeds maximum upload size (${MAX_RELAY_UPLOAD_BYTES} bytes)`);
|
|
37
|
+
}
|
|
38
|
+
const credentials = loadCredentials();
|
|
39
|
+
const convexSiteUrl = credentials?.convexSiteUrl ?? convexSiteUrlFromEnv();
|
|
40
|
+
const relaySecret = process.env.CLEOS_RELAY_SECRET?.trim() || "dev-relay-secret";
|
|
41
|
+
const response = await fetch(`${convexSiteUrl.replace(/\/$/, "")}/relay/files`, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: {
|
|
44
|
+
"content-type": "application/json",
|
|
45
|
+
"x-cleos-relay-secret": relaySecret,
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
agentId: args.agentId,
|
|
49
|
+
fileName: args.fileName,
|
|
50
|
+
mimeType: args.mimeType,
|
|
51
|
+
contentBase64: args.contentBase64,
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const text = await response.text();
|
|
56
|
+
throw new Error(`Failed to upload file to Convex (${response.status}): ${text}`);
|
|
57
|
+
}
|
|
58
|
+
const payload = (await response.json());
|
|
59
|
+
const storageId = typeof payload.storageId === "string" ? payload.storageId : "";
|
|
60
|
+
if (!storageId) {
|
|
61
|
+
throw new Error("Upload response missing storageId");
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
storageId,
|
|
65
|
+
url: typeof payload.url === "string" ? payload.url : null,
|
|
66
|
+
mimeType: args.mimeType,
|
|
67
|
+
fileName: args.fileName,
|
|
68
|
+
size: byteLength,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ConnectResult } from "./client.js";
|
|
2
|
+
export type CronBackfillSummary = {
|
|
3
|
+
clearedEntries: number;
|
|
4
|
+
pendingBefore: number;
|
|
5
|
+
deliveredCount: number;
|
|
6
|
+
remainingCount: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function describeCronDeliveryState(): {
|
|
9
|
+
outputFileCount: number;
|
|
10
|
+
deliveredCount: number;
|
|
11
|
+
pendingFiles: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function backfillCronDeliveries(session: ConnectResult, options: {
|
|
14
|
+
reset: boolean;
|
|
15
|
+
}): Promise<CronBackfillSummary>;
|
|
16
|
+
//# sourceMappingURL=cronBackfill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cronBackfill.d.ts","sourceRoot":"","sources":["../src/cronBackfill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASjD,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,yBAAyB,IAAI;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAUA;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAE9B"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { listCronOutputFiles, listPendingCronFiles, readDeliveredIndex, relativeOutputKey, runCronBackfill, } from "./cronWatcher.js";
|
|
2
|
+
export function describeCronDeliveryState() {
|
|
3
|
+
const delivered = readDeliveredIndex();
|
|
4
|
+
const outputFiles = listCronOutputFiles();
|
|
5
|
+
const pendingFiles = listPendingCronFiles(delivered);
|
|
6
|
+
return {
|
|
7
|
+
outputFileCount: outputFiles.length,
|
|
8
|
+
deliveredCount: delivered.size,
|
|
9
|
+
pendingFiles: pendingFiles.map(relativeOutputKey),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export async function backfillCronDeliveries(session, options) {
|
|
13
|
+
return runCronBackfill(session, options.reset);
|
|
14
|
+
}
|
package/dist/cronWatcher.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ConnectResult } from "./client.js";
|
|
2
|
+
export declare const HERMES_CRON_OUTPUT_DIR: string;
|
|
3
|
+
export declare const DELIVERED_INDEX_PATH: string;
|
|
2
4
|
type CronWatcherOptions = {
|
|
3
5
|
session: ConnectResult;
|
|
4
6
|
onDelivered?: (info: {
|
|
@@ -8,6 +10,18 @@ type CronWatcherOptions = {
|
|
|
8
10
|
}) => void;
|
|
9
11
|
onError?: (message: string) => void;
|
|
10
12
|
};
|
|
13
|
+
export declare function readDeliveredIndex(): Set<string>;
|
|
14
|
+
export declare function writeDeliveredIndex(delivered: Set<string>): void;
|
|
15
|
+
export declare function clearDeliveredIndex(): number;
|
|
16
|
+
export declare function listCronOutputFiles(rootDir?: string): string[];
|
|
17
|
+
export declare function relativeOutputKey(filePath: string): string;
|
|
18
|
+
export declare function listPendingCronFiles(delivered?: Set<string>): string[];
|
|
11
19
|
export declare function startCronWatcher(options: CronWatcherOptions): () => void;
|
|
20
|
+
export declare function runCronBackfill(session: ConnectResult, resetIndex: boolean): Promise<{
|
|
21
|
+
clearedEntries: number;
|
|
22
|
+
pendingBefore: number;
|
|
23
|
+
deliveredCount: number;
|
|
24
|
+
remainingCount: number;
|
|
25
|
+
}>;
|
|
12
26
|
export {};
|
|
13
27
|
//# sourceMappingURL=cronWatcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cronWatcher.d.ts","sourceRoot":"","sources":["../src/cronWatcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cronWatcher.d.ts","sourceRoot":"","sources":["../src/cronWatcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,eAAO,MAAM,sBAAsB,QAA+C,CAAC;AAEnF,eAAO,MAAM,oBAAoB,QAAmD,CAAC;AASrF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAgB,kBAAkB,IAAI,GAAG,CAAC,MAAM,CAAC,CAQhD;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAGhE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,MAA+B,GAAG,MAAM,EAAE,CAoBtF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,oBAAoB,CAAC,SAAS,GAAE,GAAG,CAAC,MAAM,CAAwB,GAAG,MAAM,EAAE,CAE5F;AAqHD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,IAAI,CA8BxE;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1F,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAsBD"}
|
package/dist/cronWatcher.js
CHANGED
|
@@ -3,12 +3,12 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { dirname, join, relative } from "node:path";
|
|
4
4
|
import { resolveActiveConversationId } from "./activeConversation.js";
|
|
5
5
|
import { loadCredentials } from "./credentials.js";
|
|
6
|
-
const HERMES_CRON_OUTPUT_DIR = join(homedir(), ".hermes", "cron", "output");
|
|
6
|
+
export const HERMES_CRON_OUTPUT_DIR = join(homedir(), ".hermes", "cron", "output");
|
|
7
7
|
const HERMES_CRON_JOBS_FILE = join(homedir(), ".hermes", "cron", "jobs.json");
|
|
8
|
-
const DELIVERED_INDEX_PATH = join(homedir(), ".cleos", "cron-delivered.json");
|
|
8
|
+
export const DELIVERED_INDEX_PATH = join(homedir(), ".cleos", "cron-delivered.json");
|
|
9
9
|
const POLL_INTERVAL_MS = 5_000;
|
|
10
10
|
const FILE_SETTLE_MS = 750;
|
|
11
|
-
function readDeliveredIndex() {
|
|
11
|
+
export function readDeliveredIndex() {
|
|
12
12
|
try {
|
|
13
13
|
const parsed = JSON.parse(readFileSync(DELIVERED_INDEX_PATH, "utf8"));
|
|
14
14
|
if (!Array.isArray(parsed))
|
|
@@ -19,11 +19,16 @@ function readDeliveredIndex() {
|
|
|
19
19
|
return new Set();
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
function writeDeliveredIndex(delivered) {
|
|
22
|
+
export function writeDeliveredIndex(delivered) {
|
|
23
23
|
mkdirSync(dirname(DELIVERED_INDEX_PATH), { recursive: true });
|
|
24
24
|
writeFileSync(DELIVERED_INDEX_PATH, JSON.stringify([...delivered].sort(), null, 2));
|
|
25
25
|
}
|
|
26
|
-
function
|
|
26
|
+
export function clearDeliveredIndex() {
|
|
27
|
+
const previous = readDeliveredIndex().size;
|
|
28
|
+
writeDeliveredIndex(new Set());
|
|
29
|
+
return previous;
|
|
30
|
+
}
|
|
31
|
+
export function listCronOutputFiles(rootDir = HERMES_CRON_OUTPUT_DIR) {
|
|
27
32
|
if (!existsSync(rootDir))
|
|
28
33
|
return [];
|
|
29
34
|
const files = [];
|
|
@@ -46,6 +51,12 @@ function listCronOutputFiles(rootDir) {
|
|
|
46
51
|
}
|
|
47
52
|
return files.sort();
|
|
48
53
|
}
|
|
54
|
+
export function relativeOutputKey(filePath) {
|
|
55
|
+
return relative(HERMES_CRON_OUTPUT_DIR, filePath).replace(/\\/g, "/");
|
|
56
|
+
}
|
|
57
|
+
export function listPendingCronFiles(delivered = readDeliveredIndex()) {
|
|
58
|
+
return listCronOutputFiles().filter((filePath) => !delivered.has(relativeOutputKey(filePath)));
|
|
59
|
+
}
|
|
49
60
|
function loadHermesCronJobs() {
|
|
50
61
|
const names = new Map();
|
|
51
62
|
if (!existsSync(HERMES_CRON_JOBS_FILE))
|
|
@@ -68,9 +79,6 @@ function loadHermesCronJobs() {
|
|
|
68
79
|
}
|
|
69
80
|
return names;
|
|
70
81
|
}
|
|
71
|
-
function relativeOutputKey(filePath) {
|
|
72
|
-
return relative(HERMES_CRON_OUTPUT_DIR, filePath).replace(/\\/g, "/");
|
|
73
|
-
}
|
|
74
82
|
function parseRunAtFromFileName(filePath) {
|
|
75
83
|
const base = filePath.split("/").pop()?.replace(/\.md$/i, "") ?? "";
|
|
76
84
|
if (!base)
|
|
@@ -103,14 +111,58 @@ async function readFileWhenStable(filePath) {
|
|
|
103
111
|
function sleep(ms) {
|
|
104
112
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
105
113
|
}
|
|
114
|
+
function isRelayNotOpenError(error) {
|
|
115
|
+
return error instanceof Error && error.message === "Cleos relay connection is not open";
|
|
116
|
+
}
|
|
117
|
+
async function deliverPendingFiles(session, delivered, conversationId, options) {
|
|
118
|
+
const jobNames = loadHermesCronJobs();
|
|
119
|
+
for (const filePath of listCronOutputFiles()) {
|
|
120
|
+
const key = relativeOutputKey(filePath);
|
|
121
|
+
if (delivered.has(key))
|
|
122
|
+
continue;
|
|
123
|
+
const parts = key.split("/");
|
|
124
|
+
const jobId = parts[0] ?? "unknown";
|
|
125
|
+
const jobName = jobNames.get(jobId) ?? jobId;
|
|
126
|
+
const content = await readFileWhenStable(filePath);
|
|
127
|
+
if (!content) {
|
|
128
|
+
delivered.add(key);
|
|
129
|
+
writeDeliveredIndex(delivered);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
session.deliverCronResult(content, {
|
|
134
|
+
conversationId,
|
|
135
|
+
jobId,
|
|
136
|
+
jobName,
|
|
137
|
+
runAt: parseRunAtFromFileName(filePath),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (isRelayNotOpenError(error)) {
|
|
142
|
+
console.log(JSON.stringify({
|
|
143
|
+
event: "cleos-bridge.cron-deferred",
|
|
144
|
+
file: key,
|
|
145
|
+
reason: "relay_not_open",
|
|
146
|
+
}));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
delivered.add(key);
|
|
152
|
+
writeDeliveredIndex(delivered);
|
|
153
|
+
options.onDelivered?.({ jobId, jobName, filePath });
|
|
154
|
+
console.log(JSON.stringify({
|
|
155
|
+
event: "cleos-bridge.cron-delivered",
|
|
156
|
+
jobId,
|
|
157
|
+
jobName,
|
|
158
|
+
file: key,
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
106
162
|
export function startCronWatcher(options) {
|
|
107
163
|
const delivered = readDeliveredIndex();
|
|
108
164
|
let conversationId = loadCredentials()?.conversationId ?? null;
|
|
109
165
|
let stopped = false;
|
|
110
|
-
for (const filePath of listCronOutputFiles(HERMES_CRON_OUTPUT_DIR)) {
|
|
111
|
-
delivered.add(relativeOutputKey(filePath));
|
|
112
|
-
}
|
|
113
|
-
writeDeliveredIndex(delivered);
|
|
114
166
|
const tick = () => {
|
|
115
167
|
void (async () => {
|
|
116
168
|
if (stopped)
|
|
@@ -119,35 +171,7 @@ export function startCronWatcher(options) {
|
|
|
119
171
|
if (!conversationId) {
|
|
120
172
|
conversationId = await resolveActiveConversationId();
|
|
121
173
|
}
|
|
122
|
-
|
|
123
|
-
for (const filePath of listCronOutputFiles(HERMES_CRON_OUTPUT_DIR)) {
|
|
124
|
-
const key = relativeOutputKey(filePath);
|
|
125
|
-
if (delivered.has(key))
|
|
126
|
-
continue;
|
|
127
|
-
const parts = key.split("/");
|
|
128
|
-
const jobId = parts[0] ?? "unknown";
|
|
129
|
-
const jobName = jobNames.get(jobId) ?? jobId;
|
|
130
|
-
const content = await readFileWhenStable(filePath);
|
|
131
|
-
if (!content) {
|
|
132
|
-
delivered.add(key);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
options.session.deliverCronResult(content, {
|
|
136
|
-
conversationId: conversationId,
|
|
137
|
-
jobId,
|
|
138
|
-
jobName,
|
|
139
|
-
runAt: parseRunAtFromFileName(filePath),
|
|
140
|
-
});
|
|
141
|
-
delivered.add(key);
|
|
142
|
-
writeDeliveredIndex(delivered);
|
|
143
|
-
options.onDelivered?.({ jobId, jobName, filePath });
|
|
144
|
-
console.log(JSON.stringify({
|
|
145
|
-
event: "cleos-bridge.cron-delivered",
|
|
146
|
-
jobId,
|
|
147
|
-
jobName,
|
|
148
|
-
file: key,
|
|
149
|
-
}));
|
|
150
|
-
}
|
|
174
|
+
await deliverPendingFiles(options.session, delivered, conversationId, options);
|
|
151
175
|
}
|
|
152
176
|
catch (error) {
|
|
153
177
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -163,3 +187,23 @@ export function startCronWatcher(options) {
|
|
|
163
187
|
clearInterval(timer);
|
|
164
188
|
};
|
|
165
189
|
}
|
|
190
|
+
export async function runCronBackfill(session, resetIndex) {
|
|
191
|
+
const pendingBefore = listPendingCronFiles().length;
|
|
192
|
+
const clearedEntries = resetIndex ? clearDeliveredIndex() : 0;
|
|
193
|
+
const delivered = readDeliveredIndex();
|
|
194
|
+
const pendingAfterReset = listPendingCronFiles(delivered).length;
|
|
195
|
+
let conversationId = loadCredentials()?.conversationId ?? null;
|
|
196
|
+
if (!conversationId) {
|
|
197
|
+
conversationId = await resolveActiveConversationId();
|
|
198
|
+
}
|
|
199
|
+
const deliveredBefore = delivered.size;
|
|
200
|
+
await deliverPendingFiles(session, delivered, conversationId, {});
|
|
201
|
+
const deliveredCount = delivered.size - deliveredBefore;
|
|
202
|
+
const remainingCount = listPendingCronFiles(delivered).length;
|
|
203
|
+
return {
|
|
204
|
+
clearedEntries,
|
|
205
|
+
pendingBefore: resetIndex ? pendingAfterReset : pendingBefore,
|
|
206
|
+
deliveredCount,
|
|
207
|
+
remainingCount,
|
|
208
|
+
};
|
|
209
|
+
}
|
package/dist/hermesCommands.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const HERMES_COMMAND_NAMES: readonly ["runtime.health", "runtime.detailedHealth", "runtime.capabilities", "models.list", "model.set", "responses.create", "runs.create", "runs.status", "runs.stop", "jobs.list", "jobs.get", "jobs.create", "jobs.update", "jobs.pause", "jobs.resume", "jobs.runNow", "jobs.delete", "profiles.list", "profiles.create", "gateway.start", "gateway.stop", "gateway.restart", "hermes.update"];
|
|
1
|
+
export declare const HERMES_COMMAND_NAMES: readonly ["runtime.health", "runtime.detailedHealth", "runtime.version", "runtime.capabilities", "models.list", "model.set", "responses.create", "runs.create", "runs.status", "runs.stop", "jobs.list", "jobs.get", "jobs.create", "jobs.update", "jobs.pause", "jobs.resume", "jobs.runNow", "jobs.delete", "profiles.list", "profiles.create", "gateway.start", "gateway.stop", "gateway.restart", "hermes.update", "sessions.messages.list", "sessions.messages.countSent", "skills.list", "files.list", "files.read"];
|
|
2
2
|
export type HermesCommandName = (typeof HERMES_COMMAND_NAMES)[number];
|
|
3
3
|
export declare function isHermesCommandName(value: string): value is HermesCommandName;
|
|
4
4
|
export type HermesCommandErrorCode = "command_unsupported" | "hermes_unreachable" | "hermes_request_failed" | "invalid_command_args" | "unsupported_by_http";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hermesCommands.d.ts","sourceRoot":"","sources":["../src/hermesCommands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hermesCommands.d.ts","sourceRoot":"","sources":["../src/hermesCommands.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,oBAAoB,4fA8BvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,iBAAiB,CAE7E;AAED,MAAM,MAAM,sBAAsB,GAC9B,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,GACvB,sBAAsB,GACtB,qBAAqB,CAAC;AAE1B,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;gBAE1B,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM;CAI1D;AAgGD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACjE,OAAO,CAAC,OAAO,CAAC,CAwLlB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAetG"}
|
package/dist/hermesCommands.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { resolveHermesApiConfig } from "./hermesForwarder.js";
|
|
2
2
|
import { restartHermesGateway, startHermesGateway, stopHermesGateway } from "./gatewayControl.js";
|
|
3
3
|
import { listHermesCronJobs } from "./cronList.js";
|
|
4
|
+
import { listSessionMessages, countUserMessagesSent } from "./hermesSessionDb.js";
|
|
5
|
+
import { listHermesSkills } from "./skillsList.js";
|
|
4
6
|
import { runHermesUpdate } from "./hermesUpdate.js";
|
|
7
|
+
import { executeFilesList, executeFilesRead } from "./hermesFileCommands.js";
|
|
8
|
+
import { fetchHermesRuntimeVersion } from "./runtimeVersion.js";
|
|
5
9
|
export const HERMES_COMMAND_NAMES = [
|
|
6
10
|
"runtime.health",
|
|
7
11
|
"runtime.detailedHealth",
|
|
12
|
+
"runtime.version",
|
|
8
13
|
"runtime.capabilities",
|
|
9
14
|
"models.list",
|
|
10
15
|
"model.set",
|
|
@@ -26,6 +31,11 @@ export const HERMES_COMMAND_NAMES = [
|
|
|
26
31
|
"gateway.stop",
|
|
27
32
|
"gateway.restart",
|
|
28
33
|
"hermes.update",
|
|
34
|
+
"sessions.messages.list",
|
|
35
|
+
"sessions.messages.countSent",
|
|
36
|
+
"skills.list",
|
|
37
|
+
"files.list",
|
|
38
|
+
"files.read",
|
|
29
39
|
];
|
|
30
40
|
export function isHermesCommandName(value) {
|
|
31
41
|
return HERMES_COMMAND_NAMES.includes(value);
|
|
@@ -71,6 +81,15 @@ function optionalRecord(args, key) {
|
|
|
71
81
|
}
|
|
72
82
|
return value;
|
|
73
83
|
}
|
|
84
|
+
function optionalNumber(args, key) {
|
|
85
|
+
const value = args[key];
|
|
86
|
+
if (value === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
89
|
+
throw new HermesCommandError("invalid_command_args", `Invalid "${key}"`);
|
|
90
|
+
}
|
|
91
|
+
return Math.floor(value);
|
|
92
|
+
}
|
|
74
93
|
async function hermesFetchJson(config, path, init = {}) {
|
|
75
94
|
const url = `${hermesBaseUrl(config.apiUrl)}${path}`;
|
|
76
95
|
const headers = {
|
|
@@ -109,6 +128,8 @@ export async function executeHermesCommand(command, args, options = {}) {
|
|
|
109
128
|
return await hermesFetchJson(config, "/health");
|
|
110
129
|
case "runtime.detailedHealth":
|
|
111
130
|
return await hermesFetchJson(config, "/health/detailed");
|
|
131
|
+
case "runtime.version":
|
|
132
|
+
return await fetchHermesRuntimeVersion();
|
|
112
133
|
case "runtime.capabilities":
|
|
113
134
|
return await hermesFetchJson(config, "/v1/capabilities");
|
|
114
135
|
case "models.list":
|
|
@@ -256,6 +277,26 @@ export async function executeHermesCommand(command, args, options = {}) {
|
|
|
256
277
|
const restartGateway = args.restartGateway === true || args.restart_gateway === true;
|
|
257
278
|
return await runHermesUpdate({ restartGateway });
|
|
258
279
|
}
|
|
280
|
+
case "sessions.messages.list": {
|
|
281
|
+
const sessionId = requireString(args, "sessionId");
|
|
282
|
+
const limit = optionalNumber(args, "limit");
|
|
283
|
+
const offset = optionalNumber(args, "offset");
|
|
284
|
+
const offsetFromEnd = optionalNumber(args, "offsetFromEnd");
|
|
285
|
+
return listSessionMessages(sessionId, { limit, offset, offsetFromEnd });
|
|
286
|
+
}
|
|
287
|
+
case "sessions.messages.countSent":
|
|
288
|
+
return countUserMessagesSent();
|
|
289
|
+
case "skills.list":
|
|
290
|
+
return await listHermesSkills();
|
|
291
|
+
case "files.list":
|
|
292
|
+
return await executeFilesList(args);
|
|
293
|
+
case "files.read": {
|
|
294
|
+
const path = optionalString(args, "path");
|
|
295
|
+
if (!path) {
|
|
296
|
+
throw new HermesCommandError("invalid_command_args", 'Missing "path"');
|
|
297
|
+
}
|
|
298
|
+
return await executeFilesRead({ ...args, path });
|
|
299
|
+
}
|
|
259
300
|
default: {
|
|
260
301
|
const _exhaustive = command;
|
|
261
302
|
throw new HermesCommandError("command_unsupported", `Unsupported command: ${String(_exhaustive)}`);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function executeFilesList(args: Record<string, unknown>): Promise<{
|
|
2
|
+
files: import("./hermesFiles.js").HermesFileEntry[];
|
|
3
|
+
}>;
|
|
4
|
+
export declare function executeFilesRead(args: Record<string, unknown>): Promise<{
|
|
5
|
+
fileName: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
size: number;
|
|
8
|
+
contentBase64: string;
|
|
9
|
+
storageId?: undefined;
|
|
10
|
+
url?: undefined;
|
|
11
|
+
} | {
|
|
12
|
+
fileName: string;
|
|
13
|
+
mimeType: string;
|
|
14
|
+
size: number;
|
|
15
|
+
storageId: string;
|
|
16
|
+
url: string | undefined;
|
|
17
|
+
contentBase64?: undefined;
|
|
18
|
+
}>;
|
|
19
|
+
//# sourceMappingURL=hermesFileCommands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermesFileCommands.d.ts","sourceRoot":"","sources":["../src/hermesFileCommands.ts"],"names":[],"mappings":"AA6BA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;GAKnE;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;;GAkCnE"}
|