@saleso.innovations/bridge 0.1.7 → 0.1.9

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 CHANGED
@@ -34,6 +34,9 @@ export async function onCleosPairingCodeSubmitted(code: string) {
34
34
  for await (const chunk of stream) {
35
35
  reply.delta(chunk, sequence++);
36
36
  }
37
+ // If you stream from Hermes /v1/chat/completions, also forward tool progress via
38
+ // reply.activity() when you receive `event: hermes.tool.progress` SSE events.
39
+ // Do not inject tool markers into assistant text (see Hermes #6972).
37
40
  reply.complete(stream.finalText, sequence);
38
41
  },
39
42
  });
package/README.md CHANGED
@@ -15,9 +15,25 @@ Requirements:
15
15
  - Node.js 20+
16
16
  - Hermes installed on the same machine (`hermes gateway` — the install script configures the API automatically)
17
17
 
18
+ ## Update
19
+
20
+ After the first install, updates are one command:
21
+
22
+ ```bash
23
+ cleos-bridge update
24
+ ```
25
+
26
+ The install script links `cleos-bridge` to `/usr/local/bin` (root) or `~/.local/bin` (non-root). If that command is not found yet, use:
27
+
28
+ ```bash
29
+ curl -fsSL https://amicable-elephant-407.convex.site/update-bridge.sh | bash
30
+ ```
31
+
32
+ This installs the latest package, refreshes the CLI symlink, and restarts `cleos-bridge.service` when present.
33
+
18
34
  ## Manual usage
19
35
 
20
- The install script puts the bridge in `~/.cleos/bridge` (no PATH setup needed). To run manually:
36
+ The install script puts the bridge in `~/.cleos/bridge`. To run without the symlink:
21
37
 
