@cfio/cohort-sync 0.9.1 → 0.9.3

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
@@ -2845,21 +2845,37 @@ async function syncAgentStatus(agentName, status, model, cfg, logger) {
2845
2845
  logger.warn(`cohort-sync: syncAgentStatus failed: ${String(err)}`);
2846
2846
  }
2847
2847
  }
2848
- async function syncSkillsToV1(skills, cfg, logger) {
2848
+ async function syncSkillsToV1(skills, cfg, logger, paceMs = 1e3) {
2849
2849
  let synced = 0;
2850
+ let failed = 0;
2850
2851
  for (const skill of skills) {
2851
- try {
2852
- await v1Post(cfg.apiUrl, cfg.apiKey, "/api/v1/skills", {
2853
- name: skill.name,
2854
- description: skill.description
2855
- });
2856
- synced++;
2857
- if (synced % 5 === 0) await new Promise((r) => setTimeout(r, 500));
2858
- } catch (err) {
2859
- logger.warn(`cohort-sync: failed to sync skill "${skill.name}": ${String(err)}`);
2852
+ let ok = false;
2853
+ for (let attempt = 0; attempt < 3; attempt++) {
2854
+ try {
2855
+ await v1Post(cfg.apiUrl, cfg.apiKey, "/api/v1/skills", {
2856
+ name: skill.name,
2857
+ description: skill.description
2858
+ });
2859
+ ok = true;
2860
+ synced++;
2861
+ break;
2862
+ } catch (err) {
2863
+ const is429 = String(err).includes("429");
2864
+ if (is429 && attempt < 2) {
2865
+ const backoffMs = paceMs ? (attempt + 1) * 2e3 : 0;
2866
+ await new Promise((r) => setTimeout(r, backoffMs));
2867
+ continue;
2868
+ }
2869
+ logger.warn(`cohort-sync: failed to sync skill "${skill.name}": ${String(err)}`);
2870
+ failed++;
2871
+ break;
2872
+ }
2860
2873
  }
2874
+ if (ok && paceMs) await new Promise((r) => setTimeout(r, paceMs));
2875
+ }
2876
+ if (synced > 0 || failed > 0) {
2877
+ logger.info(`cohort-sync: synced ${synced}/${skills.length} skills${failed > 0 ? ` (${failed} failed)` : ""}`);
2861
2878
  }
2862
- if (synced > 0) logger.info(`cohort-sync: synced ${synced}/${skills.length} skills`);
2863
2879
  }
2864
2880
  var lastKnownRoster = [];
2865
2881
  function getLastKnownRoster() {
@@ -12382,16 +12398,31 @@ import crypto from "node:crypto";
12382
12398
  import fs2 from "node:fs";
12383
12399
  import path2 from "node:path";
12384
12400
  import os2 from "node:os";
12385
- var IDENTITY_PATH = path2.join(os2.homedir(), ".openclaw", "extensions", "cohort-sync", ".device-identity.json");
12386
- function loadOrCreateDeviceIdentity() {
12401
+ var DATA_DIR = path2.join(os2.homedir(), ".openclaw", "data", "cohort-sync");
12402
+ var IDENTITY_PATH = path2.join(DATA_DIR, ".device-identity.json");
12403
+ var LEGACY_IDENTITY_PATH = path2.join(os2.homedir(), ".openclaw", "extensions", "cohort-sync", ".device-identity.json");
12404
+ function tryLoadIdentity(filePath) {
12387
12405
  try {
12388
- const data = JSON.parse(fs2.readFileSync(IDENTITY_PATH, "utf-8"));
12406
+ const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
12389
12407
  if (data.deviceId && data.publicKeyPem && data.privateKeyPem) {
12390
- diag("GW_CLIENT_DEVICE_IDENTITY_LOADED", { deviceId: data.deviceId });
12391
12408
  return data;
12392
12409
  }
12393
12410
  } catch {
12394
12411
  }
12412
+ return null;
12413
+ }
12414
+ function loadOrCreateDeviceIdentity() {
12415
+ const existing = tryLoadIdentity(IDENTITY_PATH);
12416
+ if (existing) {
12417
+ diag("GW_CLIENT_DEVICE_IDENTITY_LOADED", { deviceId: existing.deviceId });
12418
+ return existing;
12419
+ }
12420
+ const legacy = tryLoadIdentity(LEGACY_IDENTITY_PATH);
12421
+ if (legacy) {
12422
+ diag("GW_CLIENT_DEVICE_IDENTITY_MIGRATED", { deviceId: legacy.deviceId });
12423
+ persistIdentity(legacy);
12424
+ return legacy;
12425
+ }
12395
12426
  const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
12396
12427
  const publicKeyPem = publicKey.export({ type: "spki", format: "pem" });
12397
12428
  const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" });
@@ -12399,13 +12430,17 @@ function loadOrCreateDeviceIdentity() {
12399
12430
  const rawPublicKey = publicKeyDer.subarray(publicKeyDer.length - 32);
12400
12431
  const deviceId = crypto.createHash("sha256").update(rawPublicKey).digest("hex");
12401
12432
  const identity = { deviceId, publicKeyPem, privateKeyPem };
12433
+ diag("GW_CLIENT_DEVICE_IDENTITY_CREATED", { deviceId });
12434
+ persistIdentity(identity);
12435
+ return identity;
12436
+ }
12437
+ function persistIdentity(identity) {
12402
12438
  try {
12439
+ fs2.mkdirSync(DATA_DIR, { recursive: true, mode: 448 });
12403
12440
  fs2.writeFileSync(IDENTITY_PATH, JSON.stringify(identity, null, 2), { mode: 384 });
12404
- diag("GW_CLIENT_DEVICE_IDENTITY_CREATED", { deviceId });
12405
12441
  } catch (err) {
12406
12442
  diag("GW_CLIENT_DEVICE_IDENTITY_WRITE_FAILED", { error: String(err) });
12407
12443
  }
12408
- return identity;
12409
12444
  }
12410
12445
  function normalizeMetadata(value) {
12411
12446
  if (typeof value !== "string") return "";
@@ -55,5 +55,5 @@
55
55
  }
56
56
  }
57
57
  },
58
- "version": "0.9.1"
58
+ "version": "0.9.3"
59
59
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Syncs agent status and skills to Cohort dashboard",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -0,0 +1,59 @@
1
+ {
2
+ "id": "cohort-sync",
3
+ "uiHints": {
4
+ "apiUrl": {
5
+ "label": "Cohort API URL",
6
+ "placeholder": "https://api.cohort.bot",
7
+ "help": "Base URL for the Cohort telemetry API"
8
+ },
9
+ "apiKey": {
10
+ "label": "API Key",
11
+ "sensitive": true,
12
+ "placeholder": "cohort_...",
13
+ "help": "Cohort API key for telemetry writes"
14
+ },
15
+ "syncIntervalMs": {
16
+ "label": "Sync Interval (ms)",
17
+ "placeholder": "300000",
18
+ "advanced": true,
19
+ "help": "Fallback full sync interval in milliseconds (default: 5 min)"
20
+ },
21
+ "agentNameMap": {
22
+ "label": "Agent Name Map",
23
+ "advanced": true,
24
+ "help": "Map OpenClaw agent IDs to Cohort display names (e.g. {\"main\": \"yuki\"})"
25
+ },
26
+ "convexUrl": {
27
+ "label": "Convex WebSocket URL",
28
+ "placeholder": "https://ws.cohort.bot",
29
+ "advanced": true,
30
+ "help": "Override the auto-derived Convex WebSocket URL (rarely needed)"
31
+ }
32
+ },
33
+ "configSchema": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "properties": {
37
+ "apiUrl": {
38
+ "type": "string",
39
+ "default": "https://api.cohort.bot"
40
+ },
41
+ "apiKey": {
42
+ "type": "string"
43
+ },
44
+ "syncIntervalMs": {
45
+ "type": "number"
46
+ },
47
+ "agentNameMap": {
48
+ "type": "object",
49
+ "additionalProperties": {
50
+ "type": "string"
51
+ }
52
+ },
53
+ "convexUrl": {
54
+ "type": "string"
55
+ }
56
+ }
57
+ },
58
+ "version": "0.9.2"
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Syncs agent status and skills to Cohort dashboard",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.cohort.bot/gateway",
@@ -28,6 +28,7 @@
28
28
  },
29
29
  "files": [
30
30
  "dist",
31
+ "openclaw.plugin.json",
31
32
  "scripts/postinstall.mjs"
32
33
  ],
33
34
  "publishConfig": {