lobstah-openclaw-plugin 0.7.2 → 0.7.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.
Files changed (2) hide show
  1. package/dist/index.js +832 -26
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import { definePluginEntry } from "openclaw/plugin-sdk/core";
3
3
 
4
4
  // apps/node/src/tools.ts
5
- import * as fs6 from "node:fs";
6
- import * as path4 from "node:path";
7
- import { randomUUID } from "node:crypto";
5
+ import * as fs7 from "node:fs";
6
+ import * as path5 from "node:path";
7
+ import { randomUUID as randomUUID2 } from "node:crypto";
8
8
 
9
9
  // packages/core/dist/types.js
10
10
  var VERBS = ["working", "needs-decision", "blocked", "paused", "done", "failed"];
@@ -14,6 +14,10 @@ var TERMINAL_VERBS = ["done", "failed"];
14
14
  import * as os from "node:os";
15
15
  import * as path from "node:path";
16
16
  import * as fs from "node:fs";
17
+ import { randomUUID } from "node:crypto";
18
+ function uniqueTempPath(file) {
19
+ return `${file}.tmp-${process.pid}-${randomUUID()}`;
20
+ }
17
21
  function lobstahHome() {
18
22
  return process.env.LOBSTAH_HOME ?? path.join(os.homedir(), ".lobstah");
19
23
  }
