@karmaniverous/jeeves-watcher-openclaw 0.8.1 → 0.9.0

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/cli.js CHANGED
@@ -1,17 +1,1223 @@
1
1
  #!/usr/bin/env node
2
- import { dirname, join } from 'node:path';
3
- import { existsSync, mkdirSync, writeFileSync, readFileSync, renameSync } from 'node:fs';
4
- import require$$0$3, { join as join$1, dirname as dirname$1, resolve } from 'path';
5
- import require$$0$2, { existsSync as existsSync$1, rmSync, mkdirSync as mkdirSync$1, cpSync, readFileSync as readFileSync$1, writeFileSync as writeFileSync$1 } from 'fs';
2
+ import { existsSync, readFileSync, writeFileSync, renameSync, rmSync, mkdirSync, cpSync } from 'node:fs';
3
+ import { dirname, resolve, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import 'vm';
6
+ import require$$0$3 from 'path';
7
+ import require$$0$2 from 'fs';
6
8
  import require$$0 from 'constants';
7
9
  import require$$0$1 from 'stream';
8
10
  import require$$4 from 'util';
9
11
  import require$$5 from 'assert';
10
12
  import require$$2 from 'events';
11
- import 'node:url';
12
13
  import 'node:child_process';
13
- import { homedir } from 'os';
14
- import { fileURLToPath } from 'url';
14
+ import { homedir } from 'node:os';
15
+
16
+ /**
17
+ * @implements {IHooks}
18
+ */
19
+ class Hooks {
20
+ /**
21
+ * @callback HookCallback
22
+ * @this {*|Jsep} this
23
+ * @param {Jsep} env
24
+ * @returns: void
25
+ */
26
+ /**
27
+ * Adds the given callback to the list of callbacks for the given hook.
28
+ *
29
+ * The callback will be invoked when the hook it is registered for is run.
30
+ *
31
+ * One callback function can be registered to multiple hooks and the same hook multiple times.
32
+ *
33
+ * @param {string|object} name The name of the hook, or an object of callbacks keyed by name
34
+ * @param {HookCallback|boolean} callback The callback function which is given environment variables.
35
+ * @param {?boolean} [first=false] Will add the hook to the top of the list (defaults to the bottom)
36
+ * @public
37
+ */
38
+ add(name, callback, first) {
39
+ if (typeof arguments[0] != 'string') {
40
+ // Multiple hook callbacks, keyed by name
41
+ for (let name in arguments[0]) {
42
+ this.add(name, arguments[0][name], arguments[1]);
43
+ }
44
+ } else {
45
+ (Array.isArray(name) ? name : [name]).forEach(function (name) {
46
+ this[name] = this[name] || [];
47
+ if (callback) {
48
+ this[name][first ? 'unshift' : 'push'](callback);
49
+ }
50
+ }, this);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Runs a hook invoking all registered callbacks with the given environment variables.
56
+ *
57
+ * Callbacks will be invoked synchronously and in the order in which they were registered.
58
+ *
59
+ * @param {string} name The name of the hook.
60
+ * @param {Object<string, any>} env The environment variables of the hook passed to all callbacks registered.
61
+ * @public
62
+ */
63
+ run(name, env) {
64
+ this[name] = this[name] || [];
65
+ this[name].forEach(function (callback) {
66
+ callback.call(env && env.context ? env.context : env, env);
67
+ });
68
+ }
69
+ }
70
+
71
+ /**
72
+ * @implements {IPlugins}
73
+ */
74
+ class Plugins {
75
+ constructor(jsep) {
76
+ this.jsep = jsep;
77
+ this.registered = {};
78
+ }
79
+
80
+ /**
81
+ * @callback PluginSetup
82
+ * @this {Jsep} jsep
83
+ * @returns: void
84
+ */
85
+ /**
86
+ * Adds the given plugin(s) to the registry
87
+ *
88
+ * @param {object} plugins
89
+ * @param {string} plugins.name The name of the plugin
90
+ * @param {PluginSetup} plugins.init The init function
91
+ * @public
92
+ */
93
+ register(...plugins) {
94
+ plugins.forEach(plugin => {
95
+ if (typeof plugin !== 'object' || !plugin.name || !plugin.init) {
96
+ throw new Error('Invalid JSEP plugin format');
97
+ }
98
+ if (this.registered[plugin.name]) {
99
+ // already registered. Ignore.
100
+ return;
101
+ }
102
+ plugin.init(this.jsep);
103
+ this.registered[plugin.name] = plugin;
104
+ });
105
+ }
106
+ }
107
+
108
+ // JavaScript Expression Parser (JSEP) 1.4.0
109
+
110
+ class Jsep {
111
+ /**
112
+ * @returns {string}
113
+ */
114
+ static get version() {
115
+ // To be filled in by the template
116
+ return '1.4.0';
117
+ }
118
+
119
+ /**
120
+ * @returns {string}
121
+ */
122
+ static toString() {
123
+ return 'JavaScript Expression Parser (JSEP) v' + Jsep.version;
124
+ }
125
+ // ==================== CONFIG ================================
126
+ /**
127
+ * @method addUnaryOp
128
+ * @param {string} op_name The name of the unary op to add
129
+ * @returns {Jsep}
130
+ */
131
+ static addUnaryOp(op_name) {
132
+ Jsep.max_unop_len = Math.max(op_name.length, Jsep.max_unop_len);
133
+ Jsep.unary_ops[op_name] = 1;
134
+ return Jsep;
135
+ }
136
+
137
+ /**
138
+ * @method jsep.addBinaryOp
139
+ * @param {string} op_name The name of the binary op to add
140
+ * @param {number} precedence The precedence of the binary op (can be a float). Higher number = higher precedence
141
+ * @param {boolean} [isRightAssociative=false] whether operator is right-associative
142
+ * @returns {Jsep}
143
+ */
144
+ static addBinaryOp(op_name, precedence, isRightAssociative) {
145
+ Jsep.max_binop_len = Math.max(op_name.length, Jsep.max_binop_len);
146
+ Jsep.binary_ops[op_name] = precedence;
147
+ if (isRightAssociative) {
148
+ Jsep.right_associative.add(op_name);
149
+ } else {
150
+ Jsep.right_associative.delete(op_name);
151
+ }
152
+ return Jsep;
153
+ }
154
+
155
+ /**
156
+ * @method addIdentifierChar
157
+ * @param {string} char The additional character to treat as a valid part of an identifier
158
+ * @returns {Jsep}
159
+ */
160
+ static addIdentifierChar(char) {
161
+ Jsep.additional_identifier_chars.add(char);
162
+ return Jsep;
163
+ }
164
+
165
+ /**
166
+ * @method addLiteral
167
+ * @param {string} literal_name The name of the literal to add
168
+ * @param {*} literal_value The value of the literal
169
+ * @returns {Jsep}
170
+ */
171
+ static addLiteral(literal_name, literal_value) {
172
+ Jsep.literals[literal_name] = literal_value;
173
+ return Jsep;
174
+ }
175
+
176
+ /**
177
+ * @method removeUnaryOp
178
+ * @param {string} op_name The name of the unary op to remove
179
+ * @returns {Jsep}
180
+ */
181
+ static removeUnaryOp(op_name) {
182
+ delete Jsep.unary_ops[op_name];
183
+ if (op_name.length === Jsep.max_unop_len) {
184
+ Jsep.max_unop_len = Jsep.getMaxKeyLen(Jsep.unary_ops);
185
+ }
186
+ return Jsep;
187
+ }
188
+
189
+ /**
190
+ * @method removeAllUnaryOps
191
+ * @returns {Jsep}
192
+ */
193
+ static removeAllUnaryOps() {
194
+ Jsep.unary_ops = {};
195
+ Jsep.max_unop_len = 0;
196
+ return Jsep;
197
+ }
198
+
199
+ /**
200
+ * @method removeIdentifierChar
201
+ * @param {string} char The additional character to stop treating as a valid part of an identifier
202
+ * @returns {Jsep}
203
+ */
204
+ static removeIdentifierChar(char) {
205
+ Jsep.additional_identifier_chars.delete(char);
206
+ return Jsep;
207
+ }
208
+
209
+ /**
210
+ * @method removeBinaryOp
211
+ * @param {string} op_name The name of the binary op to remove
212
+ * @returns {Jsep}
213
+ */
214
+ static removeBinaryOp(op_name) {
215
+ delete Jsep.binary_ops[op_name];
216
+ if (op_name.length === Jsep.max_binop_len) {
217
+ Jsep.max_binop_len = Jsep.getMaxKeyLen(Jsep.binary_ops);
218
+ }
219
+ Jsep.right_associative.delete(op_name);
220
+ return Jsep;
221
+ }
222
+
223
+ /**
224
+ * @method removeAllBinaryOps
225
+ * @returns {Jsep}
226
+ */
227
+ static removeAllBinaryOps() {
228
+ Jsep.binary_ops = {};
229
+ Jsep.max_binop_len = 0;
230
+ return Jsep;
231
+ }
232
+
233
+ /**
234
+ * @method removeLiteral
235
+ * @param {string} literal_name The name of the literal to remove
236
+ * @returns {Jsep}
237
+ */
238
+ static removeLiteral(literal_name) {
239
+ delete Jsep.literals[literal_name];
240
+ return Jsep;
241
+ }
242
+
243
+ /**
244
+ * @method removeAllLiterals
245
+ * @returns {Jsep}
246
+ */
247
+ static removeAllLiterals() {
248
+ Jsep.literals = {};
249
+ return Jsep;
250
+ }
251
+ // ==================== END CONFIG ============================
252
+
253
+ /**
254
+ * @returns {string}
255
+ */
256
+ get char() {
257
+ return this.expr.charAt(this.index);
258
+ }
259
+
260
+ /**
261
+ * @returns {number}
262
+ */
263
+ get code() {
264
+ return this.expr.charCodeAt(this.index);
265
+ }
266
+ /**
267
+ * @param {string} expr a string with the passed in express
268
+ * @returns Jsep
269
+ */
270
+ constructor(expr) {
271
+ // `index` stores the character number we are currently at
272
+ // All of the gobbles below will modify `index` as we move along
273
+ this.expr = expr;
274
+ this.index = 0;
275
+ }
276
+
277
+ /**
278
+ * static top-level parser
279
+ * @returns {jsep.Expression}
280
+ */
281
+ static parse(expr) {
282
+ return new Jsep(expr).parse();
283
+ }
284
+
285
+ /**
286
+ * Get the longest key length of any object
287
+ * @param {object} obj
288
+ * @returns {number}
289
+ */
290
+ static getMaxKeyLen(obj) {
291
+ return Math.max(0, ...Object.keys(obj).map(k => k.length));
292
+ }
293
+
294
+ /**
295
+ * `ch` is a character code in the next three functions
296
+ * @param {number} ch
297
+ * @returns {boolean}
298
+ */
299
+ static isDecimalDigit(ch) {
300
+ return ch >= 48 && ch <= 57; // 0...9
301
+ }
302
+
303
+ /**
304
+ * Returns the precedence of a binary operator or `0` if it isn't a binary operator. Can be float.
305
+ * @param {string} op_val
306
+ * @returns {number}
307
+ */
308
+ static binaryPrecedence(op_val) {
309
+ return Jsep.binary_ops[op_val] || 0;
310
+ }
311
+
312
+ /**
313
+ * Looks for start of identifier
314
+ * @param {number} ch
315
+ * @returns {boolean}
316
+ */
317
+ static isIdentifierStart(ch) {
318
+ return ch >= 65 && ch <= 90 ||
319
+ // A...Z
320
+ ch >= 97 && ch <= 122 ||
321
+ // a...z
322
+ ch >= 128 && !Jsep.binary_ops[String.fromCharCode(ch)] ||
323
+ // any non-ASCII that is not an operator
324
+ Jsep.additional_identifier_chars.has(String.fromCharCode(ch)); // additional characters
325
+ }
326
+
327
+ /**
328
+ * @param {number} ch
329
+ * @returns {boolean}
330
+ */
331
+ static isIdentifierPart(ch) {
332
+ return Jsep.isIdentifierStart(ch) || Jsep.isDecimalDigit(ch);
333
+ }
334
+
335
+ /**
336
+ * throw error at index of the expression
337
+ * @param {string} message
338
+ * @throws
339
+ */
340
+ throwError(message) {
341
+ const error = new Error(message + ' at character ' + this.index);
342
+ error.index = this.index;
343
+ error.description = message;
344
+ throw error;
345
+ }
346
+
347
+ /**
348
+ * Run a given hook
349
+ * @param {string} name
350
+ * @param {jsep.Expression|false} [node]
351
+ * @returns {?jsep.Expression}
352
+ */
353
+ runHook(name, node) {
354
+ if (Jsep.hooks[name]) {
355
+ const env = {
356
+ context: this,
357
+ node
358
+ };
359
+ Jsep.hooks.run(name, env);
360
+ return env.node;
361
+ }
362
+ return node;
363
+ }
364
+
365
+ /**
366
+ * Runs a given hook until one returns a node
367
+ * @param {string} name
368
+ * @returns {?jsep.Expression}
369
+ */
370
+ searchHook(name) {
371
+ if (Jsep.hooks[name]) {
372
+ const env = {
373
+ context: this
374
+ };
375
+ Jsep.hooks[name].find(function (callback) {
376
+ callback.call(env.context, env);
377
+ return env.node;
378
+ });
379
+ return env.node;
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Push `index` up to the next non-space character
385
+ */
386
+ gobbleSpaces() {
387
+ let ch = this.code;
388
+ // Whitespace
389
+ while (ch === Jsep.SPACE_CODE || ch === Jsep.TAB_CODE || ch === Jsep.LF_CODE || ch === Jsep.CR_CODE) {
390
+ ch = this.expr.charCodeAt(++this.index);
391
+ }
392
+ this.runHook('gobble-spaces');
393
+ }
394
+
395
+ /**
396
+ * Top-level method to parse all expressions and returns compound or single node
397
+ * @returns {jsep.Expression}
398
+ */
399
+ parse() {
400
+ this.runHook('before-all');
401
+ const nodes = this.gobbleExpressions();
402
+
403
+ // If there's only one expression just try returning the expression
404
+ const node = nodes.length === 1 ? nodes[0] : {
405
+ type: Jsep.COMPOUND,
406
+ body: nodes
407
+ };
408
+ return this.runHook('after-all', node);
409
+ }
410
+
411
+ /**
412
+ * top-level parser (but can be reused within as well)
413
+ * @param {number} [untilICode]
414
+ * @returns {jsep.Expression[]}
415
+ */
416
+ gobbleExpressions(untilICode) {
417
+ let nodes = [],
418
+ ch_i,
419
+ node;
420
+ while (this.index < this.expr.length) {
421
+ ch_i = this.code;
422
+
423
+ // Expressions can be separated by semicolons, commas, or just inferred without any
424
+ // separators
425
+ if (ch_i === Jsep.SEMCOL_CODE || ch_i === Jsep.COMMA_CODE) {
426
+ this.index++; // ignore separators
427
+ } else {
428
+ // Try to gobble each expression individually
429
+ if (node = this.gobbleExpression()) {
430
+ nodes.push(node);
431
+ // If we weren't able to find a binary expression and are out of room, then
432
+ // the expression passed in probably has too much
433
+ } else if (this.index < this.expr.length) {
434
+ if (ch_i === untilICode) {
435
+ break;
436
+ }
437
+ this.throwError('Unexpected "' + this.char + '"');
438
+ }
439
+ }
440
+ }
441
+ return nodes;
442
+ }
443
+
444
+ /**
445
+ * The main parsing function.
446
+ * @returns {?jsep.Expression}
447
+ */
448
+ gobbleExpression() {
449
+ const node = this.searchHook('gobble-expression') || this.gobbleBinaryExpression();
450
+ this.gobbleSpaces();
451
+ return this.runHook('after-expression', node);
452
+ }
453
+
454
+ /**
455
+ * Search for the operation portion of the string (e.g. `+`, `===`)
456
+ * Start by taking the longest possible binary operations (3 characters: `===`, `!==`, `>>>`)
457
+ * and move down from 3 to 2 to 1 character until a matching binary operation is found
458
+ * then, return that binary operation
459
+ * @returns {string|boolean}
460
+ */
461
+ gobbleBinaryOp() {
462
+ this.gobbleSpaces();
463
+ let to_check = this.expr.substr(this.index, Jsep.max_binop_len);
464
+ let tc_len = to_check.length;
465
+ while (tc_len > 0) {
466
+ // Don't accept a binary op when it is an identifier.
467
+ // Binary ops that start with a identifier-valid character must be followed
468
+ // by a non identifier-part valid character
469
+ if (Jsep.binary_ops.hasOwnProperty(to_check) && (!Jsep.isIdentifierStart(this.code) || this.index + to_check.length < this.expr.length && !Jsep.isIdentifierPart(this.expr.charCodeAt(this.index + to_check.length)))) {
470
+ this.index += tc_len;
471
+ return to_check;
472
+ }
473
+ to_check = to_check.substr(0, --tc_len);
474
+ }
475
+ return false;
476
+ }
477
+
478
+ /**
479
+ * This function is responsible for gobbling an individual expression,
480
+ * e.g. `1`, `1+2`, `a+(b*2)-Math.sqrt(2)`
481
+ * @returns {?jsep.BinaryExpression}
482
+ */
483
+ gobbleBinaryExpression() {
484
+ let node, biop, prec, stack, biop_info, left, right, i, cur_biop;
485
+
486
+ // First, try to get the leftmost thing
487
+ // Then, check to see if there's a binary operator operating on that leftmost thing
488
+ // Don't gobbleBinaryOp without a left-hand-side
489
+ left = this.gobbleToken();
490
+ if (!left) {
491
+ return left;
492
+ }
493
+ biop = this.gobbleBinaryOp();
494
+
495
+ // If there wasn't a binary operator, just return the leftmost node
496
+ if (!biop) {
497
+ return left;
498
+ }
499
+
500
+ // Otherwise, we need to start a stack to properly place the binary operations in their
501
+ // precedence structure
502
+ biop_info = {
503
+ value: biop,
504
+ prec: Jsep.binaryPrecedence(biop),
505
+ right_a: Jsep.right_associative.has(biop)
506
+ };
507
+ right = this.gobbleToken();
508
+ if (!right) {
509
+ this.throwError("Expected expression after " + biop);
510
+ }
511
+ stack = [left, biop_info, right];
512
+
513
+ // Properly deal with precedence using [recursive descent](http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm)
514
+ while (biop = this.gobbleBinaryOp()) {
515
+ prec = Jsep.binaryPrecedence(biop);
516
+ if (prec === 0) {
517
+ this.index -= biop.length;
518
+ break;
519
+ }
520
+ biop_info = {
521
+ value: biop,
522
+ prec,
523
+ right_a: Jsep.right_associative.has(biop)
524
+ };
525
+ cur_biop = biop;
526
+
527
+ // Reduce: make a binary expression from the three topmost entries.
528
+ const comparePrev = prev => biop_info.right_a && prev.right_a ? prec > prev.prec : prec <= prev.prec;
529
+ while (stack.length > 2 && comparePrev(stack[stack.length - 2])) {
530
+ right = stack.pop();
531
+ biop = stack.pop().value;
532
+ left = stack.pop();
533
+ node = {
534
+ type: Jsep.BINARY_EXP,
535
+ operator: biop,
536
+ left,
537
+ right
538
+ };
539
+ stack.push(node);
540
+ }
541
+ node = this.gobbleToken();
542
+ if (!node) {
543
+ this.throwError("Expected expression after " + cur_biop);
544
+ }
545
+ stack.push(biop_info, node);
546
+ }
547
+ i = stack.length - 1;
548
+ node = stack[i];
549
+ while (i > 1) {
550
+ node = {
551
+ type: Jsep.BINARY_EXP,
552
+ operator: stack[i - 1].value,
553
+ left: stack[i - 2],
554
+ right: node
555
+ };
556
+ i -= 2;
557
+ }
558
+ return node;
559
+ }
560
+
561
+ /**
562
+ * An individual part of a binary expression:
563
+ * e.g. `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)` (because it's in parenthesis)
564
+ * @returns {boolean|jsep.Expression}
565
+ */
566
+ gobbleToken() {
567
+ let ch, to_check, tc_len, node;
568
+ this.gobbleSpaces();
569
+ node = this.searchHook('gobble-token');
570
+ if (node) {
571
+ return this.runHook('after-token', node);
572
+ }
573
+ ch = this.code;
574
+ if (Jsep.isDecimalDigit(ch) || ch === Jsep.PERIOD_CODE) {
575
+ // Char code 46 is a dot `.` which can start off a numeric literal
576
+ return this.gobbleNumericLiteral();
577
+ }
578
+ if (ch === Jsep.SQUOTE_CODE || ch === Jsep.DQUOTE_CODE) {
579
+ // Single or double quotes
580
+ node = this.gobbleStringLiteral();
581
+ } else if (ch === Jsep.OBRACK_CODE) {
582
+ node = this.gobbleArray();
583
+ } else {
584
+ to_check = this.expr.substr(this.index, Jsep.max_unop_len);
585
+ tc_len = to_check.length;
586
+ while (tc_len > 0) {
587
+ // Don't accept an unary op when it is an identifier.
588
+ // Unary ops that start with a identifier-valid character must be followed
589
+ // by a non identifier-part valid character
590
+ if (Jsep.unary_ops.hasOwnProperty(to_check) && (!Jsep.isIdentifierStart(this.code) || this.index + to_check.length < this.expr.length && !Jsep.isIdentifierPart(this.expr.charCodeAt(this.index + to_check.length)))) {
591
+ this.index += tc_len;
592
+ const argument = this.gobbleToken();
593
+ if (!argument) {
594
+ this.throwError('missing unaryOp argument');
595
+ }
596
+ return this.runHook('after-token', {
597
+ type: Jsep.UNARY_EXP,
598
+ operator: to_check,
599
+ argument,
600
+ prefix: true
601
+ });
602
+ }
603
+ to_check = to_check.substr(0, --tc_len);
604
+ }
605
+ if (Jsep.isIdentifierStart(ch)) {
606
+ node = this.gobbleIdentifier();
607
+ if (Jsep.literals.hasOwnProperty(node.name)) {
608
+ node = {
609
+ type: Jsep.LITERAL,
610
+ value: Jsep.literals[node.name],
611
+ raw: node.name
612
+ };
613
+ } else if (node.name === Jsep.this_str) {
614
+ node = {
615
+ type: Jsep.THIS_EXP
616
+ };
617
+ }
618
+ } else if (ch === Jsep.OPAREN_CODE) {
619
+ // open parenthesis
620
+ node = this.gobbleGroup();
621
+ }
622
+ }
623
+ if (!node) {
624
+ return this.runHook('after-token', false);
625
+ }
626
+ node = this.gobbleTokenProperty(node);
627
+ return this.runHook('after-token', node);
628
+ }
629
+
630
+ /**
631
+ * Gobble properties of of identifiers/strings/arrays/groups.
632
+ * e.g. `foo`, `bar.baz`, `foo['bar'].baz`
633
+ * It also gobbles function calls:
634
+ * e.g. `Math.acos(obj.angle)`
635
+ * @param {jsep.Expression} node
636
+ * @returns {jsep.Expression}
637
+ */
638
+ gobbleTokenProperty(node) {
639
+ this.gobbleSpaces();
640
+ let ch = this.code;
641
+ while (ch === Jsep.PERIOD_CODE || ch === Jsep.OBRACK_CODE || ch === Jsep.OPAREN_CODE || ch === Jsep.QUMARK_CODE) {
642
+ let optional;
643
+ if (ch === Jsep.QUMARK_CODE) {
644
+ if (this.expr.charCodeAt(this.index + 1) !== Jsep.PERIOD_CODE) {
645
+ break;
646
+ }
647
+ optional = true;
648
+ this.index += 2;
649
+ this.gobbleSpaces();
650
+ ch = this.code;
651
+ }
652
+ this.index++;
653
+ if (ch === Jsep.OBRACK_CODE) {
654
+ node = {
655
+ type: Jsep.MEMBER_EXP,
656
+ computed: true,
657
+ object: node,
658
+ property: this.gobbleExpression()
659
+ };
660
+ if (!node.property) {
661
+ this.throwError('Unexpected "' + this.char + '"');
662
+ }
663
+ this.gobbleSpaces();
664
+ ch = this.code;
665
+ if (ch !== Jsep.CBRACK_CODE) {
666
+ this.throwError('Unclosed [');
667
+ }
668
+ this.index++;
669
+ } else if (ch === Jsep.OPAREN_CODE) {
670
+ // A function call is being made; gobble all the arguments
671
+ node = {
672
+ type: Jsep.CALL_EXP,
673
+ 'arguments': this.gobbleArguments(Jsep.CPAREN_CODE),
674
+ callee: node
675
+ };
676
+ } else if (ch === Jsep.PERIOD_CODE || optional) {
677
+ if (optional) {
678
+ this.index--;
679
+ }
680
+ this.gobbleSpaces();
681
+ node = {
682
+ type: Jsep.MEMBER_EXP,
683
+ computed: false,
684
+ object: node,
685
+ property: this.gobbleIdentifier()
686
+ };
687
+ }
688
+ if (optional) {
689
+ node.optional = true;
690
+ } // else leave undefined for compatibility with esprima
691
+
692
+ this.gobbleSpaces();
693
+ ch = this.code;
694
+ }
695
+ return node;
696
+ }
697
+
698
+ /**
699
+ * Parse simple numeric literals: `12`, `3.4`, `.5`. Do this by using a string to
700
+ * keep track of everything in the numeric literal and then calling `parseFloat` on that string
701
+ * @returns {jsep.Literal}
702
+ */
703
+ gobbleNumericLiteral() {
704
+ let number = '',
705
+ ch,
706
+ chCode;
707
+ while (Jsep.isDecimalDigit(this.code)) {
708
+ number += this.expr.charAt(this.index++);
709
+ }
710
+ if (this.code === Jsep.PERIOD_CODE) {
711
+ // can start with a decimal marker
712
+ number += this.expr.charAt(this.index++);
713
+ while (Jsep.isDecimalDigit(this.code)) {
714
+ number += this.expr.charAt(this.index++);
715
+ }
716
+ }
717
+ ch = this.char;
718
+ if (ch === 'e' || ch === 'E') {
719
+ // exponent marker
720
+ number += this.expr.charAt(this.index++);
721
+ ch = this.char;
722
+ if (ch === '+' || ch === '-') {
723
+ // exponent sign
724
+ number += this.expr.charAt(this.index++);
725
+ }
726
+ while (Jsep.isDecimalDigit(this.code)) {
727
+ // exponent itself
728
+ number += this.expr.charAt(this.index++);
729
+ }
730
+ if (!Jsep.isDecimalDigit(this.expr.charCodeAt(this.index - 1))) {
731
+ this.throwError('Expected exponent (' + number + this.char + ')');
732
+ }
733
+ }
734
+ chCode = this.code;
735
+
736
+ // Check to make sure this isn't a variable name that start with a number (123abc)
737
+ if (Jsep.isIdentifierStart(chCode)) {
738
+ this.throwError('Variable names cannot start with a number (' + number + this.char + ')');
739
+ } else if (chCode === Jsep.PERIOD_CODE || number.length === 1 && number.charCodeAt(0) === Jsep.PERIOD_CODE) {
740
+ this.throwError('Unexpected period');
741
+ }
742
+ return {
743
+ type: Jsep.LITERAL,
744
+ value: parseFloat(number),
745
+ raw: number
746
+ };
747
+ }
748
+
749
+ /**
750
+ * Parses a string literal, staring with single or double quotes with basic support for escape codes
751
+ * e.g. `"hello world"`, `'this is\nJSEP'`
752
+ * @returns {jsep.Literal}
753
+ */
754
+ gobbleStringLiteral() {
755
+ let str = '';
756
+ const startIndex = this.index;
757
+ const quote = this.expr.charAt(this.index++);
758
+ let closed = false;
759
+ while (this.index < this.expr.length) {
760
+ let ch = this.expr.charAt(this.index++);
761
+ if (ch === quote) {
762
+ closed = true;
763
+ break;
764
+ } else if (ch === '\\') {
765
+ // Check for all of the common escape codes
766
+ ch = this.expr.charAt(this.index++);
767
+ switch (ch) {
768
+ case 'n':
769
+ str += '\n';
770
+ break;
771
+ case 'r':
772
+ str += '\r';
773
+ break;
774
+ case 't':
775
+ str += '\t';
776
+ break;
777
+ case 'b':
778
+ str += '\b';
779
+ break;
780
+ case 'f':
781
+ str += '\f';
782
+ break;
783
+ case 'v':
784
+ str += '\x0B';
785
+ break;
786
+ default:
787
+ str += ch;
788
+ }
789
+ } else {
790
+ str += ch;
791
+ }
792
+ }
793
+ if (!closed) {
794
+ this.throwError('Unclosed quote after "' + str + '"');
795
+ }
796
+ return {
797
+ type: Jsep.LITERAL,
798
+ value: str,
799
+ raw: this.expr.substring(startIndex, this.index)
800
+ };
801
+ }
802
+
803
+ /**
804
+ * Gobbles only identifiers
805
+ * e.g.: `foo`, `_value`, `$x1`
806
+ * Also, this function checks if that identifier is a literal:
807
+ * (e.g. `true`, `false`, `null`) or `this`
808
+ * @returns {jsep.Identifier}
809
+ */
810
+ gobbleIdentifier() {
811
+ let ch = this.code,
812
+ start = this.index;
813
+ if (Jsep.isIdentifierStart(ch)) {
814
+ this.index++;
815
+ } else {
816
+ this.throwError('Unexpected ' + this.char);
817
+ }
818
+ while (this.index < this.expr.length) {
819
+ ch = this.code;
820
+ if (Jsep.isIdentifierPart(ch)) {
821
+ this.index++;
822
+ } else {
823
+ break;
824
+ }
825
+ }
826
+ return {
827
+ type: Jsep.IDENTIFIER,
828
+ name: this.expr.slice(start, this.index)
829
+ };
830
+ }
831
+
832
+ /**
833
+ * Gobbles a list of arguments within the context of a function call
834
+ * or array literal. This function also assumes that the opening character
835
+ * `(` or `[` has already been gobbled, and gobbles expressions and commas
836
+ * until the terminator character `)` or `]` is encountered.
837
+ * e.g. `foo(bar, baz)`, `my_func()`, or `[bar, baz]`
838
+ * @param {number} termination
839
+ * @returns {jsep.Expression[]}
840
+ */
841
+ gobbleArguments(termination) {
842
+ const args = [];
843
+ let closed = false;
844
+ let separator_count = 0;
845
+ while (this.index < this.expr.length) {
846
+ this.gobbleSpaces();
847
+ let ch_i = this.code;
848
+ if (ch_i === termination) {
849
+ // done parsing
850
+ closed = true;
851
+ this.index++;
852
+ if (termination === Jsep.CPAREN_CODE && separator_count && separator_count >= args.length) {
853
+ this.throwError('Unexpected token ' + String.fromCharCode(termination));
854
+ }
855
+ break;
856
+ } else if (ch_i === Jsep.COMMA_CODE) {
857
+ // between expressions
858
+ this.index++;
859
+ separator_count++;
860
+ if (separator_count !== args.length) {
861
+ // missing argument
862
+ if (termination === Jsep.CPAREN_CODE) {
863
+ this.throwError('Unexpected token ,');
864
+ } else if (termination === Jsep.CBRACK_CODE) {
865
+ for (let arg = args.length; arg < separator_count; arg++) {
866
+ args.push(null);
867
+ }
868
+ }
869
+ }
870
+ } else if (args.length !== separator_count && separator_count !== 0) {
871
+ // NOTE: `&& separator_count !== 0` allows for either all commas, or all spaces as arguments
872
+ this.throwError('Expected comma');
873
+ } else {
874
+ const node = this.gobbleExpression();
875
+ if (!node || node.type === Jsep.COMPOUND) {
876
+ this.throwError('Expected comma');
877
+ }
878
+ args.push(node);
879
+ }
880
+ }
881
+ if (!closed) {
882
+ this.throwError('Expected ' + String.fromCharCode(termination));
883
+ }
884
+ return args;
885
+ }
886
+
887
+ /**
888
+ * Responsible for parsing a group of things within parentheses `()`
889
+ * that have no identifier in front (so not a function call)
890
+ * This function assumes that it needs to gobble the opening parenthesis
891
+ * and then tries to gobble everything within that parenthesis, assuming
892
+ * that the next thing it should see is the close parenthesis. If not,
893
+ * then the expression probably doesn't have a `)`
894
+ * @returns {boolean|jsep.Expression}
895
+ */
896
+ gobbleGroup() {
897
+ this.index++;
898
+ let nodes = this.gobbleExpressions(Jsep.CPAREN_CODE);
899
+ if (this.code === Jsep.CPAREN_CODE) {
900
+ this.index++;
901
+ if (nodes.length === 1) {
902
+ return nodes[0];
903
+ } else if (!nodes.length) {
904
+ return false;
905
+ } else {
906
+ return {
907
+ type: Jsep.SEQUENCE_EXP,
908
+ expressions: nodes
909
+ };
910
+ }
911
+ } else {
912
+ this.throwError('Unclosed (');
913
+ }
914
+ }
915
+
916
+ /**
917
+ * Responsible for parsing Array literals `[1, 2, 3]`
918
+ * This function assumes that it needs to gobble the opening bracket
919
+ * and then tries to gobble the expressions as arguments.
920
+ * @returns {jsep.ArrayExpression}
921
+ */
922
+ gobbleArray() {
923
+ this.index++;
924
+ return {
925
+ type: Jsep.ARRAY_EXP,
926
+ elements: this.gobbleArguments(Jsep.CBRACK_CODE)
927
+ };
928
+ }
929
+ }
930
+
931
+ // Static fields:
932
+ const hooks = new Hooks();
933
+ Object.assign(Jsep, {
934
+ hooks,
935
+ plugins: new Plugins(Jsep),
936
+ // Node Types
937
+ // ----------
938
+ // This is the full set of types that any JSEP node can be.
939
+ // Store them here to save space when minified
940
+ COMPOUND: 'Compound',
941
+ SEQUENCE_EXP: 'SequenceExpression',
942
+ IDENTIFIER: 'Identifier',
943
+ MEMBER_EXP: 'MemberExpression',
944
+ LITERAL: 'Literal',
945
+ THIS_EXP: 'ThisExpression',
946
+ CALL_EXP: 'CallExpression',
947
+ UNARY_EXP: 'UnaryExpression',
948
+ BINARY_EXP: 'BinaryExpression',
949
+ ARRAY_EXP: 'ArrayExpression',
950
+ TAB_CODE: 9,
951
+ LF_CODE: 10,
952
+ CR_CODE: 13,
953
+ SPACE_CODE: 32,
954
+ PERIOD_CODE: 46,
955
+ // '.'
956
+ COMMA_CODE: 44,
957
+ // ','
958
+ SQUOTE_CODE: 39,
959
+ // single quote
960
+ DQUOTE_CODE: 34,
961
+ // double quotes
962
+ OPAREN_CODE: 40,
963
+ // (
964
+ CPAREN_CODE: 41,
965
+ // )
966
+ OBRACK_CODE: 91,
967
+ // [
968
+ CBRACK_CODE: 93,
969
+ // ]
970
+ QUMARK_CODE: 63,
971
+ // ?
972
+ SEMCOL_CODE: 59,
973
+ // ;
974
+ COLON_CODE: 58,
975
+ // :
976
+
977
+ // Operations
978
+ // ----------
979
+ // Use a quickly-accessible map to store all of the unary operators
980
+ // Values are set to `1` (it really doesn't matter)
981
+ unary_ops: {
982
+ '-': 1,
983
+ '!': 1,
984
+ '~': 1,
985
+ '+': 1
986
+ },
987
+ // Also use a map for the binary operations but set their values to their
988
+ // binary precedence for quick reference (higher number = higher precedence)
989
+ // see [Order of operations](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence)
990
+ binary_ops: {
991
+ '||': 1,
992
+ '??': 1,
993
+ '&&': 2,
994
+ '|': 3,
995
+ '^': 4,
996
+ '&': 5,
997
+ '==': 6,
998
+ '!=': 6,
999
+ '===': 6,
1000
+ '!==': 6,
1001
+ '<': 7,
1002
+ '>': 7,
1003
+ '<=': 7,
1004
+ '>=': 7,
1005
+ '<<': 8,
1006
+ '>>': 8,
1007
+ '>>>': 8,
1008
+ '+': 9,
1009
+ '-': 9,
1010
+ '*': 10,
1011
+ '/': 10,
1012
+ '%': 10,
1013
+ '**': 11
1014
+ },
1015
+ // sets specific binary_ops as right-associative
1016
+ right_associative: new Set(['**']),
1017
+ // Additional valid identifier chars, apart from a-z, A-Z and 0-9 (except on the starting char)
1018
+ additional_identifier_chars: new Set(['$', '_']),
1019
+ // Literals
1020
+ // ----------
1021
+ // Store the values to return for the various literals we may encounter
1022
+ literals: {
1023
+ 'true': true,
1024
+ 'false': false,
1025
+ 'null': null
1026
+ },
1027
+ // Except for `this`, which is special. This could be changed to something like `'self'` as well
1028
+ this_str: 'this'
1029
+ });
1030
+ Jsep.max_unop_len = Jsep.getMaxKeyLen(Jsep.unary_ops);
1031
+ Jsep.max_binop_len = Jsep.getMaxKeyLen(Jsep.binary_ops);
1032
+
1033
+ // Backward Compatibility:
1034
+ const jsep = expr => new Jsep(expr).parse();
1035
+ const stdClassProps = Object.getOwnPropertyNames(class Test {});
1036
+ Object.getOwnPropertyNames(Jsep).filter(prop => !stdClassProps.includes(prop) && jsep[prop] === undefined).forEach(m => {
1037
+ jsep[m] = Jsep[m];
1038
+ });
1039
+ jsep.Jsep = Jsep; // allows for const { Jsep } = require('jsep');
1040
+
1041
+ const CONDITIONAL_EXP = 'ConditionalExpression';
1042
+ var ternary = {
1043
+ name: 'ternary',
1044
+ init(jsep) {
1045
+ // Ternary expression: test ? consequent : alternate
1046
+ jsep.hooks.add('after-expression', function gobbleTernary(env) {
1047
+ if (env.node && this.code === jsep.QUMARK_CODE) {
1048
+ this.index++;
1049
+ const test = env.node;
1050
+ const consequent = this.gobbleExpression();
1051
+ if (!consequent) {
1052
+ this.throwError('Expected expression');
1053
+ }
1054
+ this.gobbleSpaces();
1055
+ if (this.code === jsep.COLON_CODE) {
1056
+ this.index++;
1057
+ const alternate = this.gobbleExpression();
1058
+ if (!alternate) {
1059
+ this.throwError('Expected expression');
1060
+ }
1061
+ env.node = {
1062
+ type: CONDITIONAL_EXP,
1063
+ test,
1064
+ consequent,
1065
+ alternate
1066
+ };
1067
+
1068
+ // check for operators of higher priority than ternary (i.e. assignment)
1069
+ // jsep sets || at 1, and assignment at 0.9, and conditional should be between them
1070
+ if (test.operator && jsep.binary_ops[test.operator] <= 0.9) {
1071
+ let newTest = test;
1072
+ while (newTest.right.operator && jsep.binary_ops[newTest.right.operator] <= 0.9) {
1073
+ newTest = newTest.right;
1074
+ }
1075
+ env.node.test = newTest.right;
1076
+ newTest.right = env.node;
1077
+ env.node = test;
1078
+ }
1079
+ } else {
1080
+ this.throwError('Expected :');
1081
+ }
1082
+ }
1083
+ });
1084
+ }
1085
+ };
1086
+
1087
+ // Add default plugins:
1088
+
1089
+ jsep.plugins.register(ternary);
1090
+
1091
+ const FSLASH_CODE = 47; // '/'
1092
+ const BSLASH_CODE = 92; // '\\'
1093
+
1094
+ var index = {
1095
+ name: 'regex',
1096
+ init(jsep) {
1097
+ // Regex literal: /abc123/ig
1098
+ jsep.hooks.add('gobble-token', function gobbleRegexLiteral(env) {
1099
+ if (this.code === FSLASH_CODE) {
1100
+ const patternIndex = ++this.index;
1101
+ let inCharSet = false;
1102
+ while (this.index < this.expr.length) {
1103
+ if (this.code === FSLASH_CODE && !inCharSet) {
1104
+ const pattern = this.expr.slice(patternIndex, this.index);
1105
+ let flags = '';
1106
+ while (++this.index < this.expr.length) {
1107
+ const code = this.code;
1108
+ if (code >= 97 && code <= 122 // a...z
1109
+ || code >= 65 && code <= 90 // A...Z
1110
+ || code >= 48 && code <= 57) {
1111
+ // 0-9
1112
+ flags += this.char;
1113
+ } else {
1114
+ break;
1115
+ }
1116
+ }
1117
+ let value;
1118
+ try {
1119
+ value = new RegExp(pattern, flags);
1120
+ } catch (e) {
1121
+ this.throwError(e.message);
1122
+ }
1123
+ env.node = {
1124
+ type: jsep.LITERAL,
1125
+ value,
1126
+ raw: this.expr.slice(patternIndex - 1, this.index)
1127
+ };
1128
+
1129
+ // allow . [] and () after regex: /regex/.test(a)
1130
+ env.node = this.gobbleTokenProperty(env.node);
1131
+ return env.node;
1132
+ }
1133
+ if (this.code === jsep.OBRACK_CODE) {
1134
+ inCharSet = true;
1135
+ } else if (inCharSet && this.code === jsep.CBRACK_CODE) {
1136
+ inCharSet = false;
1137
+ }
1138
+ this.index += this.code === BSLASH_CODE ? 2 : 1;
1139
+ }
1140
+ this.throwError('Unclosed Regex');
1141
+ }
1142
+ });
1143
+ }
1144
+ };
1145
+
1146
+ const PLUS_CODE = 43; // +
1147
+ const MINUS_CODE = 45; // -
1148
+
1149
+ const plugin = {
1150
+ name: 'assignment',
1151
+ assignmentOperators: new Set(['=', '*=', '**=', '/=', '%=', '+=', '-=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '||=', '&&=', '??=']),
1152
+ updateOperators: [PLUS_CODE, MINUS_CODE],
1153
+ assignmentPrecedence: 0.9,
1154
+ init(jsep) {
1155
+ const updateNodeTypes = [jsep.IDENTIFIER, jsep.MEMBER_EXP];
1156
+ plugin.assignmentOperators.forEach(op => jsep.addBinaryOp(op, plugin.assignmentPrecedence, true));
1157
+ jsep.hooks.add('gobble-token', function gobbleUpdatePrefix(env) {
1158
+ const code = this.code;
1159
+ if (plugin.updateOperators.some(c => c === code && c === this.expr.charCodeAt(this.index + 1))) {
1160
+ this.index += 2;
1161
+ env.node = {
1162
+ type: 'UpdateExpression',
1163
+ operator: code === PLUS_CODE ? '++' : '--',
1164
+ argument: this.gobbleTokenProperty(this.gobbleIdentifier()),
1165
+ prefix: true
1166
+ };
1167
+ if (!env.node.argument || !updateNodeTypes.includes(env.node.argument.type)) {
1168
+ this.throwError(`Unexpected ${env.node.operator}`);
1169
+ }
1170
+ }
1171
+ });
1172
+ jsep.hooks.add('after-token', function gobbleUpdatePostfix(env) {
1173
+ if (env.node) {
1174
+ const code = this.code;
1175
+ if (plugin.updateOperators.some(c => c === code && c === this.expr.charCodeAt(this.index + 1))) {
1176
+ if (!updateNodeTypes.includes(env.node.type)) {
1177
+ this.throwError(`Unexpected ${env.node.operator}`);
1178
+ }
1179
+ this.index += 2;
1180
+ env.node = {
1181
+ type: 'UpdateExpression',
1182
+ operator: code === PLUS_CODE ? '++' : '--',
1183
+ argument: env.node,
1184
+ prefix: false
1185
+ };
1186
+ }
1187
+ }
1188
+ });
1189
+ jsep.hooks.add('after-expression', function gobbleAssignment(env) {
1190
+ if (env.node) {
1191
+ // Note: Binaries can be chained in a single expression to respect
1192
+ // operator precedence (i.e. a = b = 1 + 2 + 3)
1193
+ // Update all binary assignment nodes in the tree
1194
+ updateBinariesToAssignments(env.node);
1195
+ }
1196
+ });
1197
+ function updateBinariesToAssignments(node) {
1198
+ if (plugin.assignmentOperators.has(node.operator)) {
1199
+ node.type = 'AssignmentExpression';
1200
+ updateBinariesToAssignments(node.left);
1201
+ updateBinariesToAssignments(node.right);
1202
+ } else if (!node.operator) {
1203
+ Object.values(node).forEach(val => {
1204
+ if (val && typeof val === 'object') {
1205
+ updateBinariesToAssignments(val);
1206
+ }
1207
+ });
1208
+ }
1209
+ }
1210
+ }
1211
+ };
1212
+
1213
+ /* eslint-disable no-bitwise -- Convenient */
1214
+
1215
+ // register plugins
1216
+ jsep.plugins.register(index, plugin);
1217
+ jsep.addUnaryOp('typeof');
1218
+ jsep.addUnaryOp('void');
1219
+ jsep.addLiteral('null', null);
1220
+ jsep.addLiteral('undefined', undefined);
15
1221
 
16
1222
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
17
1223
 
@@ -4837,7 +6043,7 @@ function requireSemver () {
4837
6043
  return semver;
4838
6044
  }
4839
6045
 
4840
- var semverExports = requireSemver();
6046
+ requireSemver();
4841
6047
 
4842
6048
  function commonjsRequire(path) {
4843
6049
  throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
@@ -17122,6 +18328,64 @@ ZodPromise.create;
17122
18328
  ZodOptional.create;
17123
18329
  ZodNullable.create;
17124
18330
 
18331
+ /**
18332
+ * Shared file I/O helpers for managed section operations.
18333
+ *
18334
+ * @remarks
18335
+ * Extracts the atomic write pattern and file-level locking into
18336
+ * reusable utilities, eliminating duplication between
18337
+ * `updateManagedSection` and `removeManagedSection`.
18338
+ */
18339
+ /** Stale lock threshold in ms (2 minutes). */
18340
+ const STALE_LOCK_MS = 120_000;
18341
+ /** Default core version when none provided. */
18342
+ const DEFAULT_CORE_VERSION = '0.0.0';
18343
+ /** Lock retry options. */
18344
+ const LOCK_RETRIES = { retries: 5, minTimeout: 100, maxTimeout: 1000 };
18345
+ /**
18346
+ * Write content to a file atomically via a temp file + rename.
18347
+ *
18348
+ * @param filePath - Absolute path to the target file.
18349
+ * @param content - Content to write.
18350
+ */
18351
+ function atomicWrite(filePath, content) {
18352
+ const dir = dirname(filePath);
18353
+ const tempPath = join(dir, `.${String(Date.now())}.tmp`);
18354
+ writeFileSync(tempPath, content, 'utf-8');
18355
+ renameSync(tempPath, filePath);
18356
+ }
18357
+ /**
18358
+ * Execute a callback while holding a file lock.
18359
+ *
18360
+ * @remarks
18361
+ * Acquires a lock on the file, executes the callback, and releases
18362
+ * the lock in a finally block. The lock uses a 2-minute stale threshold
18363
+ * and retries up to 5 times.
18364
+ *
18365
+ * @param filePath - Absolute path to the file to lock.
18366
+ * @param fn - Async callback to execute while holding the lock.
18367
+ */
18368
+ async function withFileLock(filePath, fn) {
18369
+ let release;
18370
+ try {
18371
+ release = await properLockfileExports.lock(filePath, {
18372
+ stale: STALE_LOCK_MS,
18373
+ retries: LOCK_RETRIES,
18374
+ });
18375
+ await fn();
18376
+ }
18377
+ finally {
18378
+ if (release) {
18379
+ try {
18380
+ await release();
18381
+ }
18382
+ catch {
18383
+ // Lock already released or file deleted — safe to ignore
18384
+ }
18385
+ }
18386
+ }
18387
+ }
18388
+
17125
18389
  /**
17126
18390
  * Comment markers for managed content blocks.
17127
18391
  *
@@ -17148,10 +18412,6 @@ const TOOLS_MARKERS = {
17148
18412
  * Captures: [1] marker text, [2] version, [3] timestamp
17149
18413
  */
17150
18414
  const VERSION_STAMP_PATTERN = /<!--\s*(.+?)\s*\|\s*core:(\S+)\s*\|\s*(\S+)\s*-->/;
17151
- /** Staleness threshold for version-stamp convergence in milliseconds. */
17152
- const STALENESS_THRESHOLD_MS = 5 * 60 * 1000;
17153
- /** Warning text prepended inside managed block when cleanup is needed. */
17154
- const CLEANUP_FLAG = '> ⚠️ CLEANUP NEEDED: Orphaned Jeeves content may exist below this managed section. Review everything after the END marker and remove any content that duplicates what appears above.';
17155
18415
 
17156
18416
  /**
17157
18417
  * Managed section IDs and their stable ordering for TOOLS.md.
@@ -17185,61 +18445,6 @@ const SECTION_ORDER = [
17185
18445
  SECTION_IDS.Meta,
17186
18446
  ];
17187
18447
 
17188
- /**
17189
- * Similarity-based cleanup detection for orphaned managed content.
17190
- *
17191
- * @remarks
17192
- * Uses Jaccard similarity on 3-word shingles (Decision 22) to detect
17193
- * when orphaned managed content exists in the user content zone.
17194
- */
17195
- /** Default similarity threshold for cleanup detection. */
17196
- const DEFAULT_THRESHOLD = 0.15;
17197
- /**
17198
- * Generate a set of n-word shingles from text.
17199
- *
17200
- * @param text - Input text.
17201
- * @param n - Shingle size (default 3).
17202
- * @returns Set of n-word shingles.
17203
- */
17204
- function shingles(text, n = 3) {
17205
- const words = text.toLowerCase().split(/\s+/).filter(Boolean);
17206
- const set = new Set();
17207
- for (let i = 0; i <= words.length - n; i++) {
17208
- set.add(words.slice(i, i + n).join(' '));
17209
- }
17210
- return set;
17211
- }
17212
- /**
17213
- * Compute Jaccard similarity between two sets.
17214
- *
17215
- * @param a - First set.
17216
- * @param b - Second set.
17217
- * @returns Jaccard similarity coefficient (0 to 1).
17218
- */
17219
- function jaccard(a, b) {
17220
- if (a.size === 0 && b.size === 0)
17221
- return 0;
17222
- let intersection = 0;
17223
- for (const item of a) {
17224
- if (b.has(item))
17225
- intersection++;
17226
- }
17227
- return intersection / (a.size + b.size - intersection);
17228
- }
17229
- /**
17230
- * Check whether user content contains orphaned managed content.
17231
- *
17232
- * @param managedContent - The current managed block content.
17233
- * @param userContent - Content below the END marker.
17234
- * @param threshold - Jaccard threshold (default 0.15).
17235
- * @returns `true` if cleanup is needed.
17236
- */
17237
- function needsCleanup(managedContent, userContent, threshold = DEFAULT_THRESHOLD) {
17238
- if (!userContent.trim())
17239
- return false;
17240
- return jaccard(shingles(managedContent), shingles(userContent)) > threshold;
17241
- }
17242
-
17243
18448
  /**
17244
18449
  * Stable section ordering for managed TOOLS.md blocks.
17245
18450
  *
@@ -17408,149 +18613,6 @@ function formatBeginMarker(markerText, version) {
17408
18613
  function formatEndMarker(markerText) {
17409
18614
  return `<!-- ${markerText} -->`;
17410
18615
  }
17411
- /**
17412
- * Determine whether this writer should proceed based on version-stamp
17413
- * convergence rules.
17414
- *
17415
- * @param myVersion - The current core library version.
17416
- * @param existing - The existing version stamp (if any).
17417
- * @param stalenessThresholdMs - Staleness threshold in ms (default: 5 min).
17418
- * @returns `true` if the writer should proceed with the write.
17419
- */
17420
- function shouldWrite(myVersion, existing, stalenessThresholdMs = STALENESS_THRESHOLD_MS) {
17421
- // No existing stamp — always write
17422
- if (!existing)
17423
- return true;
17424
- // My version >= stamped version — always write (I'm current or newer)
17425
- if (semverExports.gte(myVersion, existing.version))
17426
- return true;
17427
- // My version < stamped version — check staleness
17428
- const stampAge = Date.now() - new Date(existing.timestamp).getTime();
17429
- return stampAge >= stalenessThresholdMs;
17430
- }
17431
-
17432
- /**
17433
- * Generic managed-section writer with block and section modes.
17434
- *
17435
- * @remarks
17436
- * Supports two modes:
17437
- * - `block`: Replaces the entire managed block (SOUL.md, AGENTS.md).
17438
- * - `section`: Upserts a named H2 section within the managed block (TOOLS.md).
17439
- *
17440
- * Provides file-level locking, version-stamp convergence, and atomic writes.
17441
- */
17442
- /** Default core version when none provided. */
17443
- const DEFAULT_VERSION = '0.0.0';
17444
- /** Stale lock threshold in ms (2 minutes). */
17445
- const STALE_LOCK_MS = 120_000;
17446
- /**
17447
- * Update a managed section in a file.
17448
- *
17449
- * @param filePath - Absolute path to the target file.
17450
- * @param content - New content to write.
17451
- * @param options - Write mode and optional configuration.
17452
- */
17453
- async function updateManagedSection(filePath, content, options = {}) {
17454
- const { mode = 'block', sectionId, markers = TOOLS_MARKERS, coreVersion = DEFAULT_VERSION, stalenessThresholdMs, } = options;
17455
- if (mode === 'section' && !sectionId) {
17456
- throw new Error('sectionId is required when mode is "section"');
17457
- }
17458
- const dir = dirname(filePath);
17459
- if (!existsSync(dir)) {
17460
- mkdirSync(dir, { recursive: true });
17461
- }
17462
- // Create file if it doesn't exist
17463
- if (!existsSync(filePath)) {
17464
- writeFileSync(filePath, '', 'utf-8');
17465
- }
17466
- let release;
17467
- try {
17468
- release = await properLockfileExports.lock(filePath, {
17469
- stale: STALE_LOCK_MS,
17470
- retries: { retries: 5, minTimeout: 100, maxTimeout: 1000 },
17471
- });
17472
- const fileContent = readFileSync(filePath, 'utf-8');
17473
- const parsed = parseManaged(fileContent, markers);
17474
- // Version-stamp convergence check (block mode only).
17475
- // In section mode, components always write their own sections — the version
17476
- // stamp governs shared content convergence, not component-specific sections.
17477
- if (mode === 'block' &&
17478
- !shouldWrite(coreVersion, parsed.versionStamp, stalenessThresholdMs)) {
17479
- return;
17480
- }
17481
- let newManagedBody;
17482
- if (mode === 'block') {
17483
- // Prepend H1 title if markers specify one
17484
- newManagedBody = markers.title
17485
- ? `# ${markers.title}\n\n${content}`
17486
- : content;
17487
- }
17488
- else {
17489
- // Section mode: upsert the named section
17490
- const sections = [...parsed.sections];
17491
- const existingIdx = sections.findIndex((s) => s.id === sectionId);
17492
- if (existingIdx >= 0) {
17493
- sections[existingIdx] = { id: sectionId, content };
17494
- }
17495
- else {
17496
- sections.push({ id: sectionId, content });
17497
- }
17498
- sortSectionsByOrder(sections);
17499
- const sectionText = sections
17500
- .map((s) => `## ${s.id}\n\n${s.content}`)
17501
- .join('\n\n');
17502
- // Prepend H1 title if markers specify one (e.g., "# Jeeves Platform Tools")
17503
- newManagedBody = markers.title
17504
- ? `# ${markers.title}\n\n${sectionText}`
17505
- : sectionText;
17506
- }
17507
- // Cleanup detection
17508
- const userContent = parsed.userContent;
17509
- const cleanupNeeded = needsCleanup(newManagedBody, userContent);
17510
- // Build the full managed block
17511
- const beginLine = formatBeginMarker(markers.begin, coreVersion);
17512
- const endLine = formatEndMarker(markers.end);
17513
- const parts = [];
17514
- if (parsed.beforeContent) {
17515
- parts.push(parsed.beforeContent);
17516
- parts.push('');
17517
- }
17518
- parts.push(beginLine);
17519
- if (cleanupNeeded) {
17520
- parts.push('');
17521
- parts.push(CLEANUP_FLAG);
17522
- }
17523
- parts.push('');
17524
- parts.push(newManagedBody);
17525
- parts.push('');
17526
- parts.push(endLine);
17527
- if (userContent) {
17528
- parts.push('');
17529
- parts.push(userContent);
17530
- }
17531
- parts.push('');
17532
- const newFileContent = parts.join('\n');
17533
- // Atomic write: write to temp file, then rename
17534
- const tempPath = join(dir, `.${String(Date.now())}.tmp`);
17535
- writeFileSync(tempPath, newFileContent, 'utf-8');
17536
- renameSync(tempPath, filePath);
17537
- }
17538
- catch (err) {
17539
- // Log warning but don't throw — writer cycles are periodic
17540
- const message = err instanceof Error ? err.message : String(err);
17541
- console.warn(`jeeves-core: updateManagedSection failed for ${filePath}: ${message}`);
17542
- }
17543
- finally {
17544
- if (release) {
17545
- try {
17546
- await release();
17547
- }
17548
- catch {
17549
- // Lock already released or file deleted — safe to ignore
17550
- }
17551
- }
17552
- }
17553
- }
17554
18616
 
17555
18617
  /**
17556
18618
  * Core configuration schema and resolution.
@@ -17591,162 +18653,313 @@ objectType({
17591
18653
  });
17592
18654
 
17593
18655
  /**
17594
- * CLI for installing/uninstalling the jeeves-watcher OpenClaw plugin.
18656
+ * Remove a managed section or entire managed block from a file.
17595
18657
  *
17596
- * Usage:
17597
- * npx \@karmaniverous/jeeves-watcher-openclaw install
17598
- * npx \@karmaniverous/jeeves-watcher-openclaw uninstall
18658
+ * @remarks
18659
+ * Supports two modes:
18660
+ * - No `sectionId`: Remove the entire managed block (markers + content),
18661
+ * leaving user content intact.
18662
+ * - With `sectionId`: Remove a specific H2 section from within the
18663
+ * managed block. If it was the last section, remove the entire block.
17599
18664
  *
17600
- * Bypasses OpenClaw's `plugins install` command, which has a known
17601
- * spawn EINVAL bug on Windows (https://github.com/openclaw/openclaw/issues/9224).
18665
+ * Provides file-level locking and atomic writes (temp file + rename).
18666
+ * Missing markers or nonexistent sections are no-ops (no error thrown).
18667
+ */
18668
+ /**
18669
+ * Remove a managed section or entire managed block from a file.
17602
18670
  *
17603
- * Supports non-default installations via:
17604
- * - OPENCLAW_CONFIG env var (path to openclaw.json)
17605
- * - OPENCLAW_HOME env var (path to .openclaw directory)
17606
- * - Default: ~/.openclaw/openclaw.json
18671
+ * @param filePath - Absolute path to the target file.
18672
+ * @param options - Optional section ID and custom markers.
18673
+ */
18674
+ async function removeManagedSection(filePath, options = {}) {
18675
+ const { sectionId, markers = TOOLS_MARKERS } = options;
18676
+ if (!existsSync(filePath))
18677
+ return;
18678
+ await withFileLock(filePath, () => {
18679
+ const fileContent = readFileSync(filePath, 'utf-8');
18680
+ const parsed = parseManaged(fileContent, markers);
18681
+ if (!parsed.found)
18682
+ return;
18683
+ let newContent;
18684
+ if (!sectionId) {
18685
+ // Remove entire managed block
18686
+ newContent = buildWithoutBlock(parsed.beforeContent, parsed.userContent);
18687
+ }
18688
+ else {
18689
+ // Remove specific section
18690
+ const remaining = parsed.sections.filter((s) => s.id !== sectionId);
18691
+ if (remaining.length === parsed.sections.length) {
18692
+ // Section not found — no-op
18693
+ return;
18694
+ }
18695
+ if (remaining.length === 0) {
18696
+ // Last section removed — remove entire block
18697
+ newContent = buildWithoutBlock(parsed.beforeContent, parsed.userContent);
18698
+ }
18699
+ else {
18700
+ // Rebuild managed block without the removed section
18701
+ newContent = buildWithSections(parsed.beforeContent, parsed.userContent, remaining, markers, parsed.versionStamp?.version);
18702
+ }
18703
+ }
18704
+ atomicWrite(filePath, newContent);
18705
+ });
18706
+ }
18707
+ /** Build file content without the managed block. */
18708
+ function buildWithoutBlock(beforeContent, userContent) {
18709
+ const parts = [];
18710
+ if (beforeContent)
18711
+ parts.push(beforeContent);
18712
+ if (userContent) {
18713
+ if (parts.length > 0)
18714
+ parts.push('');
18715
+ parts.push(userContent);
18716
+ }
18717
+ if (parts.length === 0)
18718
+ return '';
18719
+ return parts.join('\n') + '\n';
18720
+ }
18721
+ /** Rebuild file content with remaining sections. */
18722
+ function buildWithSections(beforeContent, userContent, sections, markers, coreVersion) {
18723
+ const sorted = sortSectionsByOrder([...sections]);
18724
+ const sectionText = sorted
18725
+ .map((s) => `## ${s.id}\n\n${s.content}`)
18726
+ .join('\n\n');
18727
+ const managedBody = markers.title
18728
+ ? `# ${markers.title}\n\n${sectionText}`
18729
+ : sectionText;
18730
+ const beginLine = formatBeginMarker(markers.begin, coreVersion ?? DEFAULT_CORE_VERSION);
18731
+ const endLine = formatEndMarker(markers.end);
18732
+ const parts = [];
18733
+ if (beforeContent) {
18734
+ parts.push(beforeContent);
18735
+ parts.push('');
18736
+ }
18737
+ parts.push(beginLine);
18738
+ parts.push('');
18739
+ parts.push(managedBody);
18740
+ parts.push('');
18741
+ parts.push(endLine);
18742
+ if (userContent) {
18743
+ parts.push('');
18744
+ parts.push(userContent);
18745
+ }
18746
+ parts.push('');
18747
+ return parts.join('\n');
18748
+ }
18749
+
18750
+ /**
18751
+ * OpenClaw configuration helpers for plugin CLI installers.
18752
+ *
18753
+ * @remarks
18754
+ * Provides resolution of OpenClaw home directory and config file path,
18755
+ * plus idempotent config patching for plugin install/uninstall.
18756
+ */
18757
+ /**
18758
+ * Resolve the OpenClaw home directory.
18759
+ *
18760
+ * @remarks
18761
+ * Resolution order:
18762
+ * 1. `OPENCLAW_CONFIG` env var → dirname of the config file path
18763
+ * 2. `OPENCLAW_HOME` env var → resolved path
18764
+ * 3. Default: `~/.openclaw`
18765
+ *
18766
+ * @returns Absolute path to the OpenClaw home directory.
17607
18767
  */
17608
- const PLUGIN_ID = 'jeeves-watcher-openclaw';
17609
- /** Resolve the OpenClaw home directory. */
17610
18768
  function resolveOpenClawHome() {
17611
18769
  if (process.env.OPENCLAW_CONFIG) {
17612
- return dirname$1(resolve(process.env.OPENCLAW_CONFIG));
18770
+ return dirname(resolve(process.env.OPENCLAW_CONFIG));
17613
18771
  }
17614
18772
  if (process.env.OPENCLAW_HOME) {
17615
18773
  return resolve(process.env.OPENCLAW_HOME);
17616
18774
  }
17617
- return join$1(homedir(), '.openclaw');
18775
+ return join(homedir(), '.openclaw');
17618
18776
  }
17619
- /** Resolve the config file path. */
18777
+ /**
18778
+ * Resolve the OpenClaw config file path.
18779
+ *
18780
+ * @remarks
18781
+ * If `OPENCLAW_CONFIG` is set, uses that directly.
18782
+ * Otherwise defaults to `{home}/openclaw.json`.
18783
+ *
18784
+ * @param home - The OpenClaw home directory.
18785
+ * @returns Absolute path to the config file.
18786
+ */
17620
18787
  function resolveConfigPath(home) {
17621
18788
  if (process.env.OPENCLAW_CONFIG) {
17622
18789
  return resolve(process.env.OPENCLAW_CONFIG);
17623
18790
  }
17624
- return join$1(home, 'openclaw.json');
17625
- }
17626
- /** Get the package root (where this CLI lives). */
17627
- function getPackageRoot() {
17628
- const thisFile = fileURLToPath(import.meta.url);
17629
- return resolve(dirname$1(thisFile), '..');
17630
- }
17631
- /** Read and parse JSON, returning null on failure. */
17632
- function readJson(path) {
17633
- try {
17634
- return JSON.parse(readFileSync$1(path, 'utf8'));
17635
- }
17636
- catch {
17637
- return null;
17638
- }
17639
- }
17640
- /** Write JSON with 2-space indent + trailing newline. */
17641
- function writeJson(path, data) {
17642
- writeFileSync$1(path, JSON.stringify(data, null, 2) + '\n');
18791
+ return join(home, 'openclaw.json');
17643
18792
  }
17644
18793
  /**
17645
18794
  * Patch an allowlist array: add or remove the plugin ID.
17646
- * Returns a log message if a change was made, or undefined.
18795
+ *
18796
+ * @returns A log message if a change was made, or undefined.
17647
18797
  */
17648
- function patchAllowList(parent, key, label, mode) {
17649
- if (!Array.isArray(parent[key]) || parent[key].length === 0)
17650
- return undefined;
17651
- const list = parent[key];
18798
+ function patchAllowList(parent, key, label, pluginId, mode) {
17652
18799
  if (mode === 'add') {
17653
- if (!list.includes(PLUGIN_ID)) {
17654
- list.push(PLUGIN_ID);
17655
- return `Added "${PLUGIN_ID}" to ${label}`;
18800
+ if (!Array.isArray(parent[key])) {
18801
+ parent[key] = [pluginId];
18802
+ return `Created ${label} with "${pluginId}"`;
18803
+ }
18804
+ const list = parent[key];
18805
+ if (!list.includes(pluginId)) {
18806
+ list.push(pluginId);
18807
+ return `Added "${pluginId}" to ${label}`;
17656
18808
  }
17657
18809
  }
17658
18810
  else {
17659
- const filtered = list.filter((id) => id !== PLUGIN_ID);
18811
+ if (!Array.isArray(parent[key]))
18812
+ return undefined;
18813
+ const list = parent[key];
18814
+ const filtered = list.filter((id) => id !== pluginId);
17660
18815
  if (filtered.length !== list.length) {
17661
18816
  parent[key] = filtered;
17662
- return `Removed "${PLUGIN_ID}" from ${label}`;
18817
+ return `Removed "${pluginId}" from ${label}`;
17663
18818
  }
17664
18819
  }
17665
18820
  return undefined;
17666
18821
  }
17667
- /** Patch OpenClaw config for install or uninstall. Returns log messages. */
17668
- function patchConfig(config, mode) {
18822
+ /**
18823
+ * Patch an OpenClaw config for plugin install or uninstall.
18824
+ *
18825
+ * @remarks
18826
+ * Manages `plugins.entries.{pluginId}` and `tools.alsoAllow`.
18827
+ * Idempotent: adding twice produces no duplicates; removing when absent
18828
+ * produces no errors.
18829
+ *
18830
+ * @param config - The parsed OpenClaw config object (mutated in place).
18831
+ * @param pluginId - The plugin identifier.
18832
+ * @param mode - Whether to add or remove the plugin.
18833
+ * @returns Array of log messages describing changes made.
18834
+ */
18835
+ function patchConfig(config, pluginId, mode) {
17669
18836
  const messages = [];
17670
18837
  // Ensure plugins section
17671
18838
  if (!config.plugins || typeof config.plugins !== 'object') {
17672
18839
  config.plugins = {};
17673
18840
  }
17674
18841
  const plugins = config.plugins;
17675
- // plugins.allow
17676
- const pluginAllow = patchAllowList(plugins, 'allow', 'plugins.allow', mode);
17677
- if (pluginAllow)
17678
- messages.push(pluginAllow);
17679
18842
  // plugins.entries
17680
18843
  if (!plugins.entries || typeof plugins.entries !== 'object') {
17681
18844
  plugins.entries = {};
17682
18845
  }
17683
18846
  const entries = plugins.entries;
17684
18847
  if (mode === 'add') {
17685
- if (!entries[PLUGIN_ID]) {
17686
- entries[PLUGIN_ID] = { enabled: true };
17687
- messages.push(`Added "${PLUGIN_ID}" to plugins.entries`);
18848
+ if (!entries[pluginId]) {
18849
+ entries[pluginId] = { enabled: true };
18850
+ messages.push(`Added "${pluginId}" to plugins.entries`);
17688
18851
  }
17689
18852
  }
17690
- else if (PLUGIN_ID in entries) {
17691
- Reflect.deleteProperty(entries, PLUGIN_ID);
17692
- messages.push(`Removed "${PLUGIN_ID}" from plugins.entries`);
18853
+ else if (pluginId in entries) {
18854
+ Reflect.deleteProperty(entries, pluginId);
18855
+ messages.push(`Removed "${pluginId}" from plugins.entries`);
18856
+ }
18857
+ // tools.alsoAllow
18858
+ if (!config.tools || typeof config.tools !== 'object') {
18859
+ config.tools = {};
17693
18860
  }
17694
- // tools.allow
17695
- const tools = (config.tools ?? {});
17696
- const toolAllow = patchAllowList(tools, 'allow', 'tools.allow', mode);
17697
- if (toolAllow)
17698
- messages.push(toolAllow);
18861
+ const tools = config.tools;
18862
+ const toolAlsoAllow = patchAllowList(tools, 'alsoAllow', 'tools.alsoAllow', pluginId, mode);
18863
+ if (toolAlsoAllow)
18864
+ messages.push(toolAlsoAllow);
17699
18865
  return messages;
17700
18866
  }
18867
+
18868
+ /**
18869
+ * @module plugin/constants
18870
+ * Shared constants for the OpenClaw plugin package.
18871
+ *
18872
+ * @remarks
18873
+ * Imported by both the plugin bundle (`index.ts`) and the CLI bundle
18874
+ * (`cli.ts`). Rollup inlines these into each output independently.
18875
+ */
18876
+ /** Plugin identifier used in OpenClaw config and extensions directory. */
18877
+ const PLUGIN_ID = 'jeeves-watcher-openclaw';
18878
+
18879
+ /**
18880
+ * CLI for installing/uninstalling the jeeves-watcher OpenClaw plugin.
18881
+ *
18882
+ * Usage:
18883
+ * npx \@karmaniverous/jeeves-watcher-openclaw install
18884
+ * npx \@karmaniverous/jeeves-watcher-openclaw uninstall
18885
+ *
18886
+ * Bypasses OpenClaw's `plugins install` command, which has a known
18887
+ * spawn EINVAL bug on Windows (https://github.com/openclaw/openclaw/issues/9224).
18888
+ *
18889
+ * Supports non-default installations via:
18890
+ * - OPENCLAW_CONFIG env var (path to openclaw.json)
18891
+ * - OPENCLAW_HOME env var (path to .openclaw directory)
18892
+ * - Default: ~/.openclaw/openclaw.json
18893
+ *
18894
+ * @module cli
18895
+ */
18896
+ /** Get the package root (where this CLI lives). */
18897
+ function getPackageRoot() {
18898
+ const thisFile = fileURLToPath(import.meta.url);
18899
+ return resolve(dirname(thisFile), '..');
18900
+ }
18901
+ /** Read and parse JSON, returning null on failure. */
18902
+ function readJson(path) {
18903
+ try {
18904
+ return JSON.parse(readFileSync(path, 'utf8'));
18905
+ }
18906
+ catch {
18907
+ return null;
18908
+ }
18909
+ }
18910
+ /** Write JSON with 2-space indent + trailing newline. */
18911
+ function writeJson(path, data) {
18912
+ writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
18913
+ }
17701
18914
  /** Install the plugin into OpenClaw's extensions directory. */
17702
18915
  function install() {
17703
18916
  const home = resolveOpenClawHome();
17704
18917
  const configPath = resolveConfigPath(home);
17705
- const extDir = join$1(home, 'extensions', PLUGIN_ID);
18918
+ const extDir = join(home, 'extensions', PLUGIN_ID);
17706
18919
  const pkgRoot = getPackageRoot();
17707
18920
  console.log(`OpenClaw home: ${home}`);
17708
18921
  console.log(`Config: ${configPath}`);
17709
18922
  console.log(`Extensions dir: ${extDir}`);
17710
18923
  console.log(`Package root: ${pkgRoot}`);
17711
18924
  console.log();
17712
- if (!existsSync$1(home)) {
18925
+ if (!existsSync(home)) {
17713
18926
  console.error(`Error: OpenClaw home directory not found at ${home}`);
17714
18927
  console.error('Set OPENCLAW_HOME or OPENCLAW_CONFIG if using a non-default installation.');
17715
18928
  process.exit(1);
17716
18929
  }
17717
- if (!existsSync$1(configPath)) {
18930
+ if (!existsSync(configPath)) {
17718
18931
  console.error(`Error: OpenClaw config not found at ${configPath}`);
17719
18932
  console.error('Set OPENCLAW_CONFIG if using a non-default config location.');
17720
18933
  process.exit(1);
17721
18934
  }
17722
- const pluginManifestPath = join$1(pkgRoot, 'openclaw.plugin.json');
17723
- if (!existsSync$1(pluginManifestPath)) {
18935
+ const pluginManifestPath = join(pkgRoot, 'openclaw.plugin.json');
18936
+ if (!existsSync(pluginManifestPath)) {
17724
18937
  console.error(`Error: openclaw.plugin.json not found at ${pluginManifestPath}`);
17725
18938
  process.exit(1);
17726
18939
  }
17727
18940
  // Copy package to extensions directory
17728
18941
  console.log('Copying plugin to extensions directory...');
17729
- if (existsSync$1(extDir)) {
18942
+ if (existsSync(extDir)) {
17730
18943
  rmSync(extDir, { recursive: true, force: true });
17731
18944
  }
17732
- mkdirSync$1(extDir, { recursive: true });
18945
+ mkdirSync(extDir, { recursive: true });
17733
18946
  for (const file of [
17734
18947
  'dist',
17735
18948
  'content',
17736
18949
  'openclaw.plugin.json',
17737
18950
  'package.json',
17738
18951
  ]) {
17739
- const src = join$1(pkgRoot, file);
17740
- const dest = join$1(extDir, file);
17741
- if (existsSync$1(src)) {
18952
+ const src = join(pkgRoot, file);
18953
+ const dest = join(extDir, file);
18954
+ if (existsSync(src)) {
17742
18955
  cpSync(src, dest, { recursive: true });
17743
- console.log(` ${file}`);
18956
+ console.log(` \u2713 ${file}`);
17744
18957
  }
17745
18958
  }
17746
- const nodeModulesSrc = join$1(pkgRoot, 'node_modules');
17747
- if (existsSync$1(nodeModulesSrc)) {
17748
- cpSync(nodeModulesSrc, join$1(extDir, 'node_modules'), { recursive: true });
17749
- console.log(' node_modules');
18959
+ const nodeModulesSrc = join(pkgRoot, 'node_modules');
18960
+ if (existsSync(nodeModulesSrc)) {
18961
+ cpSync(nodeModulesSrc, join(extDir, 'node_modules'), { recursive: true });
18962
+ console.log(' \u2713 node_modules');
17750
18963
  }
17751
18964
  // Patch config
17752
18965
  console.log();
@@ -17756,114 +18969,51 @@ function install() {
17756
18969
  console.error(`Error: Could not parse ${configPath}`);
17757
18970
  process.exit(1);
17758
18971
  }
17759
- for (const msg of patchConfig(config, 'add')) {
17760
- console.log(` ${msg}`);
18972
+ for (const msg of patchConfig(config, PLUGIN_ID, 'add')) {
18973
+ console.log(` \u2713 ${msg}`);
17761
18974
  }
17762
18975
  writeJson(configPath, config);
17763
18976
  console.log();
17764
- console.log(' Plugin installed successfully.');
18977
+ console.log('\u2705 Plugin installed successfully.');
17765
18978
  console.log(' Restart the OpenClaw gateway to load the plugin.');
17766
18979
  }
17767
18980
  /** Uninstall the plugin from OpenClaw's extensions directory. */
17768
18981
  async function uninstall() {
17769
18982
  const home = resolveOpenClawHome();
17770
18983
  const configPath = resolveConfigPath(home);
17771
- const extDir = join$1(home, 'extensions', PLUGIN_ID);
18984
+ const extDir = join(home, 'extensions', PLUGIN_ID);
17772
18985
  console.log(`OpenClaw home: ${home}`);
17773
18986
  console.log(`Config: ${configPath}`);
17774
18987
  console.log(`Extensions dir: ${extDir}`);
17775
18988
  console.log();
17776
- if (existsSync$1(extDir)) {
18989
+ if (existsSync(extDir)) {
17777
18990
  rmSync(extDir, { recursive: true, force: true });
17778
- console.log(`✓ Removed ${extDir}`);
18991
+ console.log(`\u2713 Removed ${extDir}`);
17779
18992
  }
17780
18993
  else {
17781
- console.log(` (extensions directory not found, skipping)`);
18994
+ console.log(' (extensions directory not found, skipping)');
17782
18995
  }
17783
- if (existsSync$1(configPath)) {
18996
+ if (existsSync(configPath)) {
17784
18997
  console.log('Patching OpenClaw config...');
17785
18998
  const config = readJson(configPath);
17786
18999
  if (config) {
17787
- for (const msg of patchConfig(config, 'remove')) {
17788
- console.log(` ${msg}`);
19000
+ for (const msg of patchConfig(config, PLUGIN_ID, 'remove')) {
19001
+ console.log(` \u2713 ${msg}`);
17789
19002
  }
17790
19003
  writeJson(configPath, config);
17791
19004
  }
17792
19005
  }
17793
- // Clean up TOOLS.md watcher section
17794
- await cleanupToolsMd(home, configPath);
19006
+ // Remove managed TOOLS.md section
19007
+ const toolsPath = join(process.cwd(), 'TOOLS.md');
19008
+ if (existsSync(toolsPath)) {
19009
+ console.log('Removing managed TOOLS.md section...');
19010
+ await removeManagedSection(toolsPath, { sectionId: 'Watcher' });
19011
+ console.log(' \u2713 Removed Watcher section from TOOLS.md');
19012
+ }
17795
19013
  console.log();
17796
- console.log(' Plugin uninstalled successfully.');
19014
+ console.log('\u2705 Plugin uninstalled successfully.');
17797
19015
  console.log(' Restart the OpenClaw gateway to complete removal.');
17798
19016
  }
17799
- /** Resolve the workspace directory from OpenClaw config. */
17800
- function resolveWorkspaceDir(home, configPath) {
17801
- const config = readJson(configPath);
17802
- if (!config)
17803
- return null;
17804
- // Check agents.defaults.workspace
17805
- const agents = config.agents;
17806
- const defaults = agents?.defaults;
17807
- const workspace = defaults?.workspace;
17808
- if (workspace) {
17809
- return resolve(workspace.replace(/^~/, homedir()));
17810
- }
17811
- // Default workspace location
17812
- return join$1(home, 'workspace');
17813
- }
17814
- /**
17815
- * Remove the Watcher section from TOOLS.md on uninstall.
17816
- *
17817
- * @remarks
17818
- * Uses core's `parseManaged` to locate the managed block and its sections,
17819
- * then rewrites without the Watcher section via `updateManagedSection`.
17820
- * If the Watcher section is the only one, removes the entire managed block.
17821
- */
17822
- async function cleanupToolsMd(home, configPath) {
17823
- const workspaceDir = resolveWorkspaceDir(home, configPath);
17824
- if (!workspaceDir)
17825
- return;
17826
- const toolsPath = join$1(workspaceDir, 'TOOLS.md');
17827
- if (!existsSync$1(toolsPath))
17828
- return;
17829
- const content = readFileSync$1(toolsPath, 'utf8');
17830
- const parsed = parseManaged(content, TOOLS_MARKERS);
17831
- if (!parsed.found)
17832
- return;
17833
- const watcherSection = parsed.sections.find((s) => s.id === 'Watcher');
17834
- if (!watcherSection)
17835
- return;
17836
- const remaining = parsed.sections.filter((s) => s.id !== 'Watcher');
17837
- if (remaining.length === 0) {
17838
- // No sections left — remove the entire managed block.
17839
- const parts = [];
17840
- if (parsed.beforeContent)
17841
- parts.push(parsed.beforeContent);
17842
- if (parsed.userContent) {
17843
- if (parts.length > 0)
17844
- parts.push('');
17845
- parts.push(parsed.userContent);
17846
- }
17847
- const newContent = parts.join('\n').trim() + '\n';
17848
- writeFileSync$1(toolsPath, newContent);
17849
- }
17850
- else {
17851
- // Rewrite the managed block without the Watcher section.
17852
- // We write an empty string for the Watcher section; core's
17853
- // updateManagedSection will rebuild from the remaining sections.
17854
- // Actually, the cleanest approach is to rebuild the section text
17855
- // from the remaining sections and write the entire block.
17856
- const sectionText = remaining
17857
- .map((s) => `## ${s.id}\n\n${s.content}`)
17858
- .join('\n\n');
17859
- const body = `# ${TOOLS_MARKERS.title}\n\n${sectionText}`;
17860
- await updateManagedSection(toolsPath, body, {
17861
- mode: 'block',
17862
- markers: TOOLS_MARKERS,
17863
- });
17864
- }
17865
- console.log('\u2713 Cleaned up TOOLS.md (removed Watcher section)');
17866
- }
17867
19017
  // Main
17868
19018
  const command = process.argv[2];
17869
19019
  switch (command) {
@@ -17874,7 +19024,7 @@ switch (command) {
17874
19024
  void uninstall();
17875
19025
  break;
17876
19026
  default:
17877
- console.log(`@karmaniverous/jeeves-watcher-openclaw OpenClaw plugin installer`);
19027
+ console.log(`@karmaniverous/jeeves-watcher-openclaw \u2014 OpenClaw plugin installer`);
17878
19028
  console.log();
17879
19029
  console.log('Usage:');
17880
19030
  console.log(' npx @karmaniverous/jeeves-watcher-openclaw install Install plugin');
@@ -17894,5 +19044,3 @@ switch (command) {
17894
19044
  }
17895
19045
  break;
17896
19046
  }
17897
-
17898
- export { patchConfig };