@stamn/stamn-plugin 0.1.0-alpha.20 → 0.1.0-alpha.22

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
@@ -4764,7 +4764,7 @@ var StamnClient = class {
4764
4764
  return Math.random() * capped;
4765
4765
  }
4766
4766
  sleep(ms) {
4767
- return new Promise((resolve) => setTimeout(resolve, ms));
4767
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
4768
4768
  }
4769
4769
  async parseErrorResponse(res) {
4770
4770
  const body = await res.text();
@@ -5337,6 +5337,124 @@ function requestService(ws) {
5337
5337
  }
5338
5338
  };
5339
5339
  }
5340
+ function createServiceListing(ws, agentId) {
5341
+ return {
5342
+ name: "stamn_create_service_listing",
5343
+ description: "Create a persistent service listing on the marketplace. This is your storefront \u2014 buyers browse these listings and purchase your services. Include a compelling description, fair price, and usage examples.",
5344
+ parameters: {
5345
+ type: "object",
5346
+ properties: {
5347
+ serviceTag: param("string", "Unique identifier, lowercase with underscores (e.g. 'code_review', 'summarize')."),
5348
+ name: param("string", "Display name (e.g. 'Code Review', 'Text Summarization')."),
5349
+ description: param("string", "Short description of what the service does (1-2 sentences)."),
5350
+ priceCents: param("string", 'Price in USDC cents (e.g. "100" = $1.00).'),
5351
+ category: param("string", "Service category.", {
5352
+ enum: ["coding", "writing", "research", "analysis", "creative", "data", "other"]
5353
+ }),
5354
+ longDescription: param("string", "Detailed description shown on the service detail page. Markdown supported."),
5355
+ inputDescription: param("string", "What input the service expects from the buyer."),
5356
+ outputDescription: param("string", "What output the service produces."),
5357
+ usageExamples: param("string", 'JSON array of {input, output} example pairs, e.g. [{"input":"Review this code...","output":"Found 3 issues..."}]'),
5358
+ tags: param("string", 'Comma-separated tags for discovery (e.g. "python, fast, automated").'),
5359
+ rateLimitPerHour: param("string", "Max requests per hour (optional)."),
5360
+ estimatedDurationSeconds: param("string", "Estimated time to complete in seconds (optional).")
5361
+ },
5362
+ required: ["serviceTag", "name", "description", "priceCents"]
5363
+ },
5364
+ execute: (args) => {
5365
+ const payload = {
5366
+ participantId: agentId,
5367
+ serviceTag: args.serviceTag,
5368
+ name: args.name,
5369
+ description: args.description,
5370
+ priceCents: Number(args.priceCents)
5371
+ };
5372
+ if (args.category) payload.category = args.category;
5373
+ if (args.longDescription) payload.longDescription = args.longDescription;
5374
+ if (args.inputDescription) payload.inputDescription = args.inputDescription;
5375
+ if (args.outputDescription) payload.outputDescription = args.outputDescription;
5376
+ if (args.tags) {
5377
+ payload.tags = args.tags.split(",").map((t2) => t2.trim()).filter(Boolean);
5378
+ }
5379
+ if (args.rateLimitPerHour) payload.rateLimitPerHour = Number(args.rateLimitPerHour);
5380
+ if (args.estimatedDurationSeconds) payload.estimatedDurationSeconds = Number(args.estimatedDurationSeconds);
5381
+ if (args.usageExamples) {
5382
+ try {
5383
+ payload.usageExamples = JSON.parse(args.usageExamples);
5384
+ } catch {
5385
+ return text("Error: usageExamples must be valid JSON array of {input, output} objects.");
5386
+ }
5387
+ }
5388
+ ws.send("participant:service_listing_create", payload);
5389
+ return text(`Service listing "${args.serviceTag}" creation sent. Check events for confirmation.`);
5390
+ }
5391
+ };
5392
+ }
5393
+ function updateServiceListing(ws) {
5394
+ return {
5395
+ name: "stamn_update_service_listing",
5396
+ description: "Update an existing marketplace service listing. Use stamn_list_service_listings first to get the serviceId.",
5397
+ parameters: {
5398
+ type: "object",
5399
+ properties: {
5400
+ serviceId: param("string", "The service listing ID to update."),
5401
+ name: param("string", "New display name."),
5402
+ description: param("string", "New short description."),
5403
+ priceCents: param("string", "New price in USDC cents."),
5404
+ isActive: param("string", 'Set to "true" or "false" to enable/disable the listing.'),
5405
+ category: param("string", "Service category.", {
5406
+ enum: ["coding", "writing", "research", "analysis", "creative", "data", "other"]
5407
+ }),
5408
+ longDescription: param("string", "Detailed description (markdown supported)."),
5409
+ inputDescription: param("string", "What input the service expects."),
5410
+ outputDescription: param("string", "What output the service produces."),
5411
+ usageExamples: param("string", "JSON array of {input, output} example pairs."),
5412
+ tags: param("string", "Comma-separated tags."),
5413
+ rateLimitPerHour: param("string", "Max requests per hour."),
5414
+ estimatedDurationSeconds: param("string", "Estimated completion time in seconds.")
5415
+ },
5416
+ required: ["serviceId"]
5417
+ },
5418
+ execute: (args) => {
5419
+ const payload = {
5420
+ serviceId: args.serviceId
5421
+ };
5422
+ if (args.name) payload.name = args.name;
5423
+ if (args.description) payload.description = args.description;
5424
+ if (args.priceCents) payload.priceCents = Number(args.priceCents);
5425
+ if (args.isActive !== void 0) payload.isActive = args.isActive === "true";
5426
+ if (args.category) payload.category = args.category;
5427
+ if (args.longDescription) payload.longDescription = args.longDescription;
5428
+ if (args.inputDescription) payload.inputDescription = args.inputDescription;
5429
+ if (args.outputDescription) payload.outputDescription = args.outputDescription;
5430
+ if (args.tags) {
5431
+ payload.tags = args.tags.split(",").map((t2) => t2.trim()).filter(Boolean);
5432
+ }
5433
+ if (args.rateLimitPerHour) payload.rateLimitPerHour = Number(args.rateLimitPerHour);
5434
+ if (args.estimatedDurationSeconds) payload.estimatedDurationSeconds = Number(args.estimatedDurationSeconds);
5435
+ if (args.usageExamples) {
5436
+ try {
5437
+ payload.usageExamples = JSON.parse(args.usageExamples);
5438
+ } catch {
5439
+ return text("Error: usageExamples must be valid JSON array of {input, output} objects.");
5440
+ }
5441
+ }
5442
+ ws.send("participant:service_listing_update", payload);
5443
+ return text(`Service listing update sent. Check events for confirmation.`);
5444
+ }
5445
+ };
5446
+ }
5447
+ function listServiceListings(ws) {
5448
+ return {
5449
+ name: "stamn_list_service_listings",
5450
+ description: "List all your marketplace service listings (both active and inactive). Returns listing IDs, names, prices, and status.",
5451
+ parameters: NO_PARAMS,
5452
+ execute: () => {
5453
+ ws.send("participant:service_listing_list", {});
5454
+ return text("Service listing request sent. Check events for the list.");
5455
+ }
5456
+ };
5457
+ }
5340
5458
  function chatReply(ws, agentId) {
5341
5459
  return {
5342
5460
  name: "stamn_chat_reply",
@@ -5405,6 +5523,9 @@ function allTools(ws, agentId) {
5405
5523
  registerService(ws, agentId),
5406
5524
  respondToService(ws),
5407
5525
  requestService(ws),
5526
+ createServiceListing(ws, agentId),
5527
+ updateServiceListing(ws),
5528
+ listServiceListings(ws),
5408
5529
  chatReply(ws, agentId),
5409
5530
  spend(ws)
5410
5531
  ];
@@ -5459,13 +5580,16 @@ function readLogs(opts) {
5459
5580
  try {
5460
5581
  stat = statSync(file);
5461
5582
  } catch {
5462
- return { lines: [], cursor: 0, size: 0, file, truncated: false, reset: false };
5583
+ return { lines: [], cursor: 0, startCursor: 0, size: 0, file, truncated: false, reset: false };
5463
5584
  }
5464
5585
  const size = stat.size;
5465
5586
  const reset = cursor > size;
5466
5587
  if (reset) cursor = 0;
5588
+ if (opts.fromEnd) {
5589
+ cursor = Math.max(0, size - maxBytes);
5590
+ }
5467
5591
  if (cursor >= size) {
5468
- return { lines: [], cursor, size, file, truncated: false, reset };
5592
+ return { lines: [], cursor, startCursor: cursor, size, file, truncated: false, reset };
5469
5593
  }
5470
5594
  const bytesToRead = Math.min(maxBytes, size - cursor);
5471
5595
  const buffer = Buffer.alloc(bytesToRead);
@@ -5500,6 +5624,7 @@ function readLogs(opts) {
5500
5624
  return {
5501
5625
  lines,
5502
5626
  cursor: cursor + actualBytesConsumed,
5627
+ startCursor: cursor,
5503
5628
  size,
5504
5629
  file,
5505
5630
  truncated: truncated || !atEof,
@@ -5507,6 +5632,57 @@ function readLogs(opts) {
5507
5632
  };
5508
5633
  }
5509
5634
 
5635
+ // src/workspace-files.ts
5636
+ import { readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync4, statSync as statSync2, mkdirSync as mkdirSync3 } from "fs";
5637
+ import { join as join6, resolve, relative, dirname as dirname2 } from "path";
5638
+ import { homedir as homedir3 } from "os";
5639
+ var WORKSPACE_DIR = join6(homedir3(), ".openclaw", "workspace");
5640
+ function assertWithinWorkspace(relativePath) {
5641
+ const full = resolve(WORKSPACE_DIR, relativePath);
5642
+ if (!full.startsWith(WORKSPACE_DIR + "/") && full !== WORKSPACE_DIR) {
5643
+ throw new Error("Path outside workspace");
5644
+ }
5645
+ return full;
5646
+ }
5647
+ function walkDir(dir, base) {
5648
+ const results = [];
5649
+ let entries;
5650
+ try {
5651
+ entries = readdirSync(dir, { withFileTypes: true });
5652
+ } catch {
5653
+ return results;
5654
+ }
5655
+ for (const entry of entries) {
5656
+ const fullPath = join6(dir, entry.name);
5657
+ if (entry.isDirectory()) {
5658
+ results.push(...walkDir(fullPath, base));
5659
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
5660
+ const stat = statSync2(fullPath);
5661
+ results.push({
5662
+ path: relative(base, fullPath),
5663
+ size: stat.size,
5664
+ modifiedAt: stat.mtime.toISOString()
5665
+ });
5666
+ }
5667
+ }
5668
+ return results;
5669
+ }
5670
+ function listWorkspaceFiles() {
5671
+ return walkDir(WORKSPACE_DIR, WORKSPACE_DIR);
5672
+ }
5673
+ function readWorkspaceFile(relativePath) {
5674
+ const full = assertWithinWorkspace(relativePath);
5675
+ const content = readFileSync3(full, "utf-8");
5676
+ const stat = statSync2(full);
5677
+ return { path: relativePath, content, size: stat.size };
5678
+ }
5679
+ function writeWorkspaceFile(relativePath, content) {
5680
+ const full = assertWithinWorkspace(relativePath);
5681
+ mkdirSync3(dirname2(full), { recursive: true });
5682
+ writeFileSync4(full, content, "utf-8");
5683
+ return { path: relativePath, written: true };
5684
+ }
5685
+
5510
5686
  // src/ws-service.ts
5511
5687
  var MAX_EVENT_BUFFER_SIZE = 200;
5512
5688
  var BASE_RECONNECT_DELAY_MS = 1e3;
@@ -5678,6 +5854,18 @@ var StamnWsService = class {
5678
5854
  this.handleRequestLogs(payload.params);
5679
5855
  return;
5680
5856
  }
5857
+ if (payload.command === "list_files") {
5858
+ this.handleListFiles(payload.params);
5859
+ return;
5860
+ }
5861
+ if (payload.command === "read_file") {
5862
+ this.handleReadFile(payload.params);
5863
+ return;
5864
+ }
5865
+ if (payload.command === "write_file") {
5866
+ this.handleWriteFile(payload.params);
5867
+ return;
5868
+ }
5681
5869
  this.logger.info(`Command received: ${payload.command} (${payload.commandId})`);
5682
5870
  if (payload.command === "update_plugin") {
5683
5871
  this.handleUpdatePlugin();
@@ -5702,7 +5890,8 @@ var StamnWsService = class {
5702
5890
  const result = readLogs({
5703
5891
  cursor: params.cursor,
5704
5892
  limit: params.limit,
5705
- maxBytes: params.maxBytes
5893
+ maxBytes: params.maxBytes,
5894
+ fromEnd: params.fromEnd
5706
5895
  });
5707
5896
  this.sendMessage("participant:log_response", {
5708
5897
  requestId: params.requestId,
@@ -5722,6 +5911,54 @@ var StamnWsService = class {
5722
5911
  });
5723
5912
  }
5724
5913
  }
5914
+ handleListFiles(params) {
5915
+ try {
5916
+ const files = listWorkspaceFiles();
5917
+ this.sendMessage("participant:file_response", {
5918
+ requestId: params.requestId,
5919
+ files
5920
+ });
5921
+ } catch (err) {
5922
+ this.sendMessage("participant:file_response", {
5923
+ requestId: params.requestId,
5924
+ files: [],
5925
+ error: err.message
5926
+ });
5927
+ }
5928
+ }
5929
+ handleReadFile(params) {
5930
+ try {
5931
+ const result = readWorkspaceFile(params.path);
5932
+ this.sendMessage("participant:file_response", {
5933
+ requestId: params.requestId,
5934
+ ...result
5935
+ });
5936
+ } catch (err) {
5937
+ this.sendMessage("participant:file_response", {
5938
+ requestId: params.requestId,
5939
+ path: params.path,
5940
+ content: "",
5941
+ size: 0,
5942
+ error: err.message
5943
+ });
5944
+ }
5945
+ }
5946
+ handleWriteFile(params) {
5947
+ try {
5948
+ const result = writeWorkspaceFile(params.path, params.content);
5949
+ this.sendMessage("participant:file_response", {
5950
+ requestId: params.requestId,
5951
+ ...result
5952
+ });
5953
+ } catch (err) {
5954
+ this.sendMessage("participant:file_response", {
5955
+ requestId: params.requestId,
5956
+ path: params.path,
5957
+ written: false,
5958
+ error: err.message
5959
+ });
5960
+ }
5961
+ }
5725
5962
  onAuthError(payload) {
5726
5963
  this.authFailed = true;
5727
5964
  this.logger.error(`Authentication failed: ${payload.reason}`);