@@ -84,7 +88,7 @@ function reconcile({ log, lastEventAt: lastEventAt2, now = Date.now(), busyThres
84
88
 
85
89
  // packages/core/dist/queue.js
86
90
  function atomicWrite(file, content) {
87
- const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
91
+ const tmp = uniqueTempPath(file);
88
92
  fs3.writeFileSync(tmp, content);
89
93
  fs3.renameSync(tmp, file);
90
94
  }
@@ -119,6 +123,729 @@ function requestCancel(id, lane) {
119
123
  }
120
124
 
121
125
  // packages/core/dist/config.js
126
+ import * as fs4 from "node:fs";
127
+ import * as os2 from "node:os";
128
+ import * as path3 from "node:path";
129
+
130
+ // node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/date.js
131
+ var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
132
+ var TomlDate = class _TomlDate extends Date {
133
+ #hasDate = false;
134
+ #hasTime = false;
135
+ #offset = null;
136
+ constructor(date) {
137
+ let hasDate = true;
138
+ let hasTime = true;
139
+ let offset = "Z";
140
+ if (typeof date === "string") {
141
+ let match = date.match(DATE_TIME_RE);
142
+ if (match) {
143
+ if (!match[1]) {
144
+ hasDate = false;
145
+ date = `0000-01-01T${date}`;
146
+ }
147
+ hasTime = !!match[2];
148
+ hasTime && date[10] === " " && (date = date.replace(" ", "T"));
149
+ if (match[2] && +match[2] > 23) {
150
+ date = "";
151
+ } else {
152
+ offset = match[3] || null;
153
+ date = date.toUpperCase();
154
+ if (!offset && hasTime)
155
+ date += "Z";
156
+ }
157
+ } else {
158
+ date = "";
159
+ }
160
+ }
161
+ super(date);
162
+ if (!isNaN(this.getTime())) {
163
+ this.#hasDate = hasDate;
164
+ this.#hasTime = hasTime;
165
+ this.#offset = offset;
166
+ }
167
+ }
168
+ isDateTime() {
169
+ return this.#hasDate && this.#hasTime;
170
+ }
171
+ isLocal() {
172
+ return !this.#hasDate || !this.#hasTime || !this.#offset;
173
+ }
174
+ isDate() {
175
+ return this.#hasDate && !this.#hasTime;
176
+ }
177
+ isTime() {
178
+ return this.#hasTime && !this.#hasDate;
179
+ }
180
+ isValid() {
181
+ return this.#hasDate || this.#hasTime;
182
+ }
183
+ toISOString() {
184
+ let iso = super.toISOString();
185
+ if (this.isDate())
186
+ return iso.slice(0, 10);
187
+ if (this.isTime())
188
+ return iso.slice(11, 23);
189
+ if (this.#offset === null)
190
+ return iso.slice(0, -1);
191
+ if (this.#offset === "Z")
192
+ return iso;
193
+ let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
194
+ offset = this.#offset[0] === "-" ? offset : -offset;
195
+ let offsetDate = new Date(this.getTime() - offset * 6e4);
196
+ return offsetDate.toISOString().slice(0, -1) + this.#offset;
197
+ }
198
+ static wrapAsOffsetDateTime(jsDate, offset = "Z") {
199
+ let date = new _TomlDate(jsDate);
200
+ date.#offset = offset;
201
+ return date;
202
+ }
203
+ static wrapAsLocalDateTime(jsDate) {
204
+ let date = new _TomlDate(jsDate);
205
+ date.#offset = null;
206
+ return date;
207
+ }
208
+ static wrapAsLocalDate(jsDate) {
209
+ let date = new _TomlDate(jsDate);
210
+ date.#hasTime = false;
211
+ date.#offset = null;
212
+ return date;
213
+ }
214
+ static wrapAsLocalTime(jsDate) {
215
+ let date = new _TomlDate(jsDate);
216
+ date.#hasDate = false;
217
+ date.#offset = null;
218
+ return date;
219
+ }
220
+ };
221
+
222
+ // node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/error.js
223
+ function getLineColFromPtr(string, ptr) {
224
+ let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
225
+ return [lines.length, lines.pop().length + 1];
226
+ }
227
+ function makeCodeBlock(string, line, column) {
228
+ let lines = string.split(/\r\n|\n|\r/g);
229
+ let codeblock = "";
230
+ let numberLen = (Math.log10(line + 1) | 0) + 1;
231
+ for (let i = line - 1; i <= line + 1; i++) {
232
+ let l = lines[i - 1];
233
+ if (!l)
234
+ continue;
235
+ codeblock += i.toString().padEnd(numberLen, " ");
236
+ codeblock += ": ";
237
+ codeblock += l;
238
+ codeblock += "\n";
239
+ if (i === line) {
240
+ codeblock += " ".repeat(numberLen + column + 2);
241
+ codeblock += "^\n";
242
+ }
243
+ }
244
+ return codeblock;
245
+ }
246
+ var TomlError = class extends Error {
247
+ line;
248
+ column;
249
+ codeblock;
250
+ constructor(message, options) {
251
+ const [line, column] = getLineColFromPtr(options.toml, options.ptr);
252
+ const codeblock = makeCodeBlock(options.toml, line, column);
253
+ super(`Invalid TOML document: ${message}
254
+
255
+ ${codeblock}`, options);
256
+ this.line = line;
257
+ this.column = column;
258
+ this.codeblock = codeblock;
259
+ }
260
+ };
261
+
262
+ // node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/util.js
263
+ function indexOfNewline(str, start = 0) {
264
+ let idx = str.indexOf("\n", start);
265
+ if (str.charCodeAt(idx - 1) === 13)
266
+ idx--;
267
+ return idx;
268
+ }
269
+ function skipComment(ctx) {
270
+ for (; ctx.p < ctx.s.length; ctx.p++) {
271
+ let c = ctx.s.charCodeAt(ctx.p);
272
+ if (c === 10)
273
+ break;
274
+ if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10) {
275
+ ctx.p++;
276
+ break;
277
+ }
278
+ if (c < 32 && c !== 9 || c === 127) {
279
+ throw new TomlError("control characters are not allowed in comments", {
280
+ toml: ctx.s,
281
+ ptr: ctx.p
282
+ });
283
+ }
284
+ }
285
+ }
286
+ function skipVoid(ctx, banNewLines, banComments) {
287
+ let c;
288
+ while (1) {
289
+ while ((c = ctx.s.charCodeAt(ctx.p)) === 32 || c === 9 || !banNewLines && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10))
290
+ ctx.p++;
291
+ if (banComments || c !== 35)
292
+ break;
293
+ skipComment(ctx);
294
+ }
295
+ }
296
+ function skipUntil(ctx, sep, end) {
297
+ let ptr = ctx.p;
298
+ if (!end) {
299
+ ptr = indexOfNewline(ctx.s, ptr);
300
+ ctx.p = ptr < 0 ? ctx.s.length : ptr;
301
+ return;
302
+ }
303
+ for (; ctx.p < ctx.s.length; ctx.p++) {
304
+ let c = ctx.s.charCodeAt(ctx.p);
305
+ if (c === 35) {
306
+ skipComment(ctx);
307
+ } else if (c === end || c === sep) {
308
+ return;
309
+ }
310
+ }
311
+ throw new TomlError("cannot find end of structure", {
312
+ toml: ctx.s,
313
+ ptr
314
+ });
315
+ }
316
+
317
+ // node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/primitive.js
318
+ var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
319
+ var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
320
+ var LEADING_ZERO = /^[+-]?0[0-9_]/;
321
+ function parseString(ctx) {
322
+ let start = ctx.p;
323
+ let c = ctx.s.charCodeAt(ctx.p++);
324
+ let first = c;
325
+ let isLiteral = c === 39;
326
+ let isMultiline = c === ctx.s.charCodeAt(ctx.p) && c === ctx.s.charCodeAt(ctx.p + 1);
327
+ if (isMultiline) {
328
+ if ((c = ctx.s.charCodeAt(ctx.p += 2)) === 10)
329
+ ctx.p++;
330
+ else if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)
331
+ ctx.p += 2;
332
+ }
333
+ let parsed = "";
334
+ let sliceStart = ctx.p;
335
+ let state = 0;
336
+ for (; ctx.p < ctx.s.length; ctx.p++) {
337
+ c = ctx.s.charCodeAt(ctx.p);
338
+ if (isMultiline && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)) {
339
+ state = state && 3;
340
+ } else if (c < 32 && c !== 9 || c === 127) {
341
+ throw new TomlError("control characters are not allowed in strings", {
342
+ toml: ctx.s,
343
+ ptr: ctx.p
344
+ });
345
+ } else if ((!state || state === 3) && c === first && (!isMultiline || ctx.s.charCodeAt(ctx.p + 1) === first && ctx.s.charCodeAt(ctx.p + 2) === first)) {
346
+ if (isMultiline) {
347
+ if (ctx.s.charCodeAt(ctx.p + 3) === first)
348
+ ctx.p++;
349
+ if (ctx.s.charCodeAt(ctx.p + 3) === first)
350
+ ctx.p++;
351
+ }
352
+ if (!state)
353
+ parsed += ctx.s.slice(sliceStart, ctx.p);
354
+ ctx.p += isMultiline ? 3 : 1;
355
+ return parsed;
356
+ } else if (!state) {
357
+ if (!isLiteral && c === 92) {
358
+ parsed += ctx.s.slice(sliceStart, sliceStart = ctx.p);
359
+ state = 1;
360
+ }
361
+ } else if (state === 1) {
362
+ if (c === 120 || c === 117 || c === 85) {
363
+ let value = 0;
364
+ let len = c === 120 ? 2 : c === 117 ? 4 : 8;
365
+ for (let j = 0; j < len; j++, ctx.p++) {
366
+ let hex = ctx.s.charCodeAt(ctx.p + 1);
367
+ let digit = (
368
+ /* 0-9 */
369
+ hex >= 48 && hex <= 57 ? hex - 48 : (
370
+ /* A-F */
371
+ hex >= 65 && hex <= 70 ? hex - 65 + 10 : (
372
+ /* a-f */
373
+ hex >= 97 && hex <= 102 ? hex - 97 + 10 : -1
374
+ )
375
+ )
376
+ );
377
+ if (digit < 0)
378
+ throw new TomlError("invalid non-hex character in unicode escape", { toml: ctx.s, ptr: ctx.p + 1 });
379
+ value = value << 4 | digit;
380
+ }
381
+ if (value < 0 || value > 1114111 || value >= 55296 && value <= 57343) {
382
+ throw new TomlError("invalid unicode escape", { toml: ctx.s, ptr: ctx.p });
383
+ }
384
+ parsed += String.fromCodePoint(value);
385
+ sliceStart = ctx.p + 1;
386
+ state = 0;
387
+ } else if (c === 32 || c === 9) {
388
+ state = 2;
389
+ } else {
390
+ if (c === 98)
391
+ parsed += "\b";
392
+ else if (c === 116)
393
+ parsed += " ";
394
+ else if (c === 110)
395
+ parsed += "\n";
396
+ else if (c === 102)
397
+ parsed += "\f";
398
+ else if (c === 114)
399
+ parsed += "\r";
400
+ else if (c === 101)
401
+ parsed += "\x1B";
402
+ else if (c === 34)
403
+ parsed += '"';
404
+ else if (c === 92)
405
+ parsed += "\\";
406
+ else
407
+ throw new TomlError("unrecognized escape sequence", { toml: ctx.s, ptr: ctx.p });
408
+ sliceStart = ctx.p + 1;
409
+ state = 0;
410
+ }
411
+ } else if (c !== 32 && c !== 9) {
412
+ if (state === 2) {
413
+ throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
414
+ toml: ctx.s,
415
+ ptr: sliceStart
416
+ });
417
+ }
418
+ state = !isLiteral && c === 92 ? 1 : 0;
419
+ sliceStart = ctx.p;
420
+ }
421
+ }
422
+ throw new TomlError("unfinished string", { toml: ctx.s, ptr: start });
423
+ }
424
+ function sliceAndTrimEndOf(ctx, start, end) {
425
+ let value = ctx.s.slice(start, end);
426
+ let commentIdx = value.indexOf("#");
427
+ if (commentIdx > 0) {
428
+ skipComment({ s: value, p: commentIdx, d: 0 });
429
+ value = value.slice(0, commentIdx);
430
+ }
431
+ return value.trimEnd();
432
+ }
433
+ function parseValue(ctx, integersAsBigInt, end) {
434
+ let ptr = ctx.p;
435
+ let err = { toml: ctx.s, ptr };
436
+ skipUntil(ctx, 44, end);
437
+ let value = sliceAndTrimEndOf(ctx, ptr, ctx.p);
438
+ if (!value)
439
+ throw new TomlError("incomplete declaration: value expected", err);
440
+ if (value === "-inf")
441
+ return -Infinity;
442
+ if (value === "inf" || value === "+inf")
443
+ return Infinity;
444
+ if (value === "nan" || value === "+nan" || value === "-nan")
445
+ return NaN;
446
+ if (value === "-0")
447
+ return integersAsBigInt ? 0n : 0;
448
+ let isInt = INT_REGEX.test(value);
449
+ if (isInt || FLOAT_REGEX.test(value)) {
450
+ if (LEADING_ZERO.test(value)) {
451
+ throw new TomlError("leading zeroes are not allowed", err);
452
+ }
453
+ value = value.replace(/_/g, "");
454
+ let numeric = +value;
455
+ if (isNaN(numeric)) {
456
+ throw new TomlError("invalid number", err);
457
+ }
458
+ if (isInt) {
459
+ if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
460
+ throw new TomlError("integer value cannot be represented losslessly", err);
461
+ }
462
+ if (isInt || integersAsBigInt === true)
463
+ numeric = BigInt(value);
464
+ }
465
+ return numeric;
466
+ }
467
+ const date = new TomlDate(value);
468
+ if (!date.isValid())
469
+ throw new TomlError("invalid value", err);
470
+ return date;
471
+ }
472
+
473
+ // node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/extract.js
474
+ function extractValue(ctx, end, integersAsBigInt) {
475
+ let ptr = ctx.p;
476
+ let c = ctx.s.charCodeAt(ptr);
477
+ if (c === 91 || c === 123) {
478
+ if (!ctx.d--) {
479
+ throw new TomlError("document contains excessively nested structures. aborting.", {
480
+ toml: ctx.s,
481
+ ptr
482
+ });
483
+ }
484
+ let value = c === 91 ? parseArray(ctx, integersAsBigInt) : parseInlineTable(ctx, integersAsBigInt);
485
+ ctx.d++;
486
+ return value;
487
+ }
488
+ if (c === 34 || c === 39) {
489
+ return parseString(ctx);
490
+ }
491
+ if (c === 116) {
492
+ if (ctx.s.charCodeAt(++ctx.p) !== 114 || ctx.s.charCodeAt(++ctx.p) !== 117 || ctx.s.charCodeAt(++ctx.p) !== 101)
493
+ throw new TomlError("invalid value", { toml: ctx.s, ptr });
494
+ ctx.p++;
495
+ return true;
496
+ }
497
+ if (c === 102) {
498
+ if (ctx.s.charCodeAt(++ctx.p) !== 97 || ctx.s.charCodeAt(++ctx.p) !== 108 || ctx.s.charCodeAt(++ctx.p) !== 115 || ctx.s.charCodeAt(++ctx.p) !== 101)
499
+ throw new TomlError("invalid value", { toml: ctx.s, ptr });
500
+ ctx.p++;
501
+ return false;
502
+ }
503
+ return parseValue(ctx, integersAsBigInt, end);
504
+ }
505
+
506
+ // node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/struct.js
507
+ var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
508
+ function parseKey(ctx, end = "=") {
509
+ let start = ctx.p;
510
+ let dot = start - 1;
511
+ let parsed = [];
512
+ let endPtr = ctx.s.indexOf(end, start);
513
+ if (endPtr < 0) {
514
+ throw new TomlError("incomplete key-value: cannot find end of key", {
515
+ toml: ctx.s,
516
+ ptr: start
517
+ });
518
+ }
519
+ do {
520
+ let c = ctx.s.charCodeAt(ctx.p = ++dot);
521
+ if (c !== 32 && c !== 9) {
522
+ if (c === 34 || c === 39) {
523
+ if (c === ctx.s.charCodeAt(ctx.p + 1) && c === ctx.s.charCodeAt(ctx.p + 2)) {
524
+ throw new TomlError("multiline strings are not allowed in keys", {
525
+ toml: ctx.s,
526
+ ptr: ctx.p
527
+ });
528
+ }
529
+ let part = parseString(ctx);
530
+ dot = ctx.s.indexOf(".", ctx.p);
531
+ let strEnd = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
532
+ let newLine = indexOfNewline(strEnd);
533
+ if (newLine > -1) {
534
+ throw new TomlError("newlines are not allowed in keys", {
535
+ toml: ctx.s,
536
+ ptr: newLine
537
+ });
538
+ }
539
+ if (strEnd.trimStart()) {
540
+ throw new TomlError("found extra tokens after the string part", {
541
+ toml: ctx.s,
542
+ ptr: ctx.p
543
+ });
544
+ }
545
+ if (endPtr < ctx.p) {
546
+ endPtr = ctx.s.indexOf(end, ctx.p);
547
+ if (endPtr < 0) {
548
+ throw new TomlError("incomplete key-value: cannot find end of key", {
549
+ toml: ctx.s,
550
+ ptr: start
551
+ });
552
+ }
553
+ }
554
+ parsed.push(part);
555
+ } else {
556
+ dot = ctx.s.indexOf(".", ctx.p);
557
+ let part = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
558
+ if (!KEY_PART_RE.test(part)) {
559
+ throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
560
+ toml: ctx.s,
561
+ ptr: ctx.p
562
+ });
563
+ }
564
+ parsed.push(part.trimEnd());
565
+ }
566
+ }
567
+ } while (dot + 1 && dot < endPtr);
568
+ ctx.p = endPtr + 1;
569
+ skipVoid(ctx, true, true);
570
+ return parsed;
571
+ }
572
+ function parseInlineTable(ctx, integersAsBigInt) {
573
+ let res = {};
574
+ let seen = /* @__PURE__ */ new Set();
575
+ let c;
576
+ ctx.p++;
577
+ while (ctx.p < ctx.s.length) {
578
+ skipVoid(ctx);
579
+ if ((c = ctx.s.charCodeAt(ctx.p)) === 125) {
580
+ ctx.p++;
581
+ return res;
582
+ }
583
+ let k;
584
+ let t = res;
585
+ let hasOwn = false;
586
+ let p = ctx.p;
587
+ let key = parseKey(ctx);
588
+ for (let i = 0; i < key.length; i++) {
589
+ if (i)
590
+ t = hasOwn ? t[k] : t[k] = {};
591
+ k = key[i];
592
+ if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
593
+ throw new TomlError("trying to redefine an already defined value", {
594
+ toml: ctx.s,
595
+ ptr: p
596
+ });
597
+ }
598
+ if (!hasOwn && k === "__proto__") {
599
+ Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
600
+ }
601
+ }
602
+ if (hasOwn) {
603
+ throw new TomlError("trying to redefine an already defined value", {
604
+ toml: ctx.s,
605
+ ptr: ctx.p
606
+ });
607
+ }
608
+ let value = extractValue(ctx, 125, integersAsBigInt);
609
+ seen.add(t[k] = value);
610
+ skipVoid(ctx);
611
+ if ((c = ctx.s.charCodeAt(ctx.p++)) === 125) {
612
+ return res;
613
+ }
614
+ if (c !== 44) {
615
+ throw new TomlError("expected comma or end of structure", { toml: ctx.s, ptr: ctx.p - 1 });
616
+ }
617
+ }
618
+ throw new TomlError("unfinished table encountered", {
619
+ toml: ctx.s,
620
+ ptr: ctx.p
621
+ });
622
+ }
623
+ function parseArray(ctx, integersAsBigInt) {
624
+ let res = [];
625
+ let c;
626
+ ctx.p++;
627
+ while (ctx.p < ctx.s.length) {
628
+ skipVoid(ctx);
629
+ if ((c = ctx.s.charCodeAt(ctx.p)) === 93) {
630
+ ctx.p++;
631
+ return res;
632
+ }
633
+ res.push(extractValue(ctx, 93, integersAsBigInt));
634
+ skipVoid(ctx);
635
+ if ((c = ctx.s.charCodeAt(ctx.p++)) === 93) {
636
+ return res;
637
+ }
638
+ if (c !== 44) {
639
+ throw new TomlError("expected comma or end of structure", { toml: ctx.s, ptr: ctx.p - 1 });
640
+ }
641
+ }
642
+ throw new TomlError("unfinished array encountered", {
643
+ toml: ctx.s,
644
+ ptr: ctx.p
645
+ });
646
+ }
647
+
648
+ // node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/parse.js
649
+ function peekTable(key, table, meta, type) {
650
+ let t = table;
651
+ let m = meta;
652
+ let k;
653
+ let hasOwn = false;
654
+ let state;
655
+ for (let i = 0; i < key.length; i++) {
656
+ if (i) {
657
+ t = hasOwn ? t[k] : t[k] = {};
658
+ m = (state = m[k]).c;
659
+ if (type === 0 && (state.t === 1 || state.t === 2)) {
660
+ return null;
661
+ }
662
+ if (state.t === 2) {
663
+ let l = t.length - 1;
664
+ t = t[l];
665
+ m = m[l].c;
666
+ }
667
+ }
668
+ k = key[i];
669
+ if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
670
+ return null;
671
+ }
672
+ if (!hasOwn) {
673
+ if (k === "__proto__") {
674
+ Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
675
+ Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
676
+ }
677
+ m[k] = {
678
+ t: i < key.length - 1 && type === 2 ? 3 : type,
679
+ d: false,
680
+ i: 0,
681
+ c: {}
682
+ };
683
+ }
684
+ }
685
+ state = m[k];
686
+ if (state.t !== type && !(type === 1 && state.t === 3)) {
687
+ return null;
688
+ }
689
+ if (type === 2) {
690
+ if (!state.d) {
691
+ state.d = true;
692
+ t[k] = [];
693
+ }
694
+ t[k].push(t = {});
695
+ state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
696
+ }
697
+ if (state.d) {
698
+ return null;
699
+ }
700
+ state.d = true;
701
+ if (type === 1) {
702
+ t = hasOwn ? t[k] : t[k] = {};
703
+ } else if (type === 0 && hasOwn) {
704
+ return null;
705
+ }
706
+ return [k, t, state.c];
707
+ }
708
+ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
709
+ let ctx = { s: toml, p: 0, d: maxDepth };
710
+ let res = {};
711
+ let meta = {};
712
+ let tmp;
713
+ let tbl = res;
714
+ let m = meta;
715
+ skipVoid(ctx);
716
+ while (ctx.p < toml.length) {
717
+ if (toml.charCodeAt(ctx.p) === 91) {
718
+ let isTableArray = toml.charCodeAt(++ctx.p) === 91;
719
+ tmp = ctx.p += +isTableArray;
720
+ let k = parseKey(ctx, "]");
721
+ if (isTableArray) {
722
+ if (toml.charCodeAt(ctx.p - 1) !== 93) {
723
+ throw new TomlError("expected end of table declaration", {
724
+ toml,
725
+ ptr: ctx.p - 1
726
+ });
727
+ }
728
+ ctx.p++;
729
+ }
730
+ let p = peekTable(
731
+ k,
732
+ res,
733
+ meta,
734
+ isTableArray ? 2 : 1
735
+ /* Type.EXPLICIT */
736
+ );
737
+ if (!p) {
738
+ throw new TomlError("trying to redefine an already defined table or value", {
739
+ toml,
740
+ ptr: tmp
741
+ });
742
+ }
743
+ m = p[2];
744
+ tbl = p[1];
745
+ } else {
746
+ tmp = ctx.p;
747
+ let k = parseKey(ctx);
748
+ let p = peekTable(
749
+ k,
750
+ tbl,
751
+ m,
752
+ 0
753
+ /* Type.DOTTED */
754
+ );
755
+ if (!p) {
756
+ throw new TomlError("trying to redefine an already defined table or value", {
757
+ toml,
758
+ ptr: tmp
759
+ });
760
+ }
761
+ p[1][p[0]] = extractValue(ctx, void 0, integersAsBigInt);
762
+ }
763
+ skipVoid(ctx, true);
764
+ if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 10 && tmp !== 13) {
765
+ throw new TomlError("each key-value declaration must be followed by an end-of-line", {
766
+ toml,
767
+ ptr: ctx.p
768
+ });
769
+ }
770
+ skipVoid(ctx);
771
+ }
772
+ return res;
773
+ }
774
+
775
+ // packages/core/dist/config.js
776
+ var POOL_NAME = /^[A-Za-z0-9_-]+$/;
777
+ function parsePools(raw, repos) {
778
+ const out = {};
779
+ if (raw === void 0)
780
+ return out;
781
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
782
+ throw new Error(`[pools] must be a table of pools in ${configPath()}`);
783
+ for (const [name, p] of Object.entries(raw)) {
784
+ const where = `[pools.${name}] in ${configPath()}`;
785
+ if (!POOL_NAME.test(name))
786
+ throw new Error(`${where}: a pool name may use letters, digits, "-" and "_" only`);
787
+ const repo = String(p?.repo ?? "");
788
+ if (!repos[repo])
789
+ throw new Error(`${where}: repo "${repo}" is not a configured [repos.<key>]`);
790
+ const size = p.size ?? 1;
791
+ if (typeof size !== "number" || !Number.isInteger(size) || size < 1)
792
+ throw new Error(`${where}: size must be a positive integer`);
793
+ const overflow = p.overflow ?? "headless";
794
+ if (overflow !== "headless" && overflow !== "queue")
795
+ throw new Error(`${where}: overflow must be "headless" or "queue"`);
796
+ out[name] = { repo, size, overflow };
797
+ }
798
+ return out;
799
+ }
800
+ var BRIEF_KINDS = ["conflict", "checks", "review", "ciFix", "rebase"];
801
+ function parseBriefHooks(raw) {
802
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
803
+ return void 0;
804
+ const out = {};
805
+ for (const kind of [...BRIEF_KINDS, "all"]) {
806
+ const text2 = raw[kind];
807
+ if (typeof text2 === "string" && text2.trim())
808
+ out[kind] = text2.trim();
809
+ }
810
+ return Object.keys(out).length ? out : void 0;
811
+ }
812
+ var ATTENTION_KINDS = ["question", "decision", "landed", "pr:draft", "pr:review", "pr:checks", "pr:conflict", "pr:ready", "report", "stack-ready"];
813
+ var DEFAULT_ATTENTION_KINDS = ["question", "decision", "pr:ready", "pr:review", "pr:conflict", "pr:checks"];
814
+ function parseAttentionKinds(raw) {
815
+ if (raw === void 0)
816
+ return [...DEFAULT_ATTENTION_KINDS];
817
+ if (!Array.isArray(raw)) {
818
+ throw new Error(`attentionKinds must be an array of kinds (${ATTENTION_KINDS.join(", ")}) in ${configPath()}`);
819
+ }
820
+ for (const k of raw) {
821
+ if (!ATTENTION_KINDS.includes(k)) {
822
+ throw new Error(`attentionKinds: unknown kind "${String(k)}" in ${configPath()} \u2014 valid kinds: ${ATTENTION_KINDS.join(", ")}`);
823
+ }
824
+ }
825
+ return [...new Set(raw)];
826
+ }
827
+ function parseSoak(raw) {
828
+ const soak = { ...DEFAULT_SOAK, ...raw ?? {} };
829
+ if (soak.terminal !== void 0 && soak.terminal !== "terminal" && soak.terminal !== "iterm") {
830
+ throw new Error(`soak.terminal: unknown terminal "${String(soak.terminal)}" in ${configPath()} \u2014 use "terminal" or "iterm"`);
831
+ }
832
+ return soak;
833
+ }
834
+ var DEFAULT_SOAK = {
835
+ deferSecs: 90,
836
+ claimIdleNoticeSecs: 180,
837
+ ttlSecs: 1800,
838
+ beat: true,
839
+ pausedTtlSecs: 86400,
840
+ signOffGraceSecs: 600
841
+ };
842
+ var DEFAULT_HELM = {
843
+ ttlSecs: 1800,
844
+ reportSecs: 900,
845
+ armGraceSecs: 5
846
+ };
847
+ var DEFAULT_GLASS = { port: 4949 };
848
+ var DEFAULT_WATCH = { maxForksPerCycle: 3, autoRepair: true, conflicts: true, checks: true, maxRepairsPerPr: 2, maxRepairsWithoutProgress: 2, repairSettleSecs: 600, repairCooldownSecs: 0, repairTrapWaitSecs: 600 };
122
849
  var DEFAULT_LIMITS = {
123
850
  maxConcurrent: 2,
124
851
  choreConcurrent: 1,
@@ -137,14 +864,82 @@ var DEFAULT_LIMITS = {
137
864
  reuseWorktree: true,
138
865
  releaseOnMerge: false
139
866
  };
867
+ function configPath() {
868
+ return path3.join(lobstahHome(), "config.toml");
869
+ }
870
+ function expandHome(p) {
871
+ return p.startsWith("~/") ? path3.join(os2.homedir(), p.slice(2)) : p;
872
+ }
873
+ function loadConfig() {
874
+ const file = configPath();
875
+ const raw = fs4.existsSync(file) ? parse(fs4.readFileSync(file, "utf8")) : {};
876
+ const watch = { ...DEFAULT_WATCH, ...raw.watch ?? {} };
877
+ if (typeof watch.repairCooldownSecs !== "number" || !Number.isFinite(watch.repairCooldownSecs) || watch.repairCooldownSecs < 0) {
878
+ throw new Error(`[watch].repairCooldownSecs must be a non-negative number in ${configPath()}`);
879
+ }
880
+ const readySettleSecs = raw.readySettleSecs ?? 600;
881
+ if (typeof readySettleSecs !== "number" || !Number.isFinite(readySettleSecs) || readySettleSecs < 0) {
882
+ throw new Error(`readySettleSecs must be a non-negative number in ${configPath()}`);
883
+ }
884
+ const reposRaw = raw.repos ?? {};
885
+ const repos = {};
886
+ for (const [key, r] of Object.entries(reposRaw)) {
887
+ repos[key] = {
888
+ path: expandHome(String(r.path ?? "")),
889
+ origin: r.origin ? String(r.origin) : void 0,
890
+ trunk: String(r.trunk ?? "main"),
891
+ setup: Array.isArray(r.setup) ? r.setup.map(String) : void 0,
892
+ env: r.env ?? void 0,
893
+ harness: r.harness ?? void 0,
894
+ pickup: r.pickup === void 0 ? void 0 : Boolean(r.pickup),
895
+ scratch: Array.isArray(r.scratch) ? r.scratch.map(String) : void 0,
896
+ pushEarly: r.pushEarly === void 0 ? void 0 : Boolean(r.pushEarly),
897
+ draftPr: r.draftPr === void 0 ? void 0 : Boolean(r.draftPr),
898
+ checkpointOnStop: r.checkpointOnStop === void 0 ? void 0 : Boolean(r.checkpointOnStop),
899
+ humanGateChecks: Array.isArray(r.humanGateChecks) ? r.humanGateChecks.map(String).filter(Boolean) : void 0,
900
+ briefHooks: parseBriefHooks(r.briefHooks),
901
+ poolKeep: Array.isArray(r.poolKeep) ? r.poolKeep.map(String).filter(Boolean) : void 0
902
+ };
903
+ }
904
+ const groundsRaw = raw.grounds ?? {};
905
+ const grounds = {};
906
+ for (const [key, g] of Object.entries(groundsRaw)) {
907
+ grounds[key] = { repos: Array.isArray(g.repos) ? g.repos.map(String) : [] };
908
+ }
909
+ return {
910
+ repos,
911
+ harness: raw.harness ?? {},
912
+ limits: { ...DEFAULT_LIMITS, ...raw.limits ?? {} },
913
+ soak: parseSoak(raw.soak),
914
+ helm: { ...DEFAULT_HELM, ...raw.helm ?? {} },
915
+ glass: { ...DEFAULT_GLASS, ...raw.glass ?? {} },
916
+ watch,
917
+ grounds,
918
+ pools: parsePools(raw.pools, repos),
919
+ notifyCommand: raw.notifyCommand ? String(raw.notifyCommand) : void 0,
920
+ notifyVerbs: Array.isArray(raw.notifyVerbs) ? raw.notifyVerbs.map(String) : void 0,
921
+ remindSecs: raw.remindSecs !== void 0 ? Number(raw.remindSecs) : void 0,
922
+ readySettleSecs,
923
+ attentionKinds: parseAttentionKinds(raw.attentionKinds)
924
+ };
925
+ }
140
926
 
141
927
  // packages/core/dist/pr.js
142
928
  var PR_VIEW_FIELDS = "title,state,isDraft,isCrossRepository,headRefOid,baseRefName,baseRefOid,headRefName,mergeStateStatus,reviewDecision,statusCheckRollup,mergedAt,closedAt,updatedAt,reviews";
143
929
  var PR_VIEW_FIELDS_NO_CHECKS = PR_VIEW_FIELDS.split(",").filter((f) => f !== "statusCheckRollup").join(",");
144
930
 
931
+ // packages/core/dist/worktrees.js
932
+ var WARM_LOCK_MAX_MS = 15 * 6e4;
933
+
145
934
  // packages/core/dist/disk.js
146
935
  var GB = 1024 ** 3;
147
936
 
937
+ // packages/core/dist/pools.js
938
+ var POOL_WARM_MAX_MS = 60 * 6e4;
939
+ var POOL_RETRY_MS = 10 * 6e4;
940
+ var POOL_FETCH_MS = 10 * 6e4;
941
+ var POOL_RECHECK_MS = 5 * 6e4;
942
+
148
943
  // packages/core/dist/decisions.js
149
944
  var DECISION_DETAIL_MAX = 64 * 1024;
150
945
  var ANSWER_TYPES = {
@@ -169,35 +964,35 @@ var ANSWER_TYPES = {
169
964
  var ANSWER_EXTENSIONS = Object.keys(ANSWER_TYPES);
170
965
 
171
966
  // packages/core/dist/inbox.js
172
- import * as fs4 from "node:fs";
173
- import * as path3 from "node:path";
967
+ import * as fs5 from "node:fs";
968
+ import * as path4 from "node:path";
174
969
  var metaName = (file) => file.replace(/\.msg$/, ".meta.json");
175
970
  function inboxDir(id, lane) {
176
- return path3.join(laneDirs(lane).inbox, id);
971
+ return path4.join(laneDirs(lane).inbox, id);
177
972
  }
178
973
  function sendMessage(id, lane, text2, from, attachments = []) {
179
974
  const dir = inboxDir(id, lane);
180
- fs4.mkdirSync(path3.join(dir, "handled"), { recursive: true });
181
- const existing = fs4.readdirSync(dir).filter((f) => f.endsWith(".msg")).length;
182
- const handled = fs4.readdirSync(path3.join(dir, "handled")).filter((f) => f.endsWith(".msg")).length;
975
+ fs5.mkdirSync(path4.join(dir, "handled"), { recursive: true });
976
+ const existing = fs5.readdirSync(dir).filter((f) => f.endsWith(".msg")).length;
977
+ const handled = fs5.readdirSync(path4.join(dir, "handled")).filter((f) => f.endsWith(".msg")).length;
183
978
  const name = `${String(existing + handled + 1).padStart(3, "0")}.msg`;
184
- const tmp = path3.join(dir, `.tmp-${process.pid}-${Date.now()}`);
979
+ const tmp = uniqueTempPath(path4.join(dir, name));
185
980
  if (from !== void 0 || attachments.length > 0) {
186
981
  const meta = { from: from ?? "unknown", at: (/* @__PURE__ */ new Date()).toISOString(), ...attachments.length ? { attachments } : {} };
187
982
  const metaTmp = `${tmp}.meta`;
188
- fs4.writeFileSync(metaTmp, JSON.stringify(meta, null, 2));
189
- fs4.renameSync(metaTmp, path3.join(dir, metaName(name)));
983
+ fs5.writeFileSync(metaTmp, JSON.stringify(meta, null, 2));
984
+ fs5.renameSync(metaTmp, path4.join(dir, metaName(name)));
190
985
  }
191
- fs4.writeFileSync(tmp, text2);
192
- fs4.renameSync(tmp, path3.join(dir, name));
986
+ fs5.writeFileSync(tmp, text2);
987
+ fs5.renameSync(tmp, path4.join(dir, name));
193
988
  return name;
194
989
  }
195
990
 
196
991
  // packages/core/dist/events.js
197
- import * as fs5 from "node:fs";
992
+ import * as fs6 from "node:fs";
198
993
  function lastEventAt(id, lane) {
199
994
  try {
200
- return fs5.statSync(eventsPath(id, lane)).mtimeMs;
995
+ return fs6.statSync(eventsPath(id, lane)).mtimeMs;
201
996
  } catch {
202
997
  return void 0;
203
998
  }
@@ -233,7 +1028,7 @@ function text(payload, details = {}) {
233
1028
  function findLane(id) {
234
1029
  for (const lane of ["work", "chore"]) {
235
1030
  const d = laneDirs(lane);
236
- if (fs6.existsSync(path4.join(d.active, id)) || fs6.existsSync(path4.join(d.queue, `${id}.json`)) || fs6.existsSync(path4.join(d.done, id)) || fs6.existsSync(path4.join(d.state, `${id}.status`))) {
1031
+ if (fs7.existsSync(path5.join(d.active, id)) || fs7.existsSync(path5.join(d.queue, `${id}.json`)) || fs7.existsSync(path5.join(d.done, id)) || fs7.existsSync(path5.join(d.state, `${id}.status`))) {
237
1032
  return lane;
238
1033
  }
239
1034
  }
@@ -259,29 +1054,40 @@ function dispatchTool() {
259
1054
  harness: { type: "string", enum: ["claude", "codex"] },
260
1055
  model: { type: "string" },
261
1056
  effort: { type: "string" },
262
- followUp: { type: "string", description: "Earlier dispatch UUID whose session to fork" }
1057
+ followUp: { type: "string", description: "Earlier dispatch UUID whose session to fork" },
1058
+ pool: {
1059
+ type: "string",
1060
+ description: "Worktree pool from the host config ([pools.<name>]): run in a warm pool worktree with a fresh session. repo defaults to the pool's repo."
1061
+ }
263
1062
  },
264
- required: ["repo", "brief"],
1063
+ required: ["brief"],
265
1064
  additionalProperties: false
266
1065
  },
267
1066
  async execute(_id, params) {
268
1067
  ensureLayout();
269
- const repo = param(params, "repo");
1068
+ const poolName = param(params, "pool");
1069
+ const pool = poolName !== void 0 ? loadConfig().pools[poolName] : void 0;
1070
+ if (poolName !== void 0 && !pool) throw new Error(`lobstah_dispatch: unknown pool "${poolName}"`);
1071
+ if (pool && param(params, "repo") !== void 0 && param(params, "repo") !== pool.repo) {
1072
+ throw new Error(`lobstah_dispatch: pool ${poolName} serves repo ${pool.repo}, not ${param(params, "repo")}`);
1073
+ }
1074
+ const repo = param(params, "repo") ?? pool?.repo;
270
1075
  const brief = param(params, "brief");
271
- if (!repo || !brief) throw new Error("lobstah_dispatch requires repo and brief");
1076
+ if (!repo || !brief) throw new Error("lobstah_dispatch requires brief, and repo or pool");
272
1077
  const d = {
273
- id: randomUUID(),
1078
+ id: randomUUID2(),
274
1079
  repo,
275
1080
  brief,
276
1081
  harness: param(params, "harness"),
277
1082
  model: param(params, "model"),
278
1083
  effort: param(params, "effort"),
279
- followUp: param(params, "followUp")
1084
+ followUp: param(params, "followUp"),
1085
+ ...poolName !== void 0 ? { pool: poolName } : {}
280
1086
  };
281
1087
  if (d.harness) d.harnessExplicit = true;
282
1088
  if (d.model) d.modelExplicit = true;
283
1089
  enqueue(d, "work");
284
- return text(toonKV({ id: d.id, repo, queued: (/* @__PURE__ */ new Date()).toISOString() }), { id: d.id });
1090
+ return text(toonKV({ id: d.id, repo, ...d.pool ? { pool: d.pool } : {}, queued: (/* @__PURE__ */ new Date()).toISOString() }), { id: d.id });
285
1091
  }
286
1092
  };
287
1093
  }
@@ -305,7 +1111,7 @@ function statusTool() {
305
1111
  return text(toonKV({ id, lane, state, lastNote: log.at(-1)?.note }), { id, state });
306
1112
  }
307
1113
  const rows = ["work", "chore"].flatMap(
308
- (lane) => fs6.readdirSync(laneDirs(lane).active).filter((f) => !f.startsWith(".")).map((did) => ({ id: did, lane, state: stateOf(did, lane) }))
1114
+ (lane) => fs7.readdirSync(laneDirs(lane).active).filter((f) => !f.startsWith(".")).map((did) => ({ id: did, lane, state: stateOf(did, lane) }))
309
1115
  );
310
1116
  return text(toonTable("active", rows, ["id", "lane", "state"]), { count: rows.length });
311
1117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lobstah-openclaw-plugin",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "OpenClaw plugin for lobstah: gives fleet agents dispatch/status/send/cancel tools and operators a /lobstah command.",
5
5
  "license": "MIT",
6
6
  "author": "aequitas labs LLC",