@bopen-io/clawnet-plugin 0.0.2 → 0.0.4

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/worker.js CHANGED
@@ -6239,8 +6239,7 @@ function createClawNetClient(config) {
6239
6239
 
6240
6240
  // src/constants.ts
6241
6241
  var JOB_KEYS = {
6242
- sync: "clawnet-sync",
6243
- clawnetSync: "clawnet-sync"
6242
+ sync: "clawnet-sync"
6244
6243
  };
6245
6244
  var TOOL_NAMES = {
6246
6245
  agentLookup: "agent-lookup",
@@ -6248,14 +6247,12 @@ var TOOL_NAMES = {
6248
6247
  fleetOverview: "fleet-overview"
6249
6248
  };
6250
6249
  var STREAM_CHANNELS = {
6251
- fleetStatus: "fleet-status",
6252
- syncProgress: "sync-progress"
6250
+ fleetStatus: "clawnet:fleet-status",
6251
+ syncProgress: "clawnet:sync-progress"
6253
6252
  };
6254
6253
  var ENTITY_TYPES = {
6255
6254
  agent: "clawnet-agent",
6256
- skill: "clawnet-skill",
6257
- clawnetAgent: "clawnet-agent",
6258
- clawnetSkill: "clawnet-skill"
6255
+ skill: "clawnet-skill"
6259
6256
  };
6260
6257
  var DATA_KEYS = {
6261
6258
  clawnetAgents: "clawnet-agents",
@@ -6272,6 +6269,11 @@ var STATE_KEYS = {
6272
6269
  syncCursor: "clawnet-sync-cursor",
6273
6270
  clawnetLink: "clawnet-link"
6274
6271
  };
6272
+ var DEFAULT_CONFIG = {
6273
+ clawnetApiUrl: "https://clawnet.sh",
6274
+ clawnetApiKey: "",
6275
+ syncIntervalMinutes: 15
6276
+ };
6275
6277
 
6276
6278
  // src/worker.ts
6277
6279
  var currentContext = null;
@@ -6317,7 +6319,7 @@ async function performSync(ctx, streamProgress) {
6317
6319
  const config = await getConfig(ctx);
6318
6320
  const apiKey = await resolveApiKey(ctx, config);
6319
6321
  const client = createClawNetClient({
6320
- baseUrl: config.clawnetApiUrl || "https://api.clawnet.bopen.io",
6322
+ baseUrl: config.clawnetApiUrl || DEFAULT_CONFIG.clawnetApiUrl,
6321
6323
  apiKey,
6322
6324
  fetchFn: ctx.http.fetch.bind(ctx.http)
6323
6325
  });
@@ -6333,19 +6335,23 @@ async function performSync(ctx, streamProgress) {
6333
6335
  message: "Fetching agents from ClawNet registry..."
6334
6336
  });
6335
6337
  }
6336
- const agentResponse = await client.listAgents();
6337
6338
  let agentCount = 0;
6338
- for (const agent of agentResponse.agents) {
6339
- await ctx.entities.upsert({
6340
- entityType: ENTITY_TYPES.clawnetAgent,
6341
- scopeKind: "instance",
6342
- externalId: agent.slug || agent._id,
6343
- title: agent.displayName || agent.name,
6344
- status: agent.deleted ? "deleted" : "active",
6345
- data: agent
6346
- });
6347
- agentCount++;
6348
- }
6339
+ let agentCursor;
6340
+ do {
6341
+ const agentResponse = await client.listAgents({ cursor: agentCursor });
6342
+ for (const agent of agentResponse.agents) {
6343
+ await ctx.entities.upsert({
6344
+ entityType: ENTITY_TYPES.agent,
6345
+ scopeKind: "instance",
6346
+ externalId: agent.slug || agent._id,
6347
+ title: agent.displayName || agent.name,
6348
+ status: agent.deleted ? "deleted" : "active",
6349
+ data: agent
6350
+ });
6351
+ agentCount++;
6352
+ }
6353
+ agentCursor = agentResponse.hasMore ? agentResponse.cursor : void 0;
6354
+ } while (agentCursor);
6349
6355
  if (streamProgress) {
6350
6356
  ctx.streams.emit(STREAM_CHANNELS.syncProgress, {
6351
6357
  phase: "agents",
@@ -6361,19 +6367,23 @@ async function performSync(ctx, streamProgress) {
6361
6367
  message: "Fetching skills from ClawNet registry..."
6362
6368
  });
6363
6369
  }
6364
- const skillResponse = await client.listSkills();
6365
6370
  let skillCount = 0;
6366
- for (const skill of skillResponse.skills) {
6367
- await ctx.entities.upsert({
6368
- entityType: ENTITY_TYPES.clawnetSkill,
6369
- scopeKind: "instance",
6370
- externalId: skill.slug || skill._id,
6371
- title: skill.name,
6372
- status: "available",
6373
- data: skill
6374
- });
6375
- skillCount++;
6376
- }
6371
+ let skillCursor;
6372
+ do {
6373
+ const skillResponse = await client.listSkills({ cursor: skillCursor });
6374
+ for (const skill of skillResponse.skills) {
6375
+ await ctx.entities.upsert({
6376
+ entityType: ENTITY_TYPES.skill,
6377
+ scopeKind: "instance",
6378
+ externalId: skill.slug || skill._id,
6379
+ title: skill.name,
6380
+ status: "available",
6381
+ data: skill
6382
+ });
6383
+ skillCount++;
6384
+ }
6385
+ skillCursor = skillResponse.hasMore ? skillResponse.cursor : void 0;
6386
+ } while (skillCursor);
6377
6387
  if (streamProgress) {
6378
6388
  ctx.streams.emit(STREAM_CHANNELS.syncProgress, {
6379
6389
  phase: "skills",
@@ -6407,7 +6417,7 @@ async function performSync(ctx, streamProgress) {
6407
6417
  return cursor;
6408
6418
  }
6409
6419
  function registerJobHandlers(ctx) {
6410
- ctx.jobs.register(JOB_KEYS.clawnetSync, async (job) => {
6420
+ ctx.jobs.register(JOB_KEYS.sync, async (job) => {
6411
6421
  ctx.logger.info("Starting scheduled ClawNet sync", {
6412
6422
  runId: job.runId,
6413
6423
  trigger: job.trigger
@@ -6452,7 +6462,7 @@ function registerEventHandlers(ctx) {
6452
6462
  const agent = await ctx.agents.get(event.entityId, event.companyId);
6453
6463
  if (!agent) return;
6454
6464
  const clawnetAgents = await ctx.entities.list({
6455
- entityType: ENTITY_TYPES.clawnetAgent,
6465
+ entityType: ENTITY_TYPES.agent,
6456
6466
  limit: 200
6457
6467
  });
6458
6468
  const matchingTemplate = clawnetAgents.find((entity) => {
@@ -6482,15 +6492,15 @@ function registerEventHandlers(ctx) {
6482
6492
  }
6483
6493
  function registerDataHandlers(ctx) {
6484
6494
  ctx.data.register(DATA_KEYS.clawnetAgents, async (params) => {
6485
- const { search, limit } = getListParams(params);
6486
- const entities = await ctx.entities.list({
6487
- entityType: ENTITY_TYPES.clawnetAgent,
6495
+ const { search, page, limit } = getListParams(params);
6496
+ let entities = await ctx.entities.list({
6497
+ entityType: ENTITY_TYPES.agent,
6488
6498
  limit,
6489
6499
  offset: 0
6490
6500
  });
6491
6501
  if (search) {
6492
6502
  const term = search.toLowerCase();
6493
- return entities.filter((entity) => {
6503
+ entities = entities.filter((entity) => {
6494
6504
  const titleMatch = entity.title?.toLowerCase().includes(term);
6495
6505
  const data = entity.data;
6496
6506
  const nameMatch = typeof data.name === "string" && data.name.toLowerCase().includes(term);
@@ -6498,18 +6508,36 @@ function registerDataHandlers(ctx) {
6498
6508
  return titleMatch || nameMatch || descMatch;
6499
6509
  });
6500
6510
  }
6501
- return entities;
6511
+ const agents = entities.map((entity) => {
6512
+ const d = entity.data;
6513
+ return {
6514
+ id: entity.externalId ?? entity.id,
6515
+ slug: d.slug ?? entity.externalId ?? "",
6516
+ displayName: entity.title ?? d.displayName ?? d.name ?? "",
6517
+ description: d.description ?? null,
6518
+ model: d.model ?? null,
6519
+ color: d.color ?? null,
6520
+ starCount: d.starCount ?? 0,
6521
+ trustScore: null,
6522
+ // Not yet available from ClawNet
6523
+ attestations: [],
6524
+ // Not yet available from ClawNet
6525
+ skills: Array.isArray(d.skills) ? d.skills : [],
6526
+ createdAt: entity.createdAt
6527
+ };
6528
+ });
6529
+ return { agents, total: agents.length, page, limit };
6502
6530
  });
6503
6531
  ctx.data.register(DATA_KEYS.clawnetSkills, async (params) => {
6504
6532
  const { search, limit } = getListParams(params);
6505
- const entities = await ctx.entities.list({
6506
- entityType: ENTITY_TYPES.clawnetSkill,
6533
+ let entities = await ctx.entities.list({
6534
+ entityType: ENTITY_TYPES.skill,
6507
6535
  limit,
6508
6536
  offset: 0
6509
6537
  });
6510
6538
  if (search) {
6511
6539
  const term = search.toLowerCase();
6512
- return entities.filter((entity) => {
6540
+ entities = entities.filter((entity) => {
6513
6541
  const titleMatch = entity.title?.toLowerCase().includes(term);
6514
6542
  const data = entity.data;
6515
6543
  const nameMatch = typeof data.name === "string" && data.name.toLowerCase().includes(term);
@@ -6517,7 +6545,18 @@ function registerDataHandlers(ctx) {
6517
6545
  return titleMatch || nameMatch || descMatch;
6518
6546
  });
6519
6547
  }
6520
- return entities;
6548
+ const skills = entities.map((entity) => {
6549
+ const d = entity.data;
6550
+ return {
6551
+ id: entity.externalId ?? entity.id,
6552
+ slug: d.slug ?? entity.externalId ?? "",
6553
+ displayName: entity.title ?? d.displayName ?? d.name ?? "",
6554
+ description: d.description ?? null,
6555
+ category: d.category ?? null,
6556
+ starCount: d.starCount ?? 0
6557
+ };
6558
+ });
6559
+ return { skills, total: skills.length };
6521
6560
  });
6522
6561
  ctx.data.register(DATA_KEYS.syncStatus, async () => {
6523
6562
  const cursor = await getSyncCursor(ctx);
@@ -6536,7 +6575,7 @@ function registerDataHandlers(ctx) {
6536
6575
  const [paperclipAgents, clawnetEntities] = await Promise.all([
6537
6576
  ctx.agents.list({ companyId, limit: 200, offset: 0 }),
6538
6577
  ctx.entities.list({
6539
- entityType: ENTITY_TYPES.clawnetAgent,
6578
+ entityType: ENTITY_TYPES.agent,
6540
6579
  limit: 200,
6541
6580
  offset: 0
6542
6581
  })
@@ -6549,6 +6588,23 @@ function registerDataHandlers(ctx) {
6549
6588
  stateKey: STATE_KEYS.clawnetLink
6550
6589
  });
6551
6590
  const clawnetMatch = linkState ? clawnetEntities.find((e) => e.externalId === linkState.clawnetExternalId) : null;
6591
+ let clawnetTemplate = null;
6592
+ if (clawnetMatch) {
6593
+ const d = clawnetMatch.data;
6594
+ clawnetTemplate = {
6595
+ id: clawnetMatch.externalId ?? clawnetMatch.id,
6596
+ slug: d.slug ?? clawnetMatch.externalId ?? "",
6597
+ displayName: clawnetMatch.title ?? d.displayName ?? d.name ?? "",
6598
+ description: d.description ?? null,
6599
+ model: d.model ?? null,
6600
+ color: d.color ?? null,
6601
+ starCount: d.starCount ?? 0,
6602
+ trustScore: null,
6603
+ attestations: [],
6604
+ skills: Array.isArray(d.skills) ? d.skills : [],
6605
+ createdAt: clawnetMatch.createdAt
6606
+ };
6607
+ }
6552
6608
  return {
6553
6609
  paperclipAgent: {
6554
6610
  id: agent.id,
@@ -6557,11 +6613,7 @@ function registerDataHandlers(ctx) {
6557
6613
  role: agent.role
6558
6614
  },
6559
6615
  clawnetLink: linkState,
6560
- clawnetTemplate: clawnetMatch ? {
6561
- externalId: clawnetMatch.externalId,
6562
- title: clawnetMatch.title,
6563
- data: clawnetMatch.data
6564
- } : null
6616
+ clawnetTemplate
6565
6617
  };
6566
6618
  })
6567
6619
  );
@@ -6604,7 +6656,7 @@ function registerActionHandlers(ctx) {
6604
6656
  throw new Error(`Agent ${agentId} not found`);
6605
6657
  }
6606
6658
  const clawnetEntities = await ctx.entities.list({
6607
- entityType: ENTITY_TYPES.clawnetAgent,
6659
+ entityType: ENTITY_TYPES.agent,
6608
6660
  externalId: clawnetExternalId,
6609
6661
  limit: 1
6610
6662
  });
@@ -6637,6 +6689,27 @@ function registerActionHandlers(ctx) {
6637
6689
  templateTitle: clawnetEntities[0].title
6638
6690
  };
6639
6691
  });
6692
+ ctx.actions.register(ACTION_KEYS.validateConfig, async (_params) => {
6693
+ const config = await getConfig(ctx);
6694
+ const errors = [];
6695
+ if (config.clawnetApiUrl) {
6696
+ try {
6697
+ new URL(config.clawnetApiUrl);
6698
+ } catch {
6699
+ errors.push("Invalid API URL");
6700
+ }
6701
+ }
6702
+ if (config.clawnetApiKey) {
6703
+ try {
6704
+ await ctx.secrets.resolve(config.clawnetApiKey);
6705
+ } catch (e) {
6706
+ errors.push("API key resolution failed: " + summarizeError(e));
6707
+ }
6708
+ } else {
6709
+ errors.push("No API key configured");
6710
+ }
6711
+ return { ok: errors.length === 0, errors };
6712
+ });
6640
6713
  }
6641
6714
  function registerToolHandlers(ctx) {
6642
6715
  ctx.tools.register(
@@ -6661,7 +6734,7 @@ function registerToolHandlers(ctx) {
6661
6734
  return { error: "slug is required" };
6662
6735
  }
6663
6736
  const entities = await ctx.entities.list({
6664
- entityType: ENTITY_TYPES.clawnetAgent,
6737
+ entityType: ENTITY_TYPES.agent,
6665
6738
  externalId: slug,
6666
6739
  limit: 1
6667
6740
  });
@@ -6673,7 +6746,7 @@ function registerToolHandlers(ctx) {
6673
6746
  };
6674
6747
  }
6675
6748
  const allAgents = await ctx.entities.list({
6676
- entityType: ENTITY_TYPES.clawnetAgent,
6749
+ entityType: ENTITY_TYPES.agent,
6677
6750
  limit: 200
6678
6751
  });
6679
6752
  const term = slug.toLowerCase();
@@ -6722,7 +6795,7 @@ function registerToolHandlers(ctx) {
6722
6795
  }
6723
6796
  const resultLimit = Math.min(maxResults ?? 10, 50);
6724
6797
  const allSkills = await ctx.entities.list({
6725
- entityType: ENTITY_TYPES.clawnetSkill,
6798
+ entityType: ENTITY_TYPES.skill,
6726
6799
  limit: 200
6727
6800
  });
6728
6801
  const term = query.toLowerCase();
@@ -6762,8 +6835,8 @@ function registerToolHandlers(ctx) {
6762
6835
  },
6763
6836
  async (_params, runCtx) => {
6764
6837
  const [agents, skills, cursor, paperclipAgents] = await Promise.all([
6765
- ctx.entities.list({ entityType: ENTITY_TYPES.clawnetAgent, limit: 200 }),
6766
- ctx.entities.list({ entityType: ENTITY_TYPES.clawnetSkill, limit: 200 }),
6838
+ ctx.entities.list({ entityType: ENTITY_TYPES.agent, limit: 200 }),
6839
+ ctx.entities.list({ entityType: ENTITY_TYPES.skill, limit: 200 }),
6767
6840
  getSyncCursor(ctx),
6768
6841
  ctx.agents.list({ companyId: runCtx.companyId, limit: 200, offset: 0 })
6769
6842
  ]);
@@ -6860,6 +6933,10 @@ var plugin = definePlugin({
6860
6933
  if (url.protocol !== "https:" && url.protocol !== "http:") {
6861
6934
  errors.push("clawnetApiUrl must use http or https protocol");
6862
6935
  }
6936
+ const hostname = url.hostname;
6937
+ if (hostname === "localhost" || hostname.startsWith("127.") || hostname.startsWith("10.") || hostname.startsWith("192.168.") || hostname.startsWith("169.254.") || /^172\.(1[6-9]|2\d|3[01])\./.test(hostname) || hostname.endsWith(".internal") || hostname.endsWith(".local")) {
6938
+ errors.push("clawnetApiUrl must not point to a private or internal address");
6939
+ }
6863
6940
  } catch {
6864
6941
  errors.push("clawnetApiUrl is not a valid URL");
6865
6942
  }
@@ -6883,7 +6960,7 @@ var plugin = definePlugin({
6883
6960
  }
6884
6961
  }
6885
6962
  if (!typed.clawnetApiUrl) {
6886
- warnings.push("clawnetApiUrl not set; will default to https://api.clawnet.bopen.io");
6963
+ warnings.push(`clawnetApiUrl not set; will default to ${DEFAULT_CONFIG.clawnetApiUrl}`);
6887
6964
  }
6888
6965
  return {
6889
6966
  ok: errors.length === 0,