@starterculture/devkeep-actions 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/consolidate-release.js +156 -6
- package/dist/crypting-candidates-scan.js +158 -8
- package/dist/draft-log-entry.js +161 -32
- package/dist/draft-release-notes.js +159 -32
- package/dist/sync-onboarding.js +159 -32
- package/package.json +1 -1
|
@@ -127,6 +127,159 @@ function defaultModelFor(tier) {
|
|
|
127
127
|
return current.id;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// ../../modules/core/dist/redactSecrets.js
|
|
131
|
+
var REDACTED = "[REDACTED]";
|
|
132
|
+
var KNOWN_SECRET_PATTERNS = [
|
|
133
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
134
|
+
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
135
|
+
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
136
|
+
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
137
|
+
/sk-[A-Za-z0-9]{20,}/g,
|
|
138
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
139
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
140
|
+
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
141
|
+
];
|
|
142
|
+
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
143
|
+
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
144
|
+
function redactText(text) {
|
|
145
|
+
let result = text;
|
|
146
|
+
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
147
|
+
result = result.replace(pattern, REDACTED);
|
|
148
|
+
}
|
|
149
|
+
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ../../modules/core/dist/ciHealth.js
|
|
154
|
+
var EMPTY_ACTION_HEALTH = {
|
|
155
|
+
lastSuccessAt: null,
|
|
156
|
+
lastFailureAt: null,
|
|
157
|
+
lastFailureMessage: null,
|
|
158
|
+
lastFailureRunUrl: null,
|
|
159
|
+
consecutiveFailures: 0
|
|
160
|
+
};
|
|
161
|
+
var MAX_MESSAGE_CHARS = 2e3;
|
|
162
|
+
function healthPath(projectName) {
|
|
163
|
+
return `projects/${projectName}/health.json`;
|
|
164
|
+
}
|
|
165
|
+
function describeError(error) {
|
|
166
|
+
const raw = error instanceof Error ? `${error.name}: ${error.message}` : typeof error === "string" ? error : JSON.stringify(error);
|
|
167
|
+
const text = raw ?? "Unknown error";
|
|
168
|
+
return redactText(text.length > MAX_MESSAGE_CHARS ? `${text.slice(0, MAX_MESSAGE_CHARS)}\u2026` : text);
|
|
169
|
+
}
|
|
170
|
+
function applySuccess(current, source, now) {
|
|
171
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
172
|
+
return {
|
|
173
|
+
...current,
|
|
174
|
+
// The failure details are deliberately kept, not cleared: "it failed
|
|
175
|
+
// on Tuesday and has worked since" is worth being able to read.
|
|
176
|
+
[source]: { ...previous, lastSuccessAt: now.toISOString(), consecutiveFailures: 0 }
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function applyFailure(current, source, error, runUrl, now) {
|
|
180
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
181
|
+
return {
|
|
182
|
+
...current,
|
|
183
|
+
[source]: {
|
|
184
|
+
...previous,
|
|
185
|
+
lastFailureAt: now.toISOString(),
|
|
186
|
+
lastFailureMessage: describeError(error),
|
|
187
|
+
lastFailureRunUrl: runUrl,
|
|
188
|
+
consecutiveFailures: previous.consecutiveFailures + 1
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function getHealthFile(octokit, { owner, repo, projectName }) {
|
|
193
|
+
try {
|
|
194
|
+
const { data } = await octokit.rest.repos.getContent({ owner, repo, path: healthPath(projectName) });
|
|
195
|
+
if (Array.isArray(data) || data.type !== "file") {
|
|
196
|
+
return { health: {} };
|
|
197
|
+
}
|
|
198
|
+
return { health: JSON.parse(Buffer.from(data.content, "base64").toString("utf-8")), sha: data.sha };
|
|
199
|
+
} catch {
|
|
200
|
+
return { health: {} };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function writeHealth(octokit, params, update, message) {
|
|
204
|
+
const { owner, repo, projectName, branch } = params;
|
|
205
|
+
await retryOnConflict(async () => {
|
|
206
|
+
const { health, sha } = await getHealthFile(octokit, params);
|
|
207
|
+
await octokit.rest.repos.createOrUpdateFileContents({
|
|
208
|
+
owner,
|
|
209
|
+
repo,
|
|
210
|
+
path: healthPath(projectName),
|
|
211
|
+
message,
|
|
212
|
+
content: Buffer.from(JSON.stringify(update(health), null, 2), "utf-8").toString("base64"),
|
|
213
|
+
branch,
|
|
214
|
+
sha
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function recordSuccess(octokit, params, source, now = /* @__PURE__ */ new Date()) {
|
|
219
|
+
await writeHealth(octokit, params, (h) => applySuccess(h, source, now), `Record ${source} success: ${params.projectName}`);
|
|
220
|
+
}
|
|
221
|
+
async function recordFailure(octokit, params, source, error, runUrl, now = /* @__PURE__ */ new Date()) {
|
|
222
|
+
await writeHealth(octokit, params, (h) => applyFailure(h, source, error, runUrl, now), `Record ${source} failure: ${params.projectName}`);
|
|
223
|
+
}
|
|
224
|
+
function currentRunUrl(env) {
|
|
225
|
+
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID } = env;
|
|
226
|
+
if (!GITHUB_SERVER_URL || !GITHUB_REPOSITORY || !GITHUB_RUN_ID) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ../../modules/core/dist/ciEntrypoint.js
|
|
233
|
+
var VAULT_BRANCH = "main";
|
|
234
|
+
function healthParams(env) {
|
|
235
|
+
const missing = ["PROJECT_NAME", "VAULT_REPO", "DEVKEEP_GITHUB_TOKEN"].filter((name) => !env[name]);
|
|
236
|
+
if (missing.length > 0) {
|
|
237
|
+
return { missing: [...missing] };
|
|
238
|
+
}
|
|
239
|
+
const [owner, repo] = env.VAULT_REPO.split("/");
|
|
240
|
+
if (!owner || !repo) {
|
|
241
|
+
return { missing: ["VAULT_REPO (expected owner/repo)"] };
|
|
242
|
+
}
|
|
243
|
+
return { params: { owner, repo, projectName: env.PROJECT_NAME, branch: VAULT_BRANCH } };
|
|
244
|
+
}
|
|
245
|
+
var defaultDeps = {
|
|
246
|
+
env: process.env,
|
|
247
|
+
createGithubClient,
|
|
248
|
+
recordSuccess,
|
|
249
|
+
recordFailure,
|
|
250
|
+
logError: console.error,
|
|
251
|
+
exit: (code) => process.exit(code)
|
|
252
|
+
};
|
|
253
|
+
async function runCiEntrypoint(source, main2, deps = defaultDeps) {
|
|
254
|
+
const resolved = healthParams(deps.env);
|
|
255
|
+
const params = "params" in resolved ? resolved.params : null;
|
|
256
|
+
if (!params) {
|
|
257
|
+
deps.logError(`Health not recorded for "${source}": ${resolved.missing.join(", ")} not set.`);
|
|
258
|
+
}
|
|
259
|
+
const octokit = params ? deps.createGithubClient(deps.env.DEVKEEP_GITHUB_TOKEN) : null;
|
|
260
|
+
try {
|
|
261
|
+
await main2();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
deps.logError(error);
|
|
264
|
+
if (params && octokit) {
|
|
265
|
+
try {
|
|
266
|
+
await deps.recordFailure(octokit, params, source, error, currentRunUrl(deps.env));
|
|
267
|
+
} catch (recordingError) {
|
|
268
|
+
deps.logError(`Could not record the failure to the vault: ${String(recordingError)}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
deps.exit(1);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (params && octokit) {
|
|
275
|
+
try {
|
|
276
|
+
await deps.recordSuccess(octokit, params, source);
|
|
277
|
+
} catch (recordingError) {
|
|
278
|
+
deps.logError(`Ran successfully, but could not record it to the vault: ${String(recordingError)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
130
283
|
// ../../modules/devlore/dist/ciUsage.js
|
|
131
284
|
var EMPTY_PROJECT_USAGE = {
|
|
132
285
|
draftLogEntry: ZERO_USAGE,
|
|
@@ -429,14 +582,14 @@ function requireEnv(env, name) {
|
|
|
429
582
|
}
|
|
430
583
|
return value;
|
|
431
584
|
}
|
|
432
|
-
var
|
|
585
|
+
var defaultDeps2 = {
|
|
433
586
|
env: process.env,
|
|
434
587
|
consolidateRelease,
|
|
435
588
|
createGithubClient,
|
|
436
589
|
createClaudeClient,
|
|
437
590
|
log: console.log
|
|
438
591
|
};
|
|
439
|
-
async function main(deps =
|
|
592
|
+
async function main(deps = defaultDeps2) {
|
|
440
593
|
const { env } = deps;
|
|
441
594
|
const projectName = requireEnv(env, "PROJECT_NAME");
|
|
442
595
|
const [projectOwner, projectRepoName] = requireEnv(env, "PROJECT_REPO").split("/");
|
|
@@ -459,10 +612,7 @@ async function main(deps = defaultDeps) {
|
|
|
459
612
|
deps.log(`Opened consolidation PR #${result.prNumber}: ${result.prUrl}`);
|
|
460
613
|
}
|
|
461
614
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
462
|
-
main
|
|
463
|
-
console.error(error);
|
|
464
|
-
process.exit(1);
|
|
465
|
-
});
|
|
615
|
+
void runCiEntrypoint("consolidateRelease", main);
|
|
466
616
|
}
|
|
467
617
|
export {
|
|
468
618
|
main,
|
|
@@ -24,6 +24,159 @@ async function retryOnConflict(attempt, maxAttempts = 3) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// ../../modules/core/dist/redactSecrets.js
|
|
28
|
+
var REDACTED = "[REDACTED]";
|
|
29
|
+
var KNOWN_SECRET_PATTERNS = [
|
|
30
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
31
|
+
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
32
|
+
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
33
|
+
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
34
|
+
/sk-[A-Za-z0-9]{20,}/g,
|
|
35
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
36
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
37
|
+
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
38
|
+
];
|
|
39
|
+
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
40
|
+
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
41
|
+
function redactText(text) {
|
|
42
|
+
let result = text;
|
|
43
|
+
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
44
|
+
result = result.replace(pattern, REDACTED);
|
|
45
|
+
}
|
|
46
|
+
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ../../modules/core/dist/ciHealth.js
|
|
51
|
+
var EMPTY_ACTION_HEALTH = {
|
|
52
|
+
lastSuccessAt: null,
|
|
53
|
+
lastFailureAt: null,
|
|
54
|
+
lastFailureMessage: null,
|
|
55
|
+
lastFailureRunUrl: null,
|
|
56
|
+
consecutiveFailures: 0
|
|
57
|
+
};
|
|
58
|
+
var MAX_MESSAGE_CHARS = 2e3;
|
|
59
|
+
function healthPath(projectName) {
|
|
60
|
+
return `projects/${projectName}/health.json`;
|
|
61
|
+
}
|
|
62
|
+
function describeError(error) {
|
|
63
|
+
const raw = error instanceof Error ? `${error.name}: ${error.message}` : typeof error === "string" ? error : JSON.stringify(error);
|
|
64
|
+
const text = raw ?? "Unknown error";
|
|
65
|
+
return redactText(text.length > MAX_MESSAGE_CHARS ? `${text.slice(0, MAX_MESSAGE_CHARS)}\u2026` : text);
|
|
66
|
+
}
|
|
67
|
+
function applySuccess(current, source, now) {
|
|
68
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
69
|
+
return {
|
|
70
|
+
...current,
|
|
71
|
+
// The failure details are deliberately kept, not cleared: "it failed
|
|
72
|
+
// on Tuesday and has worked since" is worth being able to read.
|
|
73
|
+
[source]: { ...previous, lastSuccessAt: now.toISOString(), consecutiveFailures: 0 }
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function applyFailure(current, source, error, runUrl, now) {
|
|
77
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
78
|
+
return {
|
|
79
|
+
...current,
|
|
80
|
+
[source]: {
|
|
81
|
+
...previous,
|
|
82
|
+
lastFailureAt: now.toISOString(),
|
|
83
|
+
lastFailureMessage: describeError(error),
|
|
84
|
+
lastFailureRunUrl: runUrl,
|
|
85
|
+
consecutiveFailures: previous.consecutiveFailures + 1
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async function getHealthFile(octokit, { owner, repo, projectName }) {
|
|
90
|
+
try {
|
|
91
|
+
const { data } = await octokit.rest.repos.getContent({ owner, repo, path: healthPath(projectName) });
|
|
92
|
+
if (Array.isArray(data) || data.type !== "file") {
|
|
93
|
+
return { health: {} };
|
|
94
|
+
}
|
|
95
|
+
return { health: JSON.parse(Buffer.from(data.content, "base64").toString("utf-8")), sha: data.sha };
|
|
96
|
+
} catch {
|
|
97
|
+
return { health: {} };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function writeHealth(octokit, params, update, message) {
|
|
101
|
+
const { owner, repo, projectName, branch } = params;
|
|
102
|
+
await retryOnConflict(async () => {
|
|
103
|
+
const { health, sha } = await getHealthFile(octokit, params);
|
|
104
|
+
await octokit.rest.repos.createOrUpdateFileContents({
|
|
105
|
+
owner,
|
|
106
|
+
repo,
|
|
107
|
+
path: healthPath(projectName),
|
|
108
|
+
message,
|
|
109
|
+
content: Buffer.from(JSON.stringify(update(health), null, 2), "utf-8").toString("base64"),
|
|
110
|
+
branch,
|
|
111
|
+
sha
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async function recordSuccess(octokit, params, source, now = /* @__PURE__ */ new Date()) {
|
|
116
|
+
await writeHealth(octokit, params, (h) => applySuccess(h, source, now), `Record ${source} success: ${params.projectName}`);
|
|
117
|
+
}
|
|
118
|
+
async function recordFailure(octokit, params, source, error, runUrl, now = /* @__PURE__ */ new Date()) {
|
|
119
|
+
await writeHealth(octokit, params, (h) => applyFailure(h, source, error, runUrl, now), `Record ${source} failure: ${params.projectName}`);
|
|
120
|
+
}
|
|
121
|
+
function currentRunUrl(env) {
|
|
122
|
+
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID } = env;
|
|
123
|
+
if (!GITHUB_SERVER_URL || !GITHUB_REPOSITORY || !GITHUB_RUN_ID) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ../../modules/core/dist/ciEntrypoint.js
|
|
130
|
+
var VAULT_BRANCH = "main";
|
|
131
|
+
function healthParams(env) {
|
|
132
|
+
const missing = ["PROJECT_NAME", "VAULT_REPO", "DEVKEEP_GITHUB_TOKEN"].filter((name) => !env[name]);
|
|
133
|
+
if (missing.length > 0) {
|
|
134
|
+
return { missing: [...missing] };
|
|
135
|
+
}
|
|
136
|
+
const [owner, repo] = env.VAULT_REPO.split("/");
|
|
137
|
+
if (!owner || !repo) {
|
|
138
|
+
return { missing: ["VAULT_REPO (expected owner/repo)"] };
|
|
139
|
+
}
|
|
140
|
+
return { params: { owner, repo, projectName: env.PROJECT_NAME, branch: VAULT_BRANCH } };
|
|
141
|
+
}
|
|
142
|
+
var defaultDeps = {
|
|
143
|
+
env: process.env,
|
|
144
|
+
createGithubClient,
|
|
145
|
+
recordSuccess,
|
|
146
|
+
recordFailure,
|
|
147
|
+
logError: console.error,
|
|
148
|
+
exit: (code) => process.exit(code)
|
|
149
|
+
};
|
|
150
|
+
async function runCiEntrypoint(source, main2, deps = defaultDeps) {
|
|
151
|
+
const resolved = healthParams(deps.env);
|
|
152
|
+
const params = "params" in resolved ? resolved.params : null;
|
|
153
|
+
if (!params) {
|
|
154
|
+
deps.logError(`Health not recorded for "${source}": ${resolved.missing.join(", ")} not set.`);
|
|
155
|
+
}
|
|
156
|
+
const octokit = params ? deps.createGithubClient(deps.env.DEVKEEP_GITHUB_TOKEN) : null;
|
|
157
|
+
try {
|
|
158
|
+
await main2();
|
|
159
|
+
} catch (error) {
|
|
160
|
+
deps.logError(error);
|
|
161
|
+
if (params && octokit) {
|
|
162
|
+
try {
|
|
163
|
+
await deps.recordFailure(octokit, params, source, error, currentRunUrl(deps.env));
|
|
164
|
+
} catch (recordingError) {
|
|
165
|
+
deps.logError(`Could not record the failure to the vault: ${String(recordingError)}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
deps.exit(1);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (params && octokit) {
|
|
172
|
+
try {
|
|
173
|
+
await deps.recordSuccess(octokit, params, source);
|
|
174
|
+
} catch (recordingError) {
|
|
175
|
+
deps.logError(`Ran successfully, but could not record it to the vault: ${String(recordingError)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
27
180
|
// ../../modules/devlore/dist/diff.js
|
|
28
181
|
function toDiffFile(file) {
|
|
29
182
|
return {
|
|
@@ -70,8 +223,8 @@ var SECRET_PREFIX_PATTERNS = [
|
|
|
70
223
|
// JWT-shaped
|
|
71
224
|
];
|
|
72
225
|
var PEM_BLOCK_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----/;
|
|
73
|
-
var
|
|
74
|
-
var SECRET_ASSIGNMENT_PATTERN = new RegExp(`(?:[A-Za-z0-9]+[_-])*${
|
|
226
|
+
var SECRET_KEYWORD2 = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
227
|
+
var SECRET_ASSIGNMENT_PATTERN = new RegExp(`(?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD2}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*["']([^"'\`]+)["']`, "i");
|
|
75
228
|
|
|
76
229
|
// ../../modules/devcrypt/dist/candidateScan.js
|
|
77
230
|
function addedLines(patch) {
|
|
@@ -332,13 +485,13 @@ function requireEnv(env, name) {
|
|
|
332
485
|
}
|
|
333
486
|
return value;
|
|
334
487
|
}
|
|
335
|
-
var
|
|
488
|
+
var defaultDeps2 = {
|
|
336
489
|
env: process.env,
|
|
337
490
|
scanCryptingCandidates,
|
|
338
491
|
createGithubClient,
|
|
339
492
|
log: console.log
|
|
340
493
|
};
|
|
341
|
-
async function main(deps =
|
|
494
|
+
async function main(deps = defaultDeps2) {
|
|
342
495
|
const { env } = deps;
|
|
343
496
|
const [projectOwner, projectRepoName] = requireEnv(env, "PROJECT_REPO").split("/");
|
|
344
497
|
const [vaultOwner, vaultRepo] = requireEnv(env, "VAULT_REPO").split("/");
|
|
@@ -360,10 +513,7 @@ async function main(deps = defaultDeps) {
|
|
|
360
513
|
deps.log(`Flagged ${result.candidatesFlagged} crypting candidate(s)`);
|
|
361
514
|
}
|
|
362
515
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
363
|
-
main
|
|
364
|
-
console.error(error);
|
|
365
|
-
process.exit(1);
|
|
366
|
-
});
|
|
516
|
+
void runCiEntrypoint("cryptingCandidatesScan", main);
|
|
367
517
|
}
|
|
368
518
|
export {
|
|
369
519
|
main,
|
package/dist/draft-log-entry.js
CHANGED
|
@@ -127,6 +127,159 @@ function defaultModelFor(tier) {
|
|
|
127
127
|
return current.id;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// ../../modules/core/dist/redactSecrets.js
|
|
131
|
+
var REDACTED = "[REDACTED]";
|
|
132
|
+
var KNOWN_SECRET_PATTERNS = [
|
|
133
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
134
|
+
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
135
|
+
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
136
|
+
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
137
|
+
/sk-[A-Za-z0-9]{20,}/g,
|
|
138
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
139
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
140
|
+
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
141
|
+
];
|
|
142
|
+
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
143
|
+
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
144
|
+
function redactText(text) {
|
|
145
|
+
let result = text;
|
|
146
|
+
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
147
|
+
result = result.replace(pattern, REDACTED);
|
|
148
|
+
}
|
|
149
|
+
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ../../modules/core/dist/ciHealth.js
|
|
154
|
+
var EMPTY_ACTION_HEALTH = {
|
|
155
|
+
lastSuccessAt: null,
|
|
156
|
+
lastFailureAt: null,
|
|
157
|
+
lastFailureMessage: null,
|
|
158
|
+
lastFailureRunUrl: null,
|
|
159
|
+
consecutiveFailures: 0
|
|
160
|
+
};
|
|
161
|
+
var MAX_MESSAGE_CHARS = 2e3;
|
|
162
|
+
function healthPath(projectName) {
|
|
163
|
+
return `projects/${projectName}/health.json`;
|
|
164
|
+
}
|
|
165
|
+
function describeError(error) {
|
|
166
|
+
const raw = error instanceof Error ? `${error.name}: ${error.message}` : typeof error === "string" ? error : JSON.stringify(error);
|
|
167
|
+
const text = raw ?? "Unknown error";
|
|
168
|
+
return redactText(text.length > MAX_MESSAGE_CHARS ? `${text.slice(0, MAX_MESSAGE_CHARS)}\u2026` : text);
|
|
169
|
+
}
|
|
170
|
+
function applySuccess(current, source, now) {
|
|
171
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
172
|
+
return {
|
|
173
|
+
...current,
|
|
174
|
+
// The failure details are deliberately kept, not cleared: "it failed
|
|
175
|
+
// on Tuesday and has worked since" is worth being able to read.
|
|
176
|
+
[source]: { ...previous, lastSuccessAt: now.toISOString(), consecutiveFailures: 0 }
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function applyFailure(current, source, error, runUrl, now) {
|
|
180
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
181
|
+
return {
|
|
182
|
+
...current,
|
|
183
|
+
[source]: {
|
|
184
|
+
...previous,
|
|
185
|
+
lastFailureAt: now.toISOString(),
|
|
186
|
+
lastFailureMessage: describeError(error),
|
|
187
|
+
lastFailureRunUrl: runUrl,
|
|
188
|
+
consecutiveFailures: previous.consecutiveFailures + 1
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function getHealthFile(octokit, { owner, repo, projectName }) {
|
|
193
|
+
try {
|
|
194
|
+
const { data } = await octokit.rest.repos.getContent({ owner, repo, path: healthPath(projectName) });
|
|
195
|
+
if (Array.isArray(data) || data.type !== "file") {
|
|
196
|
+
return { health: {} };
|
|
197
|
+
}
|
|
198
|
+
return { health: JSON.parse(Buffer.from(data.content, "base64").toString("utf-8")), sha: data.sha };
|
|
199
|
+
} catch {
|
|
200
|
+
return { health: {} };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function writeHealth(octokit, params, update, message) {
|
|
204
|
+
const { owner, repo, projectName, branch } = params;
|
|
205
|
+
await retryOnConflict(async () => {
|
|
206
|
+
const { health, sha } = await getHealthFile(octokit, params);
|
|
207
|
+
await octokit.rest.repos.createOrUpdateFileContents({
|
|
208
|
+
owner,
|
|
209
|
+
repo,
|
|
210
|
+
path: healthPath(projectName),
|
|
211
|
+
message,
|
|
212
|
+
content: Buffer.from(JSON.stringify(update(health), null, 2), "utf-8").toString("base64"),
|
|
213
|
+
branch,
|
|
214
|
+
sha
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function recordSuccess(octokit, params, source, now = /* @__PURE__ */ new Date()) {
|
|
219
|
+
await writeHealth(octokit, params, (h) => applySuccess(h, source, now), `Record ${source} success: ${params.projectName}`);
|
|
220
|
+
}
|
|
221
|
+
async function recordFailure(octokit, params, source, error, runUrl, now = /* @__PURE__ */ new Date()) {
|
|
222
|
+
await writeHealth(octokit, params, (h) => applyFailure(h, source, error, runUrl, now), `Record ${source} failure: ${params.projectName}`);
|
|
223
|
+
}
|
|
224
|
+
function currentRunUrl(env) {
|
|
225
|
+
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID } = env;
|
|
226
|
+
if (!GITHUB_SERVER_URL || !GITHUB_REPOSITORY || !GITHUB_RUN_ID) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ../../modules/core/dist/ciEntrypoint.js
|
|
233
|
+
var VAULT_BRANCH = "main";
|
|
234
|
+
function healthParams(env) {
|
|
235
|
+
const missing = ["PROJECT_NAME", "VAULT_REPO", "DEVKEEP_GITHUB_TOKEN"].filter((name) => !env[name]);
|
|
236
|
+
if (missing.length > 0) {
|
|
237
|
+
return { missing: [...missing] };
|
|
238
|
+
}
|
|
239
|
+
const [owner, repo] = env.VAULT_REPO.split("/");
|
|
240
|
+
if (!owner || !repo) {
|
|
241
|
+
return { missing: ["VAULT_REPO (expected owner/repo)"] };
|
|
242
|
+
}
|
|
243
|
+
return { params: { owner, repo, projectName: env.PROJECT_NAME, branch: VAULT_BRANCH } };
|
|
244
|
+
}
|
|
245
|
+
var defaultDeps = {
|
|
246
|
+
env: process.env,
|
|
247
|
+
createGithubClient,
|
|
248
|
+
recordSuccess,
|
|
249
|
+
recordFailure,
|
|
250
|
+
logError: console.error,
|
|
251
|
+
exit: (code) => process.exit(code)
|
|
252
|
+
};
|
|
253
|
+
async function runCiEntrypoint(source, main2, deps = defaultDeps) {
|
|
254
|
+
const resolved = healthParams(deps.env);
|
|
255
|
+
const params = "params" in resolved ? resolved.params : null;
|
|
256
|
+
if (!params) {
|
|
257
|
+
deps.logError(`Health not recorded for "${source}": ${resolved.missing.join(", ")} not set.`);
|
|
258
|
+
}
|
|
259
|
+
const octokit = params ? deps.createGithubClient(deps.env.DEVKEEP_GITHUB_TOKEN) : null;
|
|
260
|
+
try {
|
|
261
|
+
await main2();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
deps.logError(error);
|
|
264
|
+
if (params && octokit) {
|
|
265
|
+
try {
|
|
266
|
+
await deps.recordFailure(octokit, params, source, error, currentRunUrl(deps.env));
|
|
267
|
+
} catch (recordingError) {
|
|
268
|
+
deps.logError(`Could not record the failure to the vault: ${String(recordingError)}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
deps.exit(1);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (params && octokit) {
|
|
275
|
+
try {
|
|
276
|
+
await deps.recordSuccess(octokit, params, source);
|
|
277
|
+
} catch (recordingError) {
|
|
278
|
+
deps.logError(`Ran successfully, but could not record it to the vault: ${String(recordingError)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
130
283
|
// ../../modules/devlore/dist/ciUsage.js
|
|
131
284
|
var EMPTY_PROJECT_USAGE = {
|
|
132
285
|
draftLogEntry: ZERO_USAGE,
|
|
@@ -484,19 +637,6 @@ function routeDiffByModule(files, manifest) {
|
|
|
484
637
|
}
|
|
485
638
|
|
|
486
639
|
// ../../modules/devlore/dist/redact.js
|
|
487
|
-
var REDACTED = "[REDACTED]";
|
|
488
|
-
var KNOWN_SECRET_PATTERNS = [
|
|
489
|
-
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
490
|
-
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
491
|
-
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
492
|
-
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
493
|
-
/sk-[A-Za-z0-9]{20,}/g,
|
|
494
|
-
/AKIA[0-9A-Z]{16}/g,
|
|
495
|
-
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
496
|
-
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
497
|
-
];
|
|
498
|
-
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
499
|
-
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
500
640
|
var SENSITIVE_FILENAME_PATTERNS = [
|
|
501
641
|
/(^|\/)\.env(\..+)?$/,
|
|
502
642
|
/\.pem$/,
|
|
@@ -508,14 +648,6 @@ var SENSITIVE_FILENAME_PATTERNS = [
|
|
|
508
648
|
/credentials\.json$/,
|
|
509
649
|
/secrets?\.(json|ya?ml|toml)$/
|
|
510
650
|
];
|
|
511
|
-
function redactText(text) {
|
|
512
|
-
let result = text;
|
|
513
|
-
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
514
|
-
result = result.replace(pattern, REDACTED);
|
|
515
|
-
}
|
|
516
|
-
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
517
|
-
return result;
|
|
518
|
-
}
|
|
519
651
|
function isSensitiveFilename(filename) {
|
|
520
652
|
return SENSITIVE_FILENAME_PATTERNS.some((pattern) => pattern.test(filename));
|
|
521
653
|
}
|
|
@@ -533,7 +665,7 @@ function redactProjectDiff(diff) {
|
|
|
533
665
|
}
|
|
534
666
|
|
|
535
667
|
// ../../modules/devlore/dist/actions/draftLogEntry.js
|
|
536
|
-
var
|
|
668
|
+
var VAULT_BRANCH2 = "main";
|
|
537
669
|
var MODEL_TIER = "balanced";
|
|
538
670
|
var MAX_TOKENS = 2048;
|
|
539
671
|
function logEntryPath(projectName, commitSha, moduleId) {
|
|
@@ -567,7 +699,7 @@ async function syncProductDoc(projectOctokit, vaultOctokit, { projectOwner, proj
|
|
|
567
699
|
path: productDocPath(projectName, moduleId),
|
|
568
700
|
message: `Sync PRODUCT.md: ${projectName}@${headSha.slice(0, 7)}`,
|
|
569
701
|
content: Buffer.from(redactedDoc, "utf-8").toString("base64"),
|
|
570
|
-
branch:
|
|
702
|
+
branch: VAULT_BRANCH2,
|
|
571
703
|
sha: existing.sha
|
|
572
704
|
});
|
|
573
705
|
});
|
|
@@ -600,7 +732,7 @@ ${entryBody}
|
|
|
600
732
|
path: entryPath,
|
|
601
733
|
message: `Draft log entry: ${projectName}@${shortSha}${moduleName ? ` (${moduleName})` : ""}`,
|
|
602
734
|
content: Buffer.from(entryContent, "utf-8").toString("base64"),
|
|
603
|
-
branch:
|
|
735
|
+
branch: VAULT_BRANCH2
|
|
604
736
|
}));
|
|
605
737
|
let decisionPath = null;
|
|
606
738
|
if (decisionEntry) {
|
|
@@ -615,7 +747,7 @@ ${decisionEntry}
|
|
|
615
747
|
path: decisionPath,
|
|
616
748
|
message: `Draft decision entry: ${projectName}@${shortSha}${moduleName ? ` (${moduleName})` : ""}`,
|
|
617
749
|
content: Buffer.from(decisionContent, "utf-8").toString("base64"),
|
|
618
|
-
branch:
|
|
750
|
+
branch: VAULT_BRANCH2
|
|
619
751
|
}));
|
|
620
752
|
}
|
|
621
753
|
return { moduleId, logEntryPath: entryPath, decisionEntryPath: decisionPath, usage };
|
|
@@ -668,7 +800,7 @@ async function draftLogEntry({ projectOctokit, vaultOctokit, claude, projectOwne
|
|
|
668
800
|
});
|
|
669
801
|
}
|
|
670
802
|
const totalUsage = results.reduce((total, r) => addTokenUsage(total, r.usage), ZERO_USAGE);
|
|
671
|
-
await recordUsage(vaultOctokit, { owner: vaultOwner, repo: vaultRepoName, projectName, branch:
|
|
803
|
+
await recordUsage(vaultOctokit, { owner: vaultOwner, repo: vaultRepoName, projectName, branch: VAULT_BRANCH2 }, "draftLogEntry", totalUsage);
|
|
672
804
|
return results;
|
|
673
805
|
}
|
|
674
806
|
|
|
@@ -680,14 +812,14 @@ function requireEnv(env, name) {
|
|
|
680
812
|
}
|
|
681
813
|
return value;
|
|
682
814
|
}
|
|
683
|
-
var
|
|
815
|
+
var defaultDeps2 = {
|
|
684
816
|
env: process.env,
|
|
685
817
|
draftLogEntry,
|
|
686
818
|
createGithubClient,
|
|
687
819
|
createClaudeClient,
|
|
688
820
|
log: console.log
|
|
689
821
|
};
|
|
690
|
-
async function main(deps =
|
|
822
|
+
async function main(deps = defaultDeps2) {
|
|
691
823
|
const { env } = deps;
|
|
692
824
|
const projectName = requireEnv(env, "PROJECT_NAME");
|
|
693
825
|
const [projectOwner, projectRepoName] = requireEnv(env, "PROJECT_REPO").split("/");
|
|
@@ -717,10 +849,7 @@ async function main(deps = defaultDeps) {
|
|
|
717
849
|
}
|
|
718
850
|
}
|
|
719
851
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
720
|
-
main
|
|
721
|
-
console.error(error);
|
|
722
|
-
process.exit(1);
|
|
723
|
-
});
|
|
852
|
+
void runCiEntrypoint("draftLogEntry", main);
|
|
724
853
|
}
|
|
725
854
|
export {
|
|
726
855
|
main,
|
|
@@ -127,6 +127,159 @@ function defaultModelFor(tier) {
|
|
|
127
127
|
return current.id;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// ../../modules/core/dist/redactSecrets.js
|
|
131
|
+
var REDACTED = "[REDACTED]";
|
|
132
|
+
var KNOWN_SECRET_PATTERNS = [
|
|
133
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
134
|
+
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
135
|
+
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
136
|
+
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
137
|
+
/sk-[A-Za-z0-9]{20,}/g,
|
|
138
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
139
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
140
|
+
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
141
|
+
];
|
|
142
|
+
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
143
|
+
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
144
|
+
function redactText(text) {
|
|
145
|
+
let result = text;
|
|
146
|
+
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
147
|
+
result = result.replace(pattern, REDACTED);
|
|
148
|
+
}
|
|
149
|
+
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ../../modules/core/dist/ciHealth.js
|
|
154
|
+
var EMPTY_ACTION_HEALTH = {
|
|
155
|
+
lastSuccessAt: null,
|
|
156
|
+
lastFailureAt: null,
|
|
157
|
+
lastFailureMessage: null,
|
|
158
|
+
lastFailureRunUrl: null,
|
|
159
|
+
consecutiveFailures: 0
|
|
160
|
+
};
|
|
161
|
+
var MAX_MESSAGE_CHARS = 2e3;
|
|
162
|
+
function healthPath(projectName) {
|
|
163
|
+
return `projects/${projectName}/health.json`;
|
|
164
|
+
}
|
|
165
|
+
function describeError(error) {
|
|
166
|
+
const raw = error instanceof Error ? `${error.name}: ${error.message}` : typeof error === "string" ? error : JSON.stringify(error);
|
|
167
|
+
const text = raw ?? "Unknown error";
|
|
168
|
+
return redactText(text.length > MAX_MESSAGE_CHARS ? `${text.slice(0, MAX_MESSAGE_CHARS)}\u2026` : text);
|
|
169
|
+
}
|
|
170
|
+
function applySuccess(current, source, now) {
|
|
171
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
172
|
+
return {
|
|
173
|
+
...current,
|
|
174
|
+
// The failure details are deliberately kept, not cleared: "it failed
|
|
175
|
+
// on Tuesday and has worked since" is worth being able to read.
|
|
176
|
+
[source]: { ...previous, lastSuccessAt: now.toISOString(), consecutiveFailures: 0 }
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function applyFailure(current, source, error, runUrl, now) {
|
|
180
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
181
|
+
return {
|
|
182
|
+
...current,
|
|
183
|
+
[source]: {
|
|
184
|
+
...previous,
|
|
185
|
+
lastFailureAt: now.toISOString(),
|
|
186
|
+
lastFailureMessage: describeError(error),
|
|
187
|
+
lastFailureRunUrl: runUrl,
|
|
188
|
+
consecutiveFailures: previous.consecutiveFailures + 1
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function getHealthFile(octokit, { owner, repo, projectName }) {
|
|
193
|
+
try {
|
|
194
|
+
const { data } = await octokit.rest.repos.getContent({ owner, repo, path: healthPath(projectName) });
|
|
195
|
+
if (Array.isArray(data) || data.type !== "file") {
|
|
196
|
+
return { health: {} };
|
|
197
|
+
}
|
|
198
|
+
return { health: JSON.parse(Buffer.from(data.content, "base64").toString("utf-8")), sha: data.sha };
|
|
199
|
+
} catch {
|
|
200
|
+
return { health: {} };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function writeHealth(octokit, params, update, message) {
|
|
204
|
+
const { owner, repo, projectName, branch } = params;
|
|
205
|
+
await retryOnConflict(async () => {
|
|
206
|
+
const { health, sha } = await getHealthFile(octokit, params);
|
|
207
|
+
await octokit.rest.repos.createOrUpdateFileContents({
|
|
208
|
+
owner,
|
|
209
|
+
repo,
|
|
210
|
+
path: healthPath(projectName),
|
|
211
|
+
message,
|
|
212
|
+
content: Buffer.from(JSON.stringify(update(health), null, 2), "utf-8").toString("base64"),
|
|
213
|
+
branch,
|
|
214
|
+
sha
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function recordSuccess(octokit, params, source, now = /* @__PURE__ */ new Date()) {
|
|
219
|
+
await writeHealth(octokit, params, (h) => applySuccess(h, source, now), `Record ${source} success: ${params.projectName}`);
|
|
220
|
+
}
|
|
221
|
+
async function recordFailure(octokit, params, source, error, runUrl, now = /* @__PURE__ */ new Date()) {
|
|
222
|
+
await writeHealth(octokit, params, (h) => applyFailure(h, source, error, runUrl, now), `Record ${source} failure: ${params.projectName}`);
|
|
223
|
+
}
|
|
224
|
+
function currentRunUrl(env) {
|
|
225
|
+
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID } = env;
|
|
226
|
+
if (!GITHUB_SERVER_URL || !GITHUB_REPOSITORY || !GITHUB_RUN_ID) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ../../modules/core/dist/ciEntrypoint.js
|
|
233
|
+
var VAULT_BRANCH = "main";
|
|
234
|
+
function healthParams(env) {
|
|
235
|
+
const missing = ["PROJECT_NAME", "VAULT_REPO", "DEVKEEP_GITHUB_TOKEN"].filter((name) => !env[name]);
|
|
236
|
+
if (missing.length > 0) {
|
|
237
|
+
return { missing: [...missing] };
|
|
238
|
+
}
|
|
239
|
+
const [owner, repo] = env.VAULT_REPO.split("/");
|
|
240
|
+
if (!owner || !repo) {
|
|
241
|
+
return { missing: ["VAULT_REPO (expected owner/repo)"] };
|
|
242
|
+
}
|
|
243
|
+
return { params: { owner, repo, projectName: env.PROJECT_NAME, branch: VAULT_BRANCH } };
|
|
244
|
+
}
|
|
245
|
+
var defaultDeps = {
|
|
246
|
+
env: process.env,
|
|
247
|
+
createGithubClient,
|
|
248
|
+
recordSuccess,
|
|
249
|
+
recordFailure,
|
|
250
|
+
logError: console.error,
|
|
251
|
+
exit: (code) => process.exit(code)
|
|
252
|
+
};
|
|
253
|
+
async function runCiEntrypoint(source, main2, deps = defaultDeps) {
|
|
254
|
+
const resolved = healthParams(deps.env);
|
|
255
|
+
const params = "params" in resolved ? resolved.params : null;
|
|
256
|
+
if (!params) {
|
|
257
|
+
deps.logError(`Health not recorded for "${source}": ${resolved.missing.join(", ")} not set.`);
|
|
258
|
+
}
|
|
259
|
+
const octokit = params ? deps.createGithubClient(deps.env.DEVKEEP_GITHUB_TOKEN) : null;
|
|
260
|
+
try {
|
|
261
|
+
await main2();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
deps.logError(error);
|
|
264
|
+
if (params && octokit) {
|
|
265
|
+
try {
|
|
266
|
+
await deps.recordFailure(octokit, params, source, error, currentRunUrl(deps.env));
|
|
267
|
+
} catch (recordingError) {
|
|
268
|
+
deps.logError(`Could not record the failure to the vault: ${String(recordingError)}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
deps.exit(1);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (params && octokit) {
|
|
275
|
+
try {
|
|
276
|
+
await deps.recordSuccess(octokit, params, source);
|
|
277
|
+
} catch (recordingError) {
|
|
278
|
+
deps.logError(`Ran successfully, but could not record it to the vault: ${String(recordingError)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
130
283
|
// ../../modules/devlore/dist/prompt.js
|
|
131
284
|
function buildReleaseNotesPrompt(input) {
|
|
132
285
|
return [
|
|
@@ -150,29 +303,6 @@ function buildReleaseNotesPrompt(input) {
|
|
|
150
303
|
].join("\n");
|
|
151
304
|
}
|
|
152
305
|
|
|
153
|
-
// ../../modules/devlore/dist/redact.js
|
|
154
|
-
var REDACTED = "[REDACTED]";
|
|
155
|
-
var KNOWN_SECRET_PATTERNS = [
|
|
156
|
-
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
157
|
-
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
158
|
-
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
159
|
-
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
160
|
-
/sk-[A-Za-z0-9]{20,}/g,
|
|
161
|
-
/AKIA[0-9A-Z]{16}/g,
|
|
162
|
-
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
163
|
-
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
164
|
-
];
|
|
165
|
-
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
166
|
-
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
167
|
-
function redactText(text) {
|
|
168
|
-
let result = text;
|
|
169
|
-
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
170
|
-
result = result.replace(pattern, REDACTED);
|
|
171
|
-
}
|
|
172
|
-
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
173
|
-
return result;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
306
|
// ../../modules/devlore/dist/ciUsage.js
|
|
177
307
|
var EMPTY_PROJECT_USAGE = {
|
|
178
308
|
draftLogEntry: ZERO_USAGE,
|
|
@@ -354,7 +484,7 @@ function releaseNotesDir(projectName) {
|
|
|
354
484
|
var MODEL_TIER = "balanced";
|
|
355
485
|
var MAX_TOKENS = 2e4;
|
|
356
486
|
var MAX_CONTINUATIONS = 8;
|
|
357
|
-
var
|
|
487
|
+
var VAULT_BRANCH2 = "main";
|
|
358
488
|
async function draftReleaseNotes({ projectOctokit, vaultOctokit, claude, projectOwner, projectRepoName, vaultOwner, vaultRepoName, projectName, tag }) {
|
|
359
489
|
const { logEntries, previousTag } = await selectLogEntriesSincePreviousRelease({
|
|
360
490
|
vaultOctokit,
|
|
@@ -386,11 +516,11 @@ ${text.trim()}
|
|
|
386
516
|
path,
|
|
387
517
|
message: `Draft release notes: ${projectName} ${tag}`,
|
|
388
518
|
content: Buffer.from(content, "utf-8").toString("base64"),
|
|
389
|
-
branch:
|
|
519
|
+
branch: VAULT_BRANCH2,
|
|
390
520
|
sha: await getExistingSha(vaultOctokit, vaultOwner, vaultRepoName, path)
|
|
391
521
|
});
|
|
392
522
|
if (usage.inputTokens > 0 || usage.outputTokens > 0) {
|
|
393
|
-
await recordUsage(vaultOctokit, { owner: vaultOwner, repo: vaultRepoName, projectName, branch:
|
|
523
|
+
await recordUsage(vaultOctokit, { owner: vaultOwner, repo: vaultRepoName, projectName, branch: VAULT_BRANCH2 }, "releaseNotes", usage);
|
|
394
524
|
}
|
|
395
525
|
return { path, previousTag, entryCount: logEntries.length, content, usage };
|
|
396
526
|
}
|
|
@@ -411,14 +541,14 @@ function requireEnv(env, name) {
|
|
|
411
541
|
}
|
|
412
542
|
return value;
|
|
413
543
|
}
|
|
414
|
-
var
|
|
544
|
+
var defaultDeps2 = {
|
|
415
545
|
env: process.env,
|
|
416
546
|
draftReleaseNotes,
|
|
417
547
|
createGithubClient,
|
|
418
548
|
createClaudeClient,
|
|
419
549
|
log: console.log
|
|
420
550
|
};
|
|
421
|
-
async function main(deps =
|
|
551
|
+
async function main(deps = defaultDeps2) {
|
|
422
552
|
const { env } = deps;
|
|
423
553
|
const projectName = requireEnv(env, "PROJECT_NAME");
|
|
424
554
|
const [projectOwner, projectRepoName] = requireEnv(env, "PROJECT_REPO").split("/");
|
|
@@ -440,10 +570,7 @@ async function main(deps = defaultDeps) {
|
|
|
440
570
|
deps.log(`Drafted release notes: ${result.path} (previous tag: ${result.previousTag ?? "none"}, ${result.entryCount} entries)`);
|
|
441
571
|
}
|
|
442
572
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
443
|
-
main
|
|
444
|
-
console.error(error);
|
|
445
|
-
process.exit(1);
|
|
446
|
-
});
|
|
573
|
+
void runCiEntrypoint("releaseNotes", main);
|
|
447
574
|
}
|
|
448
575
|
export {
|
|
449
576
|
main,
|
package/dist/sync-onboarding.js
CHANGED
|
@@ -127,6 +127,159 @@ function defaultModelFor(tier) {
|
|
|
127
127
|
return current.id;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// ../../modules/core/dist/redactSecrets.js
|
|
131
|
+
var REDACTED = "[REDACTED]";
|
|
132
|
+
var KNOWN_SECRET_PATTERNS = [
|
|
133
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
134
|
+
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
135
|
+
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
136
|
+
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
137
|
+
/sk-[A-Za-z0-9]{20,}/g,
|
|
138
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
139
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
140
|
+
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
141
|
+
];
|
|
142
|
+
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
143
|
+
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
144
|
+
function redactText(text) {
|
|
145
|
+
let result = text;
|
|
146
|
+
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
147
|
+
result = result.replace(pattern, REDACTED);
|
|
148
|
+
}
|
|
149
|
+
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ../../modules/core/dist/ciHealth.js
|
|
154
|
+
var EMPTY_ACTION_HEALTH = {
|
|
155
|
+
lastSuccessAt: null,
|
|
156
|
+
lastFailureAt: null,
|
|
157
|
+
lastFailureMessage: null,
|
|
158
|
+
lastFailureRunUrl: null,
|
|
159
|
+
consecutiveFailures: 0
|
|
160
|
+
};
|
|
161
|
+
var MAX_MESSAGE_CHARS = 2e3;
|
|
162
|
+
function healthPath(projectName) {
|
|
163
|
+
return `projects/${projectName}/health.json`;
|
|
164
|
+
}
|
|
165
|
+
function describeError(error) {
|
|
166
|
+
const raw = error instanceof Error ? `${error.name}: ${error.message}` : typeof error === "string" ? error : JSON.stringify(error);
|
|
167
|
+
const text = raw ?? "Unknown error";
|
|
168
|
+
return redactText(text.length > MAX_MESSAGE_CHARS ? `${text.slice(0, MAX_MESSAGE_CHARS)}\u2026` : text);
|
|
169
|
+
}
|
|
170
|
+
function applySuccess(current, source, now) {
|
|
171
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
172
|
+
return {
|
|
173
|
+
...current,
|
|
174
|
+
// The failure details are deliberately kept, not cleared: "it failed
|
|
175
|
+
// on Tuesday and has worked since" is worth being able to read.
|
|
176
|
+
[source]: { ...previous, lastSuccessAt: now.toISOString(), consecutiveFailures: 0 }
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function applyFailure(current, source, error, runUrl, now) {
|
|
180
|
+
const previous = current[source] ?? EMPTY_ACTION_HEALTH;
|
|
181
|
+
return {
|
|
182
|
+
...current,
|
|
183
|
+
[source]: {
|
|
184
|
+
...previous,
|
|
185
|
+
lastFailureAt: now.toISOString(),
|
|
186
|
+
lastFailureMessage: describeError(error),
|
|
187
|
+
lastFailureRunUrl: runUrl,
|
|
188
|
+
consecutiveFailures: previous.consecutiveFailures + 1
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function getHealthFile(octokit, { owner, repo, projectName }) {
|
|
193
|
+
try {
|
|
194
|
+
const { data } = await octokit.rest.repos.getContent({ owner, repo, path: healthPath(projectName) });
|
|
195
|
+
if (Array.isArray(data) || data.type !== "file") {
|
|
196
|
+
return { health: {} };
|
|
197
|
+
}
|
|
198
|
+
return { health: JSON.parse(Buffer.from(data.content, "base64").toString("utf-8")), sha: data.sha };
|
|
199
|
+
} catch {
|
|
200
|
+
return { health: {} };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function writeHealth(octokit, params, update, message) {
|
|
204
|
+
const { owner, repo, projectName, branch } = params;
|
|
205
|
+
await retryOnConflict(async () => {
|
|
206
|
+
const { health, sha } = await getHealthFile(octokit, params);
|
|
207
|
+
await octokit.rest.repos.createOrUpdateFileContents({
|
|
208
|
+
owner,
|
|
209
|
+
repo,
|
|
210
|
+
path: healthPath(projectName),
|
|
211
|
+
message,
|
|
212
|
+
content: Buffer.from(JSON.stringify(update(health), null, 2), "utf-8").toString("base64"),
|
|
213
|
+
branch,
|
|
214
|
+
sha
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function recordSuccess(octokit, params, source, now = /* @__PURE__ */ new Date()) {
|
|
219
|
+
await writeHealth(octokit, params, (h) => applySuccess(h, source, now), `Record ${source} success: ${params.projectName}`);
|
|
220
|
+
}
|
|
221
|
+
async function recordFailure(octokit, params, source, error, runUrl, now = /* @__PURE__ */ new Date()) {
|
|
222
|
+
await writeHealth(octokit, params, (h) => applyFailure(h, source, error, runUrl, now), `Record ${source} failure: ${params.projectName}`);
|
|
223
|
+
}
|
|
224
|
+
function currentRunUrl(env) {
|
|
225
|
+
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID } = env;
|
|
226
|
+
if (!GITHUB_SERVER_URL || !GITHUB_REPOSITORY || !GITHUB_RUN_ID) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ../../modules/core/dist/ciEntrypoint.js
|
|
233
|
+
var VAULT_BRANCH = "main";
|
|
234
|
+
function healthParams(env) {
|
|
235
|
+
const missing = ["PROJECT_NAME", "VAULT_REPO", "DEVKEEP_GITHUB_TOKEN"].filter((name) => !env[name]);
|
|
236
|
+
if (missing.length > 0) {
|
|
237
|
+
return { missing: [...missing] };
|
|
238
|
+
}
|
|
239
|
+
const [owner, repo] = env.VAULT_REPO.split("/");
|
|
240
|
+
if (!owner || !repo) {
|
|
241
|
+
return { missing: ["VAULT_REPO (expected owner/repo)"] };
|
|
242
|
+
}
|
|
243
|
+
return { params: { owner, repo, projectName: env.PROJECT_NAME, branch: VAULT_BRANCH } };
|
|
244
|
+
}
|
|
245
|
+
var defaultDeps = {
|
|
246
|
+
env: process.env,
|
|
247
|
+
createGithubClient,
|
|
248
|
+
recordSuccess,
|
|
249
|
+
recordFailure,
|
|
250
|
+
logError: console.error,
|
|
251
|
+
exit: (code) => process.exit(code)
|
|
252
|
+
};
|
|
253
|
+
async function runCiEntrypoint(source, main2, deps = defaultDeps) {
|
|
254
|
+
const resolved = healthParams(deps.env);
|
|
255
|
+
const params = "params" in resolved ? resolved.params : null;
|
|
256
|
+
if (!params) {
|
|
257
|
+
deps.logError(`Health not recorded for "${source}": ${resolved.missing.join(", ")} not set.`);
|
|
258
|
+
}
|
|
259
|
+
const octokit = params ? deps.createGithubClient(deps.env.DEVKEEP_GITHUB_TOKEN) : null;
|
|
260
|
+
try {
|
|
261
|
+
await main2();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
deps.logError(error);
|
|
264
|
+
if (params && octokit) {
|
|
265
|
+
try {
|
|
266
|
+
await deps.recordFailure(octokit, params, source, error, currentRunUrl(deps.env));
|
|
267
|
+
} catch (recordingError) {
|
|
268
|
+
deps.logError(`Could not record the failure to the vault: ${String(recordingError)}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
deps.exit(1);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (params && octokit) {
|
|
275
|
+
try {
|
|
276
|
+
await deps.recordSuccess(octokit, params, source);
|
|
277
|
+
} catch (recordingError) {
|
|
278
|
+
deps.logError(`Ran successfully, but could not record it to the vault: ${String(recordingError)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
130
283
|
// ../../modules/devlore/dist/decisionEntries.js
|
|
131
284
|
function decisionEntriesDir(projectName) {
|
|
132
285
|
return `projects/${projectName}/decisions`;
|
|
@@ -186,29 +339,6 @@ ${input.decisions.join("\n\n")}` : "",
|
|
|
186
339
|
].filter((line) => line !== "").join("\n");
|
|
187
340
|
}
|
|
188
341
|
|
|
189
|
-
// ../../modules/devlore/dist/redact.js
|
|
190
|
-
var REDACTED = "[REDACTED]";
|
|
191
|
-
var KNOWN_SECRET_PATTERNS = [
|
|
192
|
-
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
193
|
-
/gh[pousr]_[A-Za-z0-9]{36,}/g,
|
|
194
|
-
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
195
|
-
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
196
|
-
/sk-[A-Za-z0-9]{20,}/g,
|
|
197
|
-
/AKIA[0-9A-Z]{16}/g,
|
|
198
|
-
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
199
|
-
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
200
|
-
];
|
|
201
|
-
var SECRET_KEYWORD = "(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|CREDENTIAL)";
|
|
202
|
-
var ASSIGNMENT_PATTERN = new RegExp(`((?:[A-Za-z0-9]+[_-])*${SECRET_KEYWORD}(?:[_-][A-Za-z0-9]+)*\\s*[:=]\\s*)(["']?)([^"'\`\\s]+)\\2`, "gi");
|
|
203
|
-
function redactText(text) {
|
|
204
|
-
let result = text;
|
|
205
|
-
for (const pattern of KNOWN_SECRET_PATTERNS) {
|
|
206
|
-
result = result.replace(pattern, REDACTED);
|
|
207
|
-
}
|
|
208
|
-
result = result.replace(ASSIGNMENT_PATTERN, (_match, prefix, quote) => `${prefix}${quote}${REDACTED}${quote}`);
|
|
209
|
-
return result;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
342
|
// ../../modules/devlore/dist/ciUsage.js
|
|
213
343
|
var EMPTY_PROJECT_USAGE = {
|
|
214
344
|
draftLogEntry: ZERO_USAGE,
|
|
@@ -358,7 +488,7 @@ function isNotFound4(error) {
|
|
|
358
488
|
var MODEL_TIER = "balanced";
|
|
359
489
|
var MAX_TOKENS = 2e4;
|
|
360
490
|
var MAX_CONTINUATIONS = 8;
|
|
361
|
-
var
|
|
491
|
+
var VAULT_BRANCH2 = "main";
|
|
362
492
|
async function readVaultFile(octokit, owner, repo, path) {
|
|
363
493
|
const { data } = await octokit.rest.repos.getContent({ owner, repo, path }).catch(() => ({ data: null }));
|
|
364
494
|
return data && !Array.isArray(data) && data.type === "file" ? Buffer.from(data.content, "base64").toString("utf-8") : "";
|
|
@@ -403,12 +533,12 @@ ${text.trim()}
|
|
|
403
533
|
path: vaultPath,
|
|
404
534
|
message: `Mirror onboarding guide: ${projectName}`,
|
|
405
535
|
content: Buffer.from(mirrored, "utf-8").toString("base64"),
|
|
406
|
-
branch:
|
|
536
|
+
branch: VAULT_BRANCH2,
|
|
407
537
|
sha: existingVault.sha
|
|
408
538
|
});
|
|
409
539
|
}
|
|
410
540
|
if (usage.inputTokens > 0 || usage.outputTokens > 0) {
|
|
411
|
-
await recordUsage(vaultOctokit, { owner: vaultOwner, repo: vaultRepoName, projectName, branch:
|
|
541
|
+
await recordUsage(vaultOctokit, { owner: vaultOwner, repo: vaultRepoName, projectName, branch: VAULT_BRANCH2 }, "syncOnboarding", usage);
|
|
412
542
|
}
|
|
413
543
|
return { path: ONBOARDING_PATH, vaultPath, content, usage };
|
|
414
544
|
}
|
|
@@ -421,14 +551,14 @@ function requireEnv(env, name) {
|
|
|
421
551
|
}
|
|
422
552
|
return value;
|
|
423
553
|
}
|
|
424
|
-
var
|
|
554
|
+
var defaultDeps2 = {
|
|
425
555
|
env: process.env,
|
|
426
556
|
syncOnboarding,
|
|
427
557
|
createGithubClient,
|
|
428
558
|
createClaudeClient,
|
|
429
559
|
log: console.log
|
|
430
560
|
};
|
|
431
|
-
async function main(deps =
|
|
561
|
+
async function main(deps = defaultDeps2) {
|
|
432
562
|
const { env } = deps;
|
|
433
563
|
const projectName = requireEnv(env, "PROJECT_NAME");
|
|
434
564
|
const [projectOwner, projectRepoName] = requireEnv(env, "PROJECT_REPO").split("/");
|
|
@@ -448,10 +578,7 @@ async function main(deps = defaultDeps) {
|
|
|
448
578
|
deps.log(`Synced onboarding guide: ${result.path} (mirrored to ${result.vaultPath})`);
|
|
449
579
|
}
|
|
450
580
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
451
|
-
main
|
|
452
|
-
console.error(error);
|
|
453
|
-
process.exit(1);
|
|
454
|
-
});
|
|
581
|
+
void runCiEntrypoint("syncOnboarding", main);
|
|
455
582
|
}
|
|
456
583
|
export {
|
|
457
584
|
main,
|