@craftsamo/whatsapp-tools 0.1.0 → 0.1.1
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/.antigravity-plugin/plugin.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -1
- package/README.md +58 -12
- package/dist/cli.js +212 -146
- package/dist/mcp.js +206 -107
- package/dist/opencode.js +192 -105
- package/package.json +1 -1
- package/plugin.yaml +1 -1
- package/src/hermes/whatsapp_tools_hermes/approval.py +1 -6
- package/src/hermes/whatsapp_tools_hermes/plugin.py +2 -2
- package/src/hermes/whatsapp_tools_hermes/project.py +4 -4
- package/src/hermes/whatsapp_tools_hermes/settings.py +15 -69
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,
|
|
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,
|
|
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,
|
|
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 ?? (
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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(() =>
|
|
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((
|
|
27043
|
+
const timedOut = new Promise((resolve6) => {
|
|
27038
27044
|
timer = setTimeout(() => {
|
|
27039
|
-
|
|
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,
|
|
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
|
|
27127
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27166
|
-
|
|
27167
|
-
|
|
27168
|
-
|
|
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}_${
|
|
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
|
-
|
|
27218
|
-
"
|
|
27219
|
-
|
|
27220
|
-
|
|
27221
|
-
"
|
|
27222
|
-
|
|
27223
|
-
|
|
27224
|
-
"
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
27284
|
-
const
|
|
27285
|
-
|
|
27286
|
-
|
|
27287
|
-
|
|
27288
|
-
|
|
27289
|
-
|
|
27290
|
-
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
};
|
|
27295
|
-
const toolsets =
|
|
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 =
|
|
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 =
|
|
27389
|
+
const tools = resolve6("tools", listOption("tools"), ({ text: text2 }) => splitList(text2));
|
|
27304
27390
|
if (tools !== void 0) settings.tools = tools;
|
|
27305
|
-
const excluded =
|
|
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
|
|
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 =
|
|
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.
|
|
3
|
+
"version": "0.1.1",
|
|
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
|
@@ -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,
|
|
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,
|
|
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,
|
|
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
|
|
26
|
-
#:
|
|
27
|
-
#:
|
|
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;
|