codiedev 0.10.0-beta.16 → 0.10.0-beta.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +63 -18
- package/dist/codie.js +19 -2
- package/dist/codieLauncher.js +20 -1
- package/dist/commands/agents.js +18 -1
- package/dist/commands/agentsSdk.generated.js +3 -3
- package/dist/commands/inbox.js +9 -0
- package/dist/commands/pack.js +258 -12
- package/dist/commands/ping.js +35 -10
- package/dist/commands/pingFiles.js +145 -0
- package/dist/commands/push.js +33 -4
- package/dist/commands/reply.js +49 -0
- package/dist/commands/scrub.js +1 -2
- package/dist/commands/send.js +11 -51
- package/dist/commands/shared.js +51 -0
- package/dist/commands/work.js +761 -0
- package/dist/connect.js +3 -0
- package/dist/hook.js +50 -5
- package/dist/inboxFormat.js +17 -1
- package/dist/launchContext.js +56 -0
- package/dist/launcher.js +24 -1
- package/dist/managedPi.js +1 -0
- package/dist/mcp.js +15 -77
- package/dist/packs/files.js +291 -0
- package/dist/packs/harnesses.js +2 -3
- package/dist/packs/install.js +31 -3
- package/dist/packs/localSkills.js +120 -0
- package/dist/packs/manifest.js +51 -2
- package/dist/packs/skillFiles.js +106 -0
- package/dist/packs/store.js +21 -3
- package/dist/promptGuard.js +223 -0
- package/dist/scrub/rules.js +260 -21
- package/dist/teamSkillContext.js +1 -1
- package/dist/unreadNotice.js +72 -0
- package/dist/utils.js +120 -25
- package/package.json +2 -2
- package/dist/commands/ask.js +0 -55
package/dist/scrub/rules.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GENERIC_RULES = void 0;
|
|
3
|
+
exports.SHAPE_RULES = exports.isTestFile = exports.GENERIC_RULES = void 0;
|
|
4
4
|
exports.shannonEntropy = shannonEntropy;
|
|
5
5
|
exports.scrubSecrets = scrubSecrets;
|
|
6
6
|
exports.redactSpans = redactSpans;
|
|
7
7
|
exports.looksLikeTestValue = looksLikeTestValue;
|
|
8
|
+
exports.locateInLine = locateInLine;
|
|
9
|
+
exports.locateMarkers = locateMarkers;
|
|
10
|
+
exports.namesASecret = namesASecret;
|
|
11
|
+
exports.notWorthRotating = notWorthRotating;
|
|
12
|
+
exports.hintOf = hintOf;
|
|
8
13
|
exports.secretFindings = secretFindings;
|
|
9
14
|
exports.possibleSecrets = possibleSecrets;
|
|
10
15
|
exports.redactPossible = redactPossible;
|
|
@@ -228,7 +233,10 @@ function looksLikeTestValue(value) {
|
|
|
228
233
|
const filler = parts.filter((p) => /^\d+$/.test(p) || /^(.)\1*$/.test(p)).length;
|
|
229
234
|
return words >= 2 && words + filler === parts.length;
|
|
230
235
|
}
|
|
231
|
-
const TEST_FILE = /(?:\.test\.|\.spec\.|__tests__|(
|
|
236
|
+
const TEST_FILE = /(?:\.test\.|\.spec\.|__tests__|__mocks__|fixtures?\/|(?:^|[\/_.-])tests?[\/_.-])/i;
|
|
237
|
+
const TEST_FILE_SUFFIX = /[a-z0-9]Tests?\.\w+$/;
|
|
238
|
+
const isTestFile = (file) => TEST_FILE.test(file) || TEST_FILE_SUFFIX.test(file);
|
|
239
|
+
exports.isTestFile = isTestFile;
|
|
232
240
|
const NAME_BEFORE = /([A-Za-z_][A-Za-z0-9_.-]{0,63})\??["']?\s*[:=]\s*\??["']?(?:Bearer\s+)?$/;
|
|
233
241
|
function nameBefore(input, start, lineStart) {
|
|
234
242
|
const window = input.slice(Math.max(lineStart, start - 160), start).replace(/\\[nrt]/g, " ");
|
|
@@ -238,7 +246,8 @@ function nameBefore(input, start, lineStart) {
|
|
|
238
246
|
return m;
|
|
239
247
|
}
|
|
240
248
|
const FILE_PATH_FIELD = /"(?:file_path|filePath|path|target_file)"\s*:\s*"([^"]{1,400})"/g;
|
|
241
|
-
const REDIRECT = /(?:>>?|\btee(?:\s+-a)?)\
|
|
249
|
+
const REDIRECT = /(?<![-=>\w]|\\n|\\r)(?<!^)(?:>>?|\btee(?:\s+-a)?)[ \t]*['"]?([\w.~/-]{2,300})/gm;
|
|
250
|
+
const PATH_LIKE = /[./]/;
|
|
242
251
|
const CONFIG_FILE = /((?:[\w.~-]+\/)*\.env(?:\.[\w-]+)?|(?:[\w.~-]+\/)*[\w.-]+\.(?:env|ya?ml|toml|ini|cfg|conf|properties|tfvars|npmrc|netrc|pgpass))(?![\w/])/g;
|
|
243
252
|
const lastTwo = (p) => p.replace(/\\\//g, "/").split("/").filter(Boolean).slice(-2).join("/");
|
|
244
253
|
function lastMatch(re, text) {
|
|
@@ -251,8 +260,9 @@ function fileOf(input, start, lineStart) {
|
|
|
251
260
|
const lineEnd = input.indexOf("\n", start);
|
|
252
261
|
const line = input.slice(lineStart, lineEnd === -1 ? input.length : lineEnd);
|
|
253
262
|
const before = input.slice(Math.max(lineStart, start - 400), start);
|
|
254
|
-
const
|
|
255
|
-
|
|
263
|
+
const redirect = lastMatch(REDIRECT, before);
|
|
264
|
+
const hit = lastMatch(FILE_PATH_FIELD, line) ?? (redirect && PATH_LIKE.test(redirect) && !/^[.\d-]+$/.test(redirect) ? redirect : undefined) ?? lastMatch(CONFIG_FILE, before);
|
|
265
|
+
if (!hit || hit.startsWith("/dev/"))
|
|
256
266
|
return undefined;
|
|
257
267
|
const short = lastTwo(hit);
|
|
258
268
|
return short && short.length <= 120 ? short : undefined;
|
|
@@ -261,11 +271,13 @@ const TOOL_CALL_TYPE = /^(?:tool_use|function_call|custom_tool_call|tool_call|to
|
|
|
261
271
|
const TOOL_OUTPUT_TYPE = /^(?:tool_result|function_call_output|custom_tool_call_output|tool_output|toolResult|tool\.execution_complete|mcp_tool_result)$/;
|
|
262
272
|
const SHELL_TOOL = /bash|shell|exec|terminal|run_command|run_terminal/i;
|
|
263
273
|
const GIT_COMMIT = /\bgit\s+(?:[^\n]*\s)?(?:commit|push)\b/;
|
|
264
|
-
|
|
265
|
-
|
|
274
|
+
const PASTE_LINES = 8;
|
|
275
|
+
const PASTE_CHARS = 800;
|
|
276
|
+
function lineAt(input, at, cache) {
|
|
277
|
+
const lineStart = input.lastIndexOf("\n", at - 1) + 1;
|
|
266
278
|
let parsed = cache.get(lineStart);
|
|
267
279
|
if (parsed === undefined) {
|
|
268
|
-
const lineEnd = input.indexOf("\n",
|
|
280
|
+
const lineEnd = input.indexOf("\n", at);
|
|
269
281
|
try {
|
|
270
282
|
parsed = JSON.parse(input.slice(lineStart, lineEnd === -1 ? input.length : lineEnd));
|
|
271
283
|
}
|
|
@@ -274,12 +286,60 @@ function locate(input, r, cache) {
|
|
|
274
286
|
}
|
|
275
287
|
cache.set(lineStart, parsed);
|
|
276
288
|
}
|
|
289
|
+
return parsed;
|
|
290
|
+
}
|
|
291
|
+
function toolForResult(input, id, cache) {
|
|
292
|
+
if (!/^[\w-]{6,}$/.test(id))
|
|
293
|
+
return undefined;
|
|
294
|
+
const at = input.indexOf(`"${id}"`);
|
|
295
|
+
if (at === -1)
|
|
296
|
+
return undefined;
|
|
297
|
+
let name;
|
|
298
|
+
const walk = (node) => {
|
|
299
|
+
if (name || !node || typeof node !== "object")
|
|
300
|
+
return;
|
|
301
|
+
if (Array.isArray(node)) {
|
|
302
|
+
node.forEach(walk);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const n = node;
|
|
306
|
+
if ((n.id === id || n.call_id === id || n.toolCallId === id) && typeof n.name === "string" && TOOL_CALL_TYPE.test(typeof n.type === "string" ? n.type : "tool_use")) {
|
|
307
|
+
name = n.name;
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
Object.values(n).forEach(walk);
|
|
311
|
+
};
|
|
312
|
+
walk(lineAt(input, at, cache));
|
|
313
|
+
return name;
|
|
314
|
+
}
|
|
315
|
+
function locate(input, r, cache, exact) {
|
|
316
|
+
const parsed = lineAt(input, r.start, cache);
|
|
277
317
|
if (parsed === null || typeof parsed !== "object")
|
|
278
318
|
return { where: "unknown" };
|
|
279
|
-
const runs = input.slice(r.start, r.end).match(/[A-Za-z0-9]{8,}/g);
|
|
280
|
-
const needle = runs ? runs.reduce((a, b) => (b.length > a.length ? b : a)) : null;
|
|
319
|
+
const runs = exact ? null : input.slice(r.start, r.end).match(/[A-Za-z0-9]{8,}/g);
|
|
320
|
+
const needle = exact ?? (runs ? runs.reduce((a, b) => (b.length > a.length ? b : a)) : null);
|
|
281
321
|
if (!needle)
|
|
282
322
|
return { where: "unknown" };
|
|
323
|
+
return classify(parsed, needle, input, cache);
|
|
324
|
+
}
|
|
325
|
+
function locateInLine(line, text) {
|
|
326
|
+
let parsed;
|
|
327
|
+
try {
|
|
328
|
+
parsed = JSON.parse(line);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
parsed = null;
|
|
332
|
+
}
|
|
333
|
+
const fileHit = lastMatch(FILE_PATH_FIELD, line);
|
|
334
|
+
const file = fileHit && !fileHit.startsWith("/dev/") ? lastTwo(fileHit).slice(0, 120) : undefined;
|
|
335
|
+
if (parsed === null || typeof parsed !== "object")
|
|
336
|
+
return { where: "unknown", ...(file ? { file } : {}) };
|
|
337
|
+
const runs = text.match(/[A-Za-z0-9]{4,}/g);
|
|
338
|
+
const needle = runs ? runs.reduce((a, b) => (b.length > a.length ? b : a)) : null;
|
|
339
|
+
const loc = needle ? classify(parsed, needle, line, new Map()) : { where: "unknown" };
|
|
340
|
+
return { ...loc, ...(file ? { file } : {}) };
|
|
341
|
+
}
|
|
342
|
+
function classify(parsed, needle, input, cache) {
|
|
283
343
|
let path = null;
|
|
284
344
|
const walk = (node, trail) => {
|
|
285
345
|
if (path)
|
|
@@ -314,32 +374,211 @@ function locate(input, r, cache) {
|
|
|
314
374
|
return { where: "commit", tool };
|
|
315
375
|
return tool ? { where: "tool_call", tool } : { where: "tool_call" };
|
|
316
376
|
}
|
|
317
|
-
if (TOOL_OUTPUT_TYPE.test(type))
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
return { where: "
|
|
377
|
+
if (TOOL_OUTPUT_TYPE.test(type)) {
|
|
378
|
+
const id = n.tool_use_id ?? n.call_id ?? n.toolCallId;
|
|
379
|
+
const named = tool ?? (typeof id === "string" ? toolForResult(input, id, cache) : undefined);
|
|
380
|
+
return named ? { where: "tool_output", tool: named } : { where: "tool_output" };
|
|
381
|
+
}
|
|
382
|
+
if (n.role === "user" || (type === "user" && i === 0)) {
|
|
383
|
+
const line = frames[0].node;
|
|
384
|
+
if (frames.some((f) => f.key === "toolUseResult")) {
|
|
385
|
+
const content = line.message?.content;
|
|
386
|
+
const id = Array.isArray(content) ? content.find((c) => c && typeof c === "object" && "tool_use_id" in c)?.tool_use_id : undefined;
|
|
387
|
+
const named = typeof id === "string" ? toolForResult(input, id, cache) : undefined;
|
|
388
|
+
return named ? { where: "tool_output", tool: named } : { where: "tool_output" };
|
|
389
|
+
}
|
|
390
|
+
if (line.isMeta === true || line.isCompactSummary === true)
|
|
391
|
+
return { where: "attached" };
|
|
392
|
+
const text = frames[frames.length - 1].node;
|
|
393
|
+
if (typeof text === "string" && (text.length >= PASTE_CHARS || text.split("\n").length >= PASTE_LINES))
|
|
394
|
+
return { where: "pasted" };
|
|
395
|
+
return { where: "typed" };
|
|
396
|
+
}
|
|
321
397
|
if (n.role === "assistant" || (type === "assistant" && i === 0))
|
|
322
398
|
return { where: "assistant" };
|
|
323
399
|
}
|
|
324
400
|
return { where: "unknown" };
|
|
325
401
|
}
|
|
402
|
+
function locateMarkers(scrubbed, rule) {
|
|
403
|
+
const marker = `[REDACTED:${rule}]`;
|
|
404
|
+
const cache = new Map();
|
|
405
|
+
const out = [];
|
|
406
|
+
for (let at = scrubbed.indexOf(marker); at !== -1; at = scrubbed.indexOf(marker, at + marker.length)) {
|
|
407
|
+
const loc = locate(scrubbed, { rule, start: at, end: at + marker.length }, cache, marker);
|
|
408
|
+
if (!out.some((l) => l.where === loc.where && l.tool === loc.tool))
|
|
409
|
+
out.push(loc);
|
|
410
|
+
if (out.length >= 16)
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
exports.SHAPE_RULES = new Set([
|
|
416
|
+
"secret-assignment", "bearer-token", "jwt", "pem-private-key", "url-password", "curl-auth-header", "curl-auth-user",
|
|
417
|
+
]);
|
|
418
|
+
const FILLER_WORD = /secret|password|passwd|passw|pass|pwd|token|apikey|key|auth|bearer|basic|api|app|access|refresh|client|user|name|admin|root|guest|test(?:ing)?|dummy|sample|example|demo|default|change(?:me|it)?|service|local(?:host)?|super|your|mine|my|the|new|old|temp|tmp|dev|prod|stag(?:ing)?|value|string|here|foo|bar|baz|qux|private|public|hello|world|welcome|letmein/gi;
|
|
419
|
+
const LEET = { "0": "o", "1": "l", "3": "e", "4": "a", "5": "s", "7": "t", "@": "a", $: "s" };
|
|
420
|
+
function madeOfFillerWords(v) {
|
|
421
|
+
const residue = (t) => {
|
|
422
|
+
FILLER_WORD.lastIndex = 0;
|
|
423
|
+
if (!FILLER_WORD.test(t))
|
|
424
|
+
return Infinity;
|
|
425
|
+
return t.replace(FILLER_WORD, "").replace(/[^A-Za-z]/g, "").length;
|
|
426
|
+
};
|
|
427
|
+
return residue(v) <= 3 || residue(v.replace(/[013457@$]/g, (c) => LEET[c])) <= 3;
|
|
428
|
+
}
|
|
429
|
+
function hasCountingRuns(v) {
|
|
430
|
+
const t = v;
|
|
431
|
+
const need = t.length > 24 ? 3 : 2;
|
|
432
|
+
let runs = 0;
|
|
433
|
+
for (let i = 0; i + 2 < t.length; i++) {
|
|
434
|
+
const a = t.charCodeAt(i);
|
|
435
|
+
if (!/[A-Za-z0-9]/.test(t[i]) || t.charCodeAt(i + 1) !== a + 1 || t.charCodeAt(i + 2) !== a + 2)
|
|
436
|
+
continue;
|
|
437
|
+
if (++runs >= need)
|
|
438
|
+
return true;
|
|
439
|
+
i += 2;
|
|
440
|
+
}
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
const WORD_PAIRS = new Set(("ab ac ad ae ag ai ak al am an ao ap ar as at au av aw ax ay az ba be bi bj bl bo br bs bu by ca ce ch ci ck cl co " +
|
|
444
|
+
"cr cs ct cu cy da db de dg di dm do dr ds du dw dy ea eb ec ed ee ef eg ei el em en eo ep er es et eu ev ew ex ey " +
|
|
445
|
+
"fa fe ff fi fl fo fr ft fu ga ge gh gi gl gn go gr gs gu gy ha hc he hi ho hs ht hu ia ib ic id ie if ig il im in " +
|
|
446
|
+
"io ip ir is it iu iv ix iz ja je jo ju ka ke ki kn ko ks ku la ld le lf li lk ll lm lo lp ls lt lu lv ly ma mb me " +
|
|
447
|
+
"mi mm mo mp ms mu my na nc nd ne nf ng ni nk nm nn no ns nt nv ny oa ob oc od oe of og oi ok ol om on oo op or os " +
|
|
448
|
+
"ot ou ov ow ox oz pa pe ph pi pl po pp pr ps pt pu py qu ra rc rd re rg ri rk rl rm rn ro rr rs rt ru ry sa sc se " +
|
|
449
|
+
"sh si sk sl sm sn so sp ss st su sw sy ta te th ti tl to tr ts tt tu tw ty ua ub uc ud ue ug ui ul um un uo up ur " +
|
|
450
|
+
"us ut va ve vi vo vu wa we wh wi wo ws xp xt ya yc ye yl ym yn yo yp ys yt za ze zi zo").split(" "));
|
|
451
|
+
function readsAsWord(t) {
|
|
452
|
+
if (t.length < 3 || !/^(?:[a-z]+|[A-Z]+|[A-Z][a-z]+)$/.test(t))
|
|
453
|
+
return false;
|
|
454
|
+
const l = t.toLowerCase();
|
|
455
|
+
if (!/[aeiouy]/.test(l))
|
|
456
|
+
return false;
|
|
457
|
+
let odd = 0;
|
|
458
|
+
for (let i = 0; i + 1 < l.length; i++)
|
|
459
|
+
if (!WORD_PAIRS.has(l.slice(i, i + 2)))
|
|
460
|
+
odd++;
|
|
461
|
+
return odd <= (l.length >= 7 ? 1 : 0);
|
|
462
|
+
}
|
|
463
|
+
function namesASecret(v) {
|
|
464
|
+
if (/^arn:[a-z-]+:/i.test(v))
|
|
465
|
+
return true;
|
|
466
|
+
if (/^(?:vault|ssm|secretsmanager|keyvault|op|ref|env|file):/i.test(v))
|
|
467
|
+
return true;
|
|
468
|
+
if (/(?:^|\/)secrets?\//i.test(v))
|
|
469
|
+
return true;
|
|
470
|
+
const tokens = v
|
|
471
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
472
|
+
.replace(/([A-Za-z])(\d)/g, "$1 $2")
|
|
473
|
+
.replace(/(\d)([A-Za-z])/g, "$1 $2")
|
|
474
|
+
.split(/[^A-Za-z0-9]+/)
|
|
475
|
+
.filter(Boolean);
|
|
476
|
+
let total = 0;
|
|
477
|
+
let letters = 0;
|
|
478
|
+
let inWords = 0;
|
|
479
|
+
for (const t of tokens) {
|
|
480
|
+
total += t.length;
|
|
481
|
+
if (/^\d+$/.test(t))
|
|
482
|
+
continue;
|
|
483
|
+
letters += t.length;
|
|
484
|
+
if (readsAsWord(t))
|
|
485
|
+
inWords += t.length;
|
|
486
|
+
}
|
|
487
|
+
return inWords >= 6 && inWords / letters >= 0.7 && letters / total >= 0.5;
|
|
488
|
+
}
|
|
489
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
490
|
+
function jwtNotWorthRotating(v, now) {
|
|
491
|
+
const parts = v.split(".");
|
|
492
|
+
if (parts.length < 3)
|
|
493
|
+
return true;
|
|
494
|
+
let payload;
|
|
495
|
+
try {
|
|
496
|
+
payload = JSON.parse(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")));
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
if (!payload || typeof payload.exp !== "number")
|
|
502
|
+
return false;
|
|
503
|
+
if (payload.exp * 1000 < now + DAY_MS)
|
|
504
|
+
return true;
|
|
505
|
+
return typeof payload.iat === "number" && (payload.exp - payload.iat) * 1000 <= DAY_MS;
|
|
506
|
+
}
|
|
507
|
+
function pemBody(block) {
|
|
508
|
+
return block
|
|
509
|
+
.replace(/-----BEGIN[A-Z ]*PRIVATE KEY-----/, "")
|
|
510
|
+
.split("-----END")[0]
|
|
511
|
+
.split(/\\r|\\n|\r|\n/)
|
|
512
|
+
.filter((l) => !l.includes(":"))
|
|
513
|
+
.join("")
|
|
514
|
+
.replace(/\s+/g, "");
|
|
515
|
+
}
|
|
516
|
+
function notWorthRotating(k, now = Date.now()) {
|
|
517
|
+
const { rule, name, file } = k;
|
|
518
|
+
const value = k.value.trim();
|
|
519
|
+
if (looksLikeTestValue(value))
|
|
520
|
+
return true;
|
|
521
|
+
if (exports.SHAPE_RULES.has(rule) && file && (0, exports.isTestFile)(file))
|
|
522
|
+
return true;
|
|
523
|
+
if (rule === "pem-private-key") {
|
|
524
|
+
const body = pemBody(value);
|
|
525
|
+
return body.length < 60 || !/^[A-Za-z0-9+/=]+$/.test(body) || shannonEntropy(body) < 4;
|
|
526
|
+
}
|
|
527
|
+
if (PLACEHOLDER.test(value))
|
|
528
|
+
return true;
|
|
529
|
+
if (hasCountingRuns(value))
|
|
530
|
+
return true;
|
|
531
|
+
if (rule === "aws-access-key" && value.startsWith("ASIA"))
|
|
532
|
+
return true;
|
|
533
|
+
if (name && /(?:session|security)[_-]?token$/i.test(name))
|
|
534
|
+
return true;
|
|
535
|
+
if (/^(?:IQoJ|FwoG|FQoG|AQoD)[A-Za-z0-9+/]{20,}/.test(value))
|
|
536
|
+
return true;
|
|
537
|
+
if (value.startsWith("eyJ"))
|
|
538
|
+
return jwtNotWorthRotating(value, now);
|
|
539
|
+
if (!exports.SHAPE_RULES.has(rule))
|
|
540
|
+
return false;
|
|
541
|
+
if (name && /^x-atlassian-token$/i.test(name))
|
|
542
|
+
return true;
|
|
543
|
+
let secret = value;
|
|
544
|
+
if (rule === "curl-auth-user") {
|
|
545
|
+
const at = value.indexOf(":");
|
|
546
|
+
const user = value.slice(0, at).replace(/["']/g, "");
|
|
547
|
+
secret = value.slice(at + 1).replace(/["']/g, "");
|
|
548
|
+
if (at < 0 || !secret || secret === user)
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
if (rule === "url-password" && name && secret === name)
|
|
552
|
+
return true;
|
|
553
|
+
if (secret.includes("$"))
|
|
554
|
+
return true;
|
|
555
|
+
if (/^[A-Za-z._-]+$/.test(secret))
|
|
556
|
+
return true;
|
|
557
|
+
if (/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(secret))
|
|
558
|
+
return true;
|
|
559
|
+
if (madeOfFillerWords(secret))
|
|
560
|
+
return true;
|
|
561
|
+
if (rule === "secret-assignment" && namesASecret(secret))
|
|
562
|
+
return true;
|
|
563
|
+
return rule !== "url-password" && shannonEntropy(secret) < 3;
|
|
564
|
+
}
|
|
565
|
+
function hintOf(rule, value) {
|
|
566
|
+
const body = rule === "pem-private-key" ? value.split("-----END")[0].replace(/\\[nr]/g, "") : value;
|
|
567
|
+
return body.replace(/[^A-Za-z0-9]/g, "").slice(-4);
|
|
568
|
+
}
|
|
326
569
|
const MAX_LOCATIONS = 8;
|
|
327
570
|
function secretFindings(input, redactions, opts = {}) {
|
|
328
571
|
const seen = new Map();
|
|
329
572
|
const cache = new Map();
|
|
330
573
|
for (const r of redactions) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
value = value.split("-----END")[0];
|
|
334
|
-
const hint = value.replace(/[^A-Za-z0-9]/g, "").slice(-4);
|
|
574
|
+
const value = input.slice(r.start, r.end);
|
|
575
|
+
const hint = hintOf(r.rule, value);
|
|
335
576
|
if (!hint)
|
|
336
577
|
continue;
|
|
337
578
|
const lineStart = input.lastIndexOf("\n", r.start - 1) + 1;
|
|
338
579
|
const name = nameBefore(input, r.start, lineStart);
|
|
339
580
|
const file = fileOf(input, r.start, lineStart);
|
|
340
|
-
if (
|
|
341
|
-
continue;
|
|
342
|
-
if (exports.GENERIC_RULES.has(r.rule) && file && TEST_FILE.test(file))
|
|
581
|
+
if (notWorthRotating({ rule: r.rule, value, ...(name ? { name } : {}), ...(file ? { file } : {}) }, opts.now))
|
|
343
582
|
continue;
|
|
344
583
|
if (opts.skip?.({ rule: r.rule, value, ...(name ? { name } : {}), ...(file ? { file } : {}) }))
|
|
345
584
|
continue;
|
package/dist/teamSkillContext.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.TEAM_SKILL_CONTEXT = void 0;
|
|
|
4
4
|
exports.teamSkillSessionOutput = teamSkillSessionOutput;
|
|
5
5
|
exports.TEAM_SKILL_CONTEXT = `This machine is connected to Codiedev, the team's artifact and skill library.
|
|
6
6
|
Before a substantial coding task, run codiedev search "<brief task description>" and reuse a relevant team skill by running codiedev pull <exact-key> and following its instructions. If no skill fits, continue the task.
|
|
7
|
-
Use codiedev push <file.md> to share an artifact when asked,
|
|
7
|
+
Use codiedev push <file.md> to share an artifact when asked, and codiedev inbox to check messages. Use codiedev --help for other commands. If codiedev is unavailable, use npx codiedev@latest.`;
|
|
8
8
|
function teamSkillSessionOutput(source, context = exports.TEAM_SKILL_CONTEXT) {
|
|
9
9
|
return source === "cursor" ? { additional_context: context } : { additionalContext: context };
|
|
10
10
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.UNREAD_NOTICE_INTERVAL_MS = void 0;
|
|
7
|
+
exports.parseUnreadPings = parseUnreadPings;
|
|
8
|
+
exports.unreadNoticeLine = unreadNoticeLine;
|
|
9
|
+
exports.takeUnreadNotice = takeUnreadNotice;
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const configDir_1 = require("./configDir");
|
|
13
|
+
exports.UNREAD_NOTICE_INTERVAL_MS = 20 * 60 * 60 * 1000;
|
|
14
|
+
const STATE_FILE = "unread-notice.json";
|
|
15
|
+
function parseUnreadPings(value) {
|
|
16
|
+
if (!value || typeof value !== "object")
|
|
17
|
+
return null;
|
|
18
|
+
const v = value;
|
|
19
|
+
if (typeof v.count !== "number" || !Number.isFinite(v.count) || v.count <= 0)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
count: Math.floor(v.count),
|
|
23
|
+
newestPingId: typeof v.newestPingId === "string" ? v.newestPingId : null,
|
|
24
|
+
from: Array.isArray(v.from) ? v.from.filter((n) => typeof n === "string" && n.trim() !== "") : [],
|
|
25
|
+
senderCount: typeof v.senderCount === "number" ? v.senderCount : undefined,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function nameList(from, senderCount) {
|
|
29
|
+
if (from.length === 0)
|
|
30
|
+
return "";
|
|
31
|
+
const others = Math.max(0, senderCount - from.length);
|
|
32
|
+
const names = others > 0 ? [...from, `${others} other${others === 1 ? "" : "s"}`] : from;
|
|
33
|
+
if (names.length === 1)
|
|
34
|
+
return ` from ${names[0]}`;
|
|
35
|
+
return ` from ${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
36
|
+
}
|
|
37
|
+
function unreadNoticeLine(u) {
|
|
38
|
+
const who = nameList(u.from, u.senderCount ?? u.from.length);
|
|
39
|
+
return (`Codiedev: ${u.count} unread message${u.count === 1 ? "" : "s"}${who}. ` +
|
|
40
|
+
"Tell the user at the start of your first reply, and offer to show them with `codiedev inbox --unread`. " +
|
|
41
|
+
'Answer one with `codiedev reply <ping-id> "<message>"`.');
|
|
42
|
+
}
|
|
43
|
+
function readState(dir) {
|
|
44
|
+
try {
|
|
45
|
+
const raw = JSON.parse(fs_1.default.readFileSync(path_1.default.join(dir, STATE_FILE), "utf8"));
|
|
46
|
+
if (typeof raw?.at !== "number")
|
|
47
|
+
return null;
|
|
48
|
+
return { newestPingId: typeof raw.newestPingId === "string" ? raw.newestPingId : null, at: raw.at };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function takeUnreadNotice(value, options = {}) {
|
|
55
|
+
try {
|
|
56
|
+
const unread = parseUnreadPings(value);
|
|
57
|
+
if (!unread)
|
|
58
|
+
return null;
|
|
59
|
+
const dir = options.dir ?? (0, configDir_1.codiedevStateDir)();
|
|
60
|
+
const now = options.now ?? Date.now();
|
|
61
|
+
const last = readState(dir);
|
|
62
|
+
const sameNewest = !!last && last.newestPingId === unread.newestPingId;
|
|
63
|
+
if (sameNewest && last.at <= now && now - last.at < exports.UNREAD_NOTICE_INTERVAL_MS)
|
|
64
|
+
return null;
|
|
65
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
66
|
+
fs_1.default.writeFileSync(path_1.default.join(dir, STATE_FILE), JSON.stringify({ newestPingId: unread.newestPingId, at: now }));
|
|
67
|
+
return unreadNoticeLine(unread);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|