22
38
  ```bash
23
39
  node ~/.cleos/bridge/node_modules/@saleso.innovations/bridge/dist/cli.js pair ABCD1234
@@ -0,0 +1,7 @@
1
+ export declare const BRIDGE_PACKAGE = "@saleso.innovations/bridge";
2
+ export declare function defaultBridgeInstallDir(): string;
3
+ export declare function bridgeCliPath(bridgeDir: string): string;
4
+ /** Directory used by `npm install --prefix` (usually ~/.cleos/bridge). */
5
+ export declare function resolveBridgeInstallDir(): string;
6
+ export declare function readInstalledBridgeVersion(bridgeDir: string): string | null;
7
+ //# sourceMappingURL=bridgePaths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridgePaths.d.ts","sourceRoot":"","sources":["../src/bridgePaths.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,+BAA+B,CAAC;AAE3D,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAwBD,0EAA0E;AAC1E,wBAAgB,uBAAuB,IAAI,MAAM,CAMhD;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ3E"}
@@ -0,0 +1,51 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { readFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join, sep } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ export const BRIDGE_PACKAGE = "@saleso.innovations/bridge";
7
+ export function defaultBridgeInstallDir() {
8
+ return join(homedir(), ".cleos", "bridge");
9
+ }
10
+ export function bridgeCliPath(bridgeDir) {
11
+ return join(bridgeDir, "node_modules", BRIDGE_PACKAGE, "dist", "cli.js");
12
+ }
13
+ function inferBridgeDirFromCliPath(cliPath) {
14
+ const marker = `${sep}node_modules${sep}${BRIDGE_PACKAGE.replace("/", sep)}${sep}`;
15
+ const idx = cliPath.indexOf(marker);
16
+ if (idx < 0)
17
+ return null;
18
+ const nodeModulesDir = join(cliPath.slice(0, idx), "node_modules");
19
+ return dirname(nodeModulesDir);
20
+ }
21
+ function readBridgeDirFromSystemd() {
22
+ try {
23
+ const unit = execFileSync("systemctl", ["cat", "cleos-bridge.service"], {
24
+ encoding: "utf8",
25
+ stdio: ["ignore", "pipe", "ignore"],
26
+ });
27
+ const match = unit.match(/ExecStart=\S+\s+(\S+\/dist\/cli\.js)/);
28
+ if (!match?.[1])
29
+ return null;
30
+ return inferBridgeDirFromCliPath(match[1]);
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ /** Directory used by `npm install --prefix` (usually ~/.cleos/bridge). */
37
+ export function resolveBridgeInstallDir() {
38
+ return (readBridgeDirFromSystemd() ??
39
+ inferBridgeDirFromCliPath(fileURLToPath(import.meta.url)) ??
40
+ defaultBridgeInstallDir());
41
+ }
42
+ export function readInstalledBridgeVersion(bridgeDir) {
43
+ const pkgJson = join(bridgeDir, "node_modules", BRIDGE_PACKAGE, "package.json");
44
+ try {
45
+ const parsed = JSON.parse(readFileSync(pkgJson, "utf8"));
46
+ return parsed.version ?? null;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@ import { credentialsPathForDisplay, loadCredentials } from "./credentials.js";
3
3
  import { connectHermesAgent, pairCleosAgent, reconnectHermesAgent } from "./client.js";
4
4
  import { startCronWatcher } from "./cronWatcher.js";
5
5
  import { runBridgeDaemon } from "./daemon.js";
6
+ import { runBridgeUpdate } from "./update.js";
6
7
  import { prepareHermesForBridge } from "./ensureHermesApi.js";
7
8
  import { createHermesMessageHandler } from "./hermesForwarder.js";
8
9
  import { convexSiteUrlFromEnv } from "./resolve.js";
@@ -65,6 +66,10 @@ async function main() {
65
66
  await waitForExit(session, () => startCronWatcher({ session }));
66
67
  return;
67
68
  }
69
+ if (command === "update") {
70
+ runBridgeUpdate({ tag: codeArg || "latest" });
71
+ return;
72
+ }
68
73
  printUsage();
69
74
  process.exit(1);
70
75
  }
@@ -74,6 +79,7 @@ function printUsage() {
74
79
  console.error(" cleos-bridge connect <CODE> Pair and stay connected in the foreground");
75
80
  console.error(" cleos-bridge start [CODE] Run as a reconnecting daemon (systemd)");
76
81
  console.error(" cleos-bridge reconnect Reconnect once using saved credentials");
82
+ console.error(" cleos-bridge update [TAG] Update package and restart systemd service");
77
83
  }
78
84
  async function waitForExit(session, onStart) {
79
85
  const stop = onStart?.();
package/dist/client.d.ts CHANGED
@@ -21,8 +21,16 @@ export type UserMessageMeta = {
21
21
  messageId: string;
22
22
  attachments?: UserMessageAttachment[];
23
23
  };
24
+ export type AgentActivityPayload = {
25
+ activityType: "tool.started" | "tool.completed";
26
+ tool?: string;
27
+ label?: string;
28
+ emoji?: string;
29
+ sequence: number;
30
+ };
24
31
  export type AgentReply = {
25
32
  delta: (text: string, sequence: number) => void;
33
+ activity: (activity: AgentActivityPayload) => void;
26
34
  complete: (text: string, sequence: number) => void;
27
35
  };
28
36
  export type CronDeliveryMeta = {
@@ -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;AAKhG,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,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtE,CAAC;AA2LF,wBAAsB,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CA2BD;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAcxF;AAED,wBAAsB,oBAAoB,CAAC,OAAO,GAAE;IAClD,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;CAC5C,GAAG,OAAO,CAAC,aAAa,CAAC,CAc9B"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAKhG,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,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,cAAc,GAAG,gBAAgB,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,QAAQ,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtE,CAAC;AA0MF,wBAAsB,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CA2BD;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAcxF;AAED,wBAAsB,oBAAoB,CAAC,OAAO,GAAE;IAClD,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;CAC5C,GAAG,OAAO,CAAC,aAAa,CAAC,CAc9B"}
package/dist/client.js CHANGED
@@ -57,6 +57,19 @@ function createReplySender(ws, agentId, conversationId, messageId) {
57
57
  sequence,
58
58
  }));
59
59
  },
60
+ activity(activity) {
61
+ ws.send(JSON.stringify({
62
+ type: "agent.activity",
63
+ agentId,
64
+ conversationId,
65
+ messageId,
66
+ activityType: activity.activityType,
67
+ tool: activity.tool,
68
+ label: activity.label,
69
+ emoji: activity.emoji,
70
+ sequence: activity.sequence,
71
+ }));
72
+ },
60
73
  complete(text, sequence) {
61
74
  ws.send(JSON.stringify({
62
75
  type: "agent.message",
@@ -4,5 +4,8 @@ export declare const DEFAULT_CLEOS_CONVEX_SITE_URL = "https://amicable-elephant-
4
4
  export declare const DEFAULT_HERMES_API_URL = "http://127.0.0.1:8642/v1/chat/completions";
5
5
  /** VPS one-line installer (served from public Convex HTTP). */
6
6
  export declare const DEFAULT_BRIDGE_INSTALL_URL = "https://amicable-elephant-407.convex.site/install-bridge.sh";
7
+ /** VPS one-line updater (served from public Convex HTTP). */
8
+ export declare const DEFAULT_BRIDGE_UPDATE_URL = "https://amicable-elephant-407.convex.site/update-bridge.sh";
7
9
  export declare function bridgeInstallCommand(code: string): string;
10
+ export declare function bridgeUpdateCommand(): string;
8
11
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,eAAO,MAAM,6BAA6B,8CAA8C,CAAC;AAEzF,6FAA6F;AAC7F,eAAO,MAAM,sBAAsB,8CAA8C,CAAC;AAElF,+DAA+D;AAC/D,eAAO,MAAM,0BAA0B,gEACwB,CAAC;AAEhE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,eAAO,MAAM,6BAA6B,8CAA8C,CAAC;AAEzF,6FAA6F;AAC7F,eAAO,MAAM,sBAAsB,8CAA8C,CAAC;AAElF,+DAA+D;AAC/D,eAAO,MAAM,0BAA0B,gEACwB,CAAC;AAEhE,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,+DACwB,CAAC;AAE/D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C"}
package/dist/constants.js CHANGED
@@ -4,6 +4,11 @@ export const DEFAULT_CLEOS_CONVEX_SITE_URL = "https://amicable-elephant-407.conv
4
4
  export const DEFAULT_HERMES_API_URL = "http://127.0.0.1:8642/v1/chat/completions";
5
5
  /** VPS one-line installer (served from public Convex HTTP). */
6
6
  export const DEFAULT_BRIDGE_INSTALL_URL = "https://amicable-elephant-407.convex.site/install-bridge.sh";
7
+ /** VPS one-line updater (served from public Convex HTTP). */
8
+ export const DEFAULT_BRIDGE_UPDATE_URL = "https://amicable-elephant-407.convex.site/update-bridge.sh";
7
9
  export function bridgeInstallCommand(code) {
8
10
  return `curl -fsSL ${DEFAULT_BRIDGE_INSTALL_URL} | bash -s -- ${code}`;
9
11
  }
12
+ export function bridgeUpdateCommand() {
13
+ return `curl -fsSL ${DEFAULT_BRIDGE_UPDATE_URL} | bash`;
14
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"hermesForwarder.d.ts","sourceRoot":"","sources":["../src/hermesForwarder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAyB,eAAe,EAAE,MAAM,aAAa,CAAC;AAGtF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAkBF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,sBAA2B,GAAG;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf,CAUA;AA2ED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CAyEf;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,sBAA2B,IAC/D,SAAS,MAAM,EAAE,MAAM,eAAe,EAAE,OAAO,UAAU,KAAG,OAAO,CAAC,IAAI,CAAC,CAMxF"}
1
+ {"version":3,"file":"hermesForwarder.d.ts","sourceRoot":"","sources":["../src/hermesForwarder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAyB,eAAe,EAAE,MAAM,aAAa,CAAC;AAGtF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAkBF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,sBAA2B,GAAG;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf,CAUA;AAyID,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CA4Df;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,sBAA2B,IAC/D,SAAS,MAAM,EAAE,MAAM,eAAe,EAAE,OAAO,UAAU,KAAG,OAAO,CAAC,IAAI,CAAC,CAMxF"}
@@ -64,6 +64,52 @@ function extractDeltaFromChunk(payload) {
64
64
  const content = delta.content;
65
65
  return typeof content === "string" ? content : null;
66
66
  }
67
+ function processSseEventBlock(eventBlock, reply, counters, onTextDelta) {
68
+ let eventName = "message";
69
+ for (const line of eventBlock.split("\n")) {
70
+ if (line.startsWith("event:")) {
71
+ eventName = line.slice(6).trim();
72
+ continue;
73
+ }
74
+ if (!line.startsWith("data:"))
75
+ continue;
76
+ const data = line.slice(5).trim();
77
+ if (!data || data === "[DONE]")
78
+ continue;
79
+ if (eventName === "hermes.tool.progress") {
80
+ try {
81
+ const payload = JSON.parse(data);
82
+ const tool = typeof payload.tool === "string" ? payload.tool : undefined;
83
+ const label = typeof payload.label === "string" ? payload.label : undefined;
84
+ const emoji = typeof payload.emoji === "string" ? payload.emoji : undefined;
85
+ reply.activity({
86
+ activityType: "tool.started",
87
+ tool,
88
+ label,
89
+ emoji,
90
+ sequence: counters.activitySequence,
91
+ });
92
+ counters.activitySequence += 1;
93
+ }
94
+ catch {
95
+ // Ignore malformed tool progress payloads.
96
+ }
97
+ continue;
98
+ }
99
+ try {
100
+ const payload = JSON.parse(data);
101
+ const delta = extractDeltaFromChunk(payload);
102
+ if (delta) {
103
+ onTextDelta(delta);
104
+ reply.delta(delta, counters.textSequence);
105
+ counters.textSequence += 1;
106
+ }
107
+ }
108
+ catch {
109
+ // Ignore malformed SSE chunks.
110
+ }
111
+ }
112
+ }
67
113
  function buildHermesUserContent(text, attachments) {
68
114
  const trimmed = text.trim();
69
115
  const imageParts = (attachments ?? [])
@@ -121,7 +167,7 @@ export async function forwardToHermes(content, meta, reply, options = {}) {
121
167
  const reader = response.body.getReader();
122
168
  const decoder = new TextDecoder();
123
169
  let buffer = "";
124
- let sequence = 0;
170
+ const counters = { textSequence: 0, activitySequence: 0 };
125
171
  let fullText = "";
126
172
  while (true) {
127
173
  const { done, value } = await reader.read();
@@ -130,34 +176,18 @@ export async function forwardToHermes(content, meta, reply, options = {}) {
130
176
  buffer += decoder.decode(value, { stream: true });
131
177
  const { events, rest } = parseSseDataLines(buffer);
132
178
  buffer = rest;
133
- for (const event of events) {
134
- for (const line of event.split("\n")) {
135
- if (!line.startsWith("data: "))
136
- continue;
137
- const data = line.slice(6).trim();
138
- if (!data || data === "[DONE]")
139
- continue;
140
- try {
141
- const payload = JSON.parse(data);
142
- const delta = extractDeltaFromChunk(payload);
143
- if (delta) {
144
- fullText += delta;
145
- reply.delta(delta, sequence);
146
- sequence += 1;
147
- }
148
- }
149
- catch {
150
- // Ignore malformed SSE chunks.
151
- }
152
- }
179
+ for (const eventBlock of events) {
180
+ processSseEventBlock(eventBlock, reply, counters, (delta) => {
181
+ fullText += delta;
182
+ });
153
183
  }
154
184
  }
155
185
  if (!fullText.trim()) {
156
186
  fullText = "(Hermes returned an empty response)";
157
- reply.delta(fullText, sequence);
158
- sequence += 1;
187
+ reply.delta(fullText, counters.textSequence);
188
+ counters.textSequence += 1;
159
189
  }
160
- reply.complete(fullText, sequence);
190
+ reply.complete(fullText, counters.textSequence);
161
191
  }
162
192
  export function createHermesMessageHandler(options = {}) {
163
193
  return async (content, meta, reply) => {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { connectHermesAgent, pairCleosAgent, reconnectHermesAgent } from "./client.js";
2
- export type { AgentReply, ConnectOptions, ConnectResult, CronDeliveryMeta, UserMessageMeta } from "./client.js";
2
+ export type { AgentActivityPayload, AgentReply, ConnectOptions, ConnectResult, CronDeliveryMeta, UserMessageMeta, } from "./client.js";
3
3
  export { loadCredentials, saveCredentials, credentialsPathForDisplay } from "./credentials.js";
4
4
  export type { SavedAgentCredentials } from "./credentials.js";
5
5
  export { resolvePairingCode, convexSiteUrlFromEnv } from "./resolve.js";
@@ -11,5 +11,5 @@ export { startCronWatcher } from "./cronWatcher.js";
11
11
  export { resolveActiveConversationId, rememberConversationId } from "./activeConversation.js";
12
12
  export { runBridgeDaemon } from "./daemon.js";
13
13
  export type { RunBridgeOptions } from "./daemon.js";
14
- export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, bridgeInstallCommand, } from "./constants.js";
14
+ export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_BRIDGE_UPDATE_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, bridgeInstallCommand, bridgeUpdateCommand, } from "./constants.js";
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvF,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,EAC7B,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvF,YAAY,EACV,oBAAoB,EACpB,UAAU,EACV,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,6BAA6B,EAC7B,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -6,4 +6,4 @@ export { createHermesMessageHandler, forwardToHermes, resolveHermesApiConfig } f
6
6
  export { startCronWatcher } from "./cronWatcher.js";
7
7
  export { resolveActiveConversationId, rememberConversationId } from "./activeConversation.js";
8
8
  export { runBridgeDaemon } from "./daemon.js";
9
- export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, bridgeInstallCommand, } from "./constants.js";
9
+ export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_BRIDGE_UPDATE_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, bridgeInstallCommand, bridgeUpdateCommand, } from "./constants.js";
@@ -0,0 +1,8 @@
1
+ export type RunBridgeUpdateOptions = {
2
+ /** npm dist-tag or semver, default `latest`. */
3
+ tag?: string;
4
+ /** Skip systemd / service restart after install. */
5
+ skipRestart?: boolean;
6
+ };
7
+ export declare function runBridgeUpdate(options?: RunBridgeUpdateOptions): void;
8
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,sBAAsB,GAAG;IACnC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,IAAI,CAsC1E"}
package/dist/update.js ADDED
@@ -0,0 +1,80 @@
1
+ import { execFileSync, spawnSync } from "node:child_process";
2
+ import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { BRIDGE_PACKAGE, bridgeCliPath, readInstalledBridgeVersion, resolveBridgeInstallDir, } from "./bridgePaths.js";
6
+ export function runBridgeUpdate(options = {}) {
7
+ const tag = options.tag ?? "latest";
8
+ const bridgeDir = resolveBridgeInstallDir();
9
+ const beforeVersion = readInstalledBridgeVersion(bridgeDir);
10
+ mkdirSync(bridgeDir, { recursive: true });
11
+ console.log(`Updating ${BRIDGE_PACKAGE}@${tag} in ${bridgeDir}...`);
12
+ const npm = spawnSync("npm", [
13
+ "install",
14
+ "--prefix",
15
+ bridgeDir,
16
+ `${BRIDGE_PACKAGE}@${tag}`,
17
+ "--no-fund",
18
+ "--no-audit",
19
+ "--omit=dev",
20
+ ], { stdio: "inherit", env: process.env });
21
+ if (npm.status !== 0) {
22
+ throw new Error("npm install failed");
23
+ }
24
+ const cliPath = bridgeCliPath(bridgeDir);
25
+ linkBridgeCli(cliPath);
26
+ const afterVersion = readInstalledBridgeVersion(bridgeDir);
27
+ if (beforeVersion && afterVersion && beforeVersion === afterVersion) {
28
+ console.log(`Already on ${afterVersion}.`);
29
+ }
30
+ else {
31
+ console.log(`Updated ${beforeVersion ?? "unknown"} → ${afterVersion ?? "unknown"}.`);
32
+ }
33
+ if (!options.skipRestart) {
34
+ restartBridgeService();
35
+ }
36
+ }
37
+ function linkBridgeCli(cliPath) {
38
+ const nodeBin = process.execPath;
39
+ const wrapper = `#!/usr/bin/env bash\nexec "${nodeBin}" "${cliPath}" "$@"\n`;
40
+ const targets = process.getuid?.() === 0
41
+ ? ["/usr/local/bin/cleos-bridge"]
42
+ : [join(homedir(), ".local", "bin", "cleos-bridge")];
43
+ for (const target of targets) {
44
+ try {
45
+ mkdirSync(join(target, ".."), { recursive: true });
46
+ writeFileSync(target, wrapper, { mode: 0o755 });
47
+ chmodSync(target, 0o755);
48
+ console.log(`Linked ${target}`);
49
+ return;
50
+ }
51
+ catch {
52
+ // try next target
53
+ }
54
+ }
55
+ }
56
+ function restartBridgeService() {
57
+ const unitExists = spawnSync("systemctl", ["cat", "cleos-bridge.service"], {
58
+ encoding: "utf8",
59
+ stdio: ["ignore", "pipe", "ignore"],
60
+ });
61
+ if (unitExists.status !== 0) {
62
+ console.log("No cleos-bridge.service found. Restart manually if the bridge is running:");
63
+ console.log(" cleos-bridge start");
64
+ return;
65
+ }
66
+ console.log("Restarting cleos-bridge.service...");
67
+ let restart = spawnSync("systemctl", ["restart", "cleos-bridge.service"], { stdio: "inherit" });
68
+ if (restart.status === 0) {
69
+ console.log("cleos-bridge.service restarted.");
70
+ return;
71
+ }
72
+ try {
73
+ execFileSync("sudo", ["systemctl", "restart", "cleos-bridge.service"], { stdio: "inherit" });
74
+ console.log("cleos-bridge.service restarted (via sudo).");
75
+ }
76
+ catch {
77
+ console.log("Could not restart the service. Run:");
78
+ console.log(" sudo systemctl restart cleos-bridge");
79
+ }
80
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saleso.innovations/bridge",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Connect your Hermes agent to the Cleos iOS app via pairing code.",
5
5
  "type": "module",
6
6
  "license": "MIT",