@craftsamo/telegram-tools 0.2.0 → 0.3.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/opencode.js CHANGED
@@ -25,6 +25,21 @@ var PROJECT = {
25
25
  toolsets: ["account", "chats", "sync", "files", "sends", "actions"],
26
26
  /** The toolsets on when a setting names none. */
27
27
  defaultToolsets: ["account", "chats", "sync", "sends"],
28
+ /** The package's own settings, which the tools themselves read: each snake_case name and its type, with bounds when it needs them. */
29
+ options: {
30
+ /** Where every account's state lives. */
31
+ state_dir: "path",
32
+ /** Where received files are saved. */
33
+ download_dir: "path",
34
+ /** The largest received file a call may download, in megabytes. */
35
+ download_max_mb: { type: "number", integer: true, min: 1, max: 2e3 },
36
+ /** The directories files may be sent from, besides the working directory. */
37
+ send_dirs: "paths",
38
+ /** Whether messages that disappear are kept; read by the sync service's commands. */
39
+ keep_disappearing: "boolean",
40
+ /** Whether channels are listed and their messages received; read by the sync service's commands. */
41
+ channels: "boolean"
42
+ },
28
43
  /** The environment variables the tools read besides the settings, which Codex forwards to the server after them. */
29
44
  toolVariables: []
30
45
  };
@@ -5099,7 +5114,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5099
5114
  var NONE = 0;
5100
5115
  var ASSUMED = 1;
5101
5116
  var PROVEN = 2;
