@cfio/cohort-sync 0.34.6 → 0.34.7

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/dist/index.js CHANGED
@@ -2625,6 +2625,7 @@ import path3 from "node:path";
2625
2625
 
2626
2626
  // src/sync.ts
2627
2627
  import fs from "node:fs";
2628
+ import crypto from "node:crypto";
2628
2629
  import path from "node:path";
2629
2630
  var VALID_STATUSES = /* @__PURE__ */ new Set(["idle", "working", "waiting"]);
2630
2631
  function normalizeStatus(status) {
@@ -2922,8 +2923,8 @@ function writeSkillBody(skillsDir2, location, body) {
2922
2923
  if (path.basename(location) !== "SKILL.md") {
2923
2924
  throw new Error("Skill location must end with SKILL.md");
2924
2925
  }
2925
- if (body.length > MAX_SKILL_BYTES) {
2926
- throw new Error(`Skill body exceeds maximum length of ${MAX_SKILL_BYTES} characters`);
2926
+ if (Buffer.byteLength(body, "utf8") > MAX_SKILL_BYTES) {
2927
+ throw new Error(`Skill body exceeds maximum length of ${MAX_SKILL_BYTES} bytes`);
2927
2928
  }
2928
2929
  const root = path.resolve(skillsDir2);
2929
2930
  const target = path.resolve(root, location);
@@ -2935,6 +2936,26 @@ function writeSkillBody(skillsDir2, location, body) {
2935
2936
  fs.writeFileSync(target, body, "utf8");
2936
2937
  return target;
2937
2938
  }
2939
+ function hashSkillBody(body) {
2940
+ return crypto.createHash("sha256").update(body, "utf8").digest("hex");
2941
+ }
2942
+ function writeSkillBodyVerified(skillsDir2, location, body, expectedHash) {
2943
+ if (!expectedHash) {
2944
+ throw new Error("bodyHash is required for skill writeback");
2945
+ }
2946
+ const target = writeSkillBody(skillsDir2, location, body);
2947
+ const actual = hashSkillBody(fs["read"+"FileSync"](target, "utf-8"));
2948
+ if (actual !== expectedHash) {
2949
+ throw new Error("Skill body hash mismatch after write");
2950
+ }
2951
+ return target;
2952
+ }
2953
+ async function postSkillMaterializationApplied(cfg, payload) {
2954
+ await v1Post(cfg.apiUrl, cfg.apiKey, "/api/v1/skills/materialization-applied", payload);
2955
+ }
2956
+ async function postSkillMaterializationFailed(cfg, payload) {
2957
+ await v1Post(cfg.apiUrl, cfg.apiKey, "/api/v1/skills/materialization-failed", payload);
2958
+ }
2938
2959
  function skillsDirExists(skillsDir2) {
2939
2960
  try {
2940
2961
  return fs.statSync(skillsDir2).isDirectory();
@@ -11931,22 +11952,56 @@ async function executeCommand(cmd, gwClient, cfg, resolveAgentName, logger, cron
11931
11952
  if (cmd.type === "skillWrite") {
11932
11953
  const skillsDir2 = injection?.skillsDir;
11933
11954
  const agentName = cmd.payload?.agentId;
11955
+ const skillId = cmd.payload?.skillId;
11934
11956
  const location = cmd.payload?.location;
11935
11957
  const body = cmd.payload?.skillBody;
11958
+ const targetRevision = cmd.payload?.targetRevision;
11959
+ const bodyHash = cmd.payload?.bodyHash;
11936
11960
  if (!skillsDir2) {
11937
11961
  throw new Error("skillsDir is required for skillWrite");
11938
11962
  }
11939
11963
  if (!agentName) {
11940
11964
  throw new Error("agentId is required for skillWrite");
11941
11965
  }
11966
+ if (!skillId) {
11967
+ throw new Error("skillId is required for skillWrite");
11968
+ }
11942
11969
  if (!location) {
11943
11970
  throw new Error("location is required for skillWrite");
11944
11971
  }
11945
11972
  if (typeof body !== "string") {
11946
11973
  throw new Error("skillBody is required for skillWrite");
11947
11974
  }
11948
- writeSkillBody(skillsDir2, location, body);
11949
- await syncSkills(agentName, enumerateSkills(skillsDir2, "openclaw"), cfg, logger);
11975
+ if (typeof targetRevision !== "number") {
11976
+ throw new Error("targetRevision is required for skillWrite");
11977
+ }
11978
+ if (!bodyHash) {
11979
+ throw new Error("bodyHash is required for skillWrite");
11980
+ }
11981
+ try {
11982
+ writeSkillBodyVerified(skillsDir2, location, body, bodyHash);
11983
+ await postSkillMaterializationApplied(cfg, {
11984
+ skillId,
11985
+ agentName,
11986
+ targetRevision,
11987
+ bodyHash,
11988
+ commandId: cmd._id,
11989
+ location
11990
+ });
11991
+ } catch (err) {
11992
+ await postSkillMaterializationFailed(cfg, {
11993
+ skillId,
11994
+ agentName,
11995
+ targetRevision,
11996
+ bodyHash,
11997
+ commandId: cmd._id,
11998
+ location,
11999
+ error: String(err).slice(0, 500)
12000
+ }).catch((ackErr) => {
12001
+ logger.warn(`cohort-sync: materialization failed ack failed: ${String(ackErr)}`);
12002
+ });
12003
+ throw err;
12004
+ }
11950
12005
  return;
11951
12006
  }
11952
12007
  if (cmd.type.startsWith("cron")) {
@@ -13033,10 +13088,10 @@ function subscribeChannels(apiKey2, onUpdate, logger) {
13033
13088
  }
13034
13089
 
13035
13090
  // src/gateway-client.ts
13036
- import crypto2 from "node:crypto";
13091
+ import crypto3 from "node:crypto";
13037
13092
 
13038
13093
  // src/device-identity-crypto.ts
13039
- import crypto from "node:crypto";
13094
+ import crypto2 from "node:crypto";
13040
13095
  import fs2 from "node:fs";
13041
13096
  import path2 from "node:path";
13042
13097
  import os from "node:os";
@@ -13065,12 +13120,12 @@ function loadOrCreateDeviceIdentity() {
13065
13120
  persistIdentity(legacy);
13066
13121
  return legacy;
13067
13122
  }
13068
- const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
13123
+ const { publicKey, privateKey } = crypto2.generateKeyPairSync("ed25519");
13069
13124
  const publicKeyPem = publicKey.export({ type: "spki", format: "pem" });
13070
13125
  const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" });
13071
13126
  const publicKeyDer = publicKey.export({ type: "spki", format: "der" });
13072
13127
  const rawPublicKey = publicKeyDer.subarray(publicKeyDer.length - 32);
13073
- const deviceId = crypto.createHash("sha256").update(new Uint8Array(rawPublicKey)).digest("hex");
13128
+ const deviceId = crypto2.createHash("sha256").update(new Uint8Array(rawPublicKey)).digest("hex");
13074
13129
  const identity = { deviceId, publicKeyPem, privateKeyPem };
13075
13130
  console.debug("cohort-sync: device identity created", { deviceId });
13076
13131
  persistIdentity(identity);
@@ -13106,8 +13161,8 @@ function buildDeviceAuthPayloadV3(params) {
13106
13161
  ].join("|");
13107
13162
  }
13108
13163
  function signPayload(privateKeyPem, payload) {
13109
- const key = crypto.createPrivateKey(privateKeyPem);
13110
- const signature = crypto.sign(null, new Uint8Array(Buffer.from(payload, "utf-8")), key);
13164
+ const key = crypto2.createPrivateKey(privateKeyPem);
13165
+ const signature = crypto2.sign(null, new Uint8Array(Buffer.from(payload, "utf-8")), key);
13111
13166
  return signature.toString("base64").replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
13112
13167
  }
13113
13168
 
@@ -13299,7 +13354,7 @@ var GatewayClient = class {
13299
13354
  ws.addEventListener("message", onChallengeMessage);
13300
13355
  const sendConnect = () => {
13301
13356
  ws.removeEventListener("message", onChallengeMessage);
13302
- const id = crypto2.randomUUID();
13357
+ const id = crypto3.randomUUID();
13303
13358
  const frame = buildConnectFrame(
13304
13359
  id,
13305
13360
  this._getToken(),
@@ -13412,7 +13467,7 @@ var GatewayClient = class {
13412
13467
  if (!this.isAlive()) {
13413
13468
  throw new Error("Gateway client is not connected");
13414
13469
  }
13415
- const id = crypto2.randomUUID();
13470
+ const id = crypto3.randomUUID();
13416
13471
  const frame = {
13417
13472
  type: "req",
13418
13473
  id,
@@ -14323,7 +14378,7 @@ function dumpEvent(event) {
14323
14378
  function positiveNumber(value) {
14324
14379
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
14325
14380
  }
14326
- var PLUGIN_VERSION = true ? "0.34.6" : "unknown";
14381
+ var PLUGIN_VERSION = true ? "0.34.7" : "unknown";
14327
14382
  var PRESENCE_PING_INTERVAL_MS = 12e4;
14328
14383
  function resolveGatewayToken(api) {
14329
14384
  const token2 = api.config?.gateway?.auth?.token;
@@ -85,5 +85,5 @@
85
85
  }
86
86
  }
87
87
  },
88
- "version": "0.34.6"
88
+ "version": "0.34.7"
89
89
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.34.6",
3
+ "version": "0.34.7",
4
4
  "description": "OpenClaw plugin — syncs agent telemetry, sessions, and activity to the Cohort dashboard",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.34.6",
3
+ "version": "0.34.7",
4
4
  "description": "OpenClaw plugin — syncs agent telemetry, sessions, and activity to the Cohort dashboard",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.cohort.bot/gateway",