@gethmy/mcp 3.6.0 → 3.8.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/README.md +2 -2
- package/dist/cli.js +300 -2
- package/dist/index.js +300 -2
- package/dist/lib/api-client.js +139 -2
- package/dist/lib/config.js +1 -1
- package/dist/lib/oauth-refresh.js +1 -1
- package/dist/run-hook-cli.js +317 -8
- package/package.json +4 -3
- package/src/api-client.ts +82 -1
- package/src/config.ts +20 -3
- package/src/run-hook.ts +1 -1
- package/src/server.ts +27 -0
- package/src/run-redaction.ts +0 -483
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ function noteLegacyLocalPin(path) {
|
|
|
31
31
|
if (warnedLegacyLocalPin)
|
|
32
32
|
return;
|
|
33
33
|
warnedLegacyLocalPin = true;
|
|
34
|
-
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `
|
|
34
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Run \`harmony-agent doctor --fix\` in this repo to write ` + `${LOCAL_CONFIG_FILENAME}, or rename the file yourself. ` + `The fallback that finds it is temporary.`);
|
|
35
35
|
}
|
|
36
36
|
function noteLocalPinRename(from, to) {
|
|
37
37
|
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|
|
@@ -1987,6 +1987,267 @@ var REVIEW_DISALLOWED_TOOLS = [
|
|
|
1987
1987
|
"mcp__harmony__harmony_delete_subtask",
|
|
1988
1988
|
"mcp__harmony__harmony_toggle_subtask"
|
|
1989
1989
|
];
|
|
1990
|
+
// ../harmony-shared/dist/runEventSanitize.js
|
|
1991
|
+
var REPLACEMENT = "�";
|
|
1992
|
+
function sanitizeRunEventString(value) {
|
|
1993
|
+
let out = "";
|
|
1994
|
+
for (let i = 0;i < value.length; i++) {
|
|
1995
|
+
const code = value.charCodeAt(i);
|
|
1996
|
+
if (code === 0)
|
|
1997
|
+
continue;
|
|
1998
|
+
if (code >= 55296 && code <= 56319) {
|
|
1999
|
+
const next = value.charCodeAt(i + 1);
|
|
2000
|
+
if (next >= 56320 && next <= 57343) {
|
|
2001
|
+
out += value[i] + value[i + 1];
|
|
2002
|
+
i++;
|
|
2003
|
+
continue;
|
|
2004
|
+
}
|
|
2005
|
+
out += REPLACEMENT;
|
|
2006
|
+
continue;
|
|
2007
|
+
}
|
|
2008
|
+
if (code >= 56320 && code <= 57343) {
|
|
2009
|
+
out += REPLACEMENT;
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
out += value[i];
|
|
2013
|
+
}
|
|
2014
|
+
return out;
|
|
2015
|
+
}
|
|
2016
|
+
function sanitizeRunEventPayload(payload) {
|
|
2017
|
+
return walk(payload, new Map);
|
|
2018
|
+
}
|
|
2019
|
+
function sanitizeRunEventDraft(draft) {
|
|
2020
|
+
return { ...draft, payload: sanitizeRunEventPayload(draft.payload) };
|
|
2021
|
+
}
|
|
2022
|
+
function walk(value, seen) {
|
|
2023
|
+
if (typeof value === "string")
|
|
2024
|
+
return sanitizeRunEventString(value);
|
|
2025
|
+
if (value === null || typeof value !== "object")
|
|
2026
|
+
return value;
|
|
2027
|
+
const already = seen.get(value);
|
|
2028
|
+
if (already !== undefined)
|
|
2029
|
+
return already;
|
|
2030
|
+
if (Array.isArray(value)) {
|
|
2031
|
+
const out2 = [];
|
|
2032
|
+
seen.set(value, out2);
|
|
2033
|
+
for (const entry of value)
|
|
2034
|
+
out2.push(walk(entry, seen));
|
|
2035
|
+
return out2;
|
|
2036
|
+
}
|
|
2037
|
+
const out = {};
|
|
2038
|
+
seen.set(value, out);
|
|
2039
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2040
|
+
out[sanitizeRunEventString(key)] = walk(entry, seen);
|
|
2041
|
+
}
|
|
2042
|
+
return out;
|
|
2043
|
+
}
|
|
2044
|
+
// ../harmony-shared/dist/runRedaction.js
|
|
2045
|
+
var MAX_INPUT_CHARS = 2000;
|
|
2046
|
+
var MAX_OUTPUT_CHARS = 4000;
|
|
2047
|
+
var MAX_INPUT_STRING_CHARS = 600;
|
|
2048
|
+
var REDACTION_MARK = "«redacted»";
|
|
2049
|
+
var SENSITIVE_SEGMENTS = [
|
|
2050
|
+
".ssh",
|
|
2051
|
+
".gnupg",
|
|
2052
|
+
".aws",
|
|
2053
|
+
".codex",
|
|
2054
|
+
".gemini",
|
|
2055
|
+
".docker",
|
|
2056
|
+
".kube",
|
|
2057
|
+
".hmy",
|
|
2058
|
+
".harmony-mcp",
|
|
2059
|
+
".password-store",
|
|
2060
|
+
".claude",
|
|
2061
|
+
"gh",
|
|
2062
|
+
"gcloud",
|
|
2063
|
+
"op",
|
|
2064
|
+
"anthropic"
|
|
2065
|
+
];
|
|
2066
|
+
var CONFIG_SCOPED_SEGMENTS = new Set([
|
|
2067
|
+
"gh",
|
|
2068
|
+
"gcloud",
|
|
2069
|
+
"op",
|
|
2070
|
+
"anthropic"
|
|
2071
|
+
]);
|
|
2072
|
+
var SENSITIVE_BASENAMES = new Set([
|
|
2073
|
+
".netrc",
|
|
2074
|
+
"_netrc",
|
|
2075
|
+
".npmrc",
|
|
2076
|
+
".pgpass",
|
|
2077
|
+
".git-credentials",
|
|
2078
|
+
".htpasswd",
|
|
2079
|
+
".claude.json",
|
|
2080
|
+
"credentials",
|
|
2081
|
+
".credentials",
|
|
2082
|
+
"credentials.json",
|
|
2083
|
+
".credentials.json",
|
|
2084
|
+
"credentials.yml",
|
|
2085
|
+
"credentials.yaml",
|
|
2086
|
+
"auth.json",
|
|
2087
|
+
".auth.json",
|
|
2088
|
+
"secrets",
|
|
2089
|
+
"secrets.json",
|
|
2090
|
+
"secrets.yaml",
|
|
2091
|
+
"secrets.yml",
|
|
2092
|
+
"id_rsa",
|
|
2093
|
+
"id_dsa",
|
|
2094
|
+
"id_ecdsa",
|
|
2095
|
+
"id_ed25519",
|
|
2096
|
+
"known_hosts"
|
|
2097
|
+
]);
|
|
2098
|
+
var SENSITIVE_EXTENSIONS = [
|
|
2099
|
+
".pem",
|
|
2100
|
+
".key",
|
|
2101
|
+
".p12",
|
|
2102
|
+
".pfx",
|
|
2103
|
+
".keystore",
|
|
2104
|
+
".jks",
|
|
2105
|
+
".asc",
|
|
2106
|
+
".gpg"
|
|
2107
|
+
];
|
|
2108
|
+
function isSensitivePath(rawPath) {
|
|
2109
|
+
if (typeof rawPath !== "string" || rawPath.length === 0)
|
|
2110
|
+
return false;
|
|
2111
|
+
const path = rawPath.trim().toLowerCase();
|
|
2112
|
+
const segments = path.split(/[\\/]+/).filter((s) => s.length > 0);
|
|
2113
|
+
if (segments.length === 0)
|
|
2114
|
+
return false;
|
|
2115
|
+
for (let i = 0;i < segments.length; i++) {
|
|
2116
|
+
const segment = segments[i];
|
|
2117
|
+
if (!SENSITIVE_SEGMENTS.includes(segment))
|
|
2118
|
+
continue;
|
|
2119
|
+
if (CONFIG_SCOPED_SEGMENTS.has(segment)) {
|
|
2120
|
+
if (i > 0 && segments[i - 1] === ".config")
|
|
2121
|
+
return true;
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2124
|
+
return true;
|
|
2125
|
+
}
|
|
2126
|
+
const basename = segments[segments.length - 1];
|
|
2127
|
+
if (SENSITIVE_BASENAMES.has(basename))
|
|
2128
|
+
return true;
|
|
2129
|
+
if (basename === ".env" || basename.startsWith(".env."))
|
|
2130
|
+
return true;
|
|
2131
|
+
if (basename.endsWith(".env"))
|
|
2132
|
+
return true;
|
|
2133
|
+
if (SENSITIVE_EXTENSIONS.some((ext) => basename.endsWith(ext)))
|
|
2134
|
+
return true;
|
|
2135
|
+
if (/service[-_]?account.*\.json$/.test(basename))
|
|
2136
|
+
return true;
|
|
2137
|
+
return false;
|
|
2138
|
+
}
|
|
2139
|
+
function sensitivePathsIn(input, depth = 0) {
|
|
2140
|
+
if (depth > 6)
|
|
2141
|
+
return [];
|
|
2142
|
+
if (typeof input === "string") {
|
|
2143
|
+
return isSensitivePath(input) ? [input] : [];
|
|
2144
|
+
}
|
|
2145
|
+
if (Array.isArray(input)) {
|
|
2146
|
+
return input.flatMap((item) => sensitivePathsIn(item, depth + 1));
|
|
2147
|
+
}
|
|
2148
|
+
if (input !== null && typeof input === "object") {
|
|
2149
|
+
return Object.values(input).flatMap((value) => sensitivePathsIn(value, depth + 1));
|
|
2150
|
+
}
|
|
2151
|
+
return [];
|
|
2152
|
+
}
|
|
2153
|
+
var SECRET_PATTERNS = [
|
|
2154
|
+
{
|
|
2155
|
+
pattern: /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*-----/g,
|
|
2156
|
+
replace: REDACTION_MARK
|
|
2157
|
+
},
|
|
2158
|
+
{ pattern: /\bhmy_at_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
2159
|
+
{ pattern: /\bhmy_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
2160
|
+
{ pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, replace: REDACTION_MARK },
|
|
2161
|
+
{ pattern: /\bgh[pousr]_[A-Za-z0-9]{16,}/g, replace: REDACTION_MARK },
|
|
2162
|
+
{ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, replace: REDACTION_MARK },
|
|
2163
|
+
{ pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, replace: REDACTION_MARK },
|
|
2164
|
+
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, replace: REDACTION_MARK },
|
|
2165
|
+
{ pattern: /\bAIza[0-9A-Za-z_-]{20,}/g, replace: REDACTION_MARK },
|
|
2166
|
+
{
|
|
2167
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
2168
|
+
replace: REDACTION_MARK
|
|
2169
|
+
},
|
|
2170
|
+
{
|
|
2171
|
+
pattern: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
2172
|
+
replace: `$1 ${REDACTION_MARK}`
|
|
2173
|
+
},
|
|
2174
|
+
{
|
|
2175
|
+
pattern: /(\w{1,32}:\/\/)[^/\s:@]+:[^/\s@]+@/g,
|
|
2176
|
+
replace: `$1${REDACTION_MARK}@`
|
|
2177
|
+
},
|
|
2178
|
+
{
|
|
2179
|
+
pattern: /\b([A-Za-z0-9_]{0,40}(?:TOKEN|SECRET|PASSWORD|PASSWD|APIKEY|API_KEY|ACCESS_KEY|PRIVATE_KEY|CREDENTIAL|AUTH)[A-Za-z0-9_]{0,40})\s*[=:]\s*(?:"[^"]*"|'[^']*'|`[^`]*`|[^\s,;)}\]]+)/gi,
|
|
2180
|
+
replace: `$1=${REDACTION_MARK}`
|
|
2181
|
+
},
|
|
2182
|
+
{
|
|
2183
|
+
pattern: /(--?(?:password|passwd|token|api-?key|secret|auth)(?:=|\s+))(?:"[^"]*"|'[^']*'|[^\s]+)/gi,
|
|
2184
|
+
replace: `$1${REDACTION_MARK}`
|
|
2185
|
+
}
|
|
2186
|
+
];
|
|
2187
|
+
function redactSecrets(text) {
|
|
2188
|
+
if (typeof text !== "string" || text.length === 0)
|
|
2189
|
+
return text;
|
|
2190
|
+
let out = text;
|
|
2191
|
+
for (const { pattern, replace } of SECRET_PATTERNS) {
|
|
2192
|
+
pattern.lastIndex = 0;
|
|
2193
|
+
out = out.replace(pattern, replace);
|
|
2194
|
+
}
|
|
2195
|
+
return out;
|
|
2196
|
+
}
|
|
2197
|
+
function truncate(text, max, originalLength) {
|
|
2198
|
+
const total = originalLength ?? text.length;
|
|
2199
|
+
if (total <= max)
|
|
2200
|
+
return text;
|
|
2201
|
+
return `${text.slice(0, max)}… [+${total - max} chars]`;
|
|
2202
|
+
}
|
|
2203
|
+
function redactThenTruncate(text, max) {
|
|
2204
|
+
const preCap = max * 4 + 64;
|
|
2205
|
+
const scanned = text.length > preCap ? text.slice(0, preCap) : text;
|
|
2206
|
+
return truncate(redactSecrets(scanned), max, text.length);
|
|
2207
|
+
}
|
|
2208
|
+
function redactStructure(value, depth = 0) {
|
|
2209
|
+
if (depth > 6)
|
|
2210
|
+
return REDACTION_MARK;
|
|
2211
|
+
if (typeof value === "string") {
|
|
2212
|
+
return redactThenTruncate(value, MAX_INPUT_STRING_CHARS);
|
|
2213
|
+
}
|
|
2214
|
+
if (Array.isArray(value)) {
|
|
2215
|
+
return value.slice(0, 20).map((item) => redactStructure(item, depth + 1));
|
|
2216
|
+
}
|
|
2217
|
+
if (value !== null && typeof value === "object") {
|
|
2218
|
+
const out = {};
|
|
2219
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2220
|
+
out[key] = redactStructure(item, depth + 1);
|
|
2221
|
+
}
|
|
2222
|
+
return out;
|
|
2223
|
+
}
|
|
2224
|
+
return value;
|
|
2225
|
+
}
|
|
2226
|
+
function redactToolCall(args) {
|
|
2227
|
+
const sensitive = sensitivePathsIn(args.input);
|
|
2228
|
+
if (sensitive.length > 0) {
|
|
2229
|
+
return { withheld: "sensitive-path" };
|
|
2230
|
+
}
|
|
2231
|
+
const result = {};
|
|
2232
|
+
if (args.input !== undefined) {
|
|
2233
|
+
let input = redactStructure(args.input);
|
|
2234
|
+
let serialized;
|
|
2235
|
+
try {
|
|
2236
|
+
serialized = JSON.stringify(input) ?? "";
|
|
2237
|
+
} catch {
|
|
2238
|
+
serialized = "";
|
|
2239
|
+
input = REDACTION_MARK;
|
|
2240
|
+
}
|
|
2241
|
+
if (serialized.length > MAX_INPUT_CHARS) {
|
|
2242
|
+
input = truncate(serialized, MAX_INPUT_CHARS);
|
|
2243
|
+
}
|
|
2244
|
+
result.input = input;
|
|
2245
|
+
}
|
|
2246
|
+
if (typeof args.output === "string" && args.output.length > 0) {
|
|
2247
|
+
result.output = redactThenTruncate(args.output, MAX_OUTPUT_CHARS);
|
|
2248
|
+
}
|
|
2249
|
+
return result;
|
|
2250
|
+
}
|
|
1990
2251
|
// ../harmony-shared/dist/stageHandoff.js
|
|
1991
2252
|
var HANDOFF_MARKER = "harmony:stage-handoff";
|
|
1992
2253
|
var HANDOFF_BLOCK_RE = new RegExp("```json\\s*\\n//\\s*" + HANDOFF_MARKER + "\\s*\\n([\\s\\S]*?)\\n```", "m");
|
|
@@ -2300,6 +2561,15 @@ class HarmonyApiClient {
|
|
|
2300
2561
|
async registerWorkspaceAgent(workspaceId, data) {
|
|
2301
2562
|
return this.request("POST", `/workspaces/${workspaceId}/agents`, data);
|
|
2302
2563
|
}
|
|
2564
|
+
async reportAgentConfig(workspaceId, agentId, config) {
|
|
2565
|
+
return this.request("POST", `/workspaces/${workspaceId}/agents/${agentId}/reported-config`, { config });
|
|
2566
|
+
}
|
|
2567
|
+
async getWorkspaceModelConfig(workspaceId) {
|
|
2568
|
+
return this.request("GET", `/workspaces/${workspaceId}/model-config`);
|
|
2569
|
+
}
|
|
2570
|
+
async getModelCatalog() {
|
|
2571
|
+
return this.request("GET", "/model-catalog");
|
|
2572
|
+
}
|
|
2303
2573
|
async listProjects(workspaceId) {
|
|
2304
2574
|
return this.request("GET", `/workspaces/${workspaceId}/projects`);
|
|
2305
2575
|
}
|
|
@@ -2448,6 +2718,9 @@ class HarmonyApiClient {
|
|
|
2448
2718
|
title
|
|
2449
2719
|
});
|
|
2450
2720
|
}
|
|
2721
|
+
async removeExternalLink(cardId, linkId) {
|
|
2722
|
+
return this.request("DELETE", `/cards/${cardId}/external-links/${linkId}`);
|
|
2723
|
+
}
|
|
2451
2724
|
async uploadArtifact(data) {
|
|
2452
2725
|
return this.request("POST", "/artifacts", data);
|
|
2453
2726
|
}
|
|
@@ -2540,7 +2813,10 @@ class HarmonyApiClient {
|
|
|
2540
2813
|
return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
|
|
2541
2814
|
}
|
|
2542
2815
|
async appendAgentRunEvents(cardId, data) {
|
|
2543
|
-
return this.request("POST", `/cards/${cardId}/agent-run-events`,
|
|
2816
|
+
return this.request("POST", `/cards/${cardId}/agent-run-events`, {
|
|
2817
|
+
...data,
|
|
2818
|
+
events: data.events.map((event) => sanitizeRunEventDraft(event))
|
|
2819
|
+
});
|
|
2544
2820
|
}
|
|
2545
2821
|
async getPendingUserMessages(cardId, sessionId, sinceSeq) {
|
|
2546
2822
|
return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
|
|
@@ -5230,6 +5506,23 @@ var TOOLS = {
|
|
|
5230
5506
|
required: ["cardId", "url"]
|
|
5231
5507
|
}
|
|
5232
5508
|
},
|
|
5509
|
+
harmony_remove_external_link: {
|
|
5510
|
+
description: "Remove an external reference URL from a card — the counterpart to harmony_add_external_link. Takes the link id from harmony_get_card_external_links, not the URL.",
|
|
5511
|
+
inputSchema: {
|
|
5512
|
+
type: "object",
|
|
5513
|
+
properties: {
|
|
5514
|
+
cardId: {
|
|
5515
|
+
type: "string",
|
|
5516
|
+
description: "Card UUID"
|
|
5517
|
+
},
|
|
5518
|
+
linkId: {
|
|
5519
|
+
type: "string",
|
|
5520
|
+
description: "External link UUID, as returned by harmony_get_card_external_links"
|
|
5521
|
+
}
|
|
5522
|
+
},
|
|
5523
|
+
required: ["cardId", "linkId"]
|
|
5524
|
+
}
|
|
5525
|
+
},
|
|
5233
5526
|
harmony_create_subtask: {
|
|
5234
5527
|
description: "Create a subtask on a card",
|
|
5235
5528
|
inputSchema: {
|
|
@@ -7124,6 +7417,11 @@ ${list}
|
|
|
7124
7417
|
const result = await client3.addExternalLink(cardId, url, title);
|
|
7125
7418
|
return { success: true, ...result };
|
|
7126
7419
|
}
|
|
7420
|
+
case "harmony_remove_external_link": {
|
|
7421
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
7422
|
+
const linkId = z.string().uuid().parse(args.linkId);
|
|
7423
|
+
return await client3.removeExternalLink(cardId, linkId);
|
|
7424
|
+
}
|
|
7127
7425
|
case "harmony_classify_card":
|
|
7128
7426
|
return deprecatedRemovedToolResult("harmony_classify_card");
|
|
7129
7427
|
case "harmony_create_subtask": {
|
package/dist/lib/api-client.js
CHANGED
|
@@ -32,7 +32,7 @@ function noteLegacyLocalPin(path) {
|
|
|
32
32
|
if (warnedLegacyLocalPin)
|
|
33
33
|
return;
|
|
34
34
|
warnedLegacyLocalPin = true;
|
|
35
|
-
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `
|
|
35
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Run \`harmony-agent doctor --fix\` in this repo to write ` + `${LOCAL_CONFIG_FILENAME}, or rename the file yourself. ` + `The fallback that finds it is temporary.`);
|
|
36
36
|
}
|
|
37
37
|
function noteLocalPinRename(from, to) {
|
|
38
38
|
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|
|
@@ -1014,6 +1014,128 @@ var REVIEW_DISALLOWED_TOOLS = [
|
|
|
1014
1014
|
"mcp__harmony__harmony_delete_subtask",
|
|
1015
1015
|
"mcp__harmony__harmony_toggle_subtask"
|
|
1016
1016
|
];
|
|
1017
|
+
// ../harmony-shared/dist/runEventSanitize.js
|
|
1018
|
+
var REPLACEMENT = "�";
|
|
1019
|
+
function sanitizeRunEventString(value) {
|
|
1020
|
+
let out = "";
|
|
1021
|
+
for (let i = 0;i < value.length; i++) {
|
|
1022
|
+
const code = value.charCodeAt(i);
|
|
1023
|
+
if (code === 0)
|
|
1024
|
+
continue;
|
|
1025
|
+
if (code >= 55296 && code <= 56319) {
|
|
1026
|
+
const next = value.charCodeAt(i + 1);
|
|
1027
|
+
if (next >= 56320 && next <= 57343) {
|
|
1028
|
+
out += value[i] + value[i + 1];
|
|
1029
|
+
i++;
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
out += REPLACEMENT;
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
if (code >= 56320 && code <= 57343) {
|
|
1036
|
+
out += REPLACEMENT;
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
out += value[i];
|
|
1040
|
+
}
|
|
1041
|
+
return out;
|
|
1042
|
+
}
|
|
1043
|
+
function sanitizeRunEventPayload(payload) {
|
|
1044
|
+
return walk(payload, new Map);
|
|
1045
|
+
}
|
|
1046
|
+
function sanitizeRunEventDraft(draft) {
|
|
1047
|
+
return { ...draft, payload: sanitizeRunEventPayload(draft.payload) };
|
|
1048
|
+
}
|
|
1049
|
+
function walk(value, seen) {
|
|
1050
|
+
if (typeof value === "string")
|
|
1051
|
+
return sanitizeRunEventString(value);
|
|
1052
|
+
if (value === null || typeof value !== "object")
|
|
1053
|
+
return value;
|
|
1054
|
+
const already = seen.get(value);
|
|
1055
|
+
if (already !== undefined)
|
|
1056
|
+
return already;
|
|
1057
|
+
if (Array.isArray(value)) {
|
|
1058
|
+
const out2 = [];
|
|
1059
|
+
seen.set(value, out2);
|
|
1060
|
+
for (const entry of value)
|
|
1061
|
+
out2.push(walk(entry, seen));
|
|
1062
|
+
return out2;
|
|
1063
|
+
}
|
|
1064
|
+
const out = {};
|
|
1065
|
+
seen.set(value, out);
|
|
1066
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1067
|
+
out[sanitizeRunEventString(key)] = walk(entry, seen);
|
|
1068
|
+
}
|
|
1069
|
+
return out;
|
|
1070
|
+
}
|
|
1071
|
+
// ../harmony-shared/dist/runRedaction.js
|
|
1072
|
+
var REDACTION_MARK = "«redacted»";
|
|
1073
|
+
var CONFIG_SCOPED_SEGMENTS = new Set([
|
|
1074
|
+
"gh",
|
|
1075
|
+
"gcloud",
|
|
1076
|
+
"op",
|
|
1077
|
+
"anthropic"
|
|
1078
|
+
]);
|
|
1079
|
+
var SENSITIVE_BASENAMES = new Set([
|
|
1080
|
+
".netrc",
|
|
1081
|
+
"_netrc",
|
|
1082
|
+
".npmrc",
|
|
1083
|
+
".pgpass",
|
|
1084
|
+
".git-credentials",
|
|
1085
|
+
".htpasswd",
|
|
1086
|
+
".claude.json",
|
|
1087
|
+
"credentials",
|
|
1088
|
+
".credentials",
|
|
1089
|
+
"credentials.json",
|
|
1090
|
+
".credentials.json",
|
|
1091
|
+
"credentials.yml",
|
|
1092
|
+
"credentials.yaml",
|
|
1093
|
+
"auth.json",
|
|
1094
|
+
".auth.json",
|
|
1095
|
+
"secrets",
|
|
1096
|
+
"secrets.json",
|
|
1097
|
+
"secrets.yaml",
|
|
1098
|
+
"secrets.yml",
|
|
1099
|
+
"id_rsa",
|
|
1100
|
+
"id_dsa",
|
|
1101
|
+
"id_ecdsa",
|
|
1102
|
+
"id_ed25519",
|
|
1103
|
+
"known_hosts"
|
|
1104
|
+
]);
|
|
1105
|
+
var SECRET_PATTERNS = [
|
|
1106
|
+
{
|
|
1107
|
+
pattern: /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*-----/g,
|
|
1108
|
+
replace: REDACTION_MARK
|
|
1109
|
+
},
|
|
1110
|
+
{ pattern: /\bhmy_at_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
1111
|
+
{ pattern: /\bhmy_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
1112
|
+
{ pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, replace: REDACTION_MARK },
|
|
1113
|
+
{ pattern: /\bgh[pousr]_[A-Za-z0-9]{16,}/g, replace: REDACTION_MARK },
|
|
1114
|
+
{ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, replace: REDACTION_MARK },
|
|
1115
|
+
{ pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, replace: REDACTION_MARK },
|
|
1116
|
+
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, replace: REDACTION_MARK },
|
|
1117
|
+
{ pattern: /\bAIza[0-9A-Za-z_-]{20,}/g, replace: REDACTION_MARK },
|
|
1118
|
+
{
|
|
1119
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
1120
|
+
replace: REDACTION_MARK
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
pattern: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
1124
|
+
replace: `$1 ${REDACTION_MARK}`
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
pattern: /(\w{1,32}:\/\/)[^/\s:@]+:[^/\s@]+@/g,
|
|
1128
|
+
replace: `$1${REDACTION_MARK}@`
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
pattern: /\b([A-Za-z0-9_]{0,40}(?:TOKEN|SECRET|PASSWORD|PASSWD|APIKEY|API_KEY|ACCESS_KEY|PRIVATE_KEY|CREDENTIAL|AUTH)[A-Za-z0-9_]{0,40})\s*[=:]\s*(?:"[^"]*"|'[^']*'|`[^`]*`|[^\s,;)}\]]+)/gi,
|
|
1132
|
+
replace: `$1=${REDACTION_MARK}`
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
pattern: /(--?(?:password|passwd|token|api-?key|secret|auth)(?:=|\s+))(?:"[^"]*"|'[^']*'|[^\s]+)/gi,
|
|
1136
|
+
replace: `$1${REDACTION_MARK}`
|
|
1137
|
+
}
|
|
1138
|
+
];
|
|
1017
1139
|
// ../harmony-shared/dist/stageHandoff.js
|
|
1018
1140
|
var HANDOFF_MARKER = "harmony:stage-handoff";
|
|
1019
1141
|
var HANDOFF_BLOCK_RE = new RegExp("```json\\s*\\n//\\s*" + HANDOFF_MARKER + "\\s*\\n([\\s\\S]*?)\\n```", "m");
|
|
@@ -1327,6 +1449,15 @@ class HarmonyApiClient {
|
|
|
1327
1449
|
async registerWorkspaceAgent(workspaceId, data) {
|
|
1328
1450
|
return this.request("POST", `/workspaces/${workspaceId}/agents`, data);
|
|
1329
1451
|
}
|
|
1452
|
+
async reportAgentConfig(workspaceId, agentId, config) {
|
|
1453
|
+
return this.request("POST", `/workspaces/${workspaceId}/agents/${agentId}/reported-config`, { config });
|
|
1454
|
+
}
|
|
1455
|
+
async getWorkspaceModelConfig(workspaceId) {
|
|
1456
|
+
return this.request("GET", `/workspaces/${workspaceId}/model-config`);
|
|
1457
|
+
}
|
|
1458
|
+
async getModelCatalog() {
|
|
1459
|
+
return this.request("GET", "/model-catalog");
|
|
1460
|
+
}
|
|
1330
1461
|
async listProjects(workspaceId) {
|
|
1331
1462
|
return this.request("GET", `/workspaces/${workspaceId}/projects`);
|
|
1332
1463
|
}
|
|
@@ -1475,6 +1606,9 @@ class HarmonyApiClient {
|
|
|
1475
1606
|
title
|
|
1476
1607
|
});
|
|
1477
1608
|
}
|
|
1609
|
+
async removeExternalLink(cardId, linkId) {
|
|
1610
|
+
return this.request("DELETE", `/cards/${cardId}/external-links/${linkId}`);
|
|
1611
|
+
}
|
|
1478
1612
|
async uploadArtifact(data) {
|
|
1479
1613
|
return this.request("POST", "/artifacts", data);
|
|
1480
1614
|
}
|
|
@@ -1567,7 +1701,10 @@ class HarmonyApiClient {
|
|
|
1567
1701
|
return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
|
|
1568
1702
|
}
|
|
1569
1703
|
async appendAgentRunEvents(cardId, data) {
|
|
1570
|
-
return this.request("POST", `/cards/${cardId}/agent-run-events`,
|
|
1704
|
+
return this.request("POST", `/cards/${cardId}/agent-run-events`, {
|
|
1705
|
+
...data,
|
|
1706
|
+
events: data.events.map((event) => sanitizeRunEventDraft(event))
|
|
1707
|
+
});
|
|
1571
1708
|
}
|
|
1572
1709
|
async getPendingUserMessages(cardId, sessionId, sinceSeq) {
|
|
1573
1710
|
return this.request("GET", `/cards/${cardId}/agent-messages?sessionId=${sessionId}&sinceSeq=${sinceSeq}`);
|
package/dist/lib/config.js
CHANGED
|
@@ -32,7 +32,7 @@ function noteLegacyLocalPin(path) {
|
|
|
32
32
|
if (warnedLegacyLocalPin)
|
|
33
33
|
return;
|
|
34
34
|
warnedLegacyLocalPin = true;
|
|
35
|
-
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `
|
|
35
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Run \`harmony-agent doctor --fix\` in this repo to write ` + `${LOCAL_CONFIG_FILENAME}, or rename the file yourself. ` + `The fallback that finds it is temporary.`);
|
|
36
36
|
}
|
|
37
37
|
function noteLocalPinRename(from, to) {
|
|
38
38
|
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|
|
@@ -32,7 +32,7 @@ function noteLegacyLocalPin(path) {
|
|
|
32
32
|
if (warnedLegacyLocalPin)
|
|
33
33
|
return;
|
|
34
34
|
warnedLegacyLocalPin = true;
|
|
35
|
-
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `
|
|
35
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Run \`harmony-agent doctor --fix\` in this repo to write ` + `${LOCAL_CONFIG_FILENAME}, or rename the file yourself. ` + `The fallback that finds it is temporary.`);
|
|
36
36
|
}
|
|
37
37
|
function noteLocalPinRename(from, to) {
|
|
38
38
|
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|