@agentcomms/whatsapp 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +2 -2
- package/dist/index.d.mts +12 -1
- package/dist/index.mjs +2 -2
- package/dist/{server-DeV9ut_P.mjs → server-YS_r6FYq.mjs} +1 -1
- package/dist/{stdio-entry-DeaLN63P.mjs → stdio-entry-c95dpKwO.mjs} +1 -1
- package/dist/{sync-q1U9jYX3.mjs → sync-Q9xVuh21.mjs} +325 -124
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as exemptFromUpdateGate, A as requireAccount, B as PATH_OPTIONS, Ct as writeResult, Et as VERSION, F as expandSource, G as approveAndWaitSentence, H as absoluteSearchPath, J as childEnvironment, K as approveChangeAtTerminal, L as CHANNEL_SERVERS, M as storeIdentity, N as checkStorePath, O as PLATFORM, P as defaultStorePath, Q as escapeForDisplay, R as CommsError, S as chatRefOf, St as withFileLock, U as agentMarker, W as approvalsOf, X as colorEnabled, Y as cliHandoffs, Z as commandPathOf, _ as WhatsAppContext, a as CHAT_KINDS, at as lookupName, b as requireSupportedNode, c as searchMessages, ct as openCore, dt as renderInstall, et as gatedChangeAtTerminal, f as draftMessage, ft as renderPrune, gt as serverPruneChange, ht as serverInstallChange, i as probeStore, it as isCommsError, j as requireNamedConfig, k as newWhatsAppAccount, l as Presenter, lt as paint, m as listKey, mt as runCommand, nt as handoffText, o as listChats, ot as nameAvailable, p as Visibility, pt as resolvePaths, q as canPrompt, r as whatsappStatus, rt as installExitStatus, s as readChat, st as nameShapeProblem, t as syncAccount, tt as handoffSentence, u as innerText, ut as pathOverridesFromCliOptions, v as WhatsAppIndex, vt as truncateDisplay, wt as CALLER, xt as windowsSystemProgram, yt as updateGateAtTerminal, z as EXIT_CODES } from "./sync-
|
|
2
|
+
import { $ as exemptFromUpdateGate, A as requireAccount, B as PATH_OPTIONS, Ct as writeResult, Et as VERSION, F as expandSource, G as approveAndWaitSentence, H as absoluteSearchPath, J as childEnvironment, K as approveChangeAtTerminal, L as CHANNEL_SERVERS, M as storeIdentity, N as checkStorePath, O as PLATFORM, P as defaultStorePath, Q as escapeForDisplay, R as CommsError, S as chatRefOf, St as withFileLock, U as agentMarker, W as approvalsOf, X as colorEnabled, Y as cliHandoffs, Z as commandPathOf, _ as WhatsAppContext, a as CHAT_KINDS, at as lookupName, b as requireSupportedNode, c as searchMessages, ct as openCore, dt as renderInstall, et as gatedChangeAtTerminal, f as draftMessage, ft as renderPrune, gt as serverPruneChange, ht as serverInstallChange, i as probeStore, it as isCommsError, j as requireNamedConfig, k as newWhatsAppAccount, l as Presenter, lt as paint, m as listKey, mt as runCommand, nt as handoffText, o as listChats, ot as nameAvailable, p as Visibility, pt as resolvePaths, q as canPrompt, r as whatsappStatus, rt as installExitStatus, s as readChat, st as nameShapeProblem, t as syncAccount, tt as handoffSentence, u as innerText, ut as pathOverridesFromCliOptions, v as WhatsAppIndex, vt as truncateDisplay, wt as CALLER, xt as windowsSystemProgram, yt as updateGateAtTerminal, z as EXIT_CODES } from "./sync-Q9xVuh21.mjs";
|
|
3
3
|
import { rm } from "node:fs/promises";
|
|
4
4
|
import path, { delimiter, join } from "node:path";
|
|
5
5
|
import { stripVTControlCharacters } from "node:util";
|
|
@@ -3582,7 +3582,7 @@ a macOS dialog may be waiting) · 77 permission needed (macOS privacy) · 78 con
|
|
|
3582
3582
|
ran = true;
|
|
3583
3583
|
exitCode = await runCommand(output(), async () => {
|
|
3584
3584
|
requireSupportedNode();
|
|
3585
|
-
await (deps.startMcp ?? (await import("./stdio-entry-
|
|
3585
|
+
await (deps.startMcp ?? (await import("./stdio-entry-c95dpKwO.mjs")).startWhatsAppStdioServer)({
|
|
3586
3586
|
env,
|
|
3587
3587
|
platform,
|
|
3588
3588
|
...deps,
|
package/dist/index.d.mts
CHANGED
|
@@ -4277,7 +4277,8 @@ declare namespace external_d_exports {
|
|
|
4277
4277
|
export { $brand, $input, $output, BRAND, CompileOptions, GlobalMeta, INVALID, Infer$1 as Infer, IssueData, JSONType, NEVER, $RefinementCtx as RefinementCtx, SafeExtendShape, Schema, TimePrecision, TypeOf, ZodAny, ZodArray, ZodBase64, ZodBase64URL, ZodBigInt, ZodBigIntFormat, ZodBoolean, ZodCIDRv4, ZodCIDRv6, ZodCUID, ZodCUID2, ZodCatch, ZodCodec, ZodCoercedBigInt, ZodCoercedBoolean, ZodCoercedDate, ZodCoercedNumber, ZodCoercedString, ZodCompileAsyncError, ZodCompileUnsupportedError, ZodCreditCard, ZodCustom, ZodCustomStringFormat, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodE164, ZodEmail, ZodEmoji, ZodEnum, ZodError, ZodErrorMap, ZodExactOptional, ZodFile, ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind, ZodFlattenedError, ZodFloat32, ZodFloat64, ZodFormattedError, ZodFunction, ZodGUID, ZodIBAN, ZodIPv4, ZodIPv6, ZodISODate, ZodISODateTime, ZodISODuration, ZodISOTime, ZodInstanceOf, ZodInt, ZodInt32, ZodInt64, ZodIntersection, ZodIssue, ZodIssueCode, ZodJSONSchema, ZodJSONSchemaInternals, ZodJWT, ZodKSUID, ZodLazy, ZodLiteral, ZodMAC, ZodMap, ZodNaN, ZodNanoID, ZodNever, ZodNonOptional, ZodNull, ZodNullable, ZodNumber, ZodNumberFormat, ZodObject, ZodOptional, ZodPipe, ZodPrefault, ZodPreprocess, ZodPromise, ZodRawShape$1 as ZodRawShape, ZodReadonly, ZodRealError, ZodRecord, ZodSafeParseError, ZodSafeParseResult, ZodSafeParseSuccess, ZodSchema, ZodSet, ZodStandardSchemaWithJSON, ZodString, ZodStringFormat, ZodSuccess, ZodSymbol, ZodTemplateLiteral, ZodTransform, ZodTuple, ZodType, ZodTypeAny, ZodUInt32, ZodUInt64, ZodULID, ZodURL, ZodUUID, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, ZodXID, ZodXor, _ZodBigInt, _ZodBoolean, _ZodDate, _ZodNumber, _ZodString, _ZodType, _default, _function, any, array, base64, base64url, bigint$1 as bigint, boolean$1 as boolean, _catch as catch, check, cidrv4, cidrv6, clone, codec, coerce_d_exports as coerce, compile, config, index_d_exports$1 as core, creditCard, cuid, cuid2, currencyCode, custom, date$2 as date, decode, decodeAsync, deepPartial, describe, discriminatedUnion, e164, email, emoji, encode, encodeAsync, _endsWith as endsWith, _enum as enum, exactOptional, file, flattenError, float32, float64, formatError, fromJSONSchema, _function as function, getDiscriminatedOption, getErrorMap, globalRegistry, _gt as gt, _gte as gte, guid, hash, hex, hostname, httpUrl, iban, _includes as includes, output$1 as infer, inferFlattenedErrors, inferFormattedError, input, _instanceof as instanceof, int, int32, int64, intersection, invertCodec, ipv4, ipv6, iso_d_exports as iso, json, jwt, keyof, ksuid, lazy, _length as length, literal, index_d_exports$2 as locales, looseObject, looseRecord, _lowercase as lowercase, _lt as lt, _lte as lte, mac, map, _maxLength as maxLength, _maxSize as maxSize, memoizer, meta, _mime as mime, _minLength as minLength, _minSize as minSize, _multipleOf as multipleOf, nan, nanoid, nativeEnum, _negative as negative, never, _nonnegative as nonnegative, nonoptional, _nonpositive as nonpositive, _normalize as normalize, _null as null, nullable, nullish, number$1 as number, object, optional, output, _overwrite as overwrite, parse, parseAsync, partialRecord, pipe, _positive as positive, prefault, preprocess, prettifyError, promise, _properties as properties, _property as property, readonly, record, refine, _regex as regex, regexes_d_exports as regexes, registry, safeDecode, safeDecodeAsync, safeEncode, safeEncodeAsync, safeParse, safeParseAsync, set, setErrorMap, _size as size, _slugify as slugify, _startsWith as startsWith, strictObject, string$1 as string, stringFormat, stringbool, success, superRefine, symbol, templateLiteral, toJSONSchema, _toLowerCase as toLowerCase, _toUpperCase as toUpperCase, toZod, transform, treeifyError, _trim as trim, tuple, uint32, uint64, ulid, _undefined as undefined, union, unknown, _uppercase as uppercase, url, util_d_exports as util, uuid, uuidv4, uuidv6, uuidv7, validate, validateAsync, _void as void, withParser, xid, xor };
|
|
4278
4278
|
}
|
|
4279
4279
|
//#endregion
|
|
4280
|
-
//#region ../core/dist/change-flow-
|
|
4280
|
+
//#region ../core/dist/change-flow-DOyBTYin.d.mts
|
|
4281
|
+
//#endregion
|
|
4281
4282
|
//#region src/config-hooks.d.ts
|
|
4282
4283
|
/**
|
|
4283
4284
|
* Test-only pauses inside `ConfigStore.convertToVersion3`, for the tests that prove a write racing the conversion is
|
|
@@ -4735,6 +4736,16 @@ interface ConfigBody {
|
|
|
4735
4736
|
/** Superseded rotating credentials which still need conclusive per-token cleanup. */
|
|
4736
4737
|
pendingRevocations?: PendingRevocation[] | undefined;
|
|
4737
4738
|
defaults: Defaults;
|
|
4739
|
+
/**
|
|
4740
|
+
* How what an agent sends is labelled (design 2026-10-15-agent-label, L5): the word, the name, the rules that say
|
|
4741
|
+
* who a message appears to come from, and each Slack account's agent emoji.
|
|
4742
|
+
*
|
|
4743
|
+
* `unknown` here, on purpose: it is read only through `agentLabelOf`, which reads a value it does not understand
|
|
4744
|
+
* as the stricter one — an unknown word as `agent`, a rule list it cannot read as no rules — so a hand edit or a
|
|
4745
|
+
* later release's key can only tighten. Kept through every write as the schemas keep every key they do not name, and
|
|
4746
|
+
* judged by `classifyChange`.
|
|
4747
|
+
*/
|
|
4748
|
+
agentLabel?: unknown;
|
|
4738
4749
|
}
|
|
4739
4750
|
interface ConfigV1 extends ConfigBody {
|
|
4740
4751
|
version: 1;
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as chatKindOf, D as text, E as numeric, Et as VERSION, R as CommsError, T as kindOf, Tt as PACKAGE_NAME, _ as WhatsAppContext, d as composeDraft, g as visibilityUnitKey, h as statusAuthor, m as listKey, n as withCheckedCopy, p as Visibility, w as coreDataToIso, x as inspectSchema, y as rebuildIndex } from "./sync-
|
|
2
|
-
import { t as createWhatsAppMcpServer } from "./server-
|
|
1
|
+
import { C as chatKindOf, D as text, E as numeric, Et as VERSION, R as CommsError, T as kindOf, Tt as PACKAGE_NAME, _ as WhatsAppContext, d as composeDraft, g as visibilityUnitKey, h as statusAuthor, m as listKey, n as withCheckedCopy, p as Visibility, w as coreDataToIso, x as inspectSchema, y as rebuildIndex } from "./sync-Q9xVuh21.mjs";
|
|
2
|
+
import { t as createWhatsAppMcpServer } from "./server-YS_r6FYq.mjs";
|
|
3
3
|
//#region src/source/event-reader.ts
|
|
4
4
|
function rawFromMe(value) {
|
|
5
5
|
const number = numeric(value);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Et as VERSION, I as strictToolArguments, V as UNTRUSTED_NOTICE, _ as WhatsAppContext, _t as toCommsError, a as CHAT_KINDS, bt as updateToolGate, c as searchMessages, f as draftMessage, o as listChats, r as whatsappStatus, s as readChat, t as syncAccount } from "./sync-
|
|
1
|
+
import { Et as VERSION, I as strictToolArguments, V as UNTRUSTED_NOTICE, _ as WhatsAppContext, _t as toCommsError, a as CHAT_KINDS, bt as updateToolGate, c as searchMessages, f as draftMessage, o as listChats, r as whatsappStatus, s as readChat, t as syncAccount } from "./sync-Q9xVuh21.mjs";
|
|
2
2
|
import { c as boolean, h as number, x as string } from "./iso-USuVU_sk.mjs";
|
|
3
3
|
import { t as McpServer } from "./mcp-DIH4cS6P-CIWWZQbz.mjs";
|
|
4
4
|
//#region src/mcp/server.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as createWhatsAppMcpServer } from "./server-
|
|
1
|
+
import { t as createWhatsAppMcpServer } from "./server-YS_r6FYq.mjs";
|
|
2
2
|
//#region src/mcp/stdio-entry.ts
|
|
3
3
|
/**
|
|
4
4
|
* Starting the stdio server. On stdio, stdout is the protocol, so the console is redirected to stderr first — the
|
|
@@ -11,7 +11,7 @@ import { execFile, spawn } from "node:child_process";
|
|
|
11
11
|
//#endregion
|
|
12
12
|
//#region src/version.ts
|
|
13
13
|
/** The package version, read from package.json at build time. */
|
|
14
|
-
const VERSION$1 = "0.
|
|
14
|
+
const VERSION$1 = "0.19.0";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/caller.ts
|
|
17
17
|
/** This package's name, as its manifest has it. */
|
|
@@ -28,7 +28,7 @@ const CALLER = Object.freeze({
|
|
|
28
28
|
packageName: PACKAGE_NAME
|
|
29
29
|
});
|
|
30
30
|
//#endregion
|
|
31
|
-
//#region ../core/dist/update-check-
|
|
31
|
+
//#region ../core/dist/update-check-CVvpp5G8.mjs
|
|
32
32
|
/**
|
|
33
33
|
* Every failure a user or an agent can act on carries a specific, stable code from this registry, a message, an
|
|
34
34
|
* optional one-line hint and structured `details`. The code decides the process exit status; agents and skills branch
|
|
@@ -903,6 +903,175 @@ var TaintStore = class {
|
|
|
903
903
|
return sortedOrigins([...originEntry(sidecar), ...sidecar !== void 0 && isNewer(entry.at, sidecar.at) ? ["read"] : []]);
|
|
904
904
|
}
|
|
905
905
|
};
|
|
906
|
+
/**
|
|
907
|
+
* One table of dangerous characters, shared by the inbound sanitiser (which strips them from sender-controlled text)
|
|
908
|
+
* and the preview renderer (which makes them visible), so the two can never disagree.
|
|
909
|
+
*/
|
|
910
|
+
/** Zero-width and formatting characters, bidi controls, variation selectors and Unicode tag characters. */
|
|
911
|
+
const INVISIBLE_RANGES = [
|
|
912
|
+
[173, 173],
|
|
913
|
+
[847, 847],
|
|
914
|
+
[1564, 1564],
|
|
915
|
+
[4447, 4448],
|
|
916
|
+
[6068, 6069],
|
|
917
|
+
[6155, 6159],
|
|
918
|
+
[8203, 8207],
|
|
919
|
+
[8232, 8233],
|
|
920
|
+
[8234, 8238],
|
|
921
|
+
[8288, 8292],
|
|
922
|
+
[8294, 8303],
|
|
923
|
+
[12644, 12644],
|
|
924
|
+
[65024, 65039],
|
|
925
|
+
[65279, 65279],
|
|
926
|
+
[65440, 65440],
|
|
927
|
+
[917504, 917631]
|
|
928
|
+
];
|
|
929
|
+
function isInvisible(codePoint) {
|
|
930
|
+
for (const [from, to] of INVISIBLE_RANGES) if (codePoint >= from && codePoint <= to) return true;
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* C0 controls except tab and newline (ESC, and so every ANSI/OSC sequence, starts here), DEL, and C1 controls (CSI
|
|
935
|
+
* among them). A lone carriage return counts: it moves a terminal cursor back over text already printed.
|
|
936
|
+
*/
|
|
937
|
+
function isControl(codePoint) {
|
|
938
|
+
if (codePoint === 9 || codePoint === 10) return false;
|
|
939
|
+
return codePoint < 32 || codePoint === 127 || codePoint >= 128 && codePoint <= 159;
|
|
940
|
+
}
|
|
941
|
+
function isDangerous(codePoint) {
|
|
942
|
+
return isControl(codePoint) || isInvisible(codePoint);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Strips control characters (ESC, CSI, OSC and the rest), DEL, lone carriage returns, zero-width, bidi-control and tag
|
|
946
|
+
* characters from sender-controlled text; returns the text and how many were removed. CRLF becomes LF first.
|
|
947
|
+
*
|
|
948
|
+
* This lives beside the table rather than in the sanitiser because `neutralise` needs it too, and the two must not
|
|
949
|
+
* drift: a pattern that looks for `</untrusted-content` cannot see it through a zero-width space, so stripping
|
|
950
|
+
* has to happen before any such pattern runs, on every path, not only on the ones that render a body.
|
|
951
|
+
*/
|
|
952
|
+
function stripInvisible(text) {
|
|
953
|
+
let removed = 0;
|
|
954
|
+
let out = "";
|
|
955
|
+
for (const char of text.replace(/\r\n/g, "\n")) if (isDangerous(char.codePointAt(0) ?? 0)) removed += 1;
|
|
956
|
+
else out += char;
|
|
957
|
+
return {
|
|
958
|
+
text: out,
|
|
959
|
+
removed
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Labelling what an agent sends (design 2026-10-15-agent-label): the phrase `<name> (<word>)`, who a message appears to
|
|
964
|
+
* come from, and which setting changes loosen that. Pure: nothing here reads the configuration or talks to a channel,
|
|
965
|
+
* so every channel decides the same way from the same settings.
|
|
966
|
+
*/
|
|
967
|
+
/** L2: the words a label may use, exactly as written. Adding one is a code change with its own review. */
|
|
968
|
+
const AGENT_LABEL_WORDS = Object.freeze([
|
|
969
|
+
"agent",
|
|
970
|
+
"assistant",
|
|
971
|
+
"twin",
|
|
972
|
+
"minion",
|
|
973
|
+
"bot",
|
|
974
|
+
"fake",
|
|
975
|
+
"AI"
|
|
976
|
+
]);
|
|
977
|
+
/** L4: the kinds of act a rule may name. Slack posts, replies, reacts, edits and shares files; mail sends and replies. */
|
|
978
|
+
const ACT_KINDS = Object.freeze([
|
|
979
|
+
"post",
|
|
980
|
+
"reply",
|
|
981
|
+
"reaction",
|
|
982
|
+
"edit",
|
|
983
|
+
"file",
|
|
984
|
+
"send"
|
|
985
|
+
]);
|
|
986
|
+
/** L2's characters a name may not hold: parentheses would fake a second label, the rest break an address header. */
|
|
987
|
+
const FORBIDDEN_NAME_CHARACTERS = /[()<>"@,;:\\]/u;
|
|
988
|
+
function isAgentLabelWord(value) {
|
|
989
|
+
return typeof value === "string" && AGENT_LABEL_WORDS.includes(value);
|
|
990
|
+
}
|
|
991
|
+
/** Why a name cannot be labelled, or undefined when it can. */
|
|
992
|
+
function nameProblem(name) {
|
|
993
|
+
if (name.length === 0) return "the name is empty";
|
|
994
|
+
if (name.trim() !== name) return "the name starts or ends with a space";
|
|
995
|
+
if (FORBIDDEN_NAME_CHARACTERS.test(name)) return "the name holds a parenthesis or one of <>\"@,;:\\";
|
|
996
|
+
for (const char of name) if (isDangerous(char.codePointAt(0) ?? 0)) return "the name holds a hidden or control character";
|
|
997
|
+
}
|
|
998
|
+
const ruleKey = (rule) => JSON.stringify([
|
|
999
|
+
rule.account,
|
|
1000
|
+
rule.accountId ?? null,
|
|
1001
|
+
rule.conversation ?? null,
|
|
1002
|
+
rule.kinds === void 0 ? null : [...rule.kinds].sort(),
|
|
1003
|
+
rule.as
|
|
1004
|
+
]);
|
|
1005
|
+
/**
|
|
1006
|
+
* L5: the changes that need the person's confirmation — anything that could leave a message unlabelled or change how
|
|
1007
|
+
* the label looks. Adding an `agent` rule or removing a `me` rule tightens, and is not listed.
|
|
1008
|
+
*/
|
|
1009
|
+
function classifyAgentLabelChange(before, after) {
|
|
1010
|
+
const changes = [];
|
|
1011
|
+
const wordBefore = before?.word ?? "agent";
|
|
1012
|
+
const wordAfter = after?.word ?? "agent";
|
|
1013
|
+
if (wordBefore !== wordAfter) changes.push({
|
|
1014
|
+
path: "agentLabel.word",
|
|
1015
|
+
was: wordBefore,
|
|
1016
|
+
now: wordAfter
|
|
1017
|
+
});
|
|
1018
|
+
if ((before?.name ?? null) !== (after?.name ?? null)) changes.push({
|
|
1019
|
+
path: "agentLabel.name",
|
|
1020
|
+
was: before?.name,
|
|
1021
|
+
now: after?.name
|
|
1022
|
+
});
|
|
1023
|
+
const rulesBefore = new Map((before?.rules ?? []).map((rule) => [ruleKey(rule), rule]));
|
|
1024
|
+
const rulesAfter = new Map((after?.rules ?? []).map((rule) => [ruleKey(rule), rule]));
|
|
1025
|
+
const meAdded = [...rulesAfter].some(([key, rule]) => rule.as === "me" && !rulesBefore.has(key));
|
|
1026
|
+
const agentRemoved = [...rulesBefore].some(([key, rule]) => rule.as === "agent" && !rulesAfter.has(key));
|
|
1027
|
+
if (meAdded || agentRemoved) changes.push({
|
|
1028
|
+
path: "agentLabel.rules",
|
|
1029
|
+
was: before?.rules ?? [],
|
|
1030
|
+
now: after?.rules ?? []
|
|
1031
|
+
});
|
|
1032
|
+
if (JSON.stringify(sorted(before?.slackIcons)) !== JSON.stringify(sorted(after?.slackIcons))) changes.push({
|
|
1033
|
+
path: "agentLabel.slackIcons",
|
|
1034
|
+
was: before?.slackIcons ?? {},
|
|
1035
|
+
now: after?.slackIcons ?? {}
|
|
1036
|
+
});
|
|
1037
|
+
return changes;
|
|
1038
|
+
}
|
|
1039
|
+
function sorted(record) {
|
|
1040
|
+
return Object.entries(record ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
1041
|
+
}
|
|
1042
|
+
const EMOJI_NAME = /^:[a-z0-9_+'-]{1,100}:$/u;
|
|
1043
|
+
function isRule(value) {
|
|
1044
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1045
|
+
const rule = value;
|
|
1046
|
+
if (typeof rule.account !== "string" || rule.account.length === 0) return false;
|
|
1047
|
+
if (rule.as !== "agent" && rule.as !== "me") return false;
|
|
1048
|
+
if (rule.accountId !== void 0 && (typeof rule.accountId !== "string" || rule.accountId.length === 0)) return false;
|
|
1049
|
+
if (rule.conversation !== void 0 && (typeof rule.conversation !== "string" || rule.conversation.length === 0)) return false;
|
|
1050
|
+
if (rule.kinds !== void 0 && !(Array.isArray(rule.kinds) && rule.kinds.length > 0 && rule.kinds.every((kind) => ACT_KINDS.includes(kind)))) return false;
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* The stored `agentLabel`, read so that a bad value can only tighten. An unknown word reads as `agent`, an invalid
|
|
1055
|
+
* name as no name, and a rule list with any invalid rule is ignored whole: dropping one `agent` rule could leave a
|
|
1056
|
+
* broader `me` rule in charge. A malformed icon entry is dropped, falling back to the plain photo.
|
|
1057
|
+
*/
|
|
1058
|
+
function readAgentLabelSettings(raw) {
|
|
1059
|
+
if (typeof raw !== "object" || raw === null) return {};
|
|
1060
|
+
const stored = raw;
|
|
1061
|
+
const settings = {};
|
|
1062
|
+
if (isAgentLabelWord(stored.word)) settings.word = stored.word;
|
|
1063
|
+
if (typeof stored.name === "string" && nameProblem(stored.name) === void 0) settings.name = stored.name;
|
|
1064
|
+
if (Array.isArray(stored.rules) && stored.rules.every(isRule)) settings.rules = stored.rules.map((rule) => ({ ...rule }));
|
|
1065
|
+
if (typeof stored.slackIcons === "object" && stored.slackIcons !== null) {
|
|
1066
|
+
const icons = Object.entries(stored.slackIcons).filter((entry) => typeof entry[1] === "string" && EMOJI_NAME.test(entry[1]));
|
|
1067
|
+
if (icons.length > 0) settings.slackIcons = Object.fromEntries(icons);
|
|
1068
|
+
}
|
|
1069
|
+
return settings;
|
|
1070
|
+
}
|
|
1071
|
+
/** The person's label settings from a loaded configuration, read as `readAgentLabelSettings` reads them. */
|
|
1072
|
+
function agentLabelOf(config) {
|
|
1073
|
+
return readAgentLabelSettings(config.agentLabel);
|
|
1074
|
+
}
|
|
906
1075
|
/** Every first-party channel, the core first: what core knows about each without installing it. */
|
|
907
1076
|
const CHANNEL_SNAPSHOT = [
|
|
908
1077
|
{
|
|
@@ -1072,7 +1241,11 @@ const CHANNEL_SNAPSHOT = [
|
|
|
1072
1241
|
word: "slack",
|
|
1073
1242
|
can: "post to Slack"
|
|
1074
1243
|
},
|
|
1075
|
-
hosts: [
|
|
1244
|
+
hosts: [
|
|
1245
|
+
"slack.com",
|
|
1246
|
+
"files.slack.com",
|
|
1247
|
+
"avatars.slack-edge.com"
|
|
1248
|
+
],
|
|
1076
1249
|
approve: "agent-slack approve",
|
|
1077
1250
|
skills: {
|
|
1078
1251
|
prefix: "slack-",
|
|
@@ -2968,6 +3141,99 @@ function commandLinePackage(command, args) {
|
|
|
2968
3141
|
const line = [command, ...withoutPathOptions(args).words].join(" ");
|
|
2969
3142
|
return /(@[\w.-]+\/[\w.-]+|(?<=\s)[\w.-]+-mcp)(?=@|\s|$)/.exec(line)?.[1];
|
|
2970
3143
|
}
|
|
3144
|
+
/**
|
|
3145
|
+
* The programs this package starts, named so that no folder it happens to be in can stand in for them.
|
|
3146
|
+
*
|
|
3147
|
+
* A bare name is looked up, and on Windows the lookup starts in the current folder: libuv tries it before `PATH`, and
|
|
3148
|
+
* so does every program that looks a name up with `SearchPath` or `CreateProcess`. The current folder is where a
|
|
3149
|
+
* command was run or a server was started — and, since a download asks where to save, one of the three places a
|
|
3150
|
+
* stranger's file may be saved into. A `reg.exe` or `rundll32.exe` saved there would then be what runs the next time
|
|
3151
|
+
* this package reads the registry or opens a browser. So a Windows program is named by its full path, under the Windows
|
|
3152
|
+
* folder, and every process started on Windows is told, by `NoDefaultCurrentDirectoryInExePath`, not to look in its
|
|
3153
|
+
* own current folder for the programs it starts in turn.
|
|
3154
|
+
*/
|
|
3155
|
+
/** An environment variable by name, whatever its case: Windows's are case-blind, an object passed in is not. */
|
|
3156
|
+
function envValue(env, name) {
|
|
3157
|
+
const wanted = name.toLowerCase();
|
|
3158
|
+
for (const [key, value] of Object.entries(env)) if (key.toLowerCase() === wanted && value) return value;
|
|
3159
|
+
}
|
|
3160
|
+
/**
|
|
3161
|
+
* The Windows folder: `%SystemRoot%` (or `%windir%`) when it names a folder on a drive, and `C:\Windows` otherwise.
|
|
3162
|
+
* A value that is not a drive's absolute path — relative, a share, a device path — is not taken: it would be looked up
|
|
3163
|
+
* against the current folder, or on another machine, which is what naming the program in full is to avoid.
|
|
3164
|
+
*/
|
|
3165
|
+
function windowsFolder(env = process.env) {
|
|
3166
|
+
for (const name of ["SystemRoot", "windir"]) {
|
|
3167
|
+
const value = envValue(env, name);
|
|
3168
|
+
if (value !== void 0 && /^[A-Za-z]:[\\/]/.test(value)) return path.win32.resolve(value);
|
|
3169
|
+
}
|
|
3170
|
+
return "C:\\Windows";
|
|
3171
|
+
}
|
|
3172
|
+
/** A program of Windows's own by its full path: `%SystemRoot%\System32\<name>`, never a bare name to look up. */
|
|
3173
|
+
function windowsSystemProgram(name, env = process.env) {
|
|
3174
|
+
return path.win32.join(windowsFolder(env), "System32", name);
|
|
3175
|
+
}
|
|
3176
|
+
/**
|
|
3177
|
+
* The environment a child process is started with: `env` as it is, and on Windows also
|
|
3178
|
+
* `NoDefaultCurrentDirectoryInExePath=1`, so that the child — `rundll32.exe`, a Node started again — never takes a
|
|
3179
|
+
* program from its current folder either. Elsewhere nothing is added: a Unix shell looks in the current folder only
|
|
3180
|
+
* when `PATH` says to.
|
|
3181
|
+
*/
|
|
3182
|
+
function childEnvironment(env = process.env, platform = process.platform) {
|
|
3183
|
+
if (platform !== "win32") return env;
|
|
3184
|
+
return {
|
|
3185
|
+
...env,
|
|
3186
|
+
NoDefaultCurrentDirectoryInExePath: "1"
|
|
3187
|
+
};
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* The directories of a search path that name a folder on their own: absolute, and on Windows on a drive. An empty
|
|
3191
|
+
* entry means the current folder to a Unix shell, and a relative one is read against it; either would make the program
|
|
3192
|
+
* found — and run — depend on where this was started, which is where a download may have saved a stranger's file.
|
|
3193
|
+
*/
|
|
3194
|
+
function absoluteSearchPath(entries, platform = process.platform) {
|
|
3195
|
+
return entries.filter((entry) => platform === "win32" ? /^[A-Za-z]:[\\/]/.test(entry) : entry !== "" && path.posix.isAbsolute(entry));
|
|
3196
|
+
}
|
|
3197
|
+
/** This Node's real file, or its path as reported when that cannot be resolved. */
|
|
3198
|
+
function realFile(path) {
|
|
3199
|
+
try {
|
|
3200
|
+
return realpathSync(path);
|
|
3201
|
+
} catch {
|
|
3202
|
+
return path;
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
/** Two real paths are one file: compared without case on Windows, whose paths are. */
|
|
3206
|
+
function sameRealPath(a, b, platform) {
|
|
3207
|
+
return platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
3208
|
+
}
|
|
3209
|
+
/** The names `node` is found by in one folder of PATH, as a shell finds it: with each PATHEXT extension on Windows. */
|
|
3210
|
+
function nodeNames(env, platform) {
|
|
3211
|
+
if (platform !== "win32") return ["node"];
|
|
3212
|
+
return (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean).map((extension) => `node${extension.toLowerCase()}`);
|
|
3213
|
+
}
|
|
3214
|
+
/**
|
|
3215
|
+
* The path a command names this Node by: the first `node` on `env`'s PATH that is the same real file as
|
|
3216
|
+
* `process.execPath`, as PATH spells it; otherwise `process.execPath`. With no `env`, nothing is looked for.
|
|
3217
|
+
*
|
|
3218
|
+
* Read when a command is located, as everything else in it is (D6): a link removed or repointed later is what the
|
|
3219
|
+
* command then meets. Only absolute folders of PATH are read, as the registrations' own search reads them.
|
|
3220
|
+
*/
|
|
3221
|
+
function nodeProgram(env, platform = process.platform) {
|
|
3222
|
+
const own = process.execPath;
|
|
3223
|
+
if (env === void 0) return own;
|
|
3224
|
+
const real = realFile(own);
|
|
3225
|
+
const search = env.PATH ?? env.Path ?? "";
|
|
3226
|
+
for (const directory of absoluteSearchPath(search.split(delimiter), platform)) for (const name of nodeNames(env, platform)) {
|
|
3227
|
+
const candidate = join(directory, name);
|
|
3228
|
+
try {
|
|
3229
|
+
if (!statSync(candidate).isFile()) continue;
|
|
3230
|
+
accessSync(candidate, constants$1.X_OK);
|
|
3231
|
+
if (!sameRealPath(realpathSync(candidate), real, platform)) continue;
|
|
3232
|
+
return sameRealPath(candidate, own, platform) ? own : candidate;
|
|
3233
|
+
} catch {}
|
|
3234
|
+
}
|
|
3235
|
+
return own;
|
|
3236
|
+
}
|
|
2971
3237
|
const RECEIPT_VERSION = 1;
|
|
2972
3238
|
const RECEIPT_FILE = "managed-shims.json";
|
|
2973
3239
|
const MUTATION_LOCK_FILE = "managed-shims.lock";
|
|
@@ -3305,59 +3571,6 @@ async function removeManagedShimsForRuntimes(context, runtimes, options = {}) {
|
|
|
3305
3571
|
};
|
|
3306
3572
|
});
|
|
3307
3573
|
}
|
|
3308
|
-
/**
|
|
3309
|
-
* The programs this package starts, named so that no folder it happens to be in can stand in for them.
|
|
3310
|
-
*
|
|
3311
|
-
* A bare name is looked up, and on Windows the lookup starts in the current folder: libuv tries it before `PATH`, and
|
|
3312
|
-
* so does every program that looks a name up with `SearchPath` or `CreateProcess`. The current folder is where a
|
|
3313
|
-
* command was run or a server was started — and, since a download asks where to save, one of the three places a
|
|
3314
|
-
* stranger's file may be saved into. A `reg.exe` or `rundll32.exe` saved there would then be what runs the next time
|
|
3315
|
-
* this package reads the registry or opens a browser. So a Windows program is named by its full path, under the Windows
|
|
3316
|
-
* folder, and every process started on Windows is told, by `NoDefaultCurrentDirectoryInExePath`, not to look in its
|
|
3317
|
-
* own current folder for the programs it starts in turn.
|
|
3318
|
-
*/
|
|
3319
|
-
/** An environment variable by name, whatever its case: Windows's are case-blind, an object passed in is not. */
|
|
3320
|
-
function envValue(env, name) {
|
|
3321
|
-
const wanted = name.toLowerCase();
|
|
3322
|
-
for (const [key, value] of Object.entries(env)) if (key.toLowerCase() === wanted && value) return value;
|
|
3323
|
-
}
|
|
3324
|
-
/**
|
|
3325
|
-
* The Windows folder: `%SystemRoot%` (or `%windir%`) when it names a folder on a drive, and `C:\Windows` otherwise.
|
|
3326
|
-
* A value that is not a drive's absolute path — relative, a share, a device path — is not taken: it would be looked up
|
|
3327
|
-
* against the current folder, or on another machine, which is what naming the program in full is to avoid.
|
|
3328
|
-
*/
|
|
3329
|
-
function windowsFolder(env = process.env) {
|
|
3330
|
-
for (const name of ["SystemRoot", "windir"]) {
|
|
3331
|
-
const value = envValue(env, name);
|
|
3332
|
-
if (value !== void 0 && /^[A-Za-z]:[\\/]/.test(value)) return path.win32.resolve(value);
|
|
3333
|
-
}
|
|
3334
|
-
return "C:\\Windows";
|
|
3335
|
-
}
|
|
3336
|
-
/** A program of Windows's own by its full path: `%SystemRoot%\System32\<name>`, never a bare name to look up. */
|
|
3337
|
-
function windowsSystemProgram(name, env = process.env) {
|
|
3338
|
-
return path.win32.join(windowsFolder(env), "System32", name);
|
|
3339
|
-
}
|
|
3340
|
-
/**
|
|
3341
|
-
* The environment a child process is started with: `env` as it is, and on Windows also
|
|
3342
|
-
* `NoDefaultCurrentDirectoryInExePath=1`, so that the child — `rundll32.exe`, a Node started again — never takes a
|
|
3343
|
-
* program from its current folder either. Elsewhere nothing is added: a Unix shell looks in the current folder only
|
|
3344
|
-
* when `PATH` says to.
|
|
3345
|
-
*/
|
|
3346
|
-
function childEnvironment(env = process.env, platform = process.platform) {
|
|
3347
|
-
if (platform !== "win32") return env;
|
|
3348
|
-
return {
|
|
3349
|
-
...env,
|
|
3350
|
-
NoDefaultCurrentDirectoryInExePath: "1"
|
|
3351
|
-
};
|
|
3352
|
-
}
|
|
3353
|
-
/**
|
|
3354
|
-
* The directories of a search path that name a folder on their own: absolute, and on Windows on a drive. An empty
|
|
3355
|
-
* entry means the current folder to a Unix shell, and a relative one is read against it; either would make the program
|
|
3356
|
-
* found — and run — depend on where this was started, which is where a download may have saved a stranger's file.
|
|
3357
|
-
*/
|
|
3358
|
-
function absoluteSearchPath(entries, platform = process.platform) {
|
|
3359
|
-
return entries.filter((entry) => platform === "win32" ? /^[A-Za-z]:[\\/]/.test(entry) : entry !== "" && path.posix.isAbsolute(entry));
|
|
3360
|
-
}
|
|
3361
3574
|
/** The install options that pin a server to one account: the generic one, and Gmail's and Slack's own names. */
|
|
3362
3575
|
const PIN_OPTIONS = Object.freeze([
|
|
3363
3576
|
"account",
|
|
@@ -4945,62 +5158,6 @@ function connectMailboxWords() {
|
|
|
4945
5158
|
*/
|
|
4946
5159
|
const CONVERSION_HOOKS = Symbol("agentcomms.configConversionHooks");
|
|
4947
5160
|
/**
|
|
4948
|
-
* One table of dangerous characters, shared by the inbound sanitiser (which strips them from sender-controlled text)
|
|
4949
|
-
* and the preview renderer (which makes them visible), so the two can never disagree.
|
|
4950
|
-
*/
|
|
4951
|
-
/** Zero-width and formatting characters, bidi controls, variation selectors and Unicode tag characters. */
|
|
4952
|
-
const INVISIBLE_RANGES = [
|
|
4953
|
-
[173, 173],
|
|
4954
|
-
[847, 847],
|
|
4955
|
-
[1564, 1564],
|
|
4956
|
-
[4447, 4448],
|
|
4957
|
-
[6068, 6069],
|
|
4958
|
-
[6155, 6159],
|
|
4959
|
-
[8203, 8207],
|
|
4960
|
-
[8232, 8233],
|
|
4961
|
-
[8234, 8238],
|
|
4962
|
-
[8288, 8292],
|
|
4963
|
-
[8294, 8303],
|
|
4964
|
-
[12644, 12644],
|
|
4965
|
-
[65024, 65039],
|
|
4966
|
-
[65279, 65279],
|
|
4967
|
-
[65440, 65440],
|
|
4968
|
-
[917504, 917631]
|
|
4969
|
-
];
|
|
4970
|
-
function isInvisible(codePoint) {
|
|
4971
|
-
for (const [from, to] of INVISIBLE_RANGES) if (codePoint >= from && codePoint <= to) return true;
|
|
4972
|
-
return false;
|
|
4973
|
-
}
|
|
4974
|
-
/**
|
|
4975
|
-
* C0 controls except tab and newline (ESC, and so every ANSI/OSC sequence, starts here), DEL, and C1 controls (CSI
|
|
4976
|
-
* among them). A lone carriage return counts: it moves a terminal cursor back over text already printed.
|
|
4977
|
-
*/
|
|
4978
|
-
function isControl(codePoint) {
|
|
4979
|
-
if (codePoint === 9 || codePoint === 10) return false;
|
|
4980
|
-
return codePoint < 32 || codePoint === 127 || codePoint >= 128 && codePoint <= 159;
|
|
4981
|
-
}
|
|
4982
|
-
function isDangerous(codePoint) {
|
|
4983
|
-
return isControl(codePoint) || isInvisible(codePoint);
|
|
4984
|
-
}
|
|
4985
|
-
/**
|
|
4986
|
-
* Strips control characters (ESC, CSI, OSC and the rest), DEL, lone carriage returns, zero-width, bidi-control and tag
|
|
4987
|
-
* characters from sender-controlled text; returns the text and how many were removed. CRLF becomes LF first.
|
|
4988
|
-
*
|
|
4989
|
-
* This lives beside the table rather than in the sanitiser because `neutralise` needs it too, and the two must not
|
|
4990
|
-
* drift: a pattern that looks for `</untrusted-content` cannot see it through a zero-width space, so stripping
|
|
4991
|
-
* has to happen before any such pattern runs, on every path, not only on the ones that render a body.
|
|
4992
|
-
*/
|
|
4993
|
-
function stripInvisible(text) {
|
|
4994
|
-
let removed = 0;
|
|
4995
|
-
let out = "";
|
|
4996
|
-
for (const char of text.replace(/\r\n/g, "\n")) if (isDangerous(char.codePointAt(0) ?? 0)) removed += 1;
|
|
4997
|
-
else out += char;
|
|
4998
|
-
return {
|
|
4999
|
-
text: out,
|
|
5000
|
-
removed
|
|
5001
|
-
};
|
|
5002
|
-
}
|
|
5003
|
-
/**
|
|
5004
5161
|
* Whether a folder, as written, says where it is on its own: from the home (`~`), or absolute — and on Windows with its
|
|
5005
5162
|
* drive (`C:\…`) or share (`\\server\share\…`). Windows calls `\outgoing` absolute, but it is on whichever drive is
|
|
5006
5163
|
* current when it is read, and `C:outgoing` is relative to that drive's current folder; either would move with the
|
|
@@ -6114,11 +6271,39 @@ function classifyChange(before, after) {
|
|
|
6114
6271
|
if (before.secrets === void 0 && after.secrets !== void 0 && after.secrets.store !== "keychain") {
|
|
6115
6272
|
if (holdsSecrets(before)) loosen("secrets.store", secretsStoreOf(before), after.secrets.store);
|
|
6116
6273
|
}
|
|
6274
|
+
for (const loosening of classifyAgentLabelChange(agentLabelOf(before), agentLabelOf(after))) loosen(loosening.path, labelSettingValue(loosening, loosening.was), labelSettingValue(loosening, loosening.now));
|
|
6117
6275
|
return {
|
|
6118
6276
|
loosened: changes.map((change) => change.path),
|
|
6119
6277
|
changes
|
|
6120
6278
|
};
|
|
6121
6279
|
}
|
|
6280
|
+
/**
|
|
6281
|
+
* One label rule in words, as a preview lists it: `acme/slack in C0ACME for post/reply: me`. No comma inside, since a
|
|
6282
|
+
* preview joins a list of them with commas.
|
|
6283
|
+
*/
|
|
6284
|
+
function agentLabelRuleText(rule) {
|
|
6285
|
+
const where = rule.conversation === void 0 ? "" : ` in ${rule.conversation}`;
|
|
6286
|
+
const kinds = rule.kinds === void 0 ? "" : ` for ${rule.kinds.join("/")}`;
|
|
6287
|
+
return `${rule.account}${where}${kinds}: ${rule.as}`;
|
|
6288
|
+
}
|
|
6289
|
+
/** A label setting's value as a loosening carries it: the rules in words, the icons as they are, the rest as text. */
|
|
6290
|
+
function labelSettingValue(loosening, value) {
|
|
6291
|
+
if (value === void 0 || value === null) return void 0;
|
|
6292
|
+
if (loosening.path === "agentLabel.rules") return value.map(agentLabelRuleText);
|
|
6293
|
+
if (loosening.path === "agentLabel.slackIcons") return { ...value };
|
|
6294
|
+
return String(value);
|
|
6295
|
+
}
|
|
6296
|
+
/** The label settings a change can write, as the file holds them — raw, as `changedSettings` records every setting. */
|
|
6297
|
+
const LABEL_SETTINGS = [
|
|
6298
|
+
"word",
|
|
6299
|
+
"name",
|
|
6300
|
+
"rules",
|
|
6301
|
+
"slackIcons"
|
|
6302
|
+
];
|
|
6303
|
+
function storedAgentLabel(config) {
|
|
6304
|
+
const stored = config.agentLabel;
|
|
6305
|
+
return typeof stored === "object" && stored !== null && !Array.isArray(stored) ? stored : {};
|
|
6306
|
+
}
|
|
6122
6307
|
/** The settings of a mailbox, and of an account, that a change can write — the ones the classifier judges. */
|
|
6123
6308
|
const INBOX_SETTINGS = [
|
|
6124
6309
|
"sendPolicy",
|
|
@@ -6132,7 +6317,7 @@ const ACCOUNT_SETTINGS = [
|
|
|
6132
6317
|
];
|
|
6133
6318
|
/**
|
|
6134
6319
|
* Every setting that differs between `before` and `after`, loosened or tightened: the defaults, the secret store,
|
|
6135
|
-
* and each mailbox's and account's own settings — including those of one added or removed.
|
|
6320
|
+
* the agent label's settings, and each mailbox's and account's own settings — including those of one added or removed.
|
|
6136
6321
|
*
|
|
6137
6322
|
* What a change approval binds, beside its loosenings. A preview shows the whole change — "sends approved by never;
|
|
6138
6323
|
* changes approved by chat" — and binding only the loosenings let a claim drop the tightening a person read, since
|
|
@@ -6170,6 +6355,9 @@ function changedSettings(before, after) {
|
|
|
6170
6355
|
const defaultsAfter = after.defaults;
|
|
6171
6356
|
for (const key of [.../* @__PURE__ */ new Set([...Object.keys(defaultsBefore), ...Object.keys(defaultsAfter)])].sort()) record(`defaults.${key}`, defaultsBefore[key], defaultsAfter[key]);
|
|
6172
6357
|
record("secrets.store", before.secrets?.store, after.secrets?.store);
|
|
6358
|
+
const labelBefore = storedAgentLabel(before);
|
|
6359
|
+
const labelAfter = storedAgentLabel(after);
|
|
6360
|
+
for (const key of LABEL_SETTINGS) record(`agentLabel.${key}`, labelBefore[key], labelAfter[key]);
|
|
6173
6361
|
return changes;
|
|
6174
6362
|
}
|
|
6175
6363
|
/**
|
|
@@ -9304,7 +9492,7 @@ function folding(platform) {
|
|
|
9304
9492
|
return platform === "win32" ? (text) => text.toLowerCase() : (text) => text;
|
|
9305
9493
|
}
|
|
9306
9494
|
/** The package version, read from package.json at build time. */
|
|
9307
|
-
const VERSION = "0.
|
|
9495
|
+
const VERSION = "0.19.0";
|
|
9308
9496
|
/**
|
|
9309
9497
|
* Versions as semver writes and orders them — what an update compares, and what the daily update check's reader needs
|
|
9310
9498
|
* to decide whether the release it last heard of is newer than the one running.
|
|
@@ -9482,8 +9670,10 @@ const CHANNEL_PACKAGES = new Set(CHANNEL_SNAPSHOT.filter(({ manifest }) => manif
|
|
|
9482
9670
|
/**
|
|
9483
9671
|
* Locates the command that runs `request.target`'s CLI with `request.words`, or says why there is none.
|
|
9484
9672
|
*
|
|
9485
|
-
* The program is always this process's Node
|
|
9486
|
-
*
|
|
9673
|
+
* The program is always this process's Node, which must satisfy the target's `engines.node`; no interpreter named
|
|
9674
|
+
* anywhere else is run, or asked its version. It is named as PATH finds it — the first `node` on `request.env`'s PATH
|
|
9675
|
+
* that is the same real file as `process.execPath` — and otherwise by `process.execPath` (CUE-492): the real file only
|
|
9676
|
+
* identifies it, and `process.version` is that file's. A TypeScript entry gets `--experimental-strip-types`,
|
|
9487
9677
|
* which Node 22.12–22.17 needs, and `--experimental-transform-types` only when this source run was started with it;
|
|
9488
9678
|
* no other flag this process was started with is carried — debug, preload, condition and memory flags are not what the
|
|
9489
9679
|
* CLI needs. The suite directories the command uses go between the entry and its words, one canonical option each,
|
|
@@ -9524,6 +9714,7 @@ function locateCliCommand(request, runtime = {
|
|
|
9524
9714
|
return fail("caller", `${nearest.root} cannot be resolved`);
|
|
9525
9715
|
}
|
|
9526
9716
|
if (!isAbsolute(process.execPath)) return fail("interpreter", `this Node, ${process.execPath}, is not a full path`);
|
|
9717
|
+
const program = nodeProgram(request.env);
|
|
9527
9718
|
const pins = {};
|
|
9528
9719
|
for (const key of request.uses) pins[key] = request.paths[key];
|
|
9529
9720
|
let words;
|
|
@@ -9532,7 +9723,7 @@ function locateCliCommand(request, runtime = {
|
|
|
9532
9723
|
} catch (error) {
|
|
9533
9724
|
return fail("arguments", error instanceof Error ? error.message : String(error));
|
|
9534
9725
|
}
|
|
9535
|
-
const build = (found) => buildCommand(target, found, words, runtime, callerSource, platform);
|
|
9726
|
+
const build = (found) => buildCommand(target, found, words, runtime, callerSource, platform, program);
|
|
9536
9727
|
const located = (built, found, direction) => Object.freeze({
|
|
9537
9728
|
ok: true,
|
|
9538
9729
|
command: built.command,
|
|
@@ -9672,7 +9863,7 @@ function locateCliCommand(request, runtime = {
|
|
|
9672
9863
|
* Checks what was found and makes its command: the target's Node range against this Node, then the entry — the source
|
|
9673
9864
|
* CLI or the manifest's bin for the command it declares — as a readable file inside its package.
|
|
9674
9865
|
*/
|
|
9675
|
-
function buildCommand(target, found, words, runtime, callerSource, platform) {
|
|
9866
|
+
function buildCommand(target, found, words, runtime, callerSource, platform, program) {
|
|
9676
9867
|
const engines = found.manifest.engines;
|
|
9677
9868
|
const range = typeof engines === "object" && engines !== null ? engines.node : void 0;
|
|
9678
9869
|
if (typeof range !== "string") return {
|
|
@@ -9709,7 +9900,7 @@ function buildCommand(target, found, words, runtime, callerSource, platform) {
|
|
|
9709
9900
|
return {
|
|
9710
9901
|
ok: true,
|
|
9711
9902
|
command: new PrintedCommand(GATE, [
|
|
9712
|
-
|
|
9903
|
+
program,
|
|
9713
9904
|
...flags,
|
|
9714
9905
|
checked.path,
|
|
9715
9906
|
...words
|
|
@@ -10013,7 +10204,8 @@ function cliHandoffs(options) {
|
|
|
10013
10204
|
uses: usesOf(use),
|
|
10014
10205
|
paths,
|
|
10015
10206
|
platform,
|
|
10016
|
-
...registrations === void 0 ? {} : { registrations }
|
|
10207
|
+
...registrations === void 0 ? {} : { registrations },
|
|
10208
|
+
...options.env === void 0 ? {} : { env: options.env }
|
|
10017
10209
|
}, ...options.runtime === void 0 ? [] : [options.runtime]);
|
|
10018
10210
|
return result.ok ? result.command : result;
|
|
10019
10211
|
};
|
|
@@ -10546,7 +10738,11 @@ const SETTING_LABELS = {
|
|
|
10546
10738
|
"defaults.downloadsDir": "downloads folder",
|
|
10547
10739
|
"defaults.confirm.elicitationClients": "clients trusted to ask for a send approval",
|
|
10548
10740
|
"defaults.updateCheck": "daily update check",
|
|
10549
|
-
"secrets.store": "where credentials are kept"
|
|
10741
|
+
"secrets.store": "where credentials are kept",
|
|
10742
|
+
"agentLabel.word": "the word after your name in the agent label",
|
|
10743
|
+
"agentLabel.name": "the name in the agent label",
|
|
10744
|
+
"agentLabel.rules": "who each kind of message appears to come from",
|
|
10745
|
+
"agentLabel.slackIcons": "the icon of the agent’s Slack posts"
|
|
10550
10746
|
};
|
|
10551
10747
|
/**
|
|
10552
10748
|
* What the loosening means for the person, in their terms.
|
|
@@ -10571,6 +10767,10 @@ function meaning(field, after, scope) {
|
|
|
10571
10767
|
case "defaults.confirm.elicitationClients": return "these clients may ask you to approve a send in their own window";
|
|
10572
10768
|
case "defaults.updateCheck": return "nothing on this machine will ask npm for a newer release, or stop until one is installed";
|
|
10573
10769
|
case "secrets.store": return "credentials will move out of the system keychain into files on this disk";
|
|
10770
|
+
case "agentLabel.word": return "what an agent sends will carry this word after your name";
|
|
10771
|
+
case "agentLabel.name": return after === null ? "where a channel has no name of its own for you, an agent’s message will be refused until a name is set" : "where a channel has no name of its own for you, what an agent sends will carry this name";
|
|
10772
|
+
case "agentLabel.rules": return "what a rule sends as `me` goes out as you, with no label: it will look as if you wrote it yourself";
|
|
10773
|
+
case "agentLabel.slackIcons": return "the app’s Slack posts will show this icon beside your name";
|
|
10574
10774
|
default: return "";
|
|
10575
10775
|
}
|
|
10576
10776
|
}
|
|
@@ -11612,7 +11812,8 @@ async function withManagedShim(context, product, installed) {
|
|
|
11612
11812
|
...context.core.paths,
|
|
11613
11813
|
downloadsDir: context.core.paths.dataDir
|
|
11614
11814
|
},
|
|
11615
|
-
platform: context.platform
|
|
11815
|
+
platform: context.platform,
|
|
11816
|
+
env: context.env
|
|
11616
11817
|
});
|
|
11617
11818
|
if (!located.ok) throw new CommsError("CONFIG", `the managed CLI command could not be located: ${located.message}`);
|
|
11618
11819
|
shim = await (context.writeShim ?? writeManagedShim)(shimContext, {
|
|
@@ -11675,7 +11876,7 @@ function serverPruneChange(core, env, request, own) {
|
|
|
11675
11876
|
};
|
|
11676
11877
|
const { packageName, version } = ownProduct(request.channel, own) ?? {
|
|
11677
11878
|
packageName: serverFacts(request.channel).packageName,
|
|
11678
|
-
version: "0.
|
|
11879
|
+
version: "0.19.0"
|
|
11679
11880
|
};
|
|
11680
11881
|
const product = {
|
|
11681
11882
|
packageName,
|
|
@@ -12330,7 +12531,7 @@ async function askLine(streams, question) {
|
|
|
12330
12531
|
}
|
|
12331
12532
|
channelServer("core").packageName;
|
|
12332
12533
|
//#endregion
|
|
12333
|
-
//#region ../core/dist/tool-arguments-
|
|
12534
|
+
//#region ../core/dist/tool-arguments-BkdTZEb8.mjs
|
|
12334
12535
|
const APPLIED = Symbol.for("agentcomms.strictToolArguments");
|
|
12335
12536
|
const TARGET = "draft-2020-12";
|
|
12336
12537
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentcomms/whatsapp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "WhatsApp for coding agents, read-only: list, read and search the chats WhatsApp for Mac keeps on this Mac, and draft replies as links the person sends. No network client; it never sends.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@agentcomms/core": "0.
|
|
37
|
+
"@agentcomms/core": "0.19.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@modelcontextprotocol/client": "2.3.1",
|
|
@@ -98,5 +98,5 @@
|
|
|
98
98
|
"typecheck": "tsc -p tsconfig.json",
|
|
99
99
|
"test": "node --experimental-strip-types --disable-warning=ExperimentalWarning --test --test-timeout=600000 \"test/**/*.test.ts\""
|
|
100
100
|
},
|
|
101
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "29a7121620a9ef305a4260f193c40db1980312bd"
|
|
102
102
|
}
|