candor-ts 0.4.4 → 0.4.6
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/contract.mjs +13 -0
- package/package.json +2 -1
- package/query.mjs +3 -9
- package/scan.mjs +22 -9
package/contract.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
// The agent contract for THE INSTALLED VERSION — AGENTS.md ships in the npm tarball, so the doc and
|
|
6
|
+
// engine cannot drift (the spec §2.1 version-trust rule applied to documentation). ONE implementation
|
|
7
|
+
// used by both scan.mjs and query.mjs, so `--agents` output can never diverge within an install.
|
|
8
|
+
export function printAgents() {
|
|
9
|
+
const dir = path.dirname(fileURLToPath(import.meta.url)); // the package root (where AGENTS.md ships)
|
|
10
|
+
const semver = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8")).version;
|
|
11
|
+
console.log(`<!-- candor-ts ${semver} · the agent contract for this installed version -->`);
|
|
12
|
+
process.stdout.write(fs.readFileSync(path.join(dir, "AGENTS.md"), "utf8"));
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "candor-ts",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "candor for TypeScript — per-function side effects, transitively, with a policy gate (candor-spec 0.4)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"scan.mjs",
|
|
36
36
|
"query.mjs",
|
|
37
37
|
"policy.mjs",
|
|
38
|
+
"contract.mjs",
|
|
38
39
|
"README.md",
|
|
39
40
|
"AGENTS.md",
|
|
40
41
|
"PROVE-IT.md",
|
package/query.mjs
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
|
|
21
21
|
import { parsePolicy, scopeMatches } from "./policy.mjs";
|
|
22
|
+
import { printAgents } from "./contract.mjs";
|
|
22
23
|
|
|
23
24
|
// ---- the §3.1 match ladder: exact > segment-suffix > substring ------------------------------------
|
|
24
25
|
function matchTier(name, q) {
|
|
@@ -44,16 +45,9 @@ const emit = (v) => console.log(JSON.stringify(v, null, 1));
|
|
|
44
45
|
const [, , cmd, ...args] = process.argv;
|
|
45
46
|
switch (cmd) {
|
|
46
47
|
case "--agents":
|
|
47
|
-
case "agents":
|
|
48
|
-
//
|
|
49
|
-
const { dirname: qDirname, join: qJoin } = await import("node:path");
|
|
50
|
-
const { fileURLToPath: qFile } = await import("node:url");
|
|
51
|
-
const dir = qDirname(qFile(import.meta.url));
|
|
52
|
-
const semver = JSON.parse(fs.readFileSync(qJoin(dir, "package.json"), "utf8")).version;
|
|
53
|
-
console.log(`<!-- candor-ts ${semver} · the agent contract for this installed version -->`);
|
|
54
|
-
process.stdout.write(fs.readFileSync(qJoin(dir, "AGENTS.md"), "utf8"));
|
|
48
|
+
case "agents":
|
|
49
|
+
printAgents(); // shared with scan.mjs — one implementation, can't diverge within an install
|
|
55
50
|
break;
|
|
56
|
-
}
|
|
57
51
|
case "parsepolicy": {
|
|
58
52
|
emit(parsePolicy(fs.readFileSync(args[0], "utf8")));
|
|
59
53
|
break;
|
package/scan.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import path from "node:path";
|
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
26
|
import { createRequire } from "node:module";
|
|
27
27
|
import { parsePolicy, evaluatePolicy } from "./policy.mjs";
|
|
28
|
+
import { printAgents } from "./contract.mjs";
|
|
28
29
|
|
|
29
30
|
const ENGINE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
30
31
|
|
|
@@ -51,14 +52,7 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
51
52
|
else if (outPrefix === null) outPrefix = a; // legacy positional prefix
|
|
52
53
|
else { console.error(`candor-ts: unexpected extra argument ${a} (${usage})`); process.exit(2); }
|
|
53
54
|
}
|
|
54
|
-
if (wantAgents) {
|
|
55
|
-
// The agent contract for THE INSTALLED VERSION — AGENTS.md ships in the npm tarball, so doc and
|
|
56
|
-
// engine cannot drift (the spec §2.1 version-trust rule applied to documentation).
|
|
57
|
-
const semver = JSON.parse(fs.readFileSync(path.join(ENGINE_DIR, "package.json"), "utf8")).version;
|
|
58
|
-
console.log(`<!-- candor-ts ${semver} · the agent contract for this installed version -->`);
|
|
59
|
-
process.stdout.write(fs.readFileSync(path.join(ENGINE_DIR, "AGENTS.md"), "utf8"));
|
|
60
|
-
process.exit(0);
|
|
61
|
-
}
|
|
55
|
+
if (wantAgents) { printAgents(); process.exit(0); }
|
|
62
56
|
if (target === null) { console.error(usage); process.exit(2); }
|
|
63
57
|
|
|
64
58
|
// ---- project discovery (a dir, a single file, or a tsconfig) --------------------------------------
|
|
@@ -311,6 +305,21 @@ function firstStringLiteral(node) {
|
|
|
311
305
|
}
|
|
312
306
|
return null;
|
|
313
307
|
}
|
|
308
|
+
// Refine the Exec cliff (spec §4 ⟨0.5⟩): the effects a literal, statically-known subprocess head
|
|
309
|
+
// implies, matched by basename. ADDED to a caller that already carries Exec (a subprocess is still
|
|
310
|
+
// spawned — Exec is never dropped); an unrecognised head returns [] and keeps the bare cliff (never
|
|
311
|
+
// guess). A candor engine reads Fs/Env only — spec §7 item 12 (the analyzer self-boundary) guarantees
|
|
312
|
+
// it, so that case is spec-supplied. Only UNAMBIGUOUS single-effect tools belong here: a multi-modal
|
|
313
|
+
// head (git status local vs git push Net; rsync local vs remote; make/npm run project code) would
|
|
314
|
+
// fabricate the effect for its common case. The reference engines share this table verbatim.
|
|
315
|
+
function commandHeadEffects(cmd) {
|
|
316
|
+
const base = cmd.trim().split(/\s+/)[0].split(/[/\\]/).pop();
|
|
317
|
+
if (["curl", "wget", "http", "ssh", "scp"].includes(base)) return ["Net"];
|
|
318
|
+
if (["psql", "mysql", "sqlite3", "mongosh", "redis-cli"].includes(base)) return ["Db"];
|
|
319
|
+
if (["candor", "candor-run.sh", "candor-scan", "candor-query", "candor-java",
|
|
320
|
+
"candor-classify", "candor-report", "cargo-candor"].includes(base)) return ["Env", "Fs"];
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
314
323
|
// host[:port] from an address/URL literal; non-address strings yield nothing (never fabricate).
|
|
315
324
|
function hostLiteral(s) {
|
|
316
325
|
const m = s.match(/^[a-z][a-z0-9+.-]*:\/\/([^/]+)/i); // scheme://host[:port]/…
|
|
@@ -689,7 +698,11 @@ function visitCalls(node) {
|
|
|
689
698
|
}
|
|
690
699
|
if (eff === "Exec") {
|
|
691
700
|
const lit = firstStringLiteral(node);
|
|
692
|
-
if (lit)
|
|
701
|
+
if (lit) {
|
|
702
|
+
rec.cmds.add(lit.trim().split(/\s+/)[0]); // the program of a command line
|
|
703
|
+
// a known literal head refines the cliff (curl→Net, candor→Fs/Env); Exec stays
|
|
704
|
+
for (const e of commandHeadEffects(lit)) rec.direct.add(e);
|
|
705
|
+
}
|
|
693
706
|
}
|
|
694
707
|
if (eff === "Fs") {
|
|
695
708
|
const lit = firstStringLiteral(node);
|