@yemi33/minions 0.1.1727 → 0.1.1728
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/CHANGELOG.md +5 -0
- package/engine/copilot-models.json +1 -1
- package/engine/issues.js +66 -3
- package/engine/shared.js +55 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/engine/issues.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { execFileSync: _execFileSync } = require('child_process');
|
|
8
|
+
const shared = require('./shared');
|
|
8
9
|
|
|
9
10
|
const DEFAULT_REPO = 'yemi33/minions';
|
|
10
11
|
const DEFAULT_LABELS = ['bug'];
|
|
@@ -117,6 +118,63 @@ function buildWarning(labelsSkipped, filedWithoutLabels) {
|
|
|
117
118
|
return filedWithoutLabels ? `${base} Filed without labels.` : base;
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
function _addRedactionIdentifier(entries, seen, value, replacement) {
|
|
122
|
+
const clean = String(value || '').trim().replace(/\/+$/, '');
|
|
123
|
+
if (clean.length < 4) return;
|
|
124
|
+
const key = `${replacement}:${clean.toLowerCase()}`;
|
|
125
|
+
if (seen.has(key)) return;
|
|
126
|
+
seen.add(key);
|
|
127
|
+
entries.push({ value: clean, replacement });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _stripPrUrlBase(url) {
|
|
131
|
+
return String(url || '')
|
|
132
|
+
.trim()
|
|
133
|
+
.replace(/\/(?:pull|pullrequest)\/?$/i, '')
|
|
134
|
+
.replace(/\/+$/, '');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function _buildIssueRedactionIdentifiers({ repo, projects = [] } = {}) {
|
|
138
|
+
const entries = [];
|
|
139
|
+
const seen = new Set();
|
|
140
|
+
_addRedactionIdentifier(entries, seen, repo, '[REDACTED_REPO]');
|
|
141
|
+
|
|
142
|
+
for (const project of projects || []) {
|
|
143
|
+
if (!project || typeof project !== 'object') continue;
|
|
144
|
+
const owner = project.adoOrg || project.org || project.owner || '';
|
|
145
|
+
const repoName = project.repoName || project.repositoryName || '';
|
|
146
|
+
const adoProject = project.adoProject || project.project || '';
|
|
147
|
+
|
|
148
|
+
if (owner && repoName) {
|
|
149
|
+
_addRedactionIdentifier(entries, seen, `${owner}/${repoName}`, '[REDACTED_REPO]');
|
|
150
|
+
}
|
|
151
|
+
if (owner && adoProject && repoName) {
|
|
152
|
+
_addRedactionIdentifier(entries, seen, `${owner}/${adoProject}/_git/${repoName}`, '[REDACTED_REPO]');
|
|
153
|
+
_addRedactionIdentifier(entries, seen, `${owner}/${adoProject}/${repoName}`, '[REDACTED_REPO]');
|
|
154
|
+
}
|
|
155
|
+
const repositoryId = String(project.repositoryId || '').trim();
|
|
156
|
+
if (repositoryId.length >= 8) {
|
|
157
|
+
_addRedactionIdentifier(entries, seen, repositoryId, '[REDACTED_REPOSITORY_ID]');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const field of ['remoteUrl', 'repositoryUrl', 'cloneUrl', 'sshUrl', 'webUrl']) {
|
|
161
|
+
_addRedactionIdentifier(entries, seen, project[field], '[REDACTED_REPO_URL]');
|
|
162
|
+
}
|
|
163
|
+
const prBase = _stripPrUrlBase(project.prUrlBase);
|
|
164
|
+
_addRedactionIdentifier(entries, seen, prBase, '[REDACTED_REPO_URL]');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
entries.sort((a, b) => b.value.length - a.value.length);
|
|
168
|
+
return entries;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function _redactIssueContent(value, { repo, projects } = {}) {
|
|
172
|
+
return shared.redactSecrets(String(value || ''), {
|
|
173
|
+
redactRepositoryUrls: true,
|
|
174
|
+
repositoryIdentifiers: _buildIssueRedactionIdentifiers({ repo, projects }),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
120
178
|
function createIssueWithLabels({ title, bodyFile, repo, labels, execFileSync }) {
|
|
121
179
|
const args = ['issue', 'create', '--repo', repo, '--title', title, '--body-file', bodyFile];
|
|
122
180
|
if (labels.length > 0) args.push('--label', labels.join(','));
|
|
@@ -133,6 +191,7 @@ function createGitHubIssue({
|
|
|
133
191
|
description = '',
|
|
134
192
|
labels,
|
|
135
193
|
repo = DEFAULT_REPO,
|
|
194
|
+
projects,
|
|
136
195
|
tmpDir,
|
|
137
196
|
execFileSync = _execFileSync,
|
|
138
197
|
} = {}) {
|
|
@@ -144,7 +203,10 @@ function createGitHubIssue({
|
|
|
144
203
|
throw new GitHubIssueError('gh CLI not found. Install from https://cli.github.com/');
|
|
145
204
|
}
|
|
146
205
|
|
|
147
|
-
const
|
|
206
|
+
const redactionProjects = projects || shared.getProjects();
|
|
207
|
+
const safeTitle = _redactIssueContent(title, { repo, projects: redactionProjects });
|
|
208
|
+
const safeDescription = _redactIssueContent(description || '', { repo, projects: redactionProjects });
|
|
209
|
+
const issueBody = `${safeDescription}\n\n---\n_Filed via Minions dashboard_`;
|
|
148
210
|
const dir = tmpDir || path.join(__dirname, 'tmp');
|
|
149
211
|
fs.mkdirSync(dir, { recursive: true });
|
|
150
212
|
const bodyFile = path.join(dir, `bug-body-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.md`);
|
|
@@ -154,7 +216,7 @@ function createGitHubIssue({
|
|
|
154
216
|
try {
|
|
155
217
|
resolved = resolveLabels({ labels, repo, execFileSync });
|
|
156
218
|
const created = createIssueWithLabels({
|
|
157
|
-
title,
|
|
219
|
+
title: safeTitle,
|
|
158
220
|
bodyFile,
|
|
159
221
|
repo,
|
|
160
222
|
labels: resolved.labelsToApply,
|
|
@@ -175,7 +237,7 @@ function createGitHubIssue({
|
|
|
175
237
|
if (isAuthError(e)) throw new GitHubIssueError('GitHub auth required. Run: gh auth login', 401);
|
|
176
238
|
if (resolved && resolved.labelsToApply.length > 0 && isLabelUnavailableError(e)) {
|
|
177
239
|
try {
|
|
178
|
-
const created = createIssueWithLabels({ title, bodyFile, repo, labels: [], execFileSync });
|
|
240
|
+
const created = createIssueWithLabels({ title: safeTitle, bodyFile, repo, labels: [], execFileSync });
|
|
179
241
|
const skipped = normalizeLabels([...resolved.labelsSkipped, ...resolved.labelsToApply], []);
|
|
180
242
|
return {
|
|
181
243
|
ok: true,
|
|
@@ -203,4 +265,5 @@ module.exports = {
|
|
|
203
265
|
normalizeLabels,
|
|
204
266
|
isLabelUnavailableError,
|
|
205
267
|
createGitHubIssue,
|
|
268
|
+
_buildIssueRedactionIdentifiers,
|
|
206
269
|
};
|
package/engine/shared.js
CHANGED
|
@@ -42,22 +42,71 @@ function dateStamp() { return new Date().toISOString().slice(0, 10); }
|
|
|
42
42
|
const _BEARER_RE = /Bearer\s+[A-Za-z0-9+/=._\-]{20,}/g;
|
|
43
43
|
const _JWT_RE = /ey[A-Za-z0-9_\-]{10,}\.[A-Za-z0-9_\-]{10,}(?:\.[A-Za-z0-9_\-]{10,})?/g;
|
|
44
44
|
const _AZUREAUTH_RE = /"token"\s*:\s*"[A-Za-z0-9+/=._\-]{20,}"/g;
|
|
45
|
+
const _URL_RE = /\b(?:https?|ssh):\/\/[^\s<>"'`]+/gi;
|
|
46
|
+
const _GITHUB_REPO_URL_RE = /\b(?:(?:https?:\/\/|ssh:\/\/git@)github\.com[/:]|git@github\.com:)[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?(?:\/[^\s<>"'`]*)?/gi;
|
|
47
|
+
const _ADO_DEV_REPO_URL_RE = /\bhttps?:\/\/dev\.azure\.com\/[^/\s<>"'`]+\/[^/\s<>"'`]+\/_git\/[^/\s<>"'`)]+(?:\/[^\s<>"'`]*)?/gi;
|
|
48
|
+
const _ADO_VISUALSTUDIO_REPO_URL_RE = /\bhttps?:\/\/[^/\s<>"'`]+\.visualstudio\.com\/(?:DefaultCollection\/)?[^/\s<>"'`]+\/_git\/[^/\s<>"'`)]+(?:\/[^\s<>"'`]*)?/gi;
|
|
49
|
+
const _ADO_SSH_REPO_URL_RE = /\b(?:ssh:\/\/)?git@ssh\.dev\.azure\.com[:/]v3\/[^/\s<>"'`]+\/[^/\s<>"'`]+\/[^/\s<>"'`)]+/gi;
|
|
50
|
+
const _TOKEN_URL_PARAM_RE = /[?&](?:access[_-]?token|auth[_-]?token|token|api[_-]?key|sig|signature|pat)=/i;
|
|
51
|
+
const _URL_CREDENTIALS_RE = /^[a-z][a-z0-9+.-]*:\/\/[^/\s@]+@/i;
|
|
45
52
|
|
|
46
|
-
function
|
|
47
|
-
|
|
53
|
+
function _escapeRegExp(s) {
|
|
54
|
+
return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _redactedWithTrailingPunctuation(raw, replacement) {
|
|
58
|
+
const match = String(raw).match(/^(.+?)([.,;:!?)]*)$/);
|
|
59
|
+
return replacement + (match ? match[2] : '');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _redactUrlMatch(raw, replacement) {
|
|
63
|
+
return _redactedWithTrailingPunctuation(raw, replacement);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _redactTokenBearingUrls(s) {
|
|
67
|
+
return s.replace(_URL_RE, url => (
|
|
68
|
+
_TOKEN_URL_PARAM_RE.test(url) || _URL_CREDENTIALS_RE.test(url)
|
|
69
|
+
? _redactUrlMatch(url, '[REDACTED_URL]')
|
|
70
|
+
: url
|
|
71
|
+
));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _redactRepositoryUrls(s) {
|
|
48
75
|
return s
|
|
76
|
+
.replace(_GITHUB_REPO_URL_RE, url => _redactUrlMatch(url, '[REDACTED_REPO_URL]'))
|
|
77
|
+
.replace(_ADO_DEV_REPO_URL_RE, url => _redactUrlMatch(url, '[REDACTED_REPO_URL]'))
|
|
78
|
+
.replace(_ADO_VISUALSTUDIO_REPO_URL_RE, url => _redactUrlMatch(url, '[REDACTED_REPO_URL]'))
|
|
79
|
+
.replace(_ADO_SSH_REPO_URL_RE, url => _redactUrlMatch(url, '[REDACTED_REPO_URL]'));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function _redactConfiguredRepositoryIdentifiers(s, options) {
|
|
83
|
+
const entries = Array.isArray(options?.repositoryIdentifiers) ? options.repositoryIdentifiers : [];
|
|
84
|
+
let out = s;
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
const value = typeof entry === 'string' ? entry : entry?.value;
|
|
87
|
+
const replacement = typeof entry === 'string' ? '[REDACTED_REPO]' : (entry?.replacement || '[REDACTED_REPO]');
|
|
88
|
+
if (typeof value !== 'string' || value.length < 4) continue;
|
|
89
|
+
out = out.replace(new RegExp(_escapeRegExp(value), 'gi'), replacement);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function _redactString(s, options = {}) {
|
|
95
|
+
if (typeof s !== 'string' || s.length === 0) return s;
|
|
96
|
+
const repoRedacted = options.redactRepositoryUrls ? _redactRepositoryUrls(s) : s;
|
|
97
|
+
return _redactTokenBearingUrls(_redactConfiguredRepositoryIdentifiers(repoRedacted, options))
|
|
49
98
|
.replace(_AZUREAUTH_RE, '"token":"[REDACTED_AZUREAUTH]"')
|
|
50
99
|
.replace(_BEARER_RE, 'Bearer [REDACTED]')
|
|
51
100
|
.replace(_JWT_RE, '[REDACTED_JWT]');
|
|
52
101
|
}
|
|
53
102
|
|
|
54
|
-
function redactSecrets(value) {
|
|
103
|
+
function redactSecrets(value, options = {}) {
|
|
55
104
|
if (value == null) return value;
|
|
56
|
-
if (typeof value === 'string') return _redactString(value);
|
|
57
|
-
if (Array.isArray(value)) return value.map(redactSecrets);
|
|
105
|
+
if (typeof value === 'string') return _redactString(value, options);
|
|
106
|
+
if (Array.isArray(value)) return value.map(v => redactSecrets(v, options));
|
|
58
107
|
if (typeof value === 'object') {
|
|
59
108
|
const out = {};
|
|
60
|
-
for (const k of Object.keys(value)) out[k] = redactSecrets(value[k]);
|
|
109
|
+
for (const k of Object.keys(value)) out[k] = redactSecrets(value[k], options);
|
|
61
110
|
return out;
|
|
62
111
|
}
|
|
63
112
|
return value;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1728",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|