@craftsamo/whatsapp-tools 0.1.0 → 0.1.2

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,12 @@ var PROJECT = {
25
25
  toolsets: ["account", "chats", "contacts", "history", "files", "sends", "actions"],
26
26
  /** The toolsets on when a setting names none. */
27
27
  defaultToolsets: ["account", "chats", "contacts", "history", "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
+ state_dir: "path",
31
+ download_dir: "path",
32
+ send_dirs: "paths"
33
+ },
28
34
  /** The environment variables the tools read besides the settings, which Codex forwards to the server after them. */
29
35
  toolVariables: []
30
36
  };
@@ -5099,7 +5105,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5099
5105
  var NONE = 0;
5100
5106
  var ASSUMED = 1;
5101
5107
  var PROVEN = 2;
5102
- function isRecursive(inst, stack, resolve8) {
5108
+ function isRecursive(inst, stack, resolve6) {
5103
5109
  const cached2 = recursive.get(inst);
5104
5110
  if (cached2 !== void 0)
5105
5111
  return cached2 ? PROVEN : NONE;
@@ -5109,7 +5115,7 @@ function isRecursive(inst, stack, resolve8) {
5109
5115
  let result = NONE;
5110
5116
  const check2 = (child) => {
5111
5117
  if (result !== PROVEN && child?._zod) {
5112
- const answer = isRecursive(child, stack, resolve8);
5118
+ const answer = isRecursive(child, stack, resolve6);
5113
5119
  if (answer > result)
5114
5120
  result = answer;
5115
5121
  }
@@ -5120,7 +5126,7 @@ function isRecursive(inst, stack, resolve8) {
5120
5126
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5121
5127
  if (spread && !desc.enumerable)
5122
5128
  continue;
5123
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve8) : NONE;
5129
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
5124
5130
  if (child > answer)
5125
5131
  answer = child;
5126
5132
  }
@@ -5184,7 +5190,7 @@ function isRecursive(inst, stack, resolve8) {
5184
5190
  break;
5185
5191
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5186
5192
  case "lazy": {
5187
- const inner = def._cachedInner ?? (resolve8 ? inst._zod.innerType : void 0);
5193
+ const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
5188
5194
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5189
5195
  break;
5190
5196
  }
@@ -20117,7 +20123,7 @@ function abortError(signal) {
20117
20123
  }
20118
20124
  function exec(argv, options) {
20119
20125
  const { signal } = options;
20120
- return new Promise((resolve8, reject) => {
20126
+ return new Promise((resolve6, reject) => {
20121
20127
  if (signal?.aborted) {
20122
20128
  reject(abortError(signal));
20123
20129
  return;
@@ -20176,7 +20182,7 @@ function exec(argv, options) {
20176
20182
  }
20177
20183
  const finish = () => settle2(() => {
20178
20184
  const { code, killedBy } = exitedWith ?? { code: null, killedBy: null };
20179
- resolve8({
20185
+ resolve6({
20180
20186
  code: code ?? -1,
20181
20187
  stdout,
20182
20188
  stderr,
@@ -21882,7 +21888,7 @@ function callService(paths, op, args, { mode, signal, timeoutMs: requestedMs = D
21882
21888
  if (signal?.aborted) return Promise.reject(abortError(signal));
21883
21889
  const timeoutMs = Number.isNaN(requestedMs) ? DEFAULT_TIMEOUT_MS2 : Math.min(Math.max(Math.ceil(requestedMs), 1), MAX_DEADLINE_MS);
21884
21890
  const request = encodeLine({ op, args, deadline_ms: timeoutMs });
21885
- return new Promise((resolve8, reject) => {
21891
+ return new Promise((resolve6, reject) => {
21886
21892
  const socket = createConnection(paths.socket);
21887
21893
  socket.setEncoding("utf8");
21888
21894
  let sent = false;
@@ -21927,7 +21933,7 @@ function callService(paths, op, args, { mode, signal, timeoutMs: requestedMs = D
21927
21933
  if (end === -1) return;
21928
21934
  const response = parseResponse(received.slice(0, end));
21929
21935
  if (!response) return unknownOutcome2("The sync service's answer could not be read");
21930
- if (response.ok) return finish(() => resolve8(response.data));
21936
+ if (response.ok) return finish(() => resolve6(response.data));
21931
21937
  fail(new ToolError(response.error.code, response.error.message));
21932
21938
  });
21933
21939
  socket.on("error", (error62) => {
@@ -27034,9 +27040,9 @@ async function timedCallDescription(spec, args, ctx, timeoutMs = DESCRIBE_TIMEOU
27034
27040
  let timer;
27035
27041
  let onAbort;
27036
27042
  try {
27037
- const timedOut = new Promise((resolve8) => {
27043
+ const timedOut = new Promise((resolve6) => {
27038
27044
  timer = setTimeout(() => {
27039
- resolve8(void 0);
27045
+ resolve6(void 0);
27040
27046
  controller.abort();
27041
27047
  }, timeoutMs);
27042
27048
  });
@@ -27112,60 +27118,41 @@ ${JSON.stringify(error62.details)}` : ""}`;
27112
27118
  var PROJECT_PROFILE = {
27113
27119
  name: PROJECT.name,
27114
27120
  envPrefix: PROJECT.envPrefix,
27115
- toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets }
27121
+ toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets },
27122
+ options: PROJECT.options
27116
27123
  };
27117
27124
 
27125
+ // src/entry/settings.ts
27126
+ import { homedir as homedir3 } from "node:os";
27127
+ import { isAbsolute as isAbsolute6, join as join11, resolve as resolvePath, sep as sep4 } from "node:path";
27128
+
27118
27129
  // src/entry/directory.ts
27119
27130
  import { homedir as homedir2 } from "node:os";
27120
- import { isAbsolute as isAbsolute5, join as join11, resolve as resolve4, sep as sep4 } from "node:path";
27131
+ import { isAbsolute as isAbsolute5, resolve as resolve4 } from "node:path";
27132
+ var DOWNLOADS_DIR = "Downloads";
27121
27133
  function homeDir(env7) {
27122
27134
  const envHome = env7.HOME;
27123
27135
  if (envHome && isAbsolute5(envHome)) return envHome;
27124
27136
  return homedir2();
27125
27137
  }
27126
- function expandTilde(text2, home) {
27127
- if (text2 === "~") return home;
27128
- if (text2.startsWith("~/") || text2.startsWith(`~${sep4}`)) {
27129
- return join11(home, text2.slice(2));
27130
- }
27131
- return text2;
27132
- }
27133
- function resolveGivenDirectory(given, home) {
27134
- const text2 = given.text.trim();
27135
- const expanded = expandTilde(text2, home);
27136
- if (!text2 || !isAbsolute5(expanded)) {
27137
- throw new ToolError("invalid_argument", `${given.source} must be an absolute path.`);
27138
- }
27139
- return resolve4(expanded);
27140
- }
27141
- function toolDirectories(settings) {
27142
- return { stateDir: settings.state_dir, downloadDir: settings.download_dir, sendDirs: settings.send_dirs };
27138
+ function pathOf(value) {
27139
+ return typeof value === "string" ? value : void 0;
27143
27140
  }
27144
-
27145
- // src/entry/download-dir.ts
27146
- import { resolve as resolve5 } from "node:path";
27147
- var DOWNLOADS_DIR = "Downloads";
27148
- function resolveDownloadDir(given, env7, profile) {
27149
- const home = homeDir(env7);
27150
- return given !== void 0 ? resolveGivenDirectory(given, home) : resolve5(home, DOWNLOADS_DIR, profile.name);
27141
+ function pathsOf(value) {
27142
+ return typeof value === "object" ? value : [];
27151
27143
  }
27152
-
27153
- // src/entry/send-dirs.ts
27154
- function resolveSendDirs(given, env7) {
27155
- const home = homeDir(env7);
27156
- return [...new Set(given.map((directory) => resolveGivenDirectory(directory, home)))];
27157
- }
27158
-
27159
- // src/entry/state-dir.ts
27160
- import { isAbsolute as isAbsolute6, resolve as resolve6 } from "node:path";
27161
- function resolveStateDir(given, env7, profile) {
27144
+ function directorySettings(options, env7, profile) {
27162
27145
  const home = homeDir(env7);
27163
- if (given !== void 0) return resolveGivenDirectory(given, home);
27164
27146
  const xdg = env7.XDG_STATE_HOME;
27165
- if (xdg && isAbsolute6(xdg)) {
27166
- return resolve6(xdg, profile.name);
27167
- }
27168
- return resolve6(home, ".local", "state", profile.name);
27147
+ const stateHome = xdg && isAbsolute5(xdg) ? xdg : resolve4(home, ".local", "state");
27148
+ return {
27149
+ state_dir: pathOf(options.state_dir) ?? resolve4(stateHome, profile.name),
27150
+ download_dir: pathOf(options.download_dir) ?? resolve4(home, DOWNLOADS_DIR, profile.name),
27151
+ send_dirs: [...new Set(pathsOf(options.send_dirs))]
27152
+ };
27153
+ }
27154
+ function toolDirectories(settings) {
27155
+ return { stateDir: settings.state_dir, downloadDir: settings.download_dir, sendDirs: settings.send_dirs };
27169
27156
  }
27170
27157
 
27171
27158
  // src/entry/settings.ts
@@ -27177,20 +27164,28 @@ var OPTIONS = {
27177
27164
  read_only: { flag: "--read-only", envSuffix: "READ_ONLY", boolean: true },
27178
27165
  tools: { flag: "--tools", envSuffix: "TOOLS" },
27179
27166
  exclude_tools: { flag: "--exclude-tools", envSuffix: "EXCLUDE_TOOLS" },
27180
- confirm: { flag: "--confirm", envSuffix: "CONFIRM" },
27181
- state_dir: { flag: "--state-dir", envSuffix: "STATE_DIR" },
27182
- download_dir: { flag: "--download-dir", envSuffix: "DOWNLOAD_DIR" },
27183
- send_dirs: { flag: "--send-dirs", envSuffix: "SEND_DIRS" }
27167
+ confirm: { flag: "--confirm", envSuffix: "CONFIRM" }
27184
27168
  };
27169
+ function profileOptions(profile) {
27170
+ return profile.options ?? {};
27171
+ }
27172
+ function settingOptions(profile) {
27173
+ const toolOptions = Object.entries(profileOptions(profile)).map(([name, spec]) => [
27174
+ name,
27175
+ {
27176
+ flag: `--${name.replace(/_/g, "-")}`,
27177
+ envSuffix: name.toUpperCase(),
27178
+ ...optionType(spec) === "boolean" && { boolean: true }
27179
+ }
27180
+ ]);
27181
+ return { ...OPTIONS, ...Object.fromEntries(toolOptions) };
27182
+ }
27185
27183
  function envName(name, profile) {
27186
- return `${profile.envPrefix}_${OPTIONS[name].envSuffix}`;
27184
+ return `${profile.envPrefix}_${settingOptions(profile)[name].envSuffix}`;
27187
27185
  }
27188
27186
  function splitList(value) {
27189
27187
  return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
27190
27188
  }
27191
- function listedDirectories(given) {
27192
- return given === void 0 ? [] : splitList(given.text).map((text2) => ({ text: text2, source: given.source }));
27193
- }
27194
27189
  function checkToolsets(names, source, toolsets) {
27195
27190
  if (names.includes(ALL_TOOLSETS)) return ALL_TOOLSETS;
27196
27191
  const unknown2 = names.find((name) => !toolsets.names.includes(name));
@@ -27209,30 +27204,85 @@ function parseBoolean(value, source) {
27209
27204
  `Unknown value "${value}" in ${source}; use one of ${[...TRUE_VALUES, ...FALSE_VALUES].join(", ")}.`
27210
27205
  );
27211
27206
  }
27207
+ function parseNumber2(value, source) {
27208
+ const trimmed = value.trim();
27209
+ const parsed = trimmed === "" ? Number.NaN : Number(trimmed);
27210
+ if (!Number.isFinite(parsed)) {
27211
+ throw new ToolError("invalid_argument", `Unknown number "${value}" in ${source}; use a finite number.`);
27212
+ }
27213
+ return parsed;
27214
+ }
27215
+ function absolutePath(text2, source, env7) {
27216
+ const home = env7.HOME !== void 0 && isAbsolute6(env7.HOME) ? env7.HOME : homedir3();
27217
+ const expanded = text2 === "~" || text2.startsWith("~/") || text2.startsWith(`~${sep4}`) ? join11(home, text2.slice(1)) : text2;
27218
+ if (!isAbsolute6(expanded)) throw new ToolError("invalid_argument", `${source} must be an absolute path.`);
27219
+ return resolvePath(expanded);
27220
+ }
27221
+ function absolutePaths(items, source, env7) {
27222
+ return items.length > 0 ? items.map((item) => absolutePath(item, source, env7)) : void 0;
27223
+ }
27212
27224
  function envText(name, env7, profile) {
27213
27225
  const variable = envName(name, profile);
27214
27226
  const text2 = env7[variable];
27215
27227
  return text2 === void 0 || text2.trim() === "" ? void 0 : { text: text2, source: variable };
27216
27228
  }
27217
- var TOOL_OPTION_NAMES = [
27218
- "toolsets",
27219
- "read_only",
27220
- "tools",
27221
- "exclude_tools",
27222
- "state_dir",
27223
- "download_dir",
27224
- "send_dirs"
27225
- ];
27229
+ function optionType(spec) {
27230
+ return typeof spec === "string" ? spec : spec.type;
27231
+ }
27232
+ function itemCount(count) {
27233
+ return count === 1 ? "1 item" : `${count} items`;
27234
+ }
27235
+ function numberBoundsError({ integer: integer2, min, max }, source) {
27236
+ 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}` : "";
27237
+ return new ToolError("invalid_argument", `${source} must be ${integer2 ? "a whole number" : "a number"}${range}.`);
27238
+ }
27239
+ function countBoundsError({ min, max }, source) {
27240
+ const range = min !== void 0 && max !== void 0 ? `from ${min} to ${max} items` : min !== void 0 ? `at least ${itemCount(min)}` : `at most ${itemCount(max)}`;
27241
+ return new ToolError("invalid_argument", `${source} must list ${range}.`);
27242
+ }
27243
+ function withinBounds(spec, value, source) {
27244
+ if (value === void 0 || typeof spec === "string") return value;
27245
+ const bounds = spec;
27246
+ const { integer: integer2, min, max } = bounds;
27247
+ if (typeof value === "number") {
27248
+ if (integer2 && !Number.isInteger(value) || min !== void 0 && value < min || max !== void 0 && value > max) {
27249
+ throw numberBoundsError(bounds, source);
27250
+ }
27251
+ } else if (typeof value === "object" && (min !== void 0 && value.length < min || max !== void 0 && value.length > max)) {
27252
+ throw countBoundsError(bounds, source);
27253
+ }
27254
+ return value;
27255
+ }
27256
+ function parseOptionText(spec, given, env7) {
27257
+ return withinBounds(spec, parseTypedText(optionType(spec), given, env7), given.source);
27258
+ }
27259
+ function parseTypedText(kind, given, env7) {
27260
+ switch (kind) {
27261
+ case "text":
27262
+ return nonBlank(given.text);
27263
+ case "boolean":
27264
+ return parseBoolean(given.text, given.source);
27265
+ case "number":
27266
+ return parseNumber2(given.text, given.source);
27267
+ case "list": {
27268
+ const items = splitList(given.text);
27269
+ return items.length > 0 ? items : void 0;
27270
+ }
27271
+ case "path": {
27272
+ const text2 = nonBlank(given.text);
27273
+ return text2 === void 0 ? void 0 : absolutePath(text2, given.source, env7);
27274
+ }
27275
+ case "paths":
27276
+ return absolutePaths(splitList(given.text), given.source, env7);
27277
+ }
27278
+ }
27279
+ var TOOL_OPTION_NAMES = ["toolsets", "read_only", "tools", "exclude_tools"];
27226
27280
  function stringList(value) {
27227
27281
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
27228
27282
  }
27229
27283
  function optionError(name, expected) {
27230
27284
  return new ToolError("invalid_argument", `Option "${name}" must be ${expected}.`);
27231
27285
  }
27232
- function directoryOption(name, value) {
27233
- if (typeof value !== "string") throw optionError(name, "an absolute path");
27234
- return value;
27235
- }
27236
27286
  function toolsetsOption(value, toolsets) {
27237
27287
  if (value === ALL_TOOLSETS) return ALL_TOOLSETS;
27238
27288
  const names = stringList(value);
@@ -27251,7 +27301,9 @@ function listOption(name) {
27251
27301
  };
27252
27302
  }
27253
27303
  var LANGUAGE_OPTION = "language";
27254
- var PLUGIN_OPTION_NAMES = [...TOOL_OPTION_NAMES, LANGUAGE_OPTION];
27304
+ function pluginOptionNames(profile) {
27305
+ return [...TOOL_OPTION_NAMES, LANGUAGE_OPTION, ...Object.keys(profileOptions(profile))];
27306
+ }
27255
27307
  function languageVariable(profile = PROJECT_PROFILE) {
27256
27308
  return `${profile.envPrefix}_LANGUAGE`;
27257
27309
  }
@@ -27266,43 +27318,77 @@ function languageOption(value) {
27266
27318
  if (typeof value !== "string") throw optionError(LANGUAGE_OPTION, 'a language tag such as "ja"');
27267
27319
  return nonBlank(value);
27268
27320
  }
27321
+ function toolOption(name, spec, value, env7) {
27322
+ return withinBounds(spec, toolOptionOfType(name, optionType(spec), value, env7), `Option "${name}"`);
27323
+ }
27324
+ function toolOptionOfType(name, kind, value, env7) {
27325
+ switch (kind) {
27326
+ case "text":
27327
+ if (typeof value !== "string") throw optionError(name, "text");
27328
+ return nonBlank(value);
27329
+ case "boolean":
27330
+ if (typeof value !== "boolean") throw optionError(name, "true or false");
27331
+ return value;
27332
+ case "number":
27333
+ if (typeof value !== "number" || !Number.isFinite(value)) throw optionError(name, "a finite number");
27334
+ return value;
27335
+ case "list": {
27336
+ const list = stringList(value);
27337
+ if (!list) throw optionError(name, "a list of strings");
27338
+ const items = list.map((item) => item.trim()).filter((item) => item !== "");
27339
+ return items.length > 0 ? items : void 0;
27340
+ }
27341
+ case "path": {
27342
+ if (typeof value !== "string") throw optionError(name, "an absolute path");
27343
+ const text2 = nonBlank(value);
27344
+ return text2 === void 0 ? void 0 : absolutePath(text2, `Option "${name}"`, env7);
27345
+ }
27346
+ case "paths": {
27347
+ const list = stringList(value);
27348
+ if (!list) throw optionError(name, "a list of absolute paths");
27349
+ return absolutePaths(
27350
+ list.flatMap((item) => nonBlank(item) ?? []),
27351
+ `Option "${name}"`,
27352
+ env7
27353
+ );
27354
+ }
27355
+ }
27356
+ }
27269
27357
  function settingsFromOptions(options, env7, profile = PROJECT_PROFILE) {
27270
27358
  const given = options ?? {};
27271
- const unknown2 = Object.keys(given).find((name) => !PLUGIN_OPTION_NAMES.includes(name));
27359
+ const names = pluginOptionNames(profile);
27360
+ const unknown2 = Object.keys(given).find((name) => !names.includes(name));
27272
27361
  if (unknown2 !== void 0) {
27273
- throw new ToolError(
27274
- "invalid_argument",
27275
- `Unknown option "${unknown2}"; use one of ${PLUGIN_OPTION_NAMES.join(", ")}.`
27276
- );
27362
+ throw new ToolError("invalid_argument", `Unknown option "${unknown2}"; use one of ${names.join(", ")}.`);
27277
27363
  }
27278
- const resolve8 = (name, fromOption, fromText) => {
27364
+ const resolve6 = (name, fromOption, fromText) => {
27279
27365
  if (given[name] !== void 0) return fromOption(given[name]);
27280
27366
  const text2 = envText(name, env7, profile);
27281
27367
  return text2 !== void 0 ? fromText(text2) : void 0;
27282
27368
  };
27283
- const directoryGiven = (name) => given[name] !== void 0 ? { text: directoryOption(name, given[name]), source: `Option "${name}"` } : envText(name, env7, profile);
27284
- const sendDirsGiven = () => {
27285
- if (given.send_dirs === void 0) return listedDirectories(envText("send_dirs", env7, profile));
27286
- const list = stringList(given.send_dirs);
27287
- if (!list) throw optionError("send_dirs", "a list of absolute paths");
27288
- return list.map((text2) => ({ text: text2, source: 'Option "send_dirs"' }));
27289
- };
27290
- const settings = {
27291
- state_dir: resolveStateDir(directoryGiven("state_dir"), env7, profile),
27292
- download_dir: resolveDownloadDir(directoryGiven("download_dir"), env7, profile),
27293
- send_dirs: resolveSendDirs(sendDirsGiven(), env7)
27294
- };
27295
- const toolsets = resolve8(
27369
+ const parsedOptions = {};
27370
+ for (const [name, spec] of Object.entries(profileOptions(profile))) {
27371
+ let parsed;
27372
+ if (given[name] !== void 0) {
27373
+ parsed = toolOption(name, spec, given[name], env7);
27374
+ } else {
27375
+ const fromEnv = envText(name, env7, profile);
27376
+ if (fromEnv !== void 0) parsed = parseOptionText(spec, fromEnv, env7);
27377
+ }
27378
+ if (parsed !== void 0) parsedOptions[name] = parsed;
27379
+ }
27380
+ const settings = { ...directorySettings(parsedOptions, env7, profile), options: parsedOptions };
27381
+ const toolsets = resolve6(
27296
27382
  "toolsets",
27297
27383
  (value) => toolsetsOption(value, profile.toolsets),
27298
27384
  ({ text: text2, source }) => checkToolsets(splitList(text2), source, profile.toolsets)
27299
27385
  );
27300
27386
  if (toolsets !== void 0) settings.toolsets = toolsets;
27301
- const readOnly = resolve8("read_only", booleanOption, ({ text: text2, source }) => parseBoolean(text2, source));
27387
+ const readOnly = resolve6("read_only", booleanOption, ({ text: text2, source }) => parseBoolean(text2, source));
27302
27388
  if (readOnly !== void 0) settings.read_only = readOnly;
27303
- const tools = resolve8("tools", listOption("tools"), ({ text: text2 }) => splitList(text2));
27389
+ const tools = resolve6("tools", listOption("tools"), ({ text: text2 }) => splitList(text2));
27304
27390
  if (tools !== void 0) settings.tools = tools;
27305
- const excluded = resolve8("exclude_tools", listOption("exclude_tools"), ({ text: text2 }) => splitList(text2));
27391
+ const excluded = resolve6("exclude_tools", listOption("exclude_tools"), ({ text: text2 }) => splitList(text2));
27306
27392
  if (excluded !== void 0) settings.exclude_tools = excluded;
27307
27393
  const language = given[LANGUAGE_OPTION] !== void 0 ? languageOption(given[LANGUAGE_OPTION]) : promptLanguage(void 0, env7, void 0, profile);
27308
27394
  if (language !== void 0) settings.language = language;
@@ -27329,7 +27415,8 @@ function selectedTools(specs, options, deps) {
27329
27415
  return {
27330
27416
  tools: selectTools(specs, settings, profile.toolsets),
27331
27417
  directories: { ...toolDirectories(settings), programName: PROJECT.name },
27332
- translator: promptTranslator(deps.translations ?? {}, settings.language)
27418
+ translator: promptTranslator(deps.translations ?? {}, settings.language),
27419
+ options: settings.options
27333
27420
  };
27334
27421
  }
27335
27422
  async function runTool(spec, args, ctx, pin) {
@@ -27363,12 +27450,12 @@ ${lines.join("\n")}` : "";
27363
27450
  function workingDir(context) {
27364
27451
  return context.worktree && context.worktree !== "/" ? context.worktree : context.directory;
27365
27452
  }
27366
- function v1Tool(spec, ownDir, directories, t) {
27453
+ function v1Tool(spec, ownDir, directories, t, options) {
27367
27454
  return {
27368
27455
  description: `${spec.description}${describeArguments(spec)}`,
27369
27456
  args: spec.input.shape,
27370
27457
  async execute(args, context) {
27371
- const ctx = { cwd: workingDir(context), ownDir, ...directories, signal: context.abort };
27458
+ const ctx = { cwd: workingDir(context), ownDir, ...directories, options, signal: context.abort };
27372
27459
  let pin;
27373
27460
  if (needsPermission(spec)) {
27374
27461
  const approval = await approvalOf(spec, args, ctx, DESCRIBE_TIMEOUT_MS, t);
@@ -27388,16 +27475,16 @@ function v1Tool(spec, ownDir, directories, t) {
27388
27475
  }
27389
27476
  function createV1Server(specs, deps = {}) {
27390
27477
  return async function server(_input, options) {
27391
- const { tools, directories, translator: translator2 } = selectedTools(specs, options, deps);
27478
+ const { tools, directories, translator: translator2, options: toolOptions } = selectedTools(specs, options, deps);
27392
27479
  const tool = {};
27393
- for (const spec of tools) tool[spec.id] = v1Tool(spec, deps.ownDir, directories, translator2);
27480
+ for (const spec of tools) tool[spec.id] = v1Tool(spec, deps.ownDir, directories, translator2, toolOptions);
27394
27481
  return { tool };
27395
27482
  };
27396
27483
  }
27397
27484
 
27398
27485
  // src/opencode/skills.ts
27399
27486
  import { readFile as readFile2, readdir as readdir3 } from "node:fs/promises";
27400
- import { join as join12, resolve as resolve7 } from "node:path";
27487
+ import { join as join12, resolve as resolve5 } from "node:path";
27401
27488
  import { fileURLToPath as fileURLToPath2 } from "node:url";
27402
27489
  var SKILL_FILE = "SKILL.md";
27403
27490
  var FRONTMATTER = /^---[ \t]*\r?\n([\s\S]*?\r?\n)?---[ \t]*(?:\r?\n|$)/;
@@ -27488,7 +27575,7 @@ async function readSkillFile(path) {
27488
27575
  }
27489
27576
  }
27490
27577
  async function readSkills(dir) {
27491
- const root = resolve7(typeof dir === "string" ? dir : fileURLToPath2(dir));
27578
+ const root = resolve5(typeof dir === "string" ? dir : fileURLToPath2(dir));
27492
27579
  let entries;
27493
27580
  try {
27494
27581
  entries = (await readdir3(root, { withFileTypes: true })).filter((entry2) => !entry2.isFile()).map((entry2) => entry2.name);
@@ -27518,7 +27605,7 @@ async function sessionDirectory(ctx, sessionID) {
27518
27605
  return void 0;
27519
27606
  }
27520
27607
  }
27521
- function v2Tool(ctx, spec, ownDir, directories, t) {
27608
+ function v2Tool(ctx, spec, ownDir, directories, t, options) {
27522
27609
  return {
27523
27610
  name: spec.id,
27524
27611
  description: spec.description,
@@ -27526,7 +27613,7 @@ function v2Tool(ctx, spec, ownDir, directories, t) {
27526
27613
  output: spec.output,
27527
27614
  async execute(input12, { sessionID, signal }) {
27528
27615
  const directory = await sessionDirectory(ctx, sessionID);
27529
- const toolContext = { cwd: directory ?? process.cwd(), ownDir, ...directories, signal };
27616
+ const toolContext = { cwd: directory ?? process.cwd(), ownDir, ...directories, options, signal };
27530
27617
  let pin;
27531
27618
  if (needsPermission(spec)) {
27532
27619
  if (directory === void 0) {
@@ -27554,7 +27641,7 @@ function createV2Setup(specs, deps = {}) {
27554
27641
  return async function setup(ctx) {
27555
27642
  const selection = selectedTools(specs, ctx.options, deps);
27556
27643
  const tools = selection.tools.map(
27557
- (spec) => v2Tool(ctx, spec, deps.ownDir, selection.directories, selection.translator)
27644
+ (spec) => v2Tool(ctx, spec, deps.ownDir, selection.directories, selection.translator, selection.options)
27558
27645
  );
27559
27646
  const skills = deps.skillsDir === void 0 ? [] : await loadSkills(deps.skillsDir, new Set(selection.tools.map((spec) => spec.toolset)));
27560
27647
  if (tools.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craftsamo/whatsapp-tools",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "WhatsApp tools for coding agents: chats, contacts, history, received files, sends and actions.",
5
5
  "author": "craftsamo",
6
6
  "license": "LicenseRef-AGPL-3.0-Commons-Clause",
package/plugin.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: whatsapp-tools
2
- version: 0.1.0
2
+ version: 0.1.2
3
3
  description: "WhatsApp tools for coding agents: chats, contacts, history, received files, sends and actions."
4
4
  manifest_version: 1
5
5
  requires_hermes: ">=0.22"
@@ -155,7 +155,6 @@ def resolve_target(
155
155
  runtime: Runtime,
156
156
  tool_id: str,
157
157
  args: Any,
158
- flags: list[str] | None = None,
159
158
  language: str | None = None,
160
159
  harness_language: Callable[[], str | None] = lambda: display_language(),
161
160
  option_flags: list[str] | None = None,
@@ -172,7 +171,6 @@ def resolve_target(
172
171
  tool_id,
173
172
  "--cwd",
174
173
  agent_cwd(),
175
- *(flags or []),
176
174
  *language_flags(language, _known(harness_language)),
177
175
  *(option_flags or []),
178
176
  ],
@@ -223,7 +221,6 @@ def _message(tool: ToolInfo, target: Target, surface: Callable[[], str | None])
223
221
  def make_hook(
224
222
  runtime: Runtime,
225
223
  tools: list[ToolInfo],
226
- flags: list[str] | None = None,
227
224
  surface: Callable[[], str | None] = lambda: None,
228
225
  language: str | None = None,
229
226
  harness_language: Callable[[], str | None] = lambda: display_language(),
@@ -244,7 +241,7 @@ def make_hook(
244
241
  tool = by_id.get(tool_name)
245
242
  if tool is None or tool.write_class == READ_CLASS:
246
243
  return None
247
- target = resolve_target(runtime, tool.id, args, flags, language, harness_language, option_flags)
244
+ target = resolve_target(runtime, tool.id, args, language, harness_language, option_flags)
248
245
  call = pin_key(kwargs)
249
246
  if pins is not None and call is not None:
250
247
  # An entry left by a call that never reached the pin hook must not stand for this one.
@@ -305,7 +302,6 @@ def register_approval(
305
302
  ctx: PluginContext,
306
303
  runtime: Runtime,
307
304
  tools: list[ToolInfo],
308
- flags: list[str] | None = None,
309
305
  language: str | None = None,
310
306
  option_flags: list[str] | None = None,
311
307
  ) -> None:
@@ -320,7 +316,6 @@ def register_approval(
320
316
  make_hook(
321
317
  runtime,
322
318
  tools,
323
- flags,
324
319
  surface=lambda: session_surface(ctx),
325
320
  language=language,
326
321
  pins=pins,
@@ -9,7 +9,7 @@ from .context import PluginContext
9
9
  from .listing import list_tools
10
10
  from . import project
11
11
  from .runtime import Unavailable, locate_runtime
12
- from .settings import SettingsError, directory_flags, emoji_setting, language_setting, option_flags, settings_flags
12
+ from .settings import SettingsError, emoji_setting, language_setting, option_flags, settings_flags
13
13
  from .skills import register_skills
14
14
  from .tools import register_tools
15
15
 
@@ -34,5 +34,5 @@ def register(ctx: PluginContext) -> None:
34
34
  return
35
35
 
36
36
  register_tools(ctx, runtime, tools, flags, emoji_setting(ctx))
37
- register_approval(ctx, runtime, tools, directory_flags(ctx), language_setting(ctx), option_flags(ctx))
37
+ register_approval(ctx, runtime, tools, language_setting(ctx), option_flags(ctx))
38
38
  register_skills(ctx, runtime.skills_dir, {tool.toolset for tool in tools})
@@ -22,10 +22,10 @@ TOOLSET_EMOJI: dict[str, str] = {
22
22
  "actions": "🔧",
23
23
  }
24
24
 
25
- #: The settings the tools themselves read, beside the ones every plugin takes: each snake_case name and its type
26
- #: (``text``, ``boolean``, ``number`` or ``list``), as in ``src/project.ts``. A setting ``a_b`` becomes the flag
27
- #: ``--a-b`` of the command line.
28
- OPTIONS: dict[str, str] = {}
25
+ #: The settings the tools themselves read: each snake_case name and a type (``text``, ``boolean``, ``number``,
26
+ #: ``list``, ``path`` or ``paths``) or a dict with ``type`` and optional bounds (``integer``, ``min``, ``max``),
27
+ #: as in ``src/project.ts``. Write OPTIONS on one line with double quotes. A setting ``a_b`` becomes ``--a-b``.
28
+ OPTIONS: dict[str, str | dict[str, object]] = {"state_dir": "path", "download_dir": "path", "send_dirs": "paths"}
29
29
 
30
30
  #: How long one call of a tool may take, in seconds, for a tool that may take longer than the default.
31
31
  #: ``whatsapp_history_backfill`` waits up to 300 seconds for the phone, so it gets a margin above that;