@gethmy/mcp 3.5.0 → 3.7.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 +5 -5
- package/dist/cli.js +258 -12
- package/dist/index.js +252 -8
- package/dist/lib/api-client.js +117 -8
- package/dist/lib/config.js +56 -9
- package/dist/lib/oauth-refresh.js +49 -8
- package/dist/run-hook-cli.js +264 -8
- package/package.json +3 -2
- package/src/config.ts +162 -18
- package/src/hook-install.ts +1 -1
- package/src/oauth-refresh.ts +1 -1
- package/src/run-hook.ts +1 -1
- package/src/run-state.ts +1 -1
- package/src/server.ts +1 -1
- package/src/tui/setup.ts +12 -3
- package/src/tui/writer.ts +21 -1
- package/src/run-redaction.ts +0 -461
package/dist/index.js
CHANGED
|
@@ -21,12 +21,36 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
21
21
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
22
22
|
import { homedir } from "node:os";
|
|
23
23
|
import { dirname, join as join2, parse, resolve } from "node:path";
|
|
24
|
+
function noteLegacyConfigDir(path) {
|
|
25
|
+
if (warnedLegacyConfigDir)
|
|
26
|
+
return;
|
|
27
|
+
warnedLegacyConfigDir = true;
|
|
28
|
+
console.error(`Harmony: reading the pre-#1082 config at ${path}. ` + `The current location is ${getConfigPath()}; ` + `run the agent daemon once to migrate, or move the file yourself.`);
|
|
29
|
+
}
|
|
30
|
+
function noteLegacyLocalPin(path) {
|
|
31
|
+
if (warnedLegacyLocalPin)
|
|
32
|
+
return;
|
|
33
|
+
warnedLegacyLocalPin = true;
|
|
34
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Rename it to ${LOCAL_CONFIG_FILENAME} — the fallback that finds it is temporary.`);
|
|
35
|
+
}
|
|
36
|
+
function noteLocalPinRename(from, to) {
|
|
37
|
+
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|
|
38
|
+
}
|
|
39
|
+
function getHmyRootDir() {
|
|
40
|
+
return join2(homedir(), CONFIG_DIR_NAME);
|
|
41
|
+
}
|
|
24
42
|
function getConfigDir() {
|
|
25
|
-
return join2(
|
|
43
|
+
return join2(getHmyRootDir(), CONFIG_DIR_SUBDIR);
|
|
44
|
+
}
|
|
45
|
+
function getLegacyConfigDir() {
|
|
46
|
+
return join2(homedir(), LEGACY_CONFIG_DIR_NAME);
|
|
26
47
|
}
|
|
27
48
|
function getConfigPath() {
|
|
28
49
|
return join2(getConfigDir(), "config.json");
|
|
29
50
|
}
|
|
51
|
+
function getLegacyConfigPath() {
|
|
52
|
+
return join2(getLegacyConfigDir(), "config.json");
|
|
53
|
+
}
|
|
30
54
|
function getLocalConfigPath(cwd) {
|
|
31
55
|
return join2(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
32
56
|
}
|
|
@@ -36,9 +60,14 @@ function findLocalConfigPath(cwd) {
|
|
|
36
60
|
const { root } = parse(dir);
|
|
37
61
|
for (;; ) {
|
|
38
62
|
if (dir !== home && dir !== root) {
|
|
39
|
-
const
|
|
40
|
-
if (existsSync2(
|
|
41
|
-
return
|
|
63
|
+
const current = join2(dir, LOCAL_CONFIG_FILENAME);
|
|
64
|
+
if (existsSync2(current))
|
|
65
|
+
return current;
|
|
66
|
+
const legacy = join2(dir, LEGACY_LOCAL_CONFIG_FILENAME);
|
|
67
|
+
if (existsSync2(legacy)) {
|
|
68
|
+
noteLegacyLocalPin(legacy);
|
|
69
|
+
return legacy;
|
|
70
|
+
}
|
|
42
71
|
}
|
|
43
72
|
const parent = dirname(dir);
|
|
44
73
|
if (parent === dir)
|
|
@@ -61,9 +90,12 @@ function emptyConfig() {
|
|
|
61
90
|
};
|
|
62
91
|
}
|
|
63
92
|
function loadConfig() {
|
|
64
|
-
|
|
93
|
+
let configPath = getConfigPath();
|
|
65
94
|
if (!existsSync2(configPath)) {
|
|
66
|
-
|
|
95
|
+
configPath = getLegacyConfigPath();
|
|
96
|
+
if (!existsSync2(configPath))
|
|
97
|
+
return emptyConfig();
|
|
98
|
+
noteLegacyConfigDir(configPath);
|
|
67
99
|
}
|
|
68
100
|
try {
|
|
69
101
|
const data = readFileSync2(configPath, "utf-8");
|
|
@@ -113,7 +145,11 @@ function loadLocalConfig(cwd) {
|
|
|
113
145
|
}
|
|
114
146
|
}
|
|
115
147
|
function saveLocalConfig(config, cwd) {
|
|
116
|
-
const
|
|
148
|
+
const foundPath = findLocalConfigPath(cwd);
|
|
149
|
+
const localConfigPath = foundPath ? join2(dirname(foundPath), LOCAL_CONFIG_FILENAME) : getLocalConfigPath(cwd);
|
|
150
|
+
if (foundPath !== null && foundPath !== localConfigPath) {
|
|
151
|
+
noteLocalPinRename(foundPath, localConfigPath);
|
|
152
|
+
}
|
|
117
153
|
const existingConfig = loadLocalConfig(cwd) || {
|
|
118
154
|
workspaceId: null,
|
|
119
155
|
projectId: null
|
|
@@ -125,6 +161,7 @@ function saveLocalConfig(config, cwd) {
|
|
|
125
161
|
if (newConfig.projectId)
|
|
126
162
|
cleanConfig.projectId = newConfig.projectId;
|
|
127
163
|
writeFileSync2(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
164
|
+
return localConfigPath;
|
|
128
165
|
}
|
|
129
166
|
function hasLocalConfig(cwd) {
|
|
130
167
|
return findLocalConfigPath(cwd) !== null;
|
|
@@ -259,7 +296,7 @@ function getMemoryDir() {
|
|
|
259
296
|
return config.memoryDir;
|
|
260
297
|
return join2(homedir(), ".harmony", "memory");
|
|
261
298
|
}
|
|
262
|
-
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
299
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".hmy.json", LEGACY_LOCAL_CONFIG_FILENAME = ".harmony-mcp.json", CONFIG_DIR_NAME = ".hmy", CONFIG_DIR_SUBDIR = "agent", LEGACY_CONFIG_DIR_NAME = ".harmony-mcp", warnedLegacyConfigDir = false, warnedLegacyLocalPin = false;
|
|
263
300
|
var init_config = () => {};
|
|
264
301
|
|
|
265
302
|
// src/prompt-builder.ts
|
|
@@ -1950,6 +1987,213 @@ var REVIEW_DISALLOWED_TOOLS = [
|
|
|
1950
1987
|
"mcp__harmony__harmony_delete_subtask",
|
|
1951
1988
|
"mcp__harmony__harmony_toggle_subtask"
|
|
1952
1989
|
];
|
|
1990
|
+
// ../harmony-shared/dist/runRedaction.js
|
|
1991
|
+
var MAX_INPUT_CHARS = 2000;
|
|
1992
|
+
var MAX_OUTPUT_CHARS = 4000;
|
|
1993
|
+
var MAX_INPUT_STRING_CHARS = 600;
|
|
1994
|
+
var REDACTION_MARK = "«redacted»";
|
|
1995
|
+
var SENSITIVE_SEGMENTS = [
|
|
1996
|
+
".ssh",
|
|
1997
|
+
".gnupg",
|
|
1998
|
+
".aws",
|
|
1999
|
+
".codex",
|
|
2000
|
+
".gemini",
|
|
2001
|
+
".docker",
|
|
2002
|
+
".kube",
|
|
2003
|
+
".hmy",
|
|
2004
|
+
".harmony-mcp",
|
|
2005
|
+
".password-store",
|
|
2006
|
+
".claude",
|
|
2007
|
+
"gh",
|
|
2008
|
+
"gcloud",
|
|
2009
|
+
"op",
|
|
2010
|
+
"anthropic"
|
|
2011
|
+
];
|
|
2012
|
+
var CONFIG_SCOPED_SEGMENTS = new Set([
|
|
2013
|
+
"gh",
|
|
2014
|
+
"gcloud",
|
|
2015
|
+
"op",
|
|
2016
|
+
"anthropic"
|
|
2017
|
+
]);
|
|
2018
|
+
var SENSITIVE_BASENAMES = new Set([
|
|
2019
|
+
".netrc",
|
|
2020
|
+
"_netrc",
|
|
2021
|
+
".npmrc",
|
|
2022
|
+
".pgpass",
|
|
2023
|
+
".git-credentials",
|
|
2024
|
+
".htpasswd",
|
|
2025
|
+
".claude.json",
|
|
2026
|
+
"credentials",
|
|
2027
|
+
".credentials",
|
|
2028
|
+
"credentials.json",
|
|
2029
|
+
".credentials.json",
|
|
2030
|
+
"credentials.yml",
|
|
2031
|
+
"credentials.yaml",
|
|
2032
|
+
"auth.json",
|
|
2033
|
+
".auth.json",
|
|
2034
|
+
"secrets",
|
|
2035
|
+
"secrets.json",
|
|
2036
|
+
"secrets.yaml",
|
|
2037
|
+
"secrets.yml",
|
|
2038
|
+
"id_rsa",
|
|
2039
|
+
"id_dsa",
|
|
2040
|
+
"id_ecdsa",
|
|
2041
|
+
"id_ed25519",
|
|
2042
|
+
"known_hosts"
|
|
2043
|
+
]);
|
|
2044
|
+
var SENSITIVE_EXTENSIONS = [
|
|
2045
|
+
".pem",
|
|
2046
|
+
".key",
|
|
2047
|
+
".p12",
|
|
2048
|
+
".pfx",
|
|
2049
|
+
".keystore",
|
|
2050
|
+
".jks",
|
|
2051
|
+
".asc",
|
|
2052
|
+
".gpg"
|
|
2053
|
+
];
|
|
2054
|
+
function isSensitivePath(rawPath) {
|
|
2055
|
+
if (typeof rawPath !== "string" || rawPath.length === 0)
|
|
2056
|
+
return false;
|
|
2057
|
+
const path = rawPath.trim().toLowerCase();
|
|
2058
|
+
const segments = path.split(/[\\/]+/).filter((s) => s.length > 0);
|
|
2059
|
+
if (segments.length === 0)
|
|
2060
|
+
return false;
|
|
2061
|
+
for (let i = 0;i < segments.length; i++) {
|
|
2062
|
+
const segment = segments[i];
|
|
2063
|
+
if (!SENSITIVE_SEGMENTS.includes(segment))
|
|
2064
|
+
continue;
|
|
2065
|
+
if (CONFIG_SCOPED_SEGMENTS.has(segment)) {
|
|
2066
|
+
if (i > 0 && segments[i - 1] === ".config")
|
|
2067
|
+
return true;
|
|
2068
|
+
continue;
|
|
2069
|
+
}
|
|
2070
|
+
return true;
|
|
2071
|
+
}
|
|
2072
|
+
const basename = segments[segments.length - 1];
|
|
2073
|
+
if (SENSITIVE_BASENAMES.has(basename))
|
|
2074
|
+
return true;
|
|
2075
|
+
if (basename === ".env" || basename.startsWith(".env."))
|
|
2076
|
+
return true;
|
|
2077
|
+
if (basename.endsWith(".env"))
|
|
2078
|
+
return true;
|
|
2079
|
+
if (SENSITIVE_EXTENSIONS.some((ext) => basename.endsWith(ext)))
|
|
2080
|
+
return true;
|
|
2081
|
+
if (/service[-_]?account.*\.json$/.test(basename))
|
|
2082
|
+
return true;
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
function sensitivePathsIn(input, depth = 0) {
|
|
2086
|
+
if (depth > 6)
|
|
2087
|
+
return [];
|
|
2088
|
+
if (typeof input === "string") {
|
|
2089
|
+
return isSensitivePath(input) ? [input] : [];
|
|
2090
|
+
}
|
|
2091
|
+
if (Array.isArray(input)) {
|
|
2092
|
+
return input.flatMap((item) => sensitivePathsIn(item, depth + 1));
|
|
2093
|
+
}
|
|
2094
|
+
if (input !== null && typeof input === "object") {
|
|
2095
|
+
return Object.values(input).flatMap((value) => sensitivePathsIn(value, depth + 1));
|
|
2096
|
+
}
|
|
2097
|
+
return [];
|
|
2098
|
+
}
|
|
2099
|
+
var SECRET_PATTERNS = [
|
|
2100
|
+
{
|
|
2101
|
+
pattern: /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*-----/g,
|
|
2102
|
+
replace: REDACTION_MARK
|
|
2103
|
+
},
|
|
2104
|
+
{ pattern: /\bhmy_at_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
2105
|
+
{ pattern: /\bhmy_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
2106
|
+
{ pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, replace: REDACTION_MARK },
|
|
2107
|
+
{ pattern: /\bgh[pousr]_[A-Za-z0-9]{16,}/g, replace: REDACTION_MARK },
|
|
2108
|
+
{ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, replace: REDACTION_MARK },
|
|
2109
|
+
{ pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, replace: REDACTION_MARK },
|
|
2110
|
+
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, replace: REDACTION_MARK },
|
|
2111
|
+
{ pattern: /\bAIza[0-9A-Za-z_-]{20,}/g, replace: REDACTION_MARK },
|
|
2112
|
+
{
|
|
2113
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
2114
|
+
replace: REDACTION_MARK
|
|
2115
|
+
},
|
|
2116
|
+
{
|
|
2117
|
+
pattern: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
2118
|
+
replace: `$1 ${REDACTION_MARK}`
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
pattern: /(\w{1,32}:\/\/)[^/\s:@]+:[^/\s@]+@/g,
|
|
2122
|
+
replace: `$1${REDACTION_MARK}@`
|
|
2123
|
+
},
|
|
2124
|
+
{
|
|
2125
|
+
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,
|
|
2126
|
+
replace: `$1=${REDACTION_MARK}`
|
|
2127
|
+
},
|
|
2128
|
+
{
|
|
2129
|
+
pattern: /(--?(?:password|passwd|token|api-?key|secret|auth)(?:=|\s+))(?:"[^"]*"|'[^']*'|[^\s]+)/gi,
|
|
2130
|
+
replace: `$1${REDACTION_MARK}`
|
|
2131
|
+
}
|
|
2132
|
+
];
|
|
2133
|
+
function redactSecrets(text) {
|
|
2134
|
+
if (typeof text !== "string" || text.length === 0)
|
|
2135
|
+
return text;
|
|
2136
|
+
let out = text;
|
|
2137
|
+
for (const { pattern, replace } of SECRET_PATTERNS) {
|
|
2138
|
+
pattern.lastIndex = 0;
|
|
2139
|
+
out = out.replace(pattern, replace);
|
|
2140
|
+
}
|
|
2141
|
+
return out;
|
|
2142
|
+
}
|
|
2143
|
+
function truncate(text, max, originalLength) {
|
|
2144
|
+
const total = originalLength ?? text.length;
|
|
2145
|
+
if (total <= max)
|
|
2146
|
+
return text;
|
|
2147
|
+
return `${text.slice(0, max)}… [+${total - max} chars]`;
|
|
2148
|
+
}
|
|
2149
|
+
function redactThenTruncate(text, max) {
|
|
2150
|
+
const preCap = max * 4 + 64;
|
|
2151
|
+
const scanned = text.length > preCap ? text.slice(0, preCap) : text;
|
|
2152
|
+
return truncate(redactSecrets(scanned), max, text.length);
|
|
2153
|
+
}
|
|
2154
|
+
function redactStructure(value, depth = 0) {
|
|
2155
|
+
if (depth > 6)
|
|
2156
|
+
return REDACTION_MARK;
|
|
2157
|
+
if (typeof value === "string") {
|
|
2158
|
+
return redactThenTruncate(value, MAX_INPUT_STRING_CHARS);
|
|
2159
|
+
}
|
|
2160
|
+
if (Array.isArray(value)) {
|
|
2161
|
+
return value.slice(0, 20).map((item) => redactStructure(item, depth + 1));
|
|
2162
|
+
}
|
|
2163
|
+
if (value !== null && typeof value === "object") {
|
|
2164
|
+
const out = {};
|
|
2165
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2166
|
+
out[key] = redactStructure(item, depth + 1);
|
|
2167
|
+
}
|
|
2168
|
+
return out;
|
|
2169
|
+
}
|
|
2170
|
+
return value;
|
|
2171
|
+
}
|
|
2172
|
+
function redactToolCall(args) {
|
|
2173
|
+
const sensitive = sensitivePathsIn(args.input);
|
|
2174
|
+
if (sensitive.length > 0) {
|
|
2175
|
+
return { withheld: "sensitive-path" };
|
|
2176
|
+
}
|
|
2177
|
+
const result = {};
|
|
2178
|
+
if (args.input !== undefined) {
|
|
2179
|
+
let input = redactStructure(args.input);
|
|
2180
|
+
let serialized;
|
|
2181
|
+
try {
|
|
2182
|
+
serialized = JSON.stringify(input) ?? "";
|
|
2183
|
+
} catch {
|
|
2184
|
+
serialized = "";
|
|
2185
|
+
input = REDACTION_MARK;
|
|
2186
|
+
}
|
|
2187
|
+
if (serialized.length > MAX_INPUT_CHARS) {
|
|
2188
|
+
input = truncate(serialized, MAX_INPUT_CHARS);
|
|
2189
|
+
}
|
|
2190
|
+
result.input = input;
|
|
2191
|
+
}
|
|
2192
|
+
if (typeof args.output === "string" && args.output.length > 0) {
|
|
2193
|
+
result.output = redactThenTruncate(args.output, MAX_OUTPUT_CHARS);
|
|
2194
|
+
}
|
|
2195
|
+
return result;
|
|
2196
|
+
}
|
|
1953
2197
|
// ../harmony-shared/dist/stageHandoff.js
|
|
1954
2198
|
var HANDOFF_MARKER = "harmony:stage-handoff";
|
|
1955
2199
|
var HANDOFF_BLOCK_RE = new RegExp("```json\\s*\\n//\\s*" + HANDOFF_MARKER + "\\s*\\n([\\s\\S]*?)\\n```", "m");
|
package/dist/lib/api-client.js
CHANGED
|
@@ -18,12 +18,40 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
18
18
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { homedir } from "node:os";
|
|
20
20
|
import { dirname, join, parse, resolve } from "node:path";
|
|
21
|
+
function resetLegacyNoticesForTest() {
|
|
22
|
+
warnedLegacyConfigDir = false;
|
|
23
|
+
warnedLegacyLocalPin = false;
|
|
24
|
+
}
|
|
25
|
+
function noteLegacyConfigDir(path) {
|
|
26
|
+
if (warnedLegacyConfigDir)
|
|
27
|
+
return;
|
|
28
|
+
warnedLegacyConfigDir = true;
|
|
29
|
+
console.error(`Harmony: reading the pre-#1082 config at ${path}. ` + `The current location is ${getConfigPath()}; ` + `run the agent daemon once to migrate, or move the file yourself.`);
|
|
30
|
+
}
|
|
31
|
+
function noteLegacyLocalPin(path) {
|
|
32
|
+
if (warnedLegacyLocalPin)
|
|
33
|
+
return;
|
|
34
|
+
warnedLegacyLocalPin = true;
|
|
35
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Rename it to ${LOCAL_CONFIG_FILENAME} — the fallback that finds it is temporary.`);
|
|
36
|
+
}
|
|
37
|
+
function noteLocalPinRename(from, to) {
|
|
38
|
+
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|
|
39
|
+
}
|
|
40
|
+
function getHmyRootDir() {
|
|
41
|
+
return join(homedir(), CONFIG_DIR_NAME);
|
|
42
|
+
}
|
|
21
43
|
function getConfigDir() {
|
|
22
|
-
return join(
|
|
44
|
+
return join(getHmyRootDir(), CONFIG_DIR_SUBDIR);
|
|
45
|
+
}
|
|
46
|
+
function getLegacyConfigDir() {
|
|
47
|
+
return join(homedir(), LEGACY_CONFIG_DIR_NAME);
|
|
23
48
|
}
|
|
24
49
|
function getConfigPath() {
|
|
25
50
|
return join(getConfigDir(), "config.json");
|
|
26
51
|
}
|
|
52
|
+
function getLegacyConfigPath() {
|
|
53
|
+
return join(getLegacyConfigDir(), "config.json");
|
|
54
|
+
}
|
|
27
55
|
function getLocalConfigPath(cwd) {
|
|
28
56
|
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
29
57
|
}
|
|
@@ -33,9 +61,14 @@ function findLocalConfigPath(cwd) {
|
|
|
33
61
|
const { root } = parse(dir);
|
|
34
62
|
for (;; ) {
|
|
35
63
|
if (dir !== home && dir !== root) {
|
|
36
|
-
const
|
|
37
|
-
if (existsSync(
|
|
38
|
-
return
|
|
64
|
+
const current = join(dir, LOCAL_CONFIG_FILENAME);
|
|
65
|
+
if (existsSync(current))
|
|
66
|
+
return current;
|
|
67
|
+
const legacy = join(dir, LEGACY_LOCAL_CONFIG_FILENAME);
|
|
68
|
+
if (existsSync(legacy)) {
|
|
69
|
+
noteLegacyLocalPin(legacy);
|
|
70
|
+
return legacy;
|
|
71
|
+
}
|
|
39
72
|
}
|
|
40
73
|
const parent = dirname(dir);
|
|
41
74
|
if (parent === dir)
|
|
@@ -58,9 +91,12 @@ function emptyConfig() {
|
|
|
58
91
|
};
|
|
59
92
|
}
|
|
60
93
|
function loadConfig() {
|
|
61
|
-
|
|
94
|
+
let configPath = getConfigPath();
|
|
62
95
|
if (!existsSync(configPath)) {
|
|
63
|
-
|
|
96
|
+
configPath = getLegacyConfigPath();
|
|
97
|
+
if (!existsSync(configPath))
|
|
98
|
+
return emptyConfig();
|
|
99
|
+
noteLegacyConfigDir(configPath);
|
|
64
100
|
}
|
|
65
101
|
try {
|
|
66
102
|
const data = readFileSync(configPath, "utf-8");
|
|
@@ -110,7 +146,11 @@ function loadLocalConfig(cwd) {
|
|
|
110
146
|
}
|
|
111
147
|
}
|
|
112
148
|
function saveLocalConfig(config, cwd) {
|
|
113
|
-
const
|
|
149
|
+
const foundPath = findLocalConfigPath(cwd);
|
|
150
|
+
const localConfigPath = foundPath ? join(dirname(foundPath), LOCAL_CONFIG_FILENAME) : getLocalConfigPath(cwd);
|
|
151
|
+
if (foundPath !== null && foundPath !== localConfigPath) {
|
|
152
|
+
noteLocalPinRename(foundPath, localConfigPath);
|
|
153
|
+
}
|
|
114
154
|
const existingConfig = loadLocalConfig(cwd) || {
|
|
115
155
|
workspaceId: null,
|
|
116
156
|
projectId: null
|
|
@@ -122,6 +162,7 @@ function saveLocalConfig(config, cwd) {
|
|
|
122
162
|
if (newConfig.projectId)
|
|
123
163
|
cleanConfig.projectId = newConfig.projectId;
|
|
124
164
|
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
165
|
+
return localConfigPath;
|
|
125
166
|
}
|
|
126
167
|
function hasLocalConfig(cwd) {
|
|
127
168
|
return findLocalConfigPath(cwd) !== null;
|
|
@@ -259,7 +300,7 @@ function getMemoryDir() {
|
|
|
259
300
|
return config.memoryDir;
|
|
260
301
|
return join(homedir(), ".harmony", "memory");
|
|
261
302
|
}
|
|
262
|
-
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
303
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".hmy.json", LEGACY_LOCAL_CONFIG_FILENAME = ".harmony-mcp.json", CONFIG_DIR_NAME = ".hmy", CONFIG_DIR_SUBDIR = "agent", LEGACY_CONFIG_DIR_NAME = ".harmony-mcp", warnedLegacyConfigDir = false, warnedLegacyLocalPin = false;
|
|
263
304
|
var init_config = () => {};
|
|
264
305
|
|
|
265
306
|
// src/oauth-login.ts
|
|
@@ -973,6 +1014,74 @@ var REVIEW_DISALLOWED_TOOLS = [
|
|
|
973
1014
|
"mcp__harmony__harmony_delete_subtask",
|
|
974
1015
|
"mcp__harmony__harmony_toggle_subtask"
|
|
975
1016
|
];
|
|
1017
|
+
// ../harmony-shared/dist/runRedaction.js
|
|
1018
|
+
var REDACTION_MARK = "«redacted»";
|
|
1019
|
+
var CONFIG_SCOPED_SEGMENTS = new Set([
|
|
1020
|
+
"gh",
|
|
1021
|
+
"gcloud",
|
|
1022
|
+
"op",
|
|
1023
|
+
"anthropic"
|
|
1024
|
+
]);
|
|
1025
|
+
var SENSITIVE_BASENAMES = new Set([
|
|
1026
|
+
".netrc",
|
|
1027
|
+
"_netrc",
|
|
1028
|
+
".npmrc",
|
|
1029
|
+
".pgpass",
|
|
1030
|
+
".git-credentials",
|
|
1031
|
+
".htpasswd",
|
|
1032
|
+
".claude.json",
|
|
1033
|
+
"credentials",
|
|
1034
|
+
".credentials",
|
|
1035
|
+
"credentials.json",
|
|
1036
|
+
".credentials.json",
|
|
1037
|
+
"credentials.yml",
|
|
1038
|
+
"credentials.yaml",
|
|
1039
|
+
"auth.json",
|
|
1040
|
+
".auth.json",
|
|
1041
|
+
"secrets",
|
|
1042
|
+
"secrets.json",
|
|
1043
|
+
"secrets.yaml",
|
|
1044
|
+
"secrets.yml",
|
|
1045
|
+
"id_rsa",
|
|
1046
|
+
"id_dsa",
|
|
1047
|
+
"id_ecdsa",
|
|
1048
|
+
"id_ed25519",
|
|
1049
|
+
"known_hosts"
|
|
1050
|
+
]);
|
|
1051
|
+
var SECRET_PATTERNS = [
|
|
1052
|
+
{
|
|
1053
|
+
pattern: /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*-----/g,
|
|
1054
|
+
replace: REDACTION_MARK
|
|
1055
|
+
},
|
|
1056
|
+
{ pattern: /\bhmy_at_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
1057
|
+
{ pattern: /\bhmy_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
1058
|
+
{ pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, replace: REDACTION_MARK },
|
|
1059
|
+
{ pattern: /\bgh[pousr]_[A-Za-z0-9]{16,}/g, replace: REDACTION_MARK },
|
|
1060
|
+
{ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, replace: REDACTION_MARK },
|
|
1061
|
+
{ pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, replace: REDACTION_MARK },
|
|
1062
|
+
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, replace: REDACTION_MARK },
|
|
1063
|
+
{ pattern: /\bAIza[0-9A-Za-z_-]{20,}/g, replace: REDACTION_MARK },
|
|
1064
|
+
{
|
|
1065
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
1066
|
+
replace: REDACTION_MARK
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
pattern: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
1070
|
+
replace: `$1 ${REDACTION_MARK}`
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
pattern: /(\w{1,32}:\/\/)[^/\s:@]+:[^/\s@]+@/g,
|
|
1074
|
+
replace: `$1${REDACTION_MARK}@`
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
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,
|
|
1078
|
+
replace: `$1=${REDACTION_MARK}`
|
|
1079
|
+
},
|
|
1080
|
+
{
|
|
1081
|
+
pattern: /(--?(?:password|passwd|token|api-?key|secret|auth)(?:=|\s+))(?:"[^"]*"|'[^']*'|[^\s]+)/gi,
|
|
1082
|
+
replace: `$1${REDACTION_MARK}`
|
|
1083
|
+
}
|
|
1084
|
+
];
|
|
976
1085
|
// ../harmony-shared/dist/stageHandoff.js
|
|
977
1086
|
var HANDOFF_MARKER = "harmony:stage-handoff";
|
|
978
1087
|
var HANDOFF_BLOCK_RE = new RegExp("```json\\s*\\n//\\s*" + HANDOFF_MARKER + "\\s*\\n([\\s\\S]*?)\\n```", "m");
|
package/dist/lib/config.js
CHANGED
|
@@ -18,12 +18,40 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
18
18
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { homedir } from "node:os";
|
|
20
20
|
import { dirname, join, parse, resolve } from "node:path";
|
|
21
|
+
function resetLegacyNoticesForTest() {
|
|
22
|
+
warnedLegacyConfigDir = false;
|
|
23
|
+
warnedLegacyLocalPin = false;
|
|
24
|
+
}
|
|
25
|
+
function noteLegacyConfigDir(path) {
|
|
26
|
+
if (warnedLegacyConfigDir)
|
|
27
|
+
return;
|
|
28
|
+
warnedLegacyConfigDir = true;
|
|
29
|
+
console.error(`Harmony: reading the pre-#1082 config at ${path}. ` + `The current location is ${getConfigPath()}; ` + `run the agent daemon once to migrate, or move the file yourself.`);
|
|
30
|
+
}
|
|
31
|
+
function noteLegacyLocalPin(path) {
|
|
32
|
+
if (warnedLegacyLocalPin)
|
|
33
|
+
return;
|
|
34
|
+
warnedLegacyLocalPin = true;
|
|
35
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Rename it to ${LOCAL_CONFIG_FILENAME} — the fallback that finds it is temporary.`);
|
|
36
|
+
}
|
|
37
|
+
function noteLocalPinRename(from, to) {
|
|
38
|
+
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|
|
39
|
+
}
|
|
40
|
+
function getHmyRootDir() {
|
|
41
|
+
return join(homedir(), CONFIG_DIR_NAME);
|
|
42
|
+
}
|
|
21
43
|
function getConfigDir() {
|
|
22
|
-
return join(
|
|
44
|
+
return join(getHmyRootDir(), CONFIG_DIR_SUBDIR);
|
|
45
|
+
}
|
|
46
|
+
function getLegacyConfigDir() {
|
|
47
|
+
return join(homedir(), LEGACY_CONFIG_DIR_NAME);
|
|
23
48
|
}
|
|
24
49
|
function getConfigPath() {
|
|
25
50
|
return join(getConfigDir(), "config.json");
|
|
26
51
|
}
|
|
52
|
+
function getLegacyConfigPath() {
|
|
53
|
+
return join(getLegacyConfigDir(), "config.json");
|
|
54
|
+
}
|
|
27
55
|
function getLocalConfigPath(cwd) {
|
|
28
56
|
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
29
57
|
}
|
|
@@ -33,9 +61,14 @@ function findLocalConfigPath(cwd) {
|
|
|
33
61
|
const { root } = parse(dir);
|
|
34
62
|
for (;; ) {
|
|
35
63
|
if (dir !== home && dir !== root) {
|
|
36
|
-
const
|
|
37
|
-
if (existsSync(
|
|
38
|
-
return
|
|
64
|
+
const current = join(dir, LOCAL_CONFIG_FILENAME);
|
|
65
|
+
if (existsSync(current))
|
|
66
|
+
return current;
|
|
67
|
+
const legacy = join(dir, LEGACY_LOCAL_CONFIG_FILENAME);
|
|
68
|
+
if (existsSync(legacy)) {
|
|
69
|
+
noteLegacyLocalPin(legacy);
|
|
70
|
+
return legacy;
|
|
71
|
+
}
|
|
39
72
|
}
|
|
40
73
|
const parent = dirname(dir);
|
|
41
74
|
if (parent === dir)
|
|
@@ -58,9 +91,12 @@ function emptyConfig() {
|
|
|
58
91
|
};
|
|
59
92
|
}
|
|
60
93
|
function loadConfig() {
|
|
61
|
-
|
|
94
|
+
let configPath = getConfigPath();
|
|
62
95
|
if (!existsSync(configPath)) {
|
|
63
|
-
|
|
96
|
+
configPath = getLegacyConfigPath();
|
|
97
|
+
if (!existsSync(configPath))
|
|
98
|
+
return emptyConfig();
|
|
99
|
+
noteLegacyConfigDir(configPath);
|
|
64
100
|
}
|
|
65
101
|
try {
|
|
66
102
|
const data = readFileSync(configPath, "utf-8");
|
|
@@ -110,7 +146,11 @@ function loadLocalConfig(cwd) {
|
|
|
110
146
|
}
|
|
111
147
|
}
|
|
112
148
|
function saveLocalConfig(config, cwd) {
|
|
113
|
-
const
|
|
149
|
+
const foundPath = findLocalConfigPath(cwd);
|
|
150
|
+
const localConfigPath = foundPath ? join(dirname(foundPath), LOCAL_CONFIG_FILENAME) : getLocalConfigPath(cwd);
|
|
151
|
+
if (foundPath !== null && foundPath !== localConfigPath) {
|
|
152
|
+
noteLocalPinRename(foundPath, localConfigPath);
|
|
153
|
+
}
|
|
114
154
|
const existingConfig = loadLocalConfig(cwd) || {
|
|
115
155
|
workspaceId: null,
|
|
116
156
|
projectId: null
|
|
@@ -122,6 +162,7 @@ function saveLocalConfig(config, cwd) {
|
|
|
122
162
|
if (newConfig.projectId)
|
|
123
163
|
cleanConfig.projectId = newConfig.projectId;
|
|
124
164
|
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
165
|
+
return localConfigPath;
|
|
125
166
|
}
|
|
126
167
|
function hasLocalConfig(cwd) {
|
|
127
168
|
return findLocalConfigPath(cwd) !== null;
|
|
@@ -259,7 +300,7 @@ function getMemoryDir() {
|
|
|
259
300
|
return config.memoryDir;
|
|
260
301
|
return join(homedir(), ".harmony", "memory");
|
|
261
302
|
}
|
|
262
|
-
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
303
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".hmy.json", LEGACY_LOCAL_CONFIG_FILENAME = ".harmony-mcp.json", CONFIG_DIR_NAME = ".hmy", CONFIG_DIR_SUBDIR = "agent", LEGACY_CONFIG_DIR_NAME = ".harmony-mcp", warnedLegacyConfigDir = false, warnedLegacyLocalPin = false;
|
|
263
304
|
var init_config = () => {};
|
|
264
305
|
init_config();
|
|
265
306
|
|
|
@@ -269,6 +310,8 @@ export {
|
|
|
269
310
|
setActiveContext,
|
|
270
311
|
saveLocalConfig,
|
|
271
312
|
saveConfig,
|
|
313
|
+
resetLegacyNoticesForTest,
|
|
314
|
+
noteLegacyLocalPin,
|
|
272
315
|
loadLocalConfig,
|
|
273
316
|
loadConfig,
|
|
274
317
|
isConfigured,
|
|
@@ -277,6 +320,9 @@ export {
|
|
|
277
320
|
getUserEmail,
|
|
278
321
|
getMemoryDir,
|
|
279
322
|
getLocalConfigPath,
|
|
323
|
+
getLegacyConfigPath,
|
|
324
|
+
getLegacyConfigDir,
|
|
325
|
+
getHmyRootDir,
|
|
280
326
|
getConfigPath,
|
|
281
327
|
getConfigDir,
|
|
282
328
|
getApiUrl,
|
|
@@ -287,5 +333,6 @@ export {
|
|
|
287
333
|
getActiveContext,
|
|
288
334
|
findLocalConfigPath,
|
|
289
335
|
describeActiveContext,
|
|
290
|
-
areSkillsInstalled
|
|
336
|
+
areSkillsInstalled,
|
|
337
|
+
LEGACY_LOCAL_CONFIG_FILENAME
|
|
291
338
|
};
|