5102
- function isRecursive(inst, stack, resolve8) {
5117
+ function isRecursive(inst, stack, resolve6) {
5103
5118
  const cached2 = recursive.get(inst);
5104
5119
  if (cached2 !== void 0)
5105
5120
  return cached2 ? PROVEN : NONE;
@@ -5109,7 +5124,7 @@ function isRecursive(inst, stack, resolve8) {
5109
5124
  let result = NONE;
5110
5125
  const check2 = (child) => {
5111
5126
  if (result !== PROVEN && child?._zod) {
5112
- const answer = isRecursive(child, stack, resolve8);
5127
+ const answer = isRecursive(child, stack, resolve6);
5113
5128
  if (answer > result)
5114
5129
  result = answer;
5115
5130
  }
@@ -5120,7 +5135,7 @@ function isRecursive(inst, stack, resolve8) {
5120
5135
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5121
5136
  if (spread && !desc.enumerable)
5122
5137
  continue;
5123
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve8) : NONE;
5138
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
5124
5139
  if (child > answer)
5125
5140
  answer = child;
5126
5141
  }
@@ -5184,7 +5199,7 @@ function isRecursive(inst, stack, resolve8) {
5184
5199
  break;
5185
5200
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5186
5201
  case "lazy": {
5187
- const inner = def._cachedInner ?? (resolve8 ? inst._zod.innerType : void 0);
5202
+ const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
5188
5203
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5189
5204
  break;
5190
5205
  }
@@ -9382,8 +9397,8 @@ function ko_default() {
9382
9397
  }
9383
9398
 
9384
9399
  // node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/locales/lt.js
9385
- var capitalizeFirstCharacter = (text) => {
9386
- return text.charAt(0).toUpperCase() + text.slice(1);
9400
+ var capitalizeFirstCharacter = (text2) => {
9401
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
9387
9402
  };
9388
9403
  function getUnitTypeFromNumber(number4) {
9389
9404
  const abs = Math.abs(number4);
@@ -19788,8 +19803,8 @@ function quoteBadLabel(value) {
19788
19803
  const truncated = value.length > MAX_BAD_LABEL_PREVIEW ? `${value.slice(0, MAX_BAD_LABEL_PREVIEW)}\u2026` : value;
19789
19804
  return JSON.stringify(truncated);
19790
19805
  }
19791
- function isLabel(text) {
19792
- return typeof text === "string" && LABEL_PATTERN.test(text);
19806
+ function isLabel(text2) {
19807
+ return typeof text2 === "string" && LABEL_PATTERN.test(text2);
19793
19808
  }
19794
19809
  function checkLabel(label) {
19795
19810
  if (!isLabel(label)) {
@@ -20028,10 +20043,10 @@ var MILLISECOND_DIGITS = 3;
20028
20043
  var MIN_YEAR = 1e3;
20029
20044
  var MAX_HOUR = 23;
20030
20045
  var MAX_MINUTE = 59;
20031
- function badTime(text, name) {
20046
+ function badTime(text2, name) {
20032
20047
  return new ToolError(
20033
20048
  "invalid_argument",
20034
- `${name} is not a time: ${JSON.stringify(text)}. Use RFC 3339 with an offset (2026-11-07T14:03:12+09:00), or a date or time without one (2026-11-07, 2026-11-07T14:03), which reads as local time.`
20049
+ `${name} is not a time: ${JSON.stringify(text2)}. Use RFC 3339 with an offset (2026-11-07T14:03:12+09:00), or a date or time without one (2026-11-07, 2026-11-07T14:03), which reads as local time.`
20035
20050
  );
20036
20051
  }
20037
20052
  function isCalendarDay(year, month, day) {
@@ -20039,21 +20054,21 @@ function isCalendarDay(year, month, day) {
20039
20054
  date5.setUTCFullYear(year, month - 1, day);
20040
20055
  return date5.getUTCFullYear() === year && date5.getUTCMonth() === month - 1 && date5.getUTCDate() === day;
20041
20056
  }
20042
- function parseTimeArgument(text, name) {
20043
- const match = TIME_PATTERN.exec(text.trim());
20044
- if (!match) throw badTime(text, name);
20057
+ function parseTimeArgument(text2, name) {
20058
+ const match = TIME_PATTERN.exec(text2.trim());
20059
+ if (!match) throw badTime(text2, name);
20045
20060
  const [, year, month, day, hour = "0", minute = "0", second = "0", fraction = "", zulu, sign, offsetHour, offsetMin] = match;
20046
20061
  const [y, mon, d, h, mi, s] = [year, month, day, hour, minute, second].map(Number);
20047
20062
  const ms = Number(fraction.padEnd(MILLISECOND_DIGITS, "0").slice(0, MILLISECOND_DIGITS));
20048
20063
  if (y < MIN_YEAR || !isCalendarDay(y, mon, d) || h > MAX_HOUR || mi > MAX_MINUTE || s > MAX_MINUTE) {
20049
- throw badTime(text, name);
20064
+ throw badTime(text2, name);
20050
20065
  }
20051
20066
  if (sign === void 0 && zulu === void 0) return new Date(y, mon - 1, d, h, mi, s, ms).getTime();
20052
20067
  const wall = Date.UTC(y, mon - 1, d, h, mi, s, ms);
20053
20068
  if (zulu) return wall;
20054
20069
  const offsetH = Number(offsetHour);
20055
20070
  const offsetM = Number(offsetMin);
20056
- if (offsetH > MAX_HOUR || offsetM > MAX_MINUTE) throw badTime(text, name);
20071
+ if (offsetH > MAX_HOUR || offsetM > MAX_MINUTE) throw badTime(text2, name);
20057
20072
  const offset = (offsetH * MINUTES_PER_HOUR + offsetM) * MS_PER_MINUTE;
20058
20073
  return sign === "-" ? wall + offset : wall - offset;
20059
20074
  }
@@ -20388,23 +20403,23 @@ function encodeCursor(value) {
20388
20403
  function notACursor(name) {
20389
20404
  return new ToolError("invalid_argument", `${name} is not a cursor this tool returned.`);
20390
20405
  }
20391
- function decodeCursor(text, name) {
20392
- const bytes = Buffer3.from(text, "base64url");
20393
- if (bytes.toString("base64url") !== text) throw notACursor(name);
20406
+ function decodeCursor(text2, name) {
20407
+ const bytes = Buffer3.from(text2, "base64url");
20408
+ if (bytes.toString("base64url") !== text2) throw notACursor(name);
20394
20409
  try {
20395
20410
  return JSON.parse(bytes.toString("utf8"));
20396
20411
  } catch {
20397
20412
  throw notACursor(name);
20398
20413
  }
20399
20414
  }
20400
- function readCursor(text, name, schema) {
20401
- const parsed = schema.safeParse(decodeCursor(text, name));
20415
+ function readCursor(text2, name, schema) {
20416
+ const parsed = schema.safeParse(decodeCursor(text2, name));
20402
20417
  if (!parsed.success) throw notACursor(name);
20403
20418
  return parsed.data;
20404
20419
  }
20405
20420
  var HIGH_SURROGATE = /[\uD800-\uDBFF]$/;
20406
- function cutText(text, keep) {
20407
- return text.length <= keep ? text : text.slice(0, keep).replace(HIGH_SURROGATE, "");
20421
+ function cutText(text2, keep) {
20422
+ return text2.length <= keep ? text2 : text2.slice(0, keep).replace(HIGH_SURROGATE, "");
20408
20423
  }
20409
20424
  function jsonLength(value) {
20410
20425
  return JSON.stringify(value).length;
@@ -20494,15 +20509,15 @@ var ERROR_CODES = {
20494
20509
  function isErrorCode(value) {
20495
20510
  return typeof value === "string" && Object.hasOwn(ERROR_CODES, value);
20496
20511
  }
20497
- function maskPhoneNumbers(text) {
20498
- return text.replace(PHONE_NUMBER, "[number]");
20512
+ function maskPhoneNumbers(text2) {
20513
+ return text2.replace(PHONE_NUMBER, "[number]");
20499
20514
  }
20500
20515
  function cut(message) {
20501
20516
  return maskPhoneNumbers(message).trim().slice(0, MAX_DETAIL_LENGTH);
20502
20517
  }
20503
20518
  function detail(message) {
20504
- const text = cut(message);
20505
- return text ? `: ${text}` : "";
20519
+ const text2 = cut(message);
20520
+ return text2 ? `: ${text2}` : "";
20506
20521
  }
20507
20522
  function afterWriteError(method, effect, reason) {
20508
20523
  return effect === "write" ? new ToolError("uncertain", `${method} may have taken effect (${reason}). ${UNCERTAIN_ADVICE}`) : new ToolError("upstream_error", `${method} failed (${reason}).`);
@@ -20623,7 +20638,7 @@ function abortError(signal) {
20623
20638
  }
20624
20639
  function exec(argv, options) {
20625
20640
  const { signal } = options;
20626
- return new Promise((resolve8, reject) => {
20641
+ return new Promise((resolve6, reject) => {
20627
20642
  if (signal?.aborted) {
20628
20643
  reject(abortError(signal));
20629
20644
  return;
@@ -20682,7 +20697,7 @@ function exec(argv, options) {
20682
20697
  }
20683
20698
  const finish = () => settle2(() => {
20684
20699
  const { code, killedBy } = exitedWith ?? { code: null, killedBy: null };
20685
- resolve8({
20700
+ resolve6({
20686
20701
  code: code ?? -1,
20687
20702
  stdout,
20688
20703
  stderr,
@@ -20776,19 +20791,19 @@ var ServiceConnection = class _ServiceConnection {
20776
20791
  socket.on("error", (error62) => {
20777
20792
  this.endReason ??= `the connection to the service broke${error62.code ? ` (${error62.code})` : ""}`;
20778
20793
  });
20779
- this.closed = new Promise((resolve8) => {
20794
+ this.closed = new Promise((resolve6) => {
20780
20795
  socket.once("close", () => {
20781
20796
  this.endReason ??= "the service closed the connection";
20782
20797
  const lost = { lost: this.endReason };
20783
20798
  for (const settle2 of [...this.pending.values()]) settle2(lost);
20784
- resolve8();
20799
+ resolve6();
20785
20800
  });
20786
20801
  });
20787
20802
  }
20788
20803
  /** Connects to the service's socket; rejects as `upstream_error` when nothing is listening there. */
20789
20804
  static connect(socketPath, options = {}) {
20790
20805
  const { timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS, signal, maxLineLength = DEFAULT_MAX_LINE_LENGTH } = options;
20791
- return new Promise((resolve8, reject) => {
20806
+ return new Promise((resolve6, reject) => {
20792
20807
  if (signal?.aborted) {
20793
20808
  reject(abortError(signal));
20794
20809
  return;
@@ -20810,7 +20825,7 @@ var ServiceConnection = class _ServiceConnection {
20810
20825
  };
20811
20826
  const onConnect = () => {
20812
20827
  detach();
20813
- resolve8(new _ServiceConnection(socket, maxLineLength));
20828
+ resolve6(new _ServiceConnection(socket, maxLineLength));
20814
20829
  };
20815
20830
  const onFailure = () => stop(new ToolError("upstream_error", NOT_RUNNING_MESSAGE));
20816
20831
  const onAbort = () => stop(abortError(signal));
@@ -20833,7 +20848,7 @@ var ServiceConnection = class _ServiceConnection {
20833
20848
  */
20834
20849
  call(method, params, options) {
20835
20850
  const { effect, timeoutMs = DEFAULT_CALL_TIMEOUT_MS, signal } = options;
20836
- return new Promise((resolve8, reject) => {
20851
+ return new Promise((resolve6, reject) => {
20837
20852
  if (signal?.aborted) {
20838
20853
  reject(abortError(signal));
20839
20854
  return;
@@ -20862,7 +20877,7 @@ var ServiceConnection = class _ServiceConnection {
20862
20877
  clearTimeout(timer);
20863
20878
  signal?.removeEventListener("abort", onAbort);
20864
20879
  try {
20865
- resolve8(produce());
20880
+ resolve6(produce());
20866
20881
  } catch (error62) {
20867
20882
  reject(error62);
20868
20883
  }
@@ -21182,10 +21197,10 @@ function replyTo(parsed, related) {
21182
21197
  if (reply.chat !== void 0) entry2.chat = reply.chat;
21183
21198
  if (reply.topic !== void 0) entry2.topic = reply.topic;
21184
21199
  if (reply.quote !== void 0) entry2.quote = reply.quote;
21185
- const text = repliedText(parsed, related)?.text;
21186
- if (text !== void 0) {
21187
- entry2.text = cutText(text, REPLY_TEXT_LENGTH);
21188
- if (entry2.text.length < text.length) entry2.text_cut = true;
21200
+ const text2 = repliedText(parsed, related)?.text;
21201
+ if (text2 !== void 0) {
21202
+ entry2.text = cutText(text2, REPLY_TEXT_LENGTH);
21203
+ if (entry2.text.length < text2.length) entry2.text_cut = true;
21189
21204
  }
21190
21205
  return entry2;
21191
21206
  }
@@ -21254,25 +21269,25 @@ function texts(entry2) {
21254
21269
  ];
21255
21270
  }
21256
21271
  function messageTextLength(entry2) {
21257
- return texts(entry2).reduce((total, text) => total + text.length, 0);
21272
+ return texts(entry2).reduce((total, text2) => total + text2.length, 0);
21258
21273
  }
21259
21274
  function cutMessage(entry2, keep) {
21260
21275
  let room = keep;
21261
21276
  let cut3 = false;
21262
- const take = (text) => {
21263
- const kept = cutText(text, room);
21264
- room = Math.max(0, room - text.length);
21265
- if (kept.length < text.length) cut3 = true;
21277
+ const take = (text2) => {
21278
+ const kept = cutText(text2, room);
21279
+ room = Math.max(0, room - text2.length);
21280
+ if (kept.length < text2.length) cut3 = true;
21266
21281
  return kept;
21267
21282
  };
21268
21283
  const cutEntry = { ...entry2 };
21269
21284
  if (entry2.text !== void 0) cutEntry.text = take(entry2.text);
21270
21285
  if (entry2.reply_to) {
21271
- const { quote, text } = entry2.reply_to;
21286
+ const { quote, text: text2 } = entry2.reply_to;
21272
21287
  cutEntry.reply_to = {
21273
21288
  ...entry2.reply_to,
21274
21289
  ...quote === void 0 ? {} : { quote: take(quote) },
21275
- ...text === void 0 ? {} : { text: take(text) }
21290
+ ...text2 === void 0 ? {} : { text: take(text2) }
21276
21291
  };
21277
21292
  }
21278
21293
  if (entry2.earlier_versions)
@@ -21521,8 +21536,8 @@ function fetchResult(read, stored) {
21521
21536
  }
21522
21537
 
21523
21538
  // src/core/fold.ts
21524
- function foldCase(text) {
21525
- return text.normalize("NFKC").toUpperCase().toLowerCase().replace(/ς/g, "\u03C3");
21539
+ function foldCase(text2) {
21540
+ return text2.normalize("NFKC").toUpperCase().toLowerCase().replace(/ς/g, "\u03C3");
21526
21541
  }
21527
21542
 
21528
21543
  // src/core/chat/filter.ts
@@ -21661,8 +21676,8 @@ function foldedFileName(media) {
21661
21676
  return isRecord(info) && typeof info.name === "string" ? foldCase(info.name) : void 0;
21662
21677
  }
21663
21678
  function matchesAllWords(row, words2) {
21664
- const text = foldCase(row.body ?? "");
21665
- const unmatched = words2.filter((word) => !text.includes(word));
21679
+ const text2 = foldCase(row.body ?? "");
21680
+ const unmatched = words2.filter((word) => !text2.includes(word));
21666
21681
  if (unmatched.length === 0) return true;
21667
21682
  const name = row.media === null ? void 0 : foldedFileName(row.media);
21668
21683
  return name !== void 0 && unmatched.every((word) => name.includes(word));
@@ -22306,11 +22321,11 @@ var ANIMATED_STICKER = "an animated sticker (.tgs), which cannot be inspected";
22306
22321
  function unreadableArchive(label) {
22307
22322
  return `an archive that cannot be inspected (${label})`;
22308
22323
  }
22309
- function words(text) {
22310
- return text.split(/\s+/).filter((word) => word !== "");
22324
+ function words(text2) {
22325
+ return text2.split(/\s+/).filter((word) => word !== "");
22311
22326
  }
22312
- function ascii(text) {
22313
- return Array.from(text, (character) => character.charCodeAt(0));
22327
+ function ascii(text2) {
22328
+ return Array.from(text2, (character) => character.charCodeAt(0));
22314
22329
  }
22315
22330
  function at(offset, bytes) {
22316
22331
  return { offset, bytes };
@@ -22568,9 +22583,9 @@ function parseHeader(block) {
22568
22583
  }
22569
22584
  return { path, size: parseNumber(block.subarray(SIZE_START, SIZE_END)), type: block[TYPE_OFFSET] };
22570
22585
  }
22571
- function parseDecimal(text) {
22572
- if (!/^[0-9]{1,15}$/.test(text)) return void 0;
22573
- return Number(text);
22586
+ function parseDecimal(text2) {
22587
+ if (!/^[0-9]{1,15}$/.test(text2)) return void 0;
22588
+ return Number(text2);
22574
22589
  }
22575
22590
  function parsePaxRecords(data, maxRecords = Infinity) {
22576
22591
  const records = [];
@@ -22760,8 +22775,8 @@ var EXTENSIONS = {
22760
22775
  };
22761
22776
  var KIND_PATTERN = /^[a-z]{1,16}$/;
22762
22777
  var UNKNOWN_KIND = "file";
22763
- function byteLength(text) {
22764
- return Buffer5.byteLength(text);
22778
+ function byteLength(text2) {
22779
+ return Buffer5.byteLength(text2);
22765
22780
  }
22766
22781
  function extensionOf2(type) {
22767
22782
  return type == null ? "" : EXTENSIONS[type.split(";")[0].trim().toLowerCase()] ?? "";
@@ -23030,8 +23045,8 @@ function fitListings(result) {
23030
23045
  var MAX_NAME_CHARACTERS = 255;
23031
23046
  var CUT_MARK = "\u2026";
23032
23047
  var HIDDEN_CHARACTERS = /[\u0000-\u001F\u007F-\u009F\u00AD\u034F\u061C\u115F\u1160\u17B4\u17B5\u180B-\u180F\u200B-\u200F\u2028-\u202E\u2060-\u206F\u3164\uFE00-\uFE0D\uFEFF\uFFA0\uFFF9-\uFFFC\u{1D173}-\u{1D17A}\u{E0000}-\u{E007F}]/gu;
23033
- function cleanName(text) {
23034
- const cleaned = text.normalize("NFC").replace(HIDDEN_CHARACTERS, "?");
23048
+ function cleanName(text2) {
23049
+ const cleaned = text2.normalize("NFC").replace(HIDDEN_CHARACTERS, "?");
23035
23050
  const characters = Array.from(cleaned);
23036
23051
  if (characters.length <= MAX_NAME_CHARACTERS) return cleaned;
23037
23052
  return characters.slice(0, MAX_NAME_CHARACTERS - CUT_MARK.length).join("") + CUT_MARK;
@@ -23204,9 +23219,9 @@ function decodeUtf8Lossy(bytes) {
23204
23219
  return LOSSY_UTF8.decode(bytes);
23205
23220
  }
23206
23221
  function decodeCp437(bytes) {
23207
- let text = "";
23208
- for (const byte of bytes) text += byte < ASCII_END ? String.fromCharCode(byte) : CP437_HIGH[byte - ASCII_END];
23209
- return text;
23222
+ let text2 = "";
23223
+ for (const byte of bytes) text2 += byte < ASCII_END ? String.fromCharCode(byte) : CP437_HIGH[byte - ASCII_END];
23224
+ return text2;
23210
23225
  }
23211
23226
 
23212
23227
  // src/core/archive/tar.ts
@@ -24265,14 +24280,14 @@ function storedReactions(json2) {
24265
24280
  function ownReaction(reactions) {
24266
24281
  return reactions.find((reaction2) => reaction2.mine)?.emoji;
24267
24282
  }
24268
- function checkEditable({ kind, body, captioned }, text) {
24283
+ function checkEditable({ kind, body, captioned }, text2) {
24269
24284
  if (kind === "service" || body === null && !captioned) {
24270
24285
  throw new ToolError("invalid_argument", "This message has no text or caption to edit; nothing was edited.");
24271
24286
  }
24272
- if (body === text) {
24287
+ if (body === text2) {
24273
24288
  throw new ToolError("invalid_argument", "The text is the same as the message's; nothing to edit.");
24274
24289
  }
24275
- if (captioned && text.length > CAPTION_MAX_UNITS) {
24290
+ if (captioned && text2.length > CAPTION_MAX_UNITS) {
24276
24291
  throw new ToolError(
24277
24292
  "invalid_argument",
24278
24293
  `A caption must be at most ${CAPTION_MAX_UNITS} characters; nothing was edited.`
@@ -24281,8 +24296,8 @@ function checkEditable({ kind, body, captioned }, text) {
24281
24296
  }
24282
24297
 
24283
24298
  // src/core/message/arguments.ts
24284
- function checkedText(text) {
24285
- const trimmed = text.trim();
24299
+ function checkedText(text2) {
24300
+ const trimmed = text2.trim();
24286
24301
  if (trimmed === "") throw new ToolError("invalid_argument", "text is blank; give the text to send.");
24287
24302
  if (trimmed.length > TEXT_MAX_UNITS) {
24288
24303
  throw new ToolError(
@@ -24340,8 +24355,8 @@ var Window = class {
24340
24355
  get tail() {
24341
24356
  return this.#tail;
24342
24357
  }
24343
- sees(text) {
24344
- const all = this.#tail + text;
24358
+ sees(text2) {
24359
+ const all = this.#tail + text2;
24345
24360
  this.#tail = all.slice(-TAIL_LENGTH);
24346
24361
  return MARKER.test(all);
24347
24362
  }
@@ -24351,13 +24366,13 @@ var PrivateKeyScan = class {
24351
24366
  #withoutNuls;
24352
24367
  /** Feeds the next chunk; whether a marker starts anywhere in the bytes so far. */
24353
24368
  sees(chunk) {
24354
- const text = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString("latin1");
24369
+ const text2 = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString("latin1");
24355
24370
  const before = this.#plain.tail;
24356
- const plain2 = this.#plain.sees(text);
24357
- if (this.#withoutNuls === void 0 && text.includes(NUL)) {
24371
+ const plain2 = this.#plain.sees(text2);
24372
+ if (this.#withoutNuls === void 0 && text2.includes(NUL)) {
24358
24373
  this.#withoutNuls = new Window(before);
24359
24374
  }
24360
- const stripped = this.#withoutNuls?.sees(text.replaceAll(NUL, "")) ?? false;
24375
+ const stripped = this.#withoutNuls?.sees(text2.replaceAll(NUL, "")) ?? false;
24361
24376
  return plain2 || stripped;
24362
24377
  }
24363
24378
  };
@@ -24983,12 +24998,12 @@ var DUPLICATE_NOTE = "An earlier delivery of this same call had already gone out
24983
24998
  var DUPLICATE_UNKNOWN_ID_NOTE = `${DUPLICATE_NOTE} Its id is not known yet; \`telegram_chat_read\` shows it once the service receives it.`;
24984
24999
 
24985
25000
  // src/core/message/uncertain.ts
24986
- function sentLike(paths, chat, text, startedAt) {
25001
+ function sentLike(paths, chat, text2, startedAt) {
24987
25002
  const rows = readMirror(
24988
25003
  paths,
24989
25004
  (mirror) => mirror.prepare(
24990
25005
  "SELECT id, ts FROM messages WHERE chat = ? AND from_me = 1 AND body = ? AND ts >= ? ORDER BY ts DESC, id DESC LIMIT ?"
24991
- ).all(chat, text, startedAt - SENT_LOOKBACK_MS, MAX_SENT_LIKE)
25006
+ ).all(chat, text2, startedAt - SENT_LOOKBACK_MS, MAX_SENT_LIKE)
24992
25007
  );
24993
25008
  return rows.map(({ id, ts }) => ({ id, time: formatLocalTime(ts) }));
24994
25009
  }
@@ -25008,8 +25023,8 @@ function withFound(error62, find, describe3) {
25008
25023
  details: { ...error62.details, sent_like_this: found }
25009
25024
  });
25010
25025
  }
25011
- function withSentLike(error62, paths, chat, text, startedAt) {
25012
- return withFound(error62, () => sentLike(paths, chat, text, startedAt), sentence);
25026
+ function withSentLike(error62, paths, chat, text2, startedAt) {
25027
+ return withFound(error62, () => sentLike(paths, chat, text2, startedAt), sentence);
25013
25028
  }
25014
25029
 
25015
25030
  // src/core/file/send/uncertain.ts
@@ -25146,17 +25161,17 @@ var MAX_HEADLINE_VALUES = 8;
25146
25161
  var MAX_HEADLINE_VALUE_LENGTH = 80;
25147
25162
  var MAX_PINS = 16;
25148
25163
  var HEADLINE_PLACEHOLDER = /\{([A-Za-z_][A-Za-z0-9_]*)\}/;
25149
- function cut2(text, limit) {
25150
- const points = Array.from(text);
25151
- return points.length > limit ? `${points.slice(0, limit - 1).join("")}\u2026` : text;
25164
+ function cut2(text2, limit) {
25165
+ const points = Array.from(text2);
25166
+ return points.length > limit ? `${points.slice(0, limit - 1).join("")}\u2026` : text2;
25152
25167
  }
25153
- function omittedBy(text, limit) {
25154
- const length = Array.from(text).length;
25168
+ function omittedBy(text2, limit) {
25169
+ const length = Array.from(text2).length;
25155
25170
  return length > limit ? length - (limit - 1) : 0;
25156
25171
  }
25157
- function templatePlaceholders(text) {
25172
+ function templatePlaceholders(text2) {
25158
25173
  const names = /* @__PURE__ */ new Set();
25159
- const parts = text.split(HEADLINE_PLACEHOLDER);
25174
+ const parts = text2.split(HEADLINE_PLACEHOLDER);
25160
25175
  for (const [index, part] of parts.entries()) {
25161
25176
  if (index % 2 === 1) names.add(part);
25162
25177
  else if (/[{}]/.test(part)) return void 0;
@@ -25174,10 +25189,10 @@ function valueEntries(values) {
25174
25189
  const entries = Object.entries(values);
25175
25190
  return entries.length > MAX_HEADLINE_VALUES ? void 0 : entries;
25176
25191
  }
25177
- function isTemplate(text, limit, entries) {
25178
- if (text.trim() === "" || Array.from(text).length > limit) return false;
25192
+ function isTemplate(text2, limit, entries) {
25193
+ if (text2.trim() === "" || Array.from(text2).length > limit) return false;
25179
25194
  const given = new Set(entries.map(([name]) => name));
25180
- const placeholders2 = templatePlaceholders(text);
25195
+ const placeholders2 = templatePlaceholders(text2);
25181
25196
  return placeholders2 !== void 0 && [...placeholders2].every((name) => given.has(name));
25182
25197
  }
25183
25198
  function isTemplateValue(value) {
@@ -25193,15 +25208,15 @@ function isFact(fact) {
25193
25208
  }
25194
25209
  function cutValue(value) {
25195
25210
  if (typeof value === "string") return cut2(value, MAX_VALUE_LENGTH);
25196
- const { text, values } = value;
25197
- if (values === void 0) return { text };
25211
+ const { text: text2, values } = value;
25212
+ if (values === void 0) return { text: text2 };
25198
25213
  const cutValues = Object.entries(values).map(
25199
25214
  ([name, entry2]) => [name, typeof entry2 === "string" ? cut2(entry2, MAX_VALUE_LENGTH) : entry2]
25200
25215
  );
25201
- return { text, values: Object.fromEntries(cutValues) };
25216
+ return { text: text2, values: Object.fromEntries(cutValues) };
25202
25217
  }
25203
- function isText2(text) {
25204
- return isRecord2(text) && typeof text.label === "string" && text.label.trim() !== "" && typeof text.body === "string";
25218
+ function isText2(text2) {
25219
+ return isRecord2(text2) && typeof text2.label === "string" && text2.label.trim() !== "" && typeof text2.body === "string";
25205
25220
  }
25206
25221
  function pinsOf(pins) {
25207
25222
  if (pins === void 0) return {};
@@ -25215,32 +25230,32 @@ function pinsOf(pins) {
25215
25230
  }
25216
25231
  function copyValue(value) {
25217
25232
  if (typeof value === "string") return value;
25218
- const { text, values } = value;
25219
- return values === void 0 ? { text } : { text, values: { ...values } };
25233
+ const { text: text2, values } = value;
25234
+ return values === void 0 ? { text: text2 } : { text: text2, values: { ...values } };
25220
25235
  }
25221
25236
  function headlineOf(headline) {
25222
25237
  if (!isRecord2(headline) || typeof headline.text !== "string") return void 0;
25223
- const { text, values } = headline;
25238
+ const { text: text2, values } = headline;
25224
25239
  const entries = valueEntries(values);
25225
- if (entries === void 0 || !isTemplate(text, MAX_HEADLINE_LENGTH, entries)) return void 0;
25240
+ if (entries === void 0 || !isTemplate(text2, MAX_HEADLINE_LENGTH, entries)) return void 0;
25226
25241
  if (!entries.every((entry2) => typeof entry2[1] === "string")) return void 0;
25227
- return values === void 0 ? { text } : { text, values: Object.fromEntries(entries) };
25242
+ return values === void 0 ? { text: text2 } : { text: text2, values: Object.fromEntries(entries) };
25228
25243
  }
25229
- function cutHeadline({ text, values }) {
25230
- if (values === void 0) return { text };
25244
+ function cutHeadline({ text: text2, values }) {
25245
+ if (values === void 0) return { text: text2 };
25231
25246
  const cutValues = Object.entries(values).map(
25232
25247
  ([name, value]) => [name, cut2(value, MAX_HEADLINE_VALUE_LENGTH)]
25233
25248
  );
25234
- return { text, values: Object.fromEntries(cutValues) };
25249
+ return { text: text2, values: Object.fromEntries(cutValues) };
25235
25250
  }
25236
- function cutDescription({ headline, facts, text, pins }) {
25251
+ function cutDescription({ headline, facts, text: text2, pins }) {
25237
25252
  return {
25238
25253
  ...headline && { headline: cutHeadline(headline) },
25239
25254
  facts: facts.slice(0, MAX_FACTS).map(({ label, value }) => ({
25240
25255
  label: cut2(label, MAX_LABEL_LENGTH),
25241
25256
  value: cutValue(value)
25242
25257
  })),
25243
- ...text && { text: { label: cut2(text.label, MAX_TEXT_LABEL_LENGTH), body: cut2(text.body, MAX_TEXT_LENGTH) } },
25258
+ ...text2 && { text: { label: cut2(text2.label, MAX_TEXT_LABEL_LENGTH), body: cut2(text2.body, MAX_TEXT_LENGTH) } },
25244
25259
  ...pins && { pins }
25245
25260
  };
25246
25261
  }
@@ -25264,9 +25279,9 @@ async function wholeCallDescription(spec, args, ctx) {
25264
25279
  if (kept === void 0) return void 0;
25265
25280
  const facts = entries.map(({ label, value }) => ({ label, value: copyValue(value) }));
25266
25281
  const headline = answer.headline === void 0 ? void 0 : headlineOf(answer.headline);
25267
- const text = answer.text === void 0 || answer.text.body === "" ? void 0 : { label: answer.text.label, body: answer.text.body };
25268
- if (headline === void 0 && facts.length === 0 && text === void 0) return void 0;
25269
- return { ...headline && { headline }, facts, ...text && { text }, ...kept };
25282
+ const text2 = answer.text === void 0 || answer.text.body === "" ? void 0 : { label: answer.text.label, body: answer.text.body };
25283
+ if (headline === void 0 && facts.length === 0 && text2 === void 0) return void 0;
25284
+ return { ...headline && { headline }, facts, ...text2 && { text: text2 }, ...kept };
25270
25285
  }
25271
25286
 
25272
25287
  // src/core/message/action-describe.ts
@@ -25579,10 +25594,10 @@ var telegramMessageDelete = defineTool({
25579
25594
  });
25580
25595
 
25581
25596
  // src/core/message/edit-prepare.ts
25582
- function prepareEdit(mirror, chatId, id, text) {
25597
+ function prepareEdit(mirror, chatId, id, text2) {
25583
25598
  const { chat, message } = findOwnMessage(mirror, chatId, id, "edit");
25584
25599
  if (!message) throw deletedMessage();
25585
- checkEditable({ ...message, captioned: message.media !== null && takesCaption(message.media.type) }, text);
25600
+ checkEditable({ ...message, captioned: message.media !== null && takesCaption(message.media.type) }, text2);
25586
25601
  return { chat, message };
25587
25602
  }
25588
25603
 
@@ -25605,22 +25620,22 @@ var telegramMessageEdit = defineTool({
25605
25620
  input: editInput,
25606
25621
  output: editOutput,
25607
25622
  async describe(args, ctx) {
25608
- const text = checkedText(args.text);
25623
+ const text2 = checkedText(args.text);
25609
25624
  const chat = requireChatId(args.chat, MESSAGE_EDIT_TOOL_ID);
25610
25625
  const paths = await pickPaths(ctx, args.account);
25611
- const target = readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text));
25626
+ const target = readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text2));
25612
25627
  return {
25613
25628
  ...describeAction(EDIT_HEADLINES, target.chat, args.id, target.message),
25614
- text: { label: "New text", body: text }
25629
+ text: { label: "New text", body: text2 }
25615
25630
  };
25616
25631
  },
25617
25632
  targets: (args, ctx) => actionTargets(MESSAGE_EDIT_TOOL_ID, args, ctx),
25618
25633
  async execute(args, ctx) {
25619
25634
  const chat = requireChatId(args.chat, MESSAGE_EDIT_TOOL_ID);
25620
- const text = checkedText(args.text);
25635
+ const text2 = checkedText(args.text);
25621
25636
  const paths = await pickPaths(ctx, args.account);
25622
- readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text));
25623
- const params = { chat, id: args.id, text };
25637
+ readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text2));
25638
+ const params = { chat, id: args.id, text: text2 };
25624
25639
  const edited = await callService(paths.socket, SERVICE_REQUESTS.edit, params, {
25625
25640
  effect: "write",
25626
25641
  signal: ctx.signal,
@@ -25816,7 +25831,7 @@ var telegramMessageSend = defineTool({
25816
25831
  input: input5,
25817
25832
  output: output5,
25818
25833
  async describe(args, ctx) {
25819
- const text = checkedText(args.text);
25834
+ const text2 = checkedText(args.text);
25820
25835
  const chat = requireChatId(args.chat, MESSAGE_SEND_TOOL_ID);
25821
25836
  const paths = await pickPaths(ctx, args.account);
25822
25837
  const { row, title } = readMirror(paths, (mirror) => {
@@ -25828,17 +25843,17 @@ var telegramMessageSend = defineTool({
25828
25843
  headline: { text: "About to send a message to {chat}", values: { chat: row.name } }
25829
25844
  },
25830
25845
  facts: [chatFact(row.kind), ...topicFacts(args.topic, title), ...replyFacts(args.reply_to)],
25831
- text: { label: "Message text", body: text }
25846
+ text: { label: "Message text", body: text2 }
25832
25847
  };
25833
25848
  },
25834
25849
  targets: (args, ctx) => chatTargets(MESSAGE_SEND_TOOL_ID, args, ctx),
25835
25850
  async execute(args, ctx) {
25836
25851
  const chat = requireChatId(args.chat, MESSAGE_SEND_TOOL_ID);
25837
- const text = checkedText(args.text);
25852
+ const text2 = checkedText(args.text);
25838
25853
  const paths = await pickPaths(ctx, args.account);
25839
25854
  const params = {
25840
25855
  chat,
25841
- text,
25856
+ text: text2,
25842
25857
  ...args.topic !== void 0 && { topic: args.topic },
25843
25858
  ...args.reply_to !== void 0 && { reply_to: args.reply_to }
25844
25859
  };
@@ -25851,7 +25866,7 @@ var telegramMessageSend = defineTool({
25851
25866
  });
25852
25867
  return sendResult(chat, sent);
25853
25868
  } catch (error62) {
25854
- throw isToolError(error62) && error62.code === "uncertain" ? withSentLike(error62, paths, chat, text, startedAt) : error62;
25869
+ throw isToolError(error62) && error62.code === "uncertain" ? withSentLike(error62, paths, chat, text2, startedAt) : error62;
25855
25870
  }
25856
25871
  }
25857
25872
  });
@@ -26257,9 +26272,9 @@ function mark(character) {
26257
26272
  const hex3 = character.codePointAt(0).toString(16).toUpperCase();
26258
26273
  return `<U+${hex3.padStart(MARK_DIGITS, "0")}>`;
26259
26274
  }
26260
- function visible(text, options = {}) {
26261
- if (options.keepNewlines) return text.replace(/\r\n/g, "\n").replace(HIDDEN_EXCEPT_NEWLINE, mark);
26262
- return text.replace(HIDDEN_ALL, mark);
26275
+ function visible(text2, options = {}) {
26276
+ if (options.keepNewlines) return text2.replace(/\r\n/g, "\n").replace(HIDDEN_EXCEPT_NEWLINE, mark);
26277
+ return text2.replace(HIDDEN_ALL, mark);
26263
26278
  }
26264
26279
 
26265
26280
  // src/entry/words.ts
@@ -26305,12 +26320,12 @@ var omittedOf = (count) => count > 0 ? { omitted: count } : {};
26305
26320
  function quoted(value) {
26306
26321
  return `"${visible(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
26307
26322
  }
26308
- function filled(text, literal2, slot) {
26309
- return text.split(HEADLINE_PLACEHOLDER).map((part, index) => index % 2 === 0 ? literal2(part) : slot(part)).join("");
26323
+ function filled(text2, literal2, slot) {
26324
+ return text2.split(HEADLINE_PLACEHOLDER).map((part, index) => index % 2 === 0 ? literal2(part) : slot(part)).join("");
26310
26325
  }
26311
- function renderedHeadline({ text, values = {} }) {
26326
+ function renderedHeadline({ text: text2, values = {} }) {
26312
26327
  return filled(
26313
- text,
26328
+ text2,
26314
26329
  (part) => visible(part),
26315
26330
  (name) => quoted(values[name] ?? "")
26316
26331
  );
@@ -26324,7 +26339,7 @@ function renderedValue(value, t) {
26324
26339
  return { value: visible(cut2(whole, MAX_VALUE_LENGTH)), ...omittedOf(omittedBy(whole, MAX_VALUE_LENGTH)) };
26325
26340
  }
26326
26341
  function shownDescription(description, t) {
26327
- const { headline, facts, text } = cutDescription(description);
26342
+ const { headline, facts, text: text2 } = cutDescription(description);
26328
26343
  const wholeBody = description.text?.body ?? "";
26329
26344
  const dropped = description.facts.length - facts.length;
26330
26345
  return {
@@ -26334,9 +26349,9 @@ function shownDescription(description, t) {
26334
26349
  ...renderedValue(description.facts[index].value, t)
26335
26350
  })),
26336
26351
  ...dropped > 0 && { dropped },
26337
- ...text && {
26352
+ ...text2 && {
26338
26353
  text: {
26339
- label: visible(t.word(text.label, MAX_TEXT_LABEL_LENGTH)),
26354
+ label: visible(t.word(text2.label, MAX_TEXT_LABEL_LENGTH)),
26340
26355
  body: visible(cut2(wholeBody, MAX_TEXT_LENGTH), { keepNewlines: true }),
26341
26356
  ...omittedOf(omittedBy(wholeBody, MAX_TEXT_LENGTH))
26342
26357
  }
@@ -26350,9 +26365,9 @@ async function timedCallDescription(spec, args, ctx, timeoutMs = DESCRIBE_TIMEOU
26350
26365
  let timer;
26351
26366
  let onAbort;
26352
26367
  try {
26353
- const timedOut = new Promise((resolve8) => {
26368
+ const timedOut = new Promise((resolve6) => {
26354
26369
  timer = setTimeout(() => {
26355
- resolve8(void 0);
26370
+ resolve6(void 0);
26356
26371
  controller.abort();
26357
26372
  }, timeoutMs);
26358
26373
  });
@@ -26428,76 +26443,70 @@ ${JSON.stringify(error62.details)}` : ""}`;
26428
26443
  var PROJECT_PROFILE = {
26429
26444
  name: PROJECT.name,
26430
26445
  envPrefix: PROJECT.envPrefix,
26431
- toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets }
26446
+ toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets },
26447
+ options: PROJECT.options
26432
26448
  };
26433
26449
 
26434
- // src/entry/download-dir.ts
26435
- import { resolve as resolve5 } from "node:path";
26450
+ // src/entry/settings.ts
26451
+ import { homedir as homedir3 } from "node:os";
26452
+ import { isAbsolute as isAbsolute6, join as join10, resolve as resolvePath, sep as sep4 } from "node:path";
26436
26453
 
26437
26454
  // src/entry/directory.ts
26438
26455
  import { homedir as homedir2 } from "node:os";
26439
- import { isAbsolute as isAbsolute5, join as join10, resolve as resolve4, sep as sep4 } from "node:path";
26456
+ import { isAbsolute as isAbsolute5, resolve as resolve4 } from "node:path";
26457
+ var DOWNLOADS_DIR = "Downloads";
26458
+ var DEFAULT_DOWNLOAD_MAX_MB = 100;
26440
26459
  function homeDir(env) {
26441
26460
  const envHome = env.HOME;
26442
26461
  if (envHome && isAbsolute5(envHome)) return envHome;
26443
26462
  return homedir2();
26444
26463
  }
26445
- function resolveGivenDirectory(given, home) {
26446
- const text = given.text.trim();
26447
- const expanded = text === "~" ? home : text.startsWith("~/") || text.startsWith(`~${sep4}`) ? join10(home, text.slice(2)) : text;
26448
- if (!text || !isAbsolute5(expanded)) {
26449
- throw new ToolError("invalid_argument", `${given.source} must be an absolute path.`);
26450
- }
26451
- return resolve4(expanded);
26452
- }
26453
-
26454
- // src/entry/download-dir.ts
26455
- var DOWNLOADS_DIR = "Downloads";
26456
- function resolveDownloadDir(given, env, profile) {
26457
- const home = homeDir(env);
26458
- return given !== void 0 ? resolveGivenDirectory(given, home) : resolve5(home, DOWNLOADS_DIR, profile.name);
26464
+ function defaultStateDir(name, env, home) {
26465
+ const xdg = env.XDG_STATE_HOME;
26466
+ return xdg && isAbsolute5(xdg) ? resolve4(xdg, name) : resolve4(home, ".local", "state", name);
26459
26467
  }
26460
-
26461
- // src/entry/send-dirs.ts
26462
- function resolveSendDirs(given, env) {
26463
- const home = homeDir(env);
26464
- return given.map((directory) => resolveGivenDirectory(directory, home));
26468
+ function text(value) {
26469
+ return typeof value === "string" ? value : void 0;
26465
26470
  }
26466
-
26467
- // src/entry/state-dir.ts
26468
- import { isAbsolute as isAbsolute6, resolve as resolve6 } from "node:path";
26469
- function resolveStateDir(given, env, profile) {
26471
+ function resolveDirectorySettings(options, env, profile) {
26470
26472
  const home = homeDir(env);
26471
- if (given !== void 0) return resolveGivenDirectory(given, home);
26472
- const xdg = env.XDG_STATE_HOME;
26473
- if (xdg && isAbsolute6(xdg)) {
26474
- return resolve6(xdg, profile.name);
26475
- }
26476
- return resolve6(home, ".local", "state", profile.name);
26473
+ const maxMb = options.download_max_mb;
26474
+ const sendDirs = options.send_dirs;
26475
+ return {
26476
+ state_dir: text(options.state_dir) ?? defaultStateDir(profile.name, env, home),
26477
+ download_dir: text(options.download_dir) ?? resolve4(home, DOWNLOADS_DIR, profile.name),
26478
+ download_max_mb: typeof maxMb === "number" ? maxMb : DEFAULT_DOWNLOAD_MAX_MB,
26479
+ send_dirs: Array.isArray(sendDirs) ? [...sendDirs] : []
26480
+ };
26477
26481
  }
26478
26482
 
26479
26483
  // src/entry/settings.ts
26480
26484
  var ALL_TOOLSETS = "all";
26481
26485
  var TRUE_VALUES = ["1", "true"];
26482
26486
  var FALSE_VALUES = ["0", "false"];
26483
- var DEFAULT_DOWNLOAD_MAX_MB = 100;
26484
- var MAX_DOWNLOAD_MB = 2e3;
26485
26487
  var OPTIONS = {
26486
26488
  toolsets: { flag: "--toolsets", envSuffix: "TOOLSETS" },
26487
26489
  read_only: { flag: "--read-only", envSuffix: "READ_ONLY", boolean: true },
26488
26490
  tools: { flag: "--tools", envSuffix: "TOOLS" },
26489
26491
  exclude_tools: { flag: "--exclude-tools", envSuffix: "EXCLUDE_TOOLS" },
26490
- confirm: { flag: "--confirm", envSuffix: "CONFIRM" },
26491
- state_dir: { flag: "--state-dir", envSuffix: "STATE_DIR" },
26492
- download_dir: { flag: "--download-dir", envSuffix: "DOWNLOAD_DIR" },
26493
- download_max_mb: { flag: "--download-max-mb", envSuffix: "DOWNLOAD_MAX_MB" },
26494
- send_dirs: { flag: "--send-dirs", envSuffix: "SEND_DIRS" },
26495
- keep_disappearing: { flag: "--keep-disappearing", envSuffix: "KEEP_DISAPPEARING", boolean: true },
26496
- channels: { flag: "--channels", envSuffix: "CHANNELS", boolean: true }
26492
+ confirm: { flag: "--confirm", envSuffix: "CONFIRM" }
26497
26493
  };
26498
- var SERVICE_OPTION_NAMES = ["keep_disappearing", "channels"];
26494
+ function profileOptions(profile) {
26495
+ return profile.options ?? {};
26496
+ }
26497
+ function settingOptions(profile) {
26498
+ const toolOptions = Object.entries(profileOptions(profile)).map(([name, spec]) => [
26499
+ name,
26500
+ {
26501
+ flag: `--${name.replace(/_/g, "-")}`,
26502
+ envSuffix: name.toUpperCase(),
26503
+ ...optionType(spec) === "boolean" && { boolean: true }
26504
+ }
26505
+ ]);
26506
+ return { ...OPTIONS, ...Object.fromEntries(toolOptions) };
26507
+ }
26499
26508
  function envName(name, profile) {
26500
- return `${profile.envPrefix}_${OPTIONS[name].envSuffix}`;
26509
+ return `${profile.envPrefix}_${settingOptions(profile)[name].envSuffix}`;
26501
26510
  }
26502
26511
  function splitList(value) {
26503
26512
  return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
@@ -26512,43 +26521,87 @@ function checkToolsets(names, source, toolsets) {
26512
26521
  return names.length ? names : void 0;
26513
26522
  }
26514
26523
  function parseBoolean(value, source) {
26515
- const text = value.trim().toLowerCase();
26516
- if (TRUE_VALUES.includes(text)) return true;
26517
- if (FALSE_VALUES.includes(text)) return false;
26524
+ const text2 = value.trim().toLowerCase();
26525
+ if (TRUE_VALUES.includes(text2)) return true;
26526
+ if (FALSE_VALUES.includes(text2)) return false;
26518
26527
  throw new ToolError(
26519
26528
  "invalid_argument",
26520
26529
  `Unknown value "${value}" in ${source}; use one of ${[...TRUE_VALUES, ...FALSE_VALUES].join(", ")}.`
26521
26530
  );
26522
26531
  }
26523
- function listedDirectories(given) {
26524
- return given === void 0 ? [] : splitList(given.text).map((text) => ({ text, source: given.source }));
26525
- }
26526
- function downloadMaxMb(value, source) {
26527
- if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > MAX_DOWNLOAD_MB) {
26528
- throw new ToolError("invalid_argument", `${source} must be an integer from 1 to ${MAX_DOWNLOAD_MB}.`);
26532
+ function parseNumber2(value, source) {
26533
+ const trimmed = value.trim();
26534
+ const parsed = trimmed === "" ? Number.NaN : Number(trimmed);
26535
+ if (!Number.isFinite(parsed)) {
26536
+ throw new ToolError("invalid_argument", `Unknown number "${value}" in ${source}; use a finite number.`);
26529
26537
  }
26530
- return value;
26538
+ return parsed;
26539
+ }
26540
+ function absolutePath(text2, source, env) {
26541
+ const home = env.HOME !== void 0 && isAbsolute6(env.HOME) ? env.HOME : homedir3();
26542
+ const expanded = text2 === "~" || text2.startsWith("~/") || text2.startsWith(`~${sep4}`) ? join10(home, text2.slice(1)) : text2;
26543
+ if (!isAbsolute6(expanded)) throw new ToolError("invalid_argument", `${source} must be an absolute path.`);
26544
+ return resolvePath(expanded);
26531
26545
  }
26532
- function downloadMaxMbText({ text, source }) {
26533
- const trimmed = text.trim();
26534
- return downloadMaxMb(/^\d+$/.test(trimmed) ? Number(trimmed) : Number.NaN, source);
26546
+ function absolutePaths(items, source, env) {
26547
+ return items.length > 0 ? items.map((item) => absolutePath(item, source, env)) : void 0;
26535
26548
  }
26536
26549
  function envText(name, env, profile) {
26537
26550
  const variable = envName(name, profile);
26538
- const text = env[variable];
26539
- return text === void 0 || text.trim() === "" ? void 0 : { text, source: variable };
26540
- }
26541
- var TOOL_OPTION_NAMES = [
26542
- "toolsets",
26543
- "read_only",
26544
- "tools",
26545
- "exclude_tools",
26546
- "state_dir",
26547
- "download_dir",
26548
- "download_max_mb",
26549
- "send_dirs",
26550
- ...SERVICE_OPTION_NAMES
26551
- ];
26551
+ const text2 = env[variable];
26552
+ return text2 === void 0 || text2.trim() === "" ? void 0 : { text: text2, source: variable };
26553
+ }
26554
+ function optionType(spec) {
26555
+ return typeof spec === "string" ? spec : spec.type;
26556
+ }
26557
+ function itemCount(count) {
26558
+ return count === 1 ? "1 item" : `${count} items`;
26559
+ }
26560
+ function numberBoundsError({ integer: integer2, min, max }, source) {
26561
+ const range = min !== void 0 && max !== void 0 ? ` from ${min} to ${max}` : min !== void 0 ? ` of at least ${min}` : max !== void 0 ? ` of at most ${max}` : "";
26562
+ return new ToolError("invalid_argument", `${source} must be ${integer2 ? "a whole number" : "a number"}${range}.`);
26563
+ }
26564
+ function countBoundsError({ min, max }, source) {
26565
+ const range = min !== void 0 && max !== void 0 ? `from ${min} to ${max} items` : min !== void 0 ? `at least ${itemCount(min)}` : `at most ${itemCount(max)}`;
26566
+ return new ToolError("invalid_argument", `${source} must list ${range}.`);
26567
+ }
26568
+ function withinBounds(spec, value, source) {
26569
+ if (value === void 0 || typeof spec === "string") return value;
26570
+ const bounds = spec;
26571
+ const { integer: integer2, min, max } = bounds;
26572
+ if (typeof value === "number") {
26573
+ if (integer2 && !Number.isInteger(value) || min !== void 0 && value < min || max !== void 0 && value > max) {
26574
+ throw numberBoundsError(bounds, source);
26575
+ }
26576
+ } else if (typeof value === "object" && (min !== void 0 && value.length < min || max !== void 0 && value.length > max)) {
26577
+ throw countBoundsError(bounds, source);
26578
+ }
26579
+ return value;
26580
+ }
26581
+ function parseOptionText(spec, given, env) {
26582
+ return withinBounds(spec, parseTypedText(optionType(spec), given, env), given.source);
26583
+ }
26584
+ function parseTypedText(kind, given, env) {
26585
+ switch (kind) {
26586
+ case "text":
26587
+ return nonBlank(given.text);
26588
+ case "boolean":
26589
+ return parseBoolean(given.text, given.source);
26590
+ case "number":
26591
+ return parseNumber2(given.text, given.source);
26592
+ case "list": {
26593
+ const items = splitList(given.text);
26594
+ return items.length > 0 ? items : void 0;
26595
+ }
26596
+ case "path": {
26597
+ const text2 = nonBlank(given.text);
26598
+ return text2 === void 0 ? void 0 : absolutePath(text2, given.source, env);
26599
+ }
26600
+ case "paths":
26601
+ return absolutePaths(splitList(given.text), given.source, env);
26602
+ }
26603
+ }
26604
+ var TOOL_OPTION_NAMES = ["toolsets", "read_only", "tools", "exclude_tools"];
26552
26605
  function stringList(value) {
26553
26606
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
26554
26607
  }
@@ -26556,15 +26609,6 @@ var optionSource = (name) => `Option "${name}"`;
26556
26609
  function optionError(name, expected) {
26557
26610
  return new ToolError("invalid_argument", `${optionSource(name)} must be ${expected}.`);
26558
26611
  }
26559
- function directoryOption(name, value) {
26560
- if (typeof value !== "string") throw optionError(name, "an absolute path");
26561
- return value;
26562
- }
26563
- function sendDirsOption(value) {
26564
- const list2 = stringList(value);
26565
- if (!list2) throw optionError("send_dirs", "a list of absolute paths");
26566
- return list2.map((text) => ({ text, source: optionSource("send_dirs") }));
26567
- }
26568
26612
  function toolsetsOption(value, toolsets) {
26569
26613
  if (value === ALL_TOOLSETS) return ALL_TOOLSETS;
26570
26614
  const names = stringList(value);
@@ -26585,12 +26629,14 @@ function listOption(name) {
26585
26629
  };
26586
26630
  }
26587
26631
  var LANGUAGE_OPTION = "language";
26588
- var PLUGIN_OPTION_NAMES = [...TOOL_OPTION_NAMES, LANGUAGE_OPTION];
26632
+ function pluginOptionNames(profile) {
26633
+ return [...TOOL_OPTION_NAMES, LANGUAGE_OPTION, ...Object.keys(profileOptions(profile))];
26634
+ }
26589
26635
  function languageVariable(profile = PROJECT_PROFILE) {
26590
26636
  return `${profile.envPrefix}_LANGUAGE`;
26591
26637
  }
26592
- function nonBlank(text) {
26593
- const trimmed = text?.trim();
26638
+ function nonBlank(text2) {
26639
+ const trimmed = text2?.trim();
26594
26640
  return trimmed === "" ? void 0 : trimmed;
26595
26641
  }
26596
26642
  function promptLanguage(flag, env, harness, profile = PROJECT_PROFILE) {
@@ -26600,42 +26646,80 @@ function languageOption(value) {
26600
26646
  if (typeof value !== "string") throw optionError(LANGUAGE_OPTION, 'a language tag such as "ja"');
26601
26647
  return nonBlank(value);
26602
26648
  }
26649
+ function toolOption(name, spec, value, env) {
26650
+ return withinBounds(spec, toolOptionOfType(name, optionType(spec), value, env), `Option "${name}"`);
26651
+ }
26652
+ function toolOptionOfType(name, kind, value, env) {
26653
+ switch (kind) {
26654
+ case "text":
26655
+ if (typeof value !== "string") throw optionError(name, "text");
26656
+ return nonBlank(value);
26657
+ case "boolean":
26658
+ if (typeof value !== "boolean") throw optionError(name, "true or false");
26659
+ return value;
26660
+ case "number":
26661
+ if (typeof value !== "number" || !Number.isFinite(value)) throw optionError(name, "a finite number");
26662
+ return value;
26663
+ case "list": {
26664
+ const list2 = stringList(value);
26665
+ if (!list2) throw optionError(name, "a list of strings");
26666
+ const items = list2.map((item) => item.trim()).filter((item) => item !== "");
26667
+ return items.length > 0 ? items : void 0;
26668
+ }
26669
+ case "path": {
26670
+ if (typeof value !== "string") throw optionError(name, "an absolute path");
26671
+ const text2 = nonBlank(value);
26672
+ return text2 === void 0 ? void 0 : absolutePath(text2, `Option "${name}"`, env);
26673
+ }
26674
+ case "paths": {
26675
+ const list2 = stringList(value);
26676
+ if (!list2) throw optionError(name, "a list of absolute paths");
26677
+ return absolutePaths(
26678
+ list2.flatMap((item) => nonBlank(item) ?? []),
26679
+ `Option "${name}"`,
26680
+ env
26681
+ );
26682
+ }
26683
+ }
26684
+ }
26603
26685
  function settingsFromOptions(options, env, profile = PROJECT_PROFILE) {
26604
26686
  const given = options ?? {};
26605
- const unknown2 = Object.keys(given).find((name) => !PLUGIN_OPTION_NAMES.includes(name));
26687
+ const names = pluginOptionNames(profile);
26688
+ const unknown2 = Object.keys(given).find((name) => !names.includes(name));
26606
26689
  if (unknown2 !== void 0) {
26607
- throw new ToolError(
26608
- "invalid_argument",
26609
- `Unknown option "${unknown2}"; use one of ${PLUGIN_OPTION_NAMES.join(", ")}.`
26610
- );
26690
+ throw new ToolError("invalid_argument", `Unknown option "${unknown2}"; use one of ${names.join(", ")}.`);
26611
26691
  }
26612
- const resolve8 = (name, fromOption, fromText) => {
26692
+ const resolve6 = (name, fromOption, fromText) => {
26613
26693
  if (given[name] !== void 0) return fromOption(given[name]);
26614
- const text = envText(name, env, profile);
26615
- return text !== void 0 ? fromText(text) : void 0;
26616
- };
26617
- const directoryText = (name) => given[name] !== void 0 ? { text: directoryOption(name, given[name]), source: optionSource(name) } : envText(name, env, profile);
26694
+ const text2 = envText(name, env, profile);
26695
+ return text2 !== void 0 ? fromText(text2) : void 0;
26696
+ };
26697
+ const parsedOptions = {};
26698
+ for (const [name, spec] of Object.entries(profileOptions(profile))) {
26699
+ let parsed;
26700
+ if (given[name] !== void 0) {
26701
+ parsed = toolOption(name, spec, given[name], env);
26702
+ } else {
26703
+ const fromEnv = envText(name, env, profile);
26704
+ if (fromEnv !== void 0) parsed = parseOptionText(spec, fromEnv, env);
26705
+ }
26706
+ if (parsed !== void 0) parsedOptions[name] = parsed;
26707
+ }
26618
26708
  const settings = {
26619
- state_dir: resolveStateDir(directoryText("state_dir"), env, profile),
26620
- send_dirs: resolveSendDirs(resolve8("send_dirs", sendDirsOption, listedDirectories) ?? [], env),
26621
- download_dir: resolveDownloadDir(directoryText("download_dir"), env, profile),
26622
- download_max_mb: resolve8("download_max_mb", (value) => downloadMaxMb(value, optionSource("download_max_mb")), downloadMaxMbText) ?? DEFAULT_DOWNLOAD_MAX_MB
26709
+ options: parsedOptions,
26710
+ ...resolveDirectorySettings(parsedOptions, env, profile)
26623
26711
  };
26624
- const toolsets = resolve8(
26712
+ const toolsets = resolve6(
26625
26713
  "toolsets",
26626
26714
  (value) => toolsetsOption(value, profile.toolsets),
26627
- ({ text, source }) => checkToolsets(splitList(text), source, profile.toolsets)
26715
+ ({ text: text2, source }) => checkToolsets(splitList(text2), source, profile.toolsets)
26628
26716
  );
26629
26717
  if (toolsets !== void 0) settings.toolsets = toolsets;
26630
- const readOnly = resolve8("read_only", booleanOption("read_only"), ({ text, source }) => parseBoolean(text, source));
26718
+ const readOnly = resolve6("read_only", booleanOption("read_only"), ({ text: text2, source }) => parseBoolean(text2, source));
26631
26719
  if (readOnly !== void 0) settings.read_only = readOnly;
26632
- for (const name of SERVICE_OPTION_NAMES) {
26633
- const given2 = resolve8(name, booleanOption(name), ({ text, source }) => parseBoolean(text, source));
26634
- if (given2 !== void 0) settings[name] = given2;
26635
- }
26636
- const tools = resolve8("tools", listOption("tools"), ({ text }) => splitList(text));
26720
+ const tools = resolve6("tools", listOption("tools"), ({ text: text2 }) => splitList(text2));
26637
26721
  if (tools !== void 0) settings.tools = tools;
26638
- const excluded = resolve8("exclude_tools", listOption("exclude_tools"), ({ text }) => splitList(text));
26722
+ const excluded = resolve6("exclude_tools", listOption("exclude_tools"), ({ text: text2 }) => splitList(text2));
26639
26723
  if (excluded !== void 0) settings.exclude_tools = excluded;
26640
26724
  const language = given[LANGUAGE_OPTION] !== void 0 ? languageOption(given[LANGUAGE_OPTION]) : promptLanguage(void 0, env, void 0, profile);
26641
26725
  if (language !== void 0) settings.language = language;
@@ -26666,7 +26750,8 @@ function selectedTools(specs, options, deps) {
26666
26750
  downloadMaxMb: settings.download_max_mb,
26667
26751
  sendDirs: settings.send_dirs,
26668
26752
  programName: PROJECT.name,
26669
- translator: promptTranslator(deps.translations ?? {}, settings.language)
26753
+ translator: promptTranslator(deps.translations ?? {}, settings.language),
26754
+ options: settings.options
26670
26755
  };
26671
26756
  }
26672
26757
  async function runTool(spec, args, ctx, pin) {
@@ -26689,8 +26774,8 @@ function describeArguments(spec) {
26689
26774
  const { properties = {}, required: required2 = [] } = inputJsonSchema(spec);
26690
26775
  const lines = Object.entries(properties).map(([name, property]) => {
26691
26776
  const kind = required2.includes(name) ? typeName(property) : `${typeName(property)}, optional`;
26692
- const text = property.description?.replace(/\s+/g, " ").trim();
26693
- return `- ${name} (${kind})${text ? `: ${text}` : ""}`;
26777
+ const text2 = property.description?.replace(/\s+/g, " ").trim();
26778
+ return `- ${name} (${kind})${text2 ? `: ${text2}` : ""}`;
26694
26779
  });
26695
26780
  return lines.length > 0 ? `
26696
26781
 
@@ -26734,7 +26819,7 @@ function createV1Server(specs, deps = {}) {
26734
26819
 
26735
26820
  // src/opencode/skills.ts
26736
26821
  import { readFile, readdir as readdir3 } from "node:fs/promises";
26737
- import { join as join11, resolve as resolve7 } from "node:path";
26822
+ import { join as join11, resolve as resolve5 } from "node:path";
26738
26823
  import { fileURLToPath as fileURLToPath2 } from "node:url";
26739
26824
  var SKILL_FILE = "SKILL.md";
26740
26825
  var FRONTMATTER = /^---[ \t]*\r?\n([\s\S]*?\r?\n)?---[ \t]*(?:\r?\n|$)/;
@@ -26761,8 +26846,8 @@ function unquote(value) {
26761
26846
  function scalar(raw) {
26762
26847
  if (raw === "" || UNSUPPORTED_VALUE_START.test(raw)) return void 0;
26763
26848
  if (raw.startsWith('"') || raw.startsWith("'")) return unquote(raw);
26764
- const text = raw.replace(TRAILING_COMMENT, "").trim();
26765
- return text === "" ? void 0 : text;
26849
+ const text2 = raw.replace(TRAILING_COMMENT, "").trim();
26850
+ return text2 === "" ? void 0 : text2;
26766
26851
  }
26767
26852
  function readToolset(line, indent) {
26768
26853
  if (!line.startsWith(indent)) return void 0;
@@ -26797,8 +26882,8 @@ function readFrontmatter(block) {
26797
26882
  }
26798
26883
  return found;
26799
26884
  }
26800
- function parseSkill(directory, text, path) {
26801
- const match = FRONTMATTER.exec(text);
26885
+ function parseSkill(directory, text2, path) {
26886
+ const match = FRONTMATTER.exec(text2);
26802
26887
  const { name, description, toolset } = match ? readFrontmatter(match[1] ?? "") : {};
26803
26888
  const id = name ?? directory;
26804
26889
  return {
@@ -26807,7 +26892,7 @@ function parseSkill(directory, text, path) {
26807
26892
  name: id,
26808
26893
  ...description !== void 0 && { description },
26809
26894
  path,
26810
- content: match ? text.slice(match[0].length) : text
26895
+ content: match ? text2.slice(match[0].length) : text2
26811
26896
  },
26812
26897
  ...toolset !== void 0 && { toolset }
26813
26898
  };
@@ -26825,7 +26910,7 @@ async function readSkillFile(path) {
26825
26910
  }
26826
26911
  }
26827
26912
  async function readSkills(dir) {
26828
- const root = resolve7(typeof dir === "string" ? dir : fileURLToPath2(dir));
26913
+ const root = resolve5(typeof dir === "string" ? dir : fileURLToPath2(dir));
26829
26914
  let entries;
26830
26915
  try {
26831
26916
  entries = (await readdir3(root, { withFileTypes: true })).filter((entry2) => !entry2.isFile()).map((entry2) => entry2.name);
@@ -26836,9 +26921,9 @@ async function readSkills(dir) {
26836
26921
  const skills = /* @__PURE__ */ new Map();
26837
26922
  for (const directory of entries.sort()) {
26838
26923
  const path = join11(root, directory, SKILL_FILE);
26839
- const text = await readSkillFile(path);
26840
- if (text === void 0) continue;
26841
- const found = parseSkill(directory, text, path);
26924
+ const text2 = await readSkillFile(path);
26925
+ if (text2 === void 0) continue;
26926
+ const found = parseSkill(directory, text2, path);
26842
26927
  if (!skills.has(found.skill.id)) skills.set(found.skill.id, found);
26843
26928
  }
26844
26929
  return [...skills.values()];