@helloleo/plugins 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,11 +10,7 @@ var __export = (target, all) => {
10
10
  });
11
11
  };
12
12
 
13
- // src/tools/remove.ts
14
- import { unlink } from "fs/promises";
15
- import path from "path";
16
-
17
- // node_modules/zod/v4/classic/external.js
13
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
18
14
  var exports_external = {};
19
15
  __export(exports_external, {
20
16
  xid: () => xid2,
@@ -244,7 +240,7 @@ __export(exports_external, {
244
240
  $brand: () => $brand
245
241
  });
246
242
 
247
- // node_modules/zod/v4/core/index.js
243
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/index.js
248
244
  var exports_core2 = {};
249
245
  __export(exports_core2, {
250
246
  version: () => version,
@@ -508,7 +504,7 @@ __export(exports_core2, {
508
504
  $ZodAny: () => $ZodAny
509
505
  });
510
506
 
511
- // node_modules/zod/v4/core/core.js
507
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/core.js
512
508
  var NEVER = Object.freeze({
513
509
  status: "aborted"
514
510
  });
@@ -575,7 +571,7 @@ function config(newConfig) {
575
571
  Object.assign(globalConfig, newConfig);
576
572
  return globalConfig;
577
573
  }
578
- // node_modules/zod/v4/core/util.js
574
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/util.js
579
575
  var exports_util = {};
580
576
  __export(exports_util, {
581
577
  unwrapMessage: () => unwrapMessage,
@@ -1204,7 +1200,7 @@ class Class {
1204
1200
  constructor(..._args) {}
1205
1201
  }
1206
1202
 
1207
- // node_modules/zod/v4/core/errors.js
1203
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/errors.js
1208
1204
  var initializer = (inst, def) => {
1209
1205
  inst.name = "$ZodError";
1210
1206
  Object.defineProperty(inst, "_zod", {
@@ -1347,7 +1343,7 @@ function prettifyError(error) {
1347
1343
  `);
1348
1344
  }
1349
1345
 
1350
- // node_modules/zod/v4/core/parse.js
1346
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/parse.js
1351
1347
  var _parse = (_Err) => (schema, value, _ctx, _params) => {
1352
1348
  const ctx = _ctx ? Object.assign(_ctx, { async: false }) : { async: false };
1353
1349
  const result = schema._zod.run({ value, issues: [] }, ctx);
@@ -1434,7 +1430,7 @@ var _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
1434
1430
  return _safeParseAsync(_Err)(schema, value, _ctx);
1435
1431
  };
1436
1432
  var safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync($ZodRealError);
1437
- // node_modules/zod/v4/core/regexes.js
1433
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/regexes.js
1438
1434
  var exports_regexes = {};
1439
1435
  __export(exports_regexes, {
1440
1436
  xid: () => xid,
@@ -1586,7 +1582,7 @@ var sha512_hex = /^[0-9a-fA-F]{128}$/;
1586
1582
  var sha512_base64 = /* @__PURE__ */ fixedBase64(86, "==");
1587
1583
  var sha512_base64url = /* @__PURE__ */ fixedBase64url(86);
1588
1584
 
1589
- // node_modules/zod/v4/core/checks.js
1585
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/checks.js
1590
1586
  var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => {
1591
1587
  var _a;
1592
1588
  inst._zod ?? (inst._zod = {});
@@ -2127,7 +2123,7 @@ var $ZodCheckOverwrite = /* @__PURE__ */ $constructor("$ZodCheckOverwrite", (ins
2127
2123
  };
2128
2124
  });
2129
2125
 
2130
- // node_modules/zod/v4/core/doc.js
2126
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/doc.js
2131
2127
  class Doc {
2132
2128
  constructor(args = []) {
2133
2129
  this.content = [];
@@ -2165,14 +2161,14 @@ class Doc {
2165
2161
  }
2166
2162
  }
2167
2163
 
2168
- // node_modules/zod/v4/core/versions.js
2164
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/versions.js
2169
2165
  var version = {
2170
2166
  major: 4,
2171
2167
  minor: 1,
2172
2168
  patch: 8
2173
2169
  };
2174
2170
 
2175
- // node_modules/zod/v4/core/schemas.js
2171
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/schemas.js
2176
2172
  var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
2177
2173
  var _a;
2178
2174
  inst ?? (inst = {});
@@ -3995,7 +3991,7 @@ function handleRefineResult(result, payload, input, inst) {
3995
3991
  payload.issues.push(issue(_iss));
3996
3992
  }
3997
3993
  }
3998
- // node_modules/zod/v4/locales/index.js
3994
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/index.js
3999
3995
  var exports_locales = {};
4000
3996
  __export(exports_locales, {
4001
3997
  zhTW: () => zh_TW_default,
@@ -4046,7 +4042,7 @@ __export(exports_locales, {
4046
4042
  ar: () => ar_default
4047
4043
  });
4048
4044
 
4049
- // node_modules/zod/v4/locales/ar.js
4045
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ar.js
4050
4046
  var error = () => {
4051
4047
  const Sizable = {
4052
4048
  string: { unit: "\u062D\u0631\u0641", verb: "\u0623\u0646 \u064A\u062D\u0648\u064A" },
@@ -4162,7 +4158,7 @@ function ar_default() {
4162
4158
  localeError: error()
4163
4159
  };
4164
4160
  }
4165
- // node_modules/zod/v4/locales/az.js
4161
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/az.js
4166
4162
  var error2 = () => {
4167
4163
  const Sizable = {
4168
4164
  string: { unit: "simvol", verb: "olmal\u0131d\u0131r" },
@@ -4277,7 +4273,7 @@ function az_default() {
4277
4273
  localeError: error2()
4278
4274
  };
4279
4275
  }
4280
- // node_modules/zod/v4/locales/be.js
4276
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/be.js
4281
4277
  function getBelarusianPlural(count, one, few, many) {
4282
4278
  const absCount = Math.abs(count);
4283
4279
  const lastDigit = absCount % 10;
@@ -4441,7 +4437,7 @@ function be_default() {
4441
4437
  localeError: error3()
4442
4438
  };
4443
4439
  }
4444
- // node_modules/zod/v4/locales/ca.js
4440
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ca.js
4445
4441
  var error4 = () => {
4446
4442
  const Sizable = {
4447
4443
  string: { unit: "car\xE0cters", verb: "contenir" },
@@ -4558,7 +4554,7 @@ function ca_default() {
4558
4554
  localeError: error4()
4559
4555
  };
4560
4556
  }
4561
- // node_modules/zod/v4/locales/cs.js
4557
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/cs.js
4562
4558
  var error5 = () => {
4563
4559
  const Sizable = {
4564
4560
  string: { unit: "znak\u016F", verb: "m\xEDt" },
@@ -4693,7 +4689,7 @@ function cs_default() {
4693
4689
  localeError: error5()
4694
4690
  };
4695
4691
  }
4696
- // node_modules/zod/v4/locales/da.js
4692
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/da.js
4697
4693
  var error6 = () => {
4698
4694
  const Sizable = {
4699
4695
  string: { unit: "tegn", verb: "havde" },
@@ -4824,7 +4820,7 @@ function da_default() {
4824
4820
  localeError: error6()
4825
4821
  };
4826
4822
  }
4827
- // node_modules/zod/v4/locales/de.js
4823
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/de.js
4828
4824
  var error7 = () => {
4829
4825
  const Sizable = {
4830
4826
  string: { unit: "Zeichen", verb: "zu haben" },
@@ -4940,7 +4936,7 @@ function de_default() {
4940
4936
  localeError: error7()
4941
4937
  };
4942
4938
  }
4943
- // node_modules/zod/v4/locales/en.js
4939
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/en.js
4944
4940
  var parsedType = (data) => {
4945
4941
  const t = typeof data;
4946
4942
  switch (t) {
@@ -5057,7 +5053,7 @@ function en_default() {
5057
5053
  localeError: error8()
5058
5054
  };
5059
5055
  }
5060
- // node_modules/zod/v4/locales/eo.js
5056
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/eo.js
5061
5057
  var parsedType2 = (data) => {
5062
5058
  const t = typeof data;
5063
5059
  switch (t) {
@@ -5173,7 +5169,7 @@ function eo_default() {
5173
5169
  localeError: error9()
5174
5170
  };
5175
5171
  }
5176
- // node_modules/zod/v4/locales/es.js
5172
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/es.js
5177
5173
  var error10 = () => {
5178
5174
  const Sizable = {
5179
5175
  string: { unit: "caracteres", verb: "tener" },
@@ -5321,7 +5317,7 @@ function es_default() {
5321
5317
  localeError: error10()
5322
5318
  };
5323
5319
  }
5324
- // node_modules/zod/v4/locales/fa.js
5320
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/fa.js
5325
5321
  var error11 = () => {
5326
5322
  const Sizable = {
5327
5323
  string: { unit: "\u06A9\u0627\u0631\u0627\u06A9\u062A\u0631", verb: "\u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F" },
@@ -5443,7 +5439,7 @@ function fa_default() {
5443
5439
  localeError: error11()
5444
5440
  };
5445
5441
  }
5446
- // node_modules/zod/v4/locales/fi.js
5442
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/fi.js
5447
5443
  var error12 = () => {
5448
5444
  const Sizable = {
5449
5445
  string: { unit: "merkki\xE4", subject: "merkkijonon" },
@@ -5565,7 +5561,7 @@ function fi_default() {
5565
5561
  localeError: error12()
5566
5562
  };
5567
5563
  }
5568
- // node_modules/zod/v4/locales/fr.js
5564
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/fr.js
5569
5565
  var error13 = () => {
5570
5566
  const Sizable = {
5571
5567
  string: { unit: "caract\xE8res", verb: "avoir" },
@@ -5681,7 +5677,7 @@ function fr_default() {
5681
5677
  localeError: error13()
5682
5678
  };
5683
5679
  }
5684
- // node_modules/zod/v4/locales/fr-CA.js
5680
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/fr-CA.js
5685
5681
  var error14 = () => {
5686
5682
  const Sizable = {
5687
5683
  string: { unit: "caract\xE8res", verb: "avoir" },
@@ -5798,7 +5794,7 @@ function fr_CA_default() {
5798
5794
  localeError: error14()
5799
5795
  };
5800
5796
  }
5801
- // node_modules/zod/v4/locales/he.js
5797
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/he.js
5802
5798
  var error15 = () => {
5803
5799
  const Sizable = {
5804
5800
  string: { unit: "\u05D0\u05D5\u05EA\u05D9\u05D5\u05EA", verb: "\u05DC\u05DB\u05DC\u05D5\u05DC" },
@@ -5914,7 +5910,7 @@ function he_default() {
5914
5910
  localeError: error15()
5915
5911
  };
5916
5912
  }
5917
- // node_modules/zod/v4/locales/hu.js
5913
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/hu.js
5918
5914
  var error16 = () => {
5919
5915
  const Sizable = {
5920
5916
  string: { unit: "karakter", verb: "legyen" },
@@ -6030,7 +6026,7 @@ function hu_default() {
6030
6026
  localeError: error16()
6031
6027
  };
6032
6028
  }
6033
- // node_modules/zod/v4/locales/id.js
6029
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/id.js
6034
6030
  var error17 = () => {
6035
6031
  const Sizable = {
6036
6032
  string: { unit: "karakter", verb: "memiliki" },
@@ -6146,7 +6142,7 @@ function id_default() {
6146
6142
  localeError: error17()
6147
6143
  };
6148
6144
  }
6149
- // node_modules/zod/v4/locales/is.js
6145
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/is.js
6150
6146
  var parsedType3 = (data) => {
6151
6147
  const t = typeof data;
6152
6148
  switch (t) {
@@ -6263,7 +6259,7 @@ function is_default() {
6263
6259
  localeError: error18()
6264
6260
  };
6265
6261
  }
6266
- // node_modules/zod/v4/locales/it.js
6262
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/it.js
6267
6263
  var error19 = () => {
6268
6264
  const Sizable = {
6269
6265
  string: { unit: "caratteri", verb: "avere" },
@@ -6379,7 +6375,7 @@ function it_default() {
6379
6375
  localeError: error19()
6380
6376
  };
6381
6377
  }
6382
- // node_modules/zod/v4/locales/ja.js
6378
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ja.js
6383
6379
  var error20 = () => {
6384
6380
  const Sizable = {
6385
6381
  string: { unit: "\u6587\u5B57", verb: "\u3067\u3042\u308B" },
@@ -6494,7 +6490,7 @@ function ja_default() {
6494
6490
  localeError: error20()
6495
6491
  };
6496
6492
  }
6497
- // node_modules/zod/v4/locales/ka.js
6493
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ka.js
6498
6494
  var parsedType4 = (data) => {
6499
6495
  const t = typeof data;
6500
6496
  switch (t) {
@@ -6619,7 +6615,7 @@ function ka_default() {
6619
6615
  localeError: error21()
6620
6616
  };
6621
6617
  }
6622
- // node_modules/zod/v4/locales/km.js
6618
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/km.js
6623
6619
  var error22 = () => {
6624
6620
  const Sizable = {
6625
6621
  string: { unit: "\u178F\u17BD\u17A2\u1780\u17D2\u179F\u179A", verb: "\u1782\u17BD\u179A\u1798\u17B6\u1793" },
@@ -6737,11 +6733,11 @@ function km_default() {
6737
6733
  };
6738
6734
  }
6739
6735
 
6740
- // node_modules/zod/v4/locales/kh.js
6736
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/kh.js
6741
6737
  function kh_default() {
6742
6738
  return km_default();
6743
6739
  }
6744
- // node_modules/zod/v4/locales/ko.js
6740
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ko.js
6745
6741
  var error23 = () => {
6746
6742
  const Sizable = {
6747
6743
  string: { unit: "\uBB38\uC790", verb: "to have" },
@@ -6862,7 +6858,7 @@ function ko_default() {
6862
6858
  localeError: error23()
6863
6859
  };
6864
6860
  }
6865
- // node_modules/zod/v4/locales/lt.js
6861
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/lt.js
6866
6862
  var parsedType5 = (data) => {
6867
6863
  const t = typeof data;
6868
6864
  return parsedTypeFromType(t, data);
@@ -7091,7 +7087,7 @@ function lt_default() {
7091
7087
  localeError: error24()
7092
7088
  };
7093
7089
  }
7094
- // node_modules/zod/v4/locales/mk.js
7090
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/mk.js
7095
7091
  var error25 = () => {
7096
7092
  const Sizable = {
7097
7093
  string: { unit: "\u0437\u043D\u0430\u0446\u0438", verb: "\u0434\u0430 \u0438\u043C\u0430\u0430\u0442" },
@@ -7208,7 +7204,7 @@ function mk_default() {
7208
7204
  localeError: error25()
7209
7205
  };
7210
7206
  }
7211
- // node_modules/zod/v4/locales/ms.js
7207
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ms.js
7212
7208
  var error26 = () => {
7213
7209
  const Sizable = {
7214
7210
  string: { unit: "aksara", verb: "mempunyai" },
@@ -7324,7 +7320,7 @@ function ms_default() {
7324
7320
  localeError: error26()
7325
7321
  };
7326
7322
  }
7327
- // node_modules/zod/v4/locales/nl.js
7323
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/nl.js
7328
7324
  var error27 = () => {
7329
7325
  const Sizable = {
7330
7326
  string: { unit: "tekens" },
@@ -7441,7 +7437,7 @@ function nl_default() {
7441
7437
  localeError: error27()
7442
7438
  };
7443
7439
  }
7444
- // node_modules/zod/v4/locales/no.js
7440
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/no.js
7445
7441
  var error28 = () => {
7446
7442
  const Sizable = {
7447
7443
  string: { unit: "tegn", verb: "\xE5 ha" },
@@ -7557,7 +7553,7 @@ function no_default() {
7557
7553
  localeError: error28()
7558
7554
  };
7559
7555
  }
7560
- // node_modules/zod/v4/locales/ota.js
7556
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ota.js
7561
7557
  var error29 = () => {
7562
7558
  const Sizable = {
7563
7559
  string: { unit: "harf", verb: "olmal\u0131d\u0131r" },
@@ -7673,7 +7669,7 @@ function ota_default() {
7673
7669
  localeError: error29()
7674
7670
  };
7675
7671
  }
7676
- // node_modules/zod/v4/locales/ps.js
7672
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ps.js
7677
7673
  var error30 = () => {
7678
7674
  const Sizable = {
7679
7675
  string: { unit: "\u062A\u0648\u06A9\u064A", verb: "\u0648\u0644\u0631\u064A" },
@@ -7795,7 +7791,7 @@ function ps_default() {
7795
7791
  localeError: error30()
7796
7792
  };
7797
7793
  }
7798
- // node_modules/zod/v4/locales/pl.js
7794
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/pl.js
7799
7795
  var error31 = () => {
7800
7796
  const Sizable = {
7801
7797
  string: { unit: "znak\xF3w", verb: "mie\u0107" },
@@ -7912,7 +7908,7 @@ function pl_default() {
7912
7908
  localeError: error31()
7913
7909
  };
7914
7910
  }
7915
- // node_modules/zod/v4/locales/pt.js
7911
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/pt.js
7916
7912
  var error32 = () => {
7917
7913
  const Sizable = {
7918
7914
  string: { unit: "caracteres", verb: "ter" },
@@ -8028,7 +8024,7 @@ function pt_default() {
8028
8024
  localeError: error32()
8029
8025
  };
8030
8026
  }
8031
- // node_modules/zod/v4/locales/ru.js
8027
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ru.js
8032
8028
  function getRussianPlural(count, one, few, many) {
8033
8029
  const absCount = Math.abs(count);
8034
8030
  const lastDigit = absCount % 10;
@@ -8192,7 +8188,7 @@ function ru_default() {
8192
8188
  localeError: error33()
8193
8189
  };
8194
8190
  }
8195
- // node_modules/zod/v4/locales/sl.js
8191
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/sl.js
8196
8192
  var error34 = () => {
8197
8193
  const Sizable = {
8198
8194
  string: { unit: "znakov", verb: "imeti" },
@@ -8309,7 +8305,7 @@ function sl_default() {
8309
8305
  localeError: error34()
8310
8306
  };
8311
8307
  }
8312
- // node_modules/zod/v4/locales/sv.js
8308
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/sv.js
8313
8309
  var error35 = () => {
8314
8310
  const Sizable = {
8315
8311
  string: { unit: "tecken", verb: "att ha" },
@@ -8427,7 +8423,7 @@ function sv_default() {
8427
8423
  localeError: error35()
8428
8424
  };
8429
8425
  }
8430
- // node_modules/zod/v4/locales/ta.js
8426
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ta.js
8431
8427
  var error36 = () => {
8432
8428
  const Sizable = {
8433
8429
  string: { unit: "\u0B8E\u0BB4\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1\u0B95\u0BCD\u0B95\u0BB3\u0BCD", verb: "\u0B95\u0BCA\u0BA3\u0BCD\u0B9F\u0BBF\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD" },
@@ -8544,7 +8540,7 @@ function ta_default() {
8544
8540
  localeError: error36()
8545
8541
  };
8546
8542
  }
8547
- // node_modules/zod/v4/locales/th.js
8543
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/th.js
8548
8544
  var error37 = () => {
8549
8545
  const Sizable = {
8550
8546
  string: { unit: "\u0E15\u0E31\u0E27\u0E2D\u0E31\u0E01\u0E29\u0E23", verb: "\u0E04\u0E27\u0E23\u0E21\u0E35" },
@@ -8661,7 +8657,7 @@ function th_default() {
8661
8657
  localeError: error37()
8662
8658
  };
8663
8659
  }
8664
- // node_modules/zod/v4/locales/tr.js
8660
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/tr.js
8665
8661
  var parsedType6 = (data) => {
8666
8662
  const t = typeof data;
8667
8663
  switch (t) {
@@ -8776,7 +8772,7 @@ function tr_default() {
8776
8772
  localeError: error38()
8777
8773
  };
8778
8774
  }
8779
- // node_modules/zod/v4/locales/uk.js
8775
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/uk.js
8780
8776
  var error39 = () => {
8781
8777
  const Sizable = {
8782
8778
  string: { unit: "\u0441\u0438\u043C\u0432\u043E\u043B\u0456\u0432", verb: "\u043C\u0430\u0442\u0438\u043C\u0435" },
@@ -8893,11 +8889,11 @@ function uk_default() {
8893
8889
  };
8894
8890
  }
8895
8891
 
8896
- // node_modules/zod/v4/locales/ua.js
8892
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ua.js
8897
8893
  function ua_default() {
8898
8894
  return uk_default();
8899
8895
  }
8900
- // node_modules/zod/v4/locales/ur.js
8896
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/ur.js
8901
8897
  var error40 = () => {
8902
8898
  const Sizable = {
8903
8899
  string: { unit: "\u062D\u0631\u0648\u0641", verb: "\u06C1\u0648\u0646\u0627" },
@@ -9014,7 +9010,7 @@ function ur_default() {
9014
9010
  localeError: error40()
9015
9011
  };
9016
9012
  }
9017
- // node_modules/zod/v4/locales/vi.js
9013
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/vi.js
9018
9014
  var error41 = () => {
9019
9015
  const Sizable = {
9020
9016
  string: { unit: "k\xFD t\u1EF1", verb: "c\xF3" },
@@ -9130,7 +9126,7 @@ function vi_default() {
9130
9126
  localeError: error41()
9131
9127
  };
9132
9128
  }
9133
- // node_modules/zod/v4/locales/zh-CN.js
9129
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/zh-CN.js
9134
9130
  var error42 = () => {
9135
9131
  const Sizable = {
9136
9132
  string: { unit: "\u5B57\u7B26", verb: "\u5305\u542B" },
@@ -9246,7 +9242,7 @@ function zh_CN_default() {
9246
9242
  localeError: error42()
9247
9243
  };
9248
9244
  }
9249
- // node_modules/zod/v4/locales/zh-TW.js
9245
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/zh-TW.js
9250
9246
  var error43 = () => {
9251
9247
  const Sizable = {
9252
9248
  string: { unit: "\u5B57\u5143", verb: "\u64C1\u6709" },
@@ -9363,7 +9359,7 @@ function zh_TW_default() {
9363
9359
  localeError: error43()
9364
9360
  };
9365
9361
  }
9366
- // node_modules/zod/v4/locales/yo.js
9362
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/locales/yo.js
9367
9363
  var error44 = () => {
9368
9364
  const Sizable = {
9369
9365
  string: { unit: "\xE0mi", verb: "n\xED" },
@@ -9478,7 +9474,7 @@ function yo_default() {
9478
9474
  localeError: error44()
9479
9475
  };
9480
9476
  }
9481
- // node_modules/zod/v4/core/registries.js
9477
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/registries.js
9482
9478
  var $output = Symbol("ZodOutput");
9483
9479
  var $input = Symbol("ZodInput");
9484
9480
 
@@ -9529,7 +9525,7 @@ function registry() {
9529
9525
  return new $ZodRegistry;
9530
9526
  }
9531
9527
  var globalRegistry = /* @__PURE__ */ registry();
9532
- // node_modules/zod/v4/core/api.js
9528
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/api.js
9533
9529
  function _string(Class2, params) {
9534
9530
  return new Class2({
9535
9531
  type: "string",
@@ -10407,7 +10403,7 @@ function _stringFormat(Class2, format, fnOrRegex, _params = {}) {
10407
10403
  const inst = new Class2(def);
10408
10404
  return inst;
10409
10405
  }
10410
- // node_modules/zod/v4/core/to-json-schema.js
10406
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/to-json-schema.js
10411
10407
  class JSONSchemaGenerator {
10412
10408
  constructor(params) {
10413
10409
  this.counter = 0;
@@ -11211,9 +11207,9 @@ function isTransforming(_schema, _ctx) {
11211
11207
  }
11212
11208
  throw new Error(`Unknown schema type: ${def.type}`);
11213
11209
  }
11214
- // node_modules/zod/v4/core/json-schema.js
11210
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/core/json-schema.js
11215
11211
  var exports_json_schema = {};
11216
- // node_modules/zod/v4/classic/iso.js
11212
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/iso.js
11217
11213
  var exports_iso = {};
11218
11214
  __export(exports_iso, {
11219
11215
  time: () => time2,
@@ -11254,7 +11250,7 @@ function duration2(params) {
11254
11250
  return _isoDuration(ZodISODuration, params);
11255
11251
  }
11256
11252
 
11257
- // node_modules/zod/v4/classic/errors.js
11253
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/errors.js
11258
11254
  var initializer2 = (inst, issues) => {
11259
11255
  $ZodError.init(inst, issues);
11260
11256
  inst.name = "ZodError";
@@ -11289,7 +11285,7 @@ var ZodRealError = $constructor("ZodError", initializer2, {
11289
11285
  Parent: Error
11290
11286
  });
11291
11287
 
11292
- // node_modules/zod/v4/classic/parse.js
11288
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/parse.js
11293
11289
  var parse3 = /* @__PURE__ */ _parse(ZodRealError);
11294
11290
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
11295
11291
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
@@ -11303,7 +11299,7 @@ var safeDecode2 = /* @__PURE__ */ _safeDecode(ZodRealError);
11303
11299
  var safeEncodeAsync2 = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);
11304
11300
  var safeDecodeAsync2 = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);
11305
11301
 
11306
- // node_modules/zod/v4/classic/schemas.js
11302
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/schemas.js
11307
11303
  var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
11308
11304
  $ZodType.init(inst, def);
11309
11305
  inst.def = def;
@@ -12278,7 +12274,7 @@ function json(params) {
12278
12274
  function preprocess(fn, schema) {
12279
12275
  return pipe(transform(fn), schema);
12280
12276
  }
12281
- // node_modules/zod/v4/classic/compat.js
12277
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/compat.js
12282
12278
  var ZodIssueCode = {
12283
12279
  invalid_type: "invalid_type",
12284
12280
  too_big: "too_big",
@@ -12302,7 +12298,7 @@ function getErrorMap() {
12302
12298
  }
12303
12299
  var ZodFirstPartyTypeKind;
12304
12300
  (function(ZodFirstPartyTypeKind2) {})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
12305
- // node_modules/zod/v4/classic/coerce.js
12301
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/coerce.js
12306
12302
  var exports_coerce = {};
12307
12303
  __export(exports_coerce, {
12308
12304
  string: () => string3,
@@ -12327,14 +12323,193 @@ function date4(params) {
12327
12323
  return _coercedDate(ZodDate, params);
12328
12324
  }
12329
12325
 
12330
- // node_modules/zod/v4/classic/external.js
12326
+ // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
12331
12327
  config(en_default());
12332
12328
  // node_modules/@opencode-ai/plugin/dist/tool.js
12333
12329
  function tool(input) {
12334
12330
  return input;
12335
12331
  }
12336
12332
  tool.schema = exports_external;
12333
+ // src/tools/inspect-http.ts
12334
+ var z = tool.schema;
12335
+ var ALLOWED_EXACT_HOSTS = new Set(["api.app.helloleo.dev"]);
12336
+ var ALLOWED_HOST_SUFFIXES = [
12337
+ ".augustin-d60.workers.dev",
12338
+ ".live.helloleo.dev"
12339
+ ];
12340
+ var DEFAULT_TIMEOUT_MS = 15000;
12341
+ var MAX_TIMEOUT_MS = 30000;
12342
+ var MAX_RESPONSE_BYTES = 1e6;
12343
+ var METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
12344
+ var METHODS_AUTO_JSON = new Set(["POST", "PUT", "PATCH"]);
12345
+ var argsShape = {
12346
+ url: z.string().describe("Fully-qualified URL to request. Must be on the HelloLeo whitelist " + "(api.app.helloleo.dev, *.augustin-d60.workers.dev, *.live.helloleo.dev)."),
12347
+ method: z.enum(METHODS).default("GET").describe(`HTTP method (${METHODS.join(", ")}). Defaults to GET.`),
12348
+ body: z.string().optional().describe("Request body as a string. Not valid for GET. Sent with " + "Content-Type: application/json on POST/PUT/PATCH unless a " + "different Content-Type is supplied via headers."),
12349
+ headers: z.record(z.string(), z.string()).default({}).describe("Optional additional request headers as key/value pairs (e.g. Authorization)."),
12350
+ timeout: z.number().max(MAX_TIMEOUT_MS).default(DEFAULT_TIMEOUT_MS).describe(`Optional timeout in milliseconds (max ${MAX_TIMEOUT_MS}, default ${DEFAULT_TIMEOUT_MS}).`)
12351
+ };
12352
+ var argsSchema = z.object(argsShape);
12353
+ var inspectHttp = tool({
12354
+ description: "Send an HTTP request to a HelloLeo endpoint and return the status code, " + "response headers, and response body. Use this to inspect the live behaviour " + "of HelloLeo workers, the production API, or preview deployments so you can " + "self-debug instead of operating blind. " + "Only URLs on the HelloLeo whitelist are allowed: " + "api.app.helloleo.dev, *.augustin-d60.workers.dev, *.live.helloleo.dev. " + "Supports GET, POST, PUT, PATCH and DELETE (with an optional JSON body).",
12355
+ args: argsShape,
12356
+ async execute(rawArgs, context) {
12357
+ const args = argsSchema.parse(rawArgs);
12358
+ const url2 = parseTargetUrl(args.url);
12359
+ if (args.method === "GET" && args.body !== undefined) {
12360
+ throw new Error("GET requests cannot have a body");
12361
+ }
12362
+ await context.ask({
12363
+ permission: "inspect_http",
12364
+ patterns: [url2.toString()],
12365
+ always: [`${url2.protocol}//${url2.hostname}/*`],
12366
+ metadata: {
12367
+ url: url2.toString(),
12368
+ method: args.method,
12369
+ hasBody: args.body !== undefined
12370
+ }
12371
+ });
12372
+ const timeoutMs = Math.max(1, Math.min(args.timeout, MAX_TIMEOUT_MS));
12373
+ const requestHeaders = {
12374
+ Accept: "application/json, text/plain, */*",
12375
+ "User-Agent": "helloleo-plugins-inspect-http/0.1",
12376
+ Origin: "https://app.helloleo.dev",
12377
+ ...args.headers
12378
+ };
12379
+ if (METHODS_AUTO_JSON.has(args.method) && args.body !== undefined && !hasHeader(requestHeaders, "content-type")) {
12380
+ requestHeaders["Content-Type"] = "application/json";
12381
+ }
12382
+ const signal = AbortSignal.any([
12383
+ context.abort,
12384
+ AbortSignal.timeout(timeoutMs)
12385
+ ]);
12386
+ let response;
12387
+ try {
12388
+ response = await fetch(url2, {
12389
+ method: args.method,
12390
+ headers: requestHeaders,
12391
+ body: args.method === "GET" ? undefined : args.body,
12392
+ signal,
12393
+ redirect: "follow"
12394
+ });
12395
+ } catch (err) {
12396
+ if (err instanceof DOMException && err.name === "TimeoutError") {
12397
+ throw new Error(`Request timed out after ${timeoutMs}ms`);
12398
+ }
12399
+ if (err instanceof DOMException && err.name === "AbortError") {
12400
+ throw new Error("Request aborted");
12401
+ }
12402
+ const message = err instanceof Error ? err.message : String(err);
12403
+ throw new Error(`Request failed: ${message}`);
12404
+ }
12405
+ const { text, truncated } = await readBody(response);
12406
+ const contentType = response.headers.get("content-type");
12407
+ const formattedBody = formatBody(text, contentType);
12408
+ const redirected = response.url && response.url !== url2.toString();
12409
+ const lines = [`${args.method} ${url2.toString()}`];
12410
+ if (redirected) {
12411
+ lines.push(`Final URL: ${response.url}`);
12412
+ }
12413
+ lines.push(`Status: ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`, "", "Response headers:", formatHeaders(response.headers) || "(none)", "", "Response body:", formattedBody.length > 0 ? formattedBody : "(empty)");
12414
+ if (truncated) {
12415
+ lines.push("", `(body truncated at ${MAX_RESPONSE_BYTES} bytes)`);
12416
+ }
12417
+ return lines.join(`
12418
+ `);
12419
+ }
12420
+ });
12421
+ function parseTargetUrl(raw) {
12422
+ let url2;
12423
+ try {
12424
+ url2 = new URL(raw);
12425
+ } catch {
12426
+ throw new Error(`Invalid URL: ${raw}`);
12427
+ }
12428
+ if (url2.protocol !== "https:" && url2.protocol !== "http:") {
12429
+ throw new Error(`URL must use http or https scheme (got "${url2.protocol}")`);
12430
+ }
12431
+ if (url2.username || url2.password) {
12432
+ throw new Error("URL must not contain embedded credentials");
12433
+ }
12434
+ if (!isAllowedHost(url2.hostname)) {
12435
+ throw new Error(`Host "${url2.hostname}" is not on the HelloLeo whitelist. Allowed: ${allowedHostsHuman()}`);
12436
+ }
12437
+ return url2;
12438
+ }
12439
+ function allowedHostsHuman() {
12440
+ return [
12441
+ ...ALLOWED_EXACT_HOSTS,
12442
+ ...ALLOWED_HOST_SUFFIXES.map((s) => `*${s}`)
12443
+ ].join(", ");
12444
+ }
12445
+ function isAllowedHost(hostname3) {
12446
+ const host = hostname3.toLowerCase().replace(/\.$/, "");
12447
+ if (ALLOWED_EXACT_HOSTS.has(host))
12448
+ return true;
12449
+ for (const suffix of ALLOWED_HOST_SUFFIXES) {
12450
+ if (host.endsWith(suffix) && host.length > suffix.length)
12451
+ return true;
12452
+ }
12453
+ return false;
12454
+ }
12455
+ function hasHeader(headers, name) {
12456
+ const target = name.toLowerCase();
12457
+ for (const key of Object.keys(headers)) {
12458
+ if (key.toLowerCase() === target)
12459
+ return true;
12460
+ }
12461
+ return false;
12462
+ }
12463
+ function formatHeaders(headers) {
12464
+ const entries = [];
12465
+ headers.forEach((value, key) => {
12466
+ entries.push(`${key}: ${value}`);
12467
+ });
12468
+ return entries.sort().join(`
12469
+ `);
12470
+ }
12471
+ function formatBody(raw, contentType) {
12472
+ const mime = contentType?.split(";")[0]?.trim().toLowerCase() ?? "";
12473
+ if (mime.includes("application/json") || mime.endsWith("+json")) {
12474
+ try {
12475
+ return JSON.stringify(JSON.parse(raw), null, 2);
12476
+ } catch {}
12477
+ }
12478
+ return raw;
12479
+ }
12480
+ async function readBody(response) {
12481
+ if (!response.body)
12482
+ return { text: "", truncated: false };
12483
+ const reader = response.body.getReader();
12484
+ const decoder = new TextDecoder;
12485
+ let text = "";
12486
+ let total = 0;
12487
+ let truncated = false;
12488
+ while (true) {
12489
+ const { done, value } = await reader.read();
12490
+ if (done)
12491
+ break;
12492
+ if (!value)
12493
+ continue;
12494
+ total += value.byteLength;
12495
+ if (total > MAX_RESPONSE_BYTES) {
12496
+ truncated = true;
12497
+ await reader.cancel();
12498
+ break;
12499
+ }
12500
+ text += decoder.decode(value, { stream: true });
12501
+ }
12502
+ text += decoder.decode();
12503
+ return { text, truncated };
12504
+ }
12505
+ var InspectHttpPlugin = async () => {
12506
+ return {
12507
+ tool: { inspect_http: inspectHttp }
12508
+ };
12509
+ };
12337
12510
  // src/tools/remove.ts
12511
+ import { unlink } from "fs/promises";
12512
+ import path from "path";
12338
12513
  var remove = tool({
12339
12514
  description: "Remove a file from the project. The file path must be relative to the project root. " + "This tool will ask for permission before deleting. " + "Cannot delete files outside the project boundary.",
12340
12515
  args: {
@@ -12366,262 +12541,7 @@ var RemovePlugin = async () => {
12366
12541
  tool: { remove }
12367
12542
  };
12368
12543
  };
12369
- // src/tools/scaffold.ts
12370
- import { mkdir } from "fs/promises";
12371
- import path2 from "path";
12372
- function packageJson(name) {
12373
- return JSON.stringify({
12374
- name,
12375
- private: true,
12376
- version: "0.0.0",
12377
- type: "module",
12378
- scripts: {
12379
- dev: "vite",
12380
- start: "vite",
12381
- build: "vite build",
12382
- preview: "vite preview"
12383
- },
12384
- dependencies: {
12385
- react: "^18.3.1",
12386
- "react-dom": "^18.3.1",
12387
- clsx: "^2.1.1",
12388
- "tailwind-merge": "^2.6.0"
12389
- },
12390
- devDependencies: {
12391
- "@types/react": "^18.3.12",
12392
- "@types/react-dom": "^18.3.1",
12393
- "@vitejs/plugin-react": "^4.3.4",
12394
- autoprefixer: "^10.4.20",
12395
- postcss: "^8.4.49",
12396
- tailwindcss: "^3.4.17",
12397
- typescript: "^5.6.3",
12398
- vite: "^6.0.5"
12399
- }
12400
- }, null, 2);
12401
- }
12402
- function indexHtml(fontFamily) {
12403
- const fontParam = fontFamily.replace(/\s+/g, "+");
12404
- return `<!DOCTYPE html>
12405
- <html lang="en">
12406
- <head>
12407
- <meta charset="UTF-8" />
12408
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
12409
- <link rel="preconnect" href="https://fonts.googleapis.com">
12410
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12411
- <link href="https://fonts.googleapis.com/css2?family=${fontParam}:wght@400;500;600;700&display=swap" rel="stylesheet">
12412
- <title>App</title>
12413
- </head>
12414
- <body>
12415
- <div id="root"></div>
12416
- <script type="module" src="/src/main.tsx"></script>
12417
- </body>
12418
- </html>`;
12419
- }
12420
- function viteConfig() {
12421
- return `import { defineConfig } from 'vite'
12422
- import react from '@vitejs/plugin-react'
12423
- import path from 'path'
12424
-
12425
- export default defineConfig({
12426
- plugins: [react()],
12427
- server: {
12428
- allowedHosts: true,
12429
- },
12430
- resolve: {
12431
- alias: {
12432
- '@': path.resolve(__dirname, './src'),
12433
- },
12434
- },
12435
- })`;
12436
- }
12437
- function tailwindConfig() {
12438
- return `export default {
12439
- darkMode: ["class"],
12440
- content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
12441
- theme: {
12442
- extend: {
12443
- colors: {
12444
- background: "hsl(var(--background))",
12445
- foreground: "hsl(var(--foreground))",
12446
- card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))" },
12447
- primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))" },
12448
- secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))" },
12449
- muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))" },
12450
- accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))" },
12451
- destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))" },
12452
- success: { DEFAULT: "hsl(var(--success))", foreground: "hsl(var(--success-foreground))" },
12453
- warning: { DEFAULT: "hsl(var(--warning))", foreground: "hsl(var(--warning-foreground))" },
12454
- border: "hsl(var(--border))",
12455
- input: "hsl(var(--input))",
12456
- ring: "hsl(var(--ring))",
12457
- },
12458
- fontFamily: {
12459
- sans: "var(--font-sans)",
12460
- heading: "var(--font-heading)",
12461
- },
12462
- borderRadius: {
12463
- lg: "var(--radius)",
12464
- md: "calc(var(--radius) - 2px)",
12465
- sm: "calc(var(--radius) - 4px)",
12466
- },
12467
- borderWidth: {
12468
- DEFAULT: "var(--border-width)",
12469
- },
12470
- boxShadow: {
12471
- DEFAULT: "var(--shadow)",
12472
- lg: "var(--shadow-lg)",
12473
- },
12474
- },
12475
- },
12476
- plugins: [],
12477
- }`;
12478
- }
12479
- function postcssConfig() {
12480
- return `export default {
12481
- plugins: {
12482
- tailwindcss: {},
12483
- autoprefixer: {},
12484
- },
12485
- }`;
12486
- }
12487
- function indexCss(primaryColor, fontFamily, borderRadius) {
12488
- const fontValue = `'${fontFamily}', ui-sans-serif, system-ui, sans-serif`;
12489
- return `@tailwind base;
12490
- @tailwind components;
12491
- @tailwind utilities;
12492
-
12493
- @layer base {
12494
- :root {
12495
- --background: 0 0% 100%;
12496
- --foreground: 222.2 84% 4.9%;
12497
- --card: 0 0% 100%;
12498
- --card-foreground: 222.2 84% 4.9%;
12499
- --primary: ${primaryColor};
12500
- --primary-foreground: 210 40% 98%;
12501
- --secondary: 210 40% 96.1%;
12502
- --secondary-foreground: 222.2 47.4% 11.2%;
12503
- --muted: 210 40% 96.1%;
12504
- --muted-foreground: 215.4 16.3% 46.9%;
12505
- --accent: 210 40% 96.1%;
12506
- --accent-foreground: 222.2 47.4% 11.2%;
12507
- --destructive: 0 84.2% 60.2%;
12508
- --destructive-foreground: 210 40% 98%;
12509
- --success: 142 76% 36%;
12510
- --success-foreground: 210 40% 98%;
12511
- --warning: 38 92% 50%;
12512
- --warning-foreground: 222.2 47.4% 11.2%;
12513
- --border: 214.3 31.8% 91.4%;
12514
- --input: 214.3 31.8% 91.4%;
12515
- --ring: 221.2 83.2% 53.3%;
12516
- --radius: ${borderRadius};
12517
- --border-width: 1px;
12518
- --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
12519
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
12520
- --font-sans: ${fontValue};
12521
- --font-heading: ${fontValue};
12522
- }
12523
-
12524
- .dark {
12525
- --background: 222.2 84% 4.9%;
12526
- --foreground: 210 40% 98%;
12527
- --card: 222.2 84% 4.9%;
12528
- --card-foreground: 210 40% 98%;
12529
- --primary: 217.2 91.2% 59.8%;
12530
- --primary-foreground: 222.2 47.4% 11.2%;
12531
- --secondary: 217.2 32.6% 17.5%;
12532
- --secondary-foreground: 210 40% 98%;
12533
- --muted: 217.2 32.6% 17.5%;
12534
- --muted-foreground: 215 20.2% 65.1%;
12535
- --accent: 217.2 32.6% 17.5%;
12536
- --accent-foreground: 210 40% 98%;
12537
- --destructive: 0 62.8% 30.6%;
12538
- --destructive-foreground: 210 40% 98%;
12539
- --success: 142 76% 36%;
12540
- --success-foreground: 210 40% 98%;
12541
- --warning: 38 92% 50%;
12542
- --warning-foreground: 222.2 47.4% 11.2%;
12543
- --border: 217.2 32.6% 17.5%;
12544
- --input: 217.2 32.6% 17.5%;
12545
- --ring: 224.3 76.3% 48%;
12546
- --border-width: 1px;
12547
- --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.3);
12548
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.3);
12549
- --font-sans: ${fontValue};
12550
- --font-heading: ${fontValue};
12551
- }
12552
- }
12553
-
12554
- body {
12555
- @apply bg-background text-foreground font-sans;
12556
- }`;
12557
- }
12558
- function utilsTs() {
12559
- return `import { clsx, type ClassValue } from 'clsx'
12560
- import { twMerge } from 'tailwind-merge'
12561
-
12562
- export function cn(...inputs: ClassValue[]) {
12563
- return twMerge(clsx(inputs))
12564
- }`;
12565
- }
12566
- function mainTsx() {
12567
- return `import React from 'react'
12568
- import ReactDOM from 'react-dom/client'
12569
- import App from './App'
12570
- import './index.css'
12571
-
12572
- ReactDOM.createRoot(document.getElementById('root')!).render(
12573
- <React.StrictMode>
12574
- <App />
12575
- </React.StrictMode>,
12576
- )`;
12577
- }
12578
- var scaffold = tool({
12579
- description: "Scaffold a new React + Vite + Tailwind CSS project with the HelloLeo design system. " + "Creates all mandatory boilerplate: package.json, index.html (with Google Fonts), " + "vite.config.js, tailwind.config.js, postcss.config.js, src/index.css (CSS variables " + "light+dark), src/lib/utils.ts (cn helper), and src/main.tsx. " + "Call this FIRST for any new project before writing App.tsx and components.",
12580
- args: {
12581
- name: tool.schema.string().optional().describe("Project name for package.json (default: leo-project)"),
12582
- primaryColor: tool.schema.string().optional().describe('HSL values for primary color, e.g. "221.2 83.2% 53.3%" (default: blue)'),
12583
- fontFamily: tool.schema.string().optional().describe('Google Font family name, e.g. "Inter", "Poppins" (default: Inter)'),
12584
- borderRadius: tool.schema.string().optional().describe('Base border radius, e.g. "0.5rem", "0.75rem" (default: 0.5rem)')
12585
- },
12586
- async execute(args, context) {
12587
- const dir = context.directory;
12588
- const name = args.name ?? "leo-project";
12589
- const primary = args.primaryColor ?? "221.2 83.2% 53.3%";
12590
- const font = args.fontFamily ?? "Inter";
12591
- const radius = args.borderRadius ?? "0.5rem";
12592
- await mkdir(path2.join(dir, "src", "lib"), { recursive: true });
12593
- const files = [
12594
- ["package.json", packageJson(name)],
12595
- ["index.html", indexHtml(font)],
12596
- ["vite.config.js", viteConfig()],
12597
- ["tailwind.config.js", tailwindConfig()],
12598
- ["postcss.config.js", postcssConfig()],
12599
- [path2.join("src", "index.css"), indexCss(primary, font, radius)],
12600
- [path2.join("src", "lib", "utils.ts"), utilsTs()],
12601
- [path2.join("src", "main.tsx"), mainTsx()]
12602
- ];
12603
- const created = [];
12604
- for (const [filePath, content] of files) {
12605
- await Bun.write(path2.join(dir, filePath), content);
12606
- created.push(filePath);
12607
- }
12608
- return [
12609
- `Scaffolded ${created.length} files:`,
12610
- ...created.map((f) => ` - ${f}`),
12611
- "",
12612
- "Next: create src/App.tsx with your application code.",
12613
- "The design system is ready -- use design tokens (bg-primary, text-foreground, etc.)",
12614
- "and create UI components in src/components/ui/."
12615
- ].join(`
12616
- `);
12617
- }
12618
- });
12619
- var ScaffoldPlugin = async () => {
12620
- return {
12621
- tool: { scaffold }
12622
- };
12623
- };
12624
12544
  export {
12625
- ScaffoldPlugin,
12626
- RemovePlugin
12545
+ RemovePlugin,
12546
+ InspectHttpPlugin
12627
12547
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helloleo/plugins",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "OpenCode plugins for HelloLeo",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
+ export { InspectHttpPlugin } from "./tools/inspect-http.ts";
1
2
  export { RemovePlugin } from "./tools/remove.ts";
2
- export { ScaffoldPlugin } from "./tools/scaffold.ts";
3
+ // export { ScaffoldPlugin } from "./tools/scaffold.ts";
3
4
  // export { CreatePlanPlugin } from "./tools/create-plan.ts";
4
5
  // export { ListPlansPlugin } from "./tools/list-plans.ts";
5
6
  // export { ModifyPlanPlugin } from "./tools/modify-plan.ts";
@@ -0,0 +1,258 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ import { tool } from "@opencode-ai/plugin";
3
+
4
+ const z = tool.schema;
5
+
6
+ const ALLOWED_EXACT_HOSTS = new Set(["api.app.helloleo.dev"]);
7
+ const ALLOWED_HOST_SUFFIXES = [
8
+ ".augustin-d60.workers.dev",
9
+ ".live.helloleo.dev",
10
+ ] as const;
11
+
12
+ const DEFAULT_TIMEOUT_MS = 15_000;
13
+ const MAX_TIMEOUT_MS = 30_000;
14
+ const MAX_RESPONSE_BYTES = 1_000_000;
15
+
16
+ const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"] as const;
17
+ const METHODS_AUTO_JSON = new Set(["POST", "PUT", "PATCH"]);
18
+
19
+ // tool() is an identity function; parse args explicitly so .default() applies.
20
+ const argsShape = {
21
+ url: z
22
+ .string()
23
+ .describe(
24
+ "Fully-qualified URL to request. Must be on the HelloLeo whitelist " +
25
+ "(api.app.helloleo.dev, *.augustin-d60.workers.dev, *.live.helloleo.dev).",
26
+ ),
27
+ method: z
28
+ .enum(METHODS)
29
+ .default("GET")
30
+ .describe(`HTTP method (${METHODS.join(", ")}). Defaults to GET.`),
31
+ body: z
32
+ .string()
33
+ .optional()
34
+ .describe(
35
+ "Request body as a string. Not valid for GET. Sent with " +
36
+ "Content-Type: application/json on POST/PUT/PATCH unless a " +
37
+ "different Content-Type is supplied via headers.",
38
+ ),
39
+ headers: z
40
+ .record(z.string(), z.string())
41
+ .default({})
42
+ .describe(
43
+ "Optional additional request headers as key/value pairs (e.g. Authorization).",
44
+ ),
45
+ timeout: z
46
+ .number()
47
+ .max(MAX_TIMEOUT_MS)
48
+ .default(DEFAULT_TIMEOUT_MS)
49
+ .describe(
50
+ `Optional timeout in milliseconds (max ${MAX_TIMEOUT_MS}, default ${DEFAULT_TIMEOUT_MS}).`,
51
+ ),
52
+ };
53
+
54
+ const argsSchema = z.object(argsShape);
55
+
56
+ export const inspectHttp = tool({
57
+ description:
58
+ "Send an HTTP request to a HelloLeo endpoint and return the status code, " +
59
+ "response headers, and response body. Use this to inspect the live behaviour " +
60
+ "of HelloLeo workers, the production API, or preview deployments so you can " +
61
+ "self-debug instead of operating blind. " +
62
+ "Only URLs on the HelloLeo whitelist are allowed: " +
63
+ "api.app.helloleo.dev, *.augustin-d60.workers.dev, *.live.helloleo.dev. " +
64
+ "Supports GET, POST, PUT, PATCH and DELETE (with an optional JSON body).",
65
+ args: argsShape,
66
+ async execute(rawArgs, context) {
67
+ const args = argsSchema.parse(rawArgs);
68
+ const url = parseTargetUrl(args.url);
69
+
70
+ if (args.method === "GET" && args.body !== undefined) {
71
+ throw new Error("GET requests cannot have a body");
72
+ }
73
+
74
+ await context.ask({
75
+ permission: "inspect_http",
76
+ patterns: [url.toString()],
77
+ always: [`${url.protocol}//${url.hostname}/*`],
78
+ metadata: {
79
+ url: url.toString(),
80
+ method: args.method,
81
+ hasBody: args.body !== undefined,
82
+ },
83
+ });
84
+
85
+ const timeoutMs = Math.max(1, Math.min(args.timeout, MAX_TIMEOUT_MS));
86
+
87
+ const requestHeaders: Record<string, string> = {
88
+ Accept: "application/json, text/plain, */*",
89
+ "User-Agent": "helloleo-plugins-inspect-http/0.1",
90
+ Origin: "https://app.helloleo.dev",
91
+ ...args.headers,
92
+ };
93
+
94
+ if (
95
+ METHODS_AUTO_JSON.has(args.method) &&
96
+ args.body !== undefined &&
97
+ !hasHeader(requestHeaders, "content-type")
98
+ ) {
99
+ requestHeaders["Content-Type"] = "application/json";
100
+ }
101
+
102
+ const signal = AbortSignal.any([
103
+ context.abort,
104
+ AbortSignal.timeout(timeoutMs),
105
+ ]);
106
+
107
+ let response: Response;
108
+ try {
109
+ response = await fetch(url, {
110
+ method: args.method,
111
+ headers: requestHeaders,
112
+ body: args.method === "GET" ? undefined : args.body,
113
+ signal,
114
+ redirect: "follow",
115
+ });
116
+ } catch (err) {
117
+ if (err instanceof DOMException && err.name === "TimeoutError") {
118
+ throw new Error(`Request timed out after ${timeoutMs}ms`);
119
+ }
120
+ if (err instanceof DOMException && err.name === "AbortError") {
121
+ throw new Error("Request aborted");
122
+ }
123
+ const message = err instanceof Error ? err.message : String(err);
124
+ throw new Error(`Request failed: ${message}`);
125
+ }
126
+
127
+ const { text, truncated } = await readBody(response);
128
+ const contentType = response.headers.get("content-type");
129
+ const formattedBody = formatBody(text, contentType);
130
+
131
+ const redirected = response.url && response.url !== url.toString();
132
+
133
+ const lines: string[] = [`${args.method} ${url.toString()}`];
134
+ if (redirected) {
135
+ lines.push(`Final URL: ${response.url}`);
136
+ }
137
+ lines.push(
138
+ `Status: ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`,
139
+ "",
140
+ "Response headers:",
141
+ formatHeaders(response.headers) || "(none)",
142
+ "",
143
+ "Response body:",
144
+ formattedBody.length > 0 ? formattedBody : "(empty)",
145
+ );
146
+
147
+ if (truncated) {
148
+ lines.push("", `(body truncated at ${MAX_RESPONSE_BYTES} bytes)`);
149
+ }
150
+
151
+ return lines.join("\n");
152
+ },
153
+ });
154
+
155
+ function parseTargetUrl(raw: string): URL {
156
+ let url: URL;
157
+ try {
158
+ url = new URL(raw);
159
+ } catch {
160
+ throw new Error(`Invalid URL: ${raw}`);
161
+ }
162
+
163
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
164
+ throw new Error(
165
+ `URL must use http or https scheme (got "${url.protocol}")`,
166
+ );
167
+ }
168
+
169
+ if (url.username || url.password) {
170
+ throw new Error("URL must not contain embedded credentials");
171
+ }
172
+
173
+ if (!isAllowedHost(url.hostname)) {
174
+ throw new Error(
175
+ `Host "${url.hostname}" is not on the HelloLeo whitelist. Allowed: ${allowedHostsHuman()}`,
176
+ );
177
+ }
178
+
179
+ return url;
180
+ }
181
+
182
+ function allowedHostsHuman(): string {
183
+ return [
184
+ ...ALLOWED_EXACT_HOSTS,
185
+ ...ALLOWED_HOST_SUFFIXES.map((s) => `*${s}`),
186
+ ].join(", ");
187
+ }
188
+
189
+ function isAllowedHost(hostname: string): boolean {
190
+ const host = hostname.toLowerCase().replace(/\.$/, "");
191
+ if (ALLOWED_EXACT_HOSTS.has(host)) return true;
192
+ for (const suffix of ALLOWED_HOST_SUFFIXES) {
193
+ if (host.endsWith(suffix) && host.length > suffix.length) return true;
194
+ }
195
+ return false;
196
+ }
197
+
198
+ function hasHeader(headers: Record<string, string>, name: string): boolean {
199
+ const target = name.toLowerCase();
200
+ for (const key of Object.keys(headers)) {
201
+ if (key.toLowerCase() === target) return true;
202
+ }
203
+ return false;
204
+ }
205
+
206
+ function formatHeaders(headers: Headers): string {
207
+ const entries: string[] = [];
208
+ headers.forEach((value, key) => {
209
+ entries.push(`${key}: ${value}`);
210
+ });
211
+ return entries.sort().join("\n");
212
+ }
213
+
214
+ function formatBody(raw: string, contentType: string | null): string {
215
+ const mime = contentType?.split(";")[0]?.trim().toLowerCase() ?? "";
216
+ if (mime.includes("application/json") || mime.endsWith("+json")) {
217
+ try {
218
+ return JSON.stringify(JSON.parse(raw), null, 2);
219
+ } catch {}
220
+ }
221
+ return raw;
222
+ }
223
+
224
+ async function readBody(response: Response): Promise<{
225
+ text: string;
226
+ truncated: boolean;
227
+ }> {
228
+ if (!response.body) return { text: "", truncated: false };
229
+
230
+ const reader = response.body.getReader();
231
+ const decoder = new TextDecoder();
232
+ let text = "";
233
+ let total = 0;
234
+ let truncated = false;
235
+
236
+ while (true) {
237
+ const { done, value } = await reader.read();
238
+ if (done) break;
239
+ if (!value) continue;
240
+
241
+ total += value.byteLength;
242
+ if (total > MAX_RESPONSE_BYTES) {
243
+ truncated = true;
244
+ await reader.cancel();
245
+ break;
246
+ }
247
+ text += decoder.decode(value, { stream: true });
248
+ }
249
+ text += decoder.decode();
250
+
251
+ return { text, truncated };
252
+ }
253
+
254
+ export const InspectHttpPlugin: Plugin = async () => {
255
+ return {
256
+ tool: { inspect_http: inspectHttp },
257
+ };
258
+ };
@@ -0,0 +1,22 @@
1
+ type Success<T> = {
2
+ data: T;
3
+ error: null;
4
+ };
5
+
6
+ type Failure<E> = {
7
+ data: null;
8
+ error: E;
9
+ };
10
+
11
+ type Result<T, E = Error> = Success<T> | Failure<E>;
12
+
13
+ export async function tryCatch<T, E = Error>(
14
+ promise: Promise<T>,
15
+ ): Promise<Result<T, E>> {
16
+ try {
17
+ const data = await promise;
18
+ return { data, error: null };
19
+ } catch (error) {
20
+ return { data: null, error: error as E };
21
+ }
22
+ }