codiedev 0.10.0-beta.15 → 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/localScrub.js +6 -0
- package/dist/localScrubReport.js +4 -2
- 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
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.toRef = void 0;
|
|
37
|
+
exports.skillFolderFiles = skillFolderFiles;
|
|
38
|
+
exports.sameFileRefs = sameFileRefs;
|
|
39
|
+
exports.uploadBlobs = uploadBlobs;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const shared_1 = require("../commands/shared");
|
|
44
|
+
const files_1 = require("./files");
|
|
45
|
+
const manifest_1 = require("./manifest");
|
|
46
|
+
const toRef = (f) => ({
|
|
47
|
+
path: f.path,
|
|
48
|
+
sha256: f.sha256,
|
|
49
|
+
...(f.executable ? { executable: true } : {}),
|
|
50
|
+
});
|
|
51
|
+
exports.toRef = toRef;
|
|
52
|
+
function skillFolderFiles(skillFile, home = os.homedir()) {
|
|
53
|
+
const abs = path.resolve(skillFile);
|
|
54
|
+
if (path.basename(abs).toLowerCase() !== "skill.md")
|
|
55
|
+
return null;
|
|
56
|
+
let data;
|
|
57
|
+
try {
|
|
58
|
+
data = (0, manifest_1.parseFrontmatter)(fs.readFileSync(abs, "utf8")).data;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (!(data.name ?? "").trim() || !(data.description ?? "").trim())
|
|
64
|
+
return null;
|
|
65
|
+
const dir = path.dirname(abs);
|
|
66
|
+
if (dir === path.resolve(home) || dir === path.parse(dir).root || fs.existsSync(path.join(dir, ".git")))
|
|
67
|
+
return null;
|
|
68
|
+
let walked;
|
|
69
|
+
try {
|
|
70
|
+
walked = (0, files_1.collectLocalFiles)([{ dest: ".", source: dir, line: 0 }], dir, home, { lenient: true, checkAs: (p) => `skills/s/${p}` });
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return { skipped: `couldn't read its folder (${error.message})` };
|
|
74
|
+
}
|
|
75
|
+
const files = walked.files.filter((f) => f.path.toLowerCase() !== "skill.md");
|
|
76
|
+
const bytes = files.reduce((n, f) => n + f.size, 0);
|
|
77
|
+
if (files.length > files_1.PACK_MAX_FILES || bytes > files_1.PACK_FILES_MAX_TOTAL_BYTES) {
|
|
78
|
+
return {
|
|
79
|
+
skipped: `its folder is over the limit (${files.length} files, ${(bytes / (1024 * 1024)).toFixed(1)}MB; at most ${files_1.PACK_MAX_FILES} files and ${files_1.PACK_FILES_MAX_TOTAL_BYTES / (1024 * 1024)}MB)`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { files, notes: walked.notes, bytes };
|
|
83
|
+
}
|
|
84
|
+
function sameFileRefs(a, b) {
|
|
85
|
+
const key = (l) => JSON.stringify((l ?? []).map((f) => [f.path, f.sha256, !!f.executable]).sort());
|
|
86
|
+
return key(a) === key(b);
|
|
87
|
+
}
|
|
88
|
+
async function uploadBlobs(config, files, base) {
|
|
89
|
+
const unique = [...new Map(files.map((f) => [f.sha256, f])).values()];
|
|
90
|
+
if (!unique.length)
|
|
91
|
+
return 0;
|
|
92
|
+
const have = new Set((await (0, shared_1.apiRequest)("POST", `${base}/known`, { config, body: { sha256s: unique.map((f) => f.sha256) } })).known ?? []);
|
|
93
|
+
let sent = 0;
|
|
94
|
+
for (const f of unique) {
|
|
95
|
+
if (have.has(f.sha256))
|
|
96
|
+
continue;
|
|
97
|
+
try {
|
|
98
|
+
await (0, shared_1.apiRequestBytes)("POST", `${base}/upload`, { config, body: fs.readFileSync(f.abs), query: { sha256: f.sha256 } });
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
throw new Error(`couldn't upload ${f.path} (${error.message})`);
|
|
102
|
+
}
|
|
103
|
+
sent += 1;
|
|
104
|
+
}
|
|
105
|
+
return sent;
|
|
106
|
+
}
|
package/dist/packs/store.js
CHANGED
|
@@ -46,6 +46,7 @@ exports.writeMarketplace = writeMarketplace;
|
|
|
46
46
|
const fs = __importStar(require("fs"));
|
|
47
47
|
const skillMarker_1 = require("../skillMarker");
|
|
48
48
|
const path = __importStar(require("path"));
|
|
49
|
+
const files_1 = require("./files");
|
|
49
50
|
const paths_1 = require("./paths");
|
|
50
51
|
function pluginVersion(version) {
|
|
51
52
|
return `${version}.0.0`;
|
|
@@ -71,6 +72,7 @@ function portableAgentContent(content) {
|
|
|
71
72
|
return fm[1] + kept.join("\n") + fm[3] + content.slice(fm[0].length);
|
|
72
73
|
}
|
|
73
74
|
function pluginFiles(manifest, options = {}) {
|
|
75
|
+
const rewrite = (content) => (options.portable && options.pluginRoot ? (0, files_1.rewritePluginRoot)(content, options.pluginRoot) : content);
|
|
74
76
|
const files = {};
|
|
75
77
|
files[path.join(".claude-plugin", "plugin.json")] = JSON.stringify({
|
|
76
78
|
name: manifest.slug,
|
|
@@ -78,10 +80,10 @@ function pluginFiles(manifest, options = {}) {
|
|
|
78
80
|
description: manifest.description || manifest.name,
|
|
79
81
|
}, null, 2) + "\n";
|
|
80
82
|
for (const skill of manifest.skills) {
|
|
81
|
-
files[path.join("skills", skill.name, "SKILL.md")] = (0, skillMarker_1.stampSkillMarker)(skill.content, skill.artifactId);
|
|
83
|
+
files[path.join("skills", skill.name, "SKILL.md")] = (0, skillMarker_1.stampSkillMarker)(rewrite(skill.content), skill.artifactId);
|
|
82
84
|
}
|
|
83
85
|
for (const agent of manifest.agents) {
|
|
84
|
-
files[path.join("agents", `${agent.name}.md`)] = options.portable ? portableAgentContent(agent.content) : agent.content;
|
|
86
|
+
files[path.join("agents", `${agent.name}.md`)] = options.portable ? rewrite(portableAgentContent(agent.content)) : agent.content;
|
|
85
87
|
}
|
|
86
88
|
if (manifest.hooksJson) {
|
|
87
89
|
files[path.join("hooks", "hooks.json")] = manifest.hooksJson;
|
|
@@ -103,12 +105,28 @@ function writePluginDir(root, manifest, options = {}) {
|
|
|
103
105
|
const tmp = path.join(parent, `.${manifest.slug}.tmp-${stamp}`);
|
|
104
106
|
const old = path.join(parent, `.${manifest.slug}.old-${stamp}`);
|
|
105
107
|
try {
|
|
106
|
-
|
|
108
|
+
const text = pluginFiles(manifest, { ...options, pluginRoot: options.pluginRoot ?? target });
|
|
109
|
+
for (const [rel, content] of Object.entries(text)) {
|
|
107
110
|
const file = path.join(tmp, rel);
|
|
108
111
|
assertInside(tmp, file);
|
|
109
112
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
110
113
|
fs.writeFileSync(file, content, "utf8");
|
|
111
114
|
}
|
|
115
|
+
const written = new Set(Object.keys(text).map((rel) => rel.split(path.sep).join("/").toLowerCase()));
|
|
116
|
+
for (const f of manifest.files ?? []) {
|
|
117
|
+
const problem = (0, files_1.packFilePathProblem)(f.path);
|
|
118
|
+
if (problem)
|
|
119
|
+
throw new Error(`refusing pack file ${f.path}: ${problem}`);
|
|
120
|
+
if (written.has(f.path.toLowerCase()))
|
|
121
|
+
throw new Error(`pack file ${f.path} clashes with a file Codiedev writes`);
|
|
122
|
+
if (!options.blobDir)
|
|
123
|
+
throw new Error(`pack file ${f.path} wasn't downloaded`);
|
|
124
|
+
const file = path.join(tmp, ...f.path.split("/"));
|
|
125
|
+
assertInside(tmp, file);
|
|
126
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
127
|
+
fs.copyFileSync((0, files_1.blobPath)(options.blobDir, f.sha256), file);
|
|
128
|
+
fs.chmodSync(file, f.executable ? 0o755 : 0o644);
|
|
129
|
+
}
|
|
112
130
|
if (fs.existsSync(target))
|
|
113
131
|
fs.renameSync(target, old);
|
|
114
132
|
fs.renameSync(tmp, target);
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.REFRESH_MS = void 0;
|
|
37
|
+
exports.keyLabel = keyLabel;
|
|
38
|
+
exports.formatRules = formatRules;
|
|
39
|
+
exports.findPromptKey = findPromptKey;
|
|
40
|
+
exports.blockMessage = blockMessage;
|
|
41
|
+
exports.blockResponse = blockResponse;
|
|
42
|
+
exports.readPromptGuardPolicy = readPromptGuardPolicy;
|
|
43
|
+
exports.writePromptGuardPolicy = writePromptGuardPolicy;
|
|
44
|
+
exports.refreshPromptGuardPolicy = refreshPromptGuardPolicy;
|
|
45
|
+
exports.maybeSpawnPromptGuardRefresh = maybeSpawnPromptGuardRefresh;
|
|
46
|
+
exports.checkPrompt = checkPrompt;
|
|
47
|
+
exports.reportPromptBlocked = reportPromptBlocked;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const child_process_1 = require("child_process");
|
|
51
|
+
const configDir_1 = require("./configDir");
|
|
52
|
+
const rules_1 = require("./scrub/rules");
|
|
53
|
+
const scrubProviderRules_1 = require("./scrub/scrubProviderRules");
|
|
54
|
+
exports.REFRESH_MS = 10 * 60_000;
|
|
55
|
+
const CACHE_FILE = "prompt-guard.json";
|
|
56
|
+
const FETCH_TIMEOUT_MS = 8_000;
|
|
57
|
+
const LABELS = {
|
|
58
|
+
...scrubProviderRules_1.PROVIDER_RULE_LABELS,
|
|
59
|
+
"supabase-token": "Supabase token", "groq-key": "Groq key", "xai-key": "xAI key", "replicate-token": "Replicate token",
|
|
60
|
+
"aws-access-key": "AWS key", "openai-key": "OpenAI key", "anthropic-key": "Anthropic key",
|
|
61
|
+
"github-token": "GitHub token", "gitlab-token": "GitLab token", "stripe-key": "Stripe key",
|
|
62
|
+
"slack-token": "Slack token", "slack-webhook": "Slack webhook", "google-api-key": "Google API key",
|
|
63
|
+
"pem-private-key": "private key", "convex-deploy-key": "Convex deploy key", "linear-key": "Linear key",
|
|
64
|
+
"codiedev-key": "Codiedev key", "fireworks-key": "Fireworks key", "npm-token": "npm token",
|
|
65
|
+
"sendgrid-key": "SendGrid key", "twilio-key": "Twilio key", "azure-storage-key": "Azure storage key",
|
|
66
|
+
"azure-sas": "Azure SAS token", "url-password": "password in a URL",
|
|
67
|
+
"curl-auth-header": "token in a curl command", "curl-auth-user": "password in a curl command",
|
|
68
|
+
};
|
|
69
|
+
function harmlessHere(rule, value, prompt) {
|
|
70
|
+
if (rule === "stripe-key" && /^[sr]k_test_/.test(value))
|
|
71
|
+
return true;
|
|
72
|
+
if (rule === "google-api-key" && /firebaseapp\.com|authDomain|firebaseConfig/i.test(prompt))
|
|
73
|
+
return true;
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
function keyLabel(rule) {
|
|
77
|
+
if (rule.startsWith("custom:"))
|
|
78
|
+
return rule.slice("custom:".length);
|
|
79
|
+
return LABELS[rule] ?? rule.replace(/-/g, " ");
|
|
80
|
+
}
|
|
81
|
+
function formatRules(formats) {
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const f of formats) {
|
|
84
|
+
try {
|
|
85
|
+
out.push({ name: `custom:${f.name}`, re: new RegExp(`(?<![A-Za-z0-9])${f.source}(?![A-Za-z0-9_-])`, "g"), ...(f.keyword ? { keywords: [f.keyword] } : {}) });
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
function findPromptKey(prompt, formats = [], now = Date.now()) {
|
|
92
|
+
if (!prompt || prompt.length > 2_000_000)
|
|
93
|
+
return null;
|
|
94
|
+
const { redactions } = (0, rules_1.scrubSecrets)(prompt, { extraRules: formatRules(formats) });
|
|
95
|
+
for (const r of redactions) {
|
|
96
|
+
if (rules_1.GENERIC_RULES.has(r.rule))
|
|
97
|
+
continue;
|
|
98
|
+
const value = prompt.slice(r.start, r.end);
|
|
99
|
+
if ((0, rules_1.notWorthRotating)({ rule: r.rule, value }, now))
|
|
100
|
+
continue;
|
|
101
|
+
if (harmlessHere(r.rule, value, prompt))
|
|
102
|
+
continue;
|
|
103
|
+
return { rule: r.rule, label: keyLabel(r.rule), hint: (0, rules_1.hintOf)(r.rule, value) };
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function blockMessage(key) {
|
|
108
|
+
const article = /^[aeiou]/i.test(key.label) ? "an" : "a";
|
|
109
|
+
return `Codiedev stopped this prompt: it has ${article} ${key.label} in it (…${key.hint}). ` +
|
|
110
|
+
"Nothing was sent to the model. Put the key in an environment variable or .env file and refer to it by name " +
|
|
111
|
+
"(for example $API_KEY), then send the prompt again. Your team turned on this check.";
|
|
112
|
+
}
|
|
113
|
+
function blockResponse(harness, message) {
|
|
114
|
+
if (harness === "cursor")
|
|
115
|
+
return { continue: false, user_message: message };
|
|
116
|
+
return { decision: "block", reason: message };
|
|
117
|
+
}
|
|
118
|
+
function cachePath(stateDir) {
|
|
119
|
+
return path.join(stateDir, CACHE_FILE);
|
|
120
|
+
}
|
|
121
|
+
function readPromptGuardPolicy(stateDir = (0, configDir_1.codiedevStateDir)()) {
|
|
122
|
+
try {
|
|
123
|
+
const v = JSON.parse(fs.readFileSync(cachePath(stateDir), "utf8"));
|
|
124
|
+
if (typeof v.fetchedAt !== "number")
|
|
125
|
+
return null;
|
|
126
|
+
return {
|
|
127
|
+
blockPrompts: v.blockPrompts === true,
|
|
128
|
+
formats: Array.isArray(v.formats) ? v.formats.filter((f) => f && typeof f.name === "string" && typeof f.source === "string").slice(0, 50) : [],
|
|
129
|
+
fetchedAt: v.fetchedAt,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function writePromptGuardPolicy(policy, stateDir = (0, configDir_1.codiedevStateDir)()) {
|
|
137
|
+
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
138
|
+
const file = cachePath(stateDir);
|
|
139
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
140
|
+
fs.writeFileSync(tmp, JSON.stringify(policy), { mode: 0o600 });
|
|
141
|
+
fs.renameSync(tmp, file);
|
|
142
|
+
}
|
|
143
|
+
async function refreshPromptGuardPolicy(config, fetchImpl = fetch, stateDir = (0, configDir_1.codiedevStateDir)(), now = Date.now()) {
|
|
144
|
+
if (!config?.token || !config.backendUrl)
|
|
145
|
+
return null;
|
|
146
|
+
try {
|
|
147
|
+
const res = await fetchImpl(`${config.backendUrl}/api/cli/promptGuard`, {
|
|
148
|
+
headers: { Authorization: `Bearer ${config.token}` },
|
|
149
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
150
|
+
});
|
|
151
|
+
if (!res.ok)
|
|
152
|
+
return null;
|
|
153
|
+
const body = (await res.json());
|
|
154
|
+
const formats = Array.isArray(body.formats)
|
|
155
|
+
? body.formats.filter((f) => f && typeof f.name === "string" && typeof f.source === "string" && f.source.length <= 300).slice(0, 50)
|
|
156
|
+
: [];
|
|
157
|
+
const policy = { blockPrompts: body.blockPrompts === true, formats, fetchedAt: now };
|
|
158
|
+
writePromptGuardPolicy(policy, stateDir);
|
|
159
|
+
return policy;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function maybeSpawnPromptGuardRefresh(opts) {
|
|
166
|
+
try {
|
|
167
|
+
if (!opts.connected || process.env.CODIEDEV_PROMPT_GUARD_CHILD)
|
|
168
|
+
return "disabled";
|
|
169
|
+
const stateDir = opts.stateDir ?? (0, configDir_1.codiedevStateDir)();
|
|
170
|
+
const now = opts.now ?? Date.now();
|
|
171
|
+
const stamp = path.join(stateDir, "prompt-guard.refresh");
|
|
172
|
+
if (!opts.force) {
|
|
173
|
+
const cached = readPromptGuardPolicy(stateDir);
|
|
174
|
+
if (cached && now - cached.fetchedAt < exports.REFRESH_MS && cached.fetchedAt <= now)
|
|
175
|
+
return "fresh";
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const last = fs.statSync(stamp).mtimeMs;
|
|
179
|
+
if (now - last < 60_000 && last <= now)
|
|
180
|
+
return "fresh";
|
|
181
|
+
}
|
|
182
|
+
catch { }
|
|
183
|
+
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
184
|
+
fs.writeFileSync(stamp, String(now));
|
|
185
|
+
const t = new Date(now);
|
|
186
|
+
fs.utimesSync(stamp, t, t);
|
|
187
|
+
const child = (opts.spawn ?? child_process_1.spawn)(process.execPath, [opts.hookEntry ?? path.join(__dirname, "hook.js"), "prompt-guard-refresh"], {
|
|
188
|
+
detached: true,
|
|
189
|
+
stdio: "ignore",
|
|
190
|
+
env: { ...process.env, CODIEDEV_PROMPT_GUARD_CHILD: "1" },
|
|
191
|
+
});
|
|
192
|
+
child.on?.("error", () => { });
|
|
193
|
+
child.unref?.();
|
|
194
|
+
return "spawned";
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return "error";
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function checkPrompt(prompt, harness, policy, now = Date.now()) {
|
|
201
|
+
try {
|
|
202
|
+
if (!policy?.blockPrompts || !prompt)
|
|
203
|
+
return { blocked: false };
|
|
204
|
+
const key = findPromptKey(prompt, policy.formats, now);
|
|
205
|
+
if (!key)
|
|
206
|
+
return { blocked: false };
|
|
207
|
+
const message = blockMessage(key);
|
|
208
|
+
return { blocked: true, key, message, response: blockResponse(harness, message) };
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return { blocked: false };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function reportPromptBlocked(config, rule, harness, fetchImpl = fetch) {
|
|
215
|
+
if (!config?.token || !config.backendUrl)
|
|
216
|
+
return Promise.resolve();
|
|
217
|
+
return fetchImpl(`${config.backendUrl}/api/cli/promptBlocked`, {
|
|
218
|
+
method: "POST",
|
|
219
|
+
headers: { Authorization: `Bearer ${config.token}`, "Content-Type": "application/json" },
|
|
220
|
+
body: JSON.stringify({ rule: rule.slice(0, 80), harness }),
|
|
221
|
+
signal: AbortSignal.timeout(3_000),
|
|
222
|
+
}).then(() => undefined, () => undefined);
|
|
223
|
+
}
|