@rightkit/release 0.2.115 → 0.2.117
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/cli/right-release.mjs +6 -2
- package/ownership-command.mjs +47 -0
- package/ownership.mjs +273 -0
- package/package.json +1 -1
- package/preflight.mjs +29 -0
- package/publication-gate.mjs +69 -8
- package/rightkit-ownership.json +363 -0
- package/rightkit-versions.json +32 -14
package/cli/right-release.mjs
CHANGED
|
@@ -22,6 +22,7 @@ const commands = new Map([
|
|
|
22
22
|
["mirror-root-artifact", "mirror-root-artifact.mjs"],
|
|
23
23
|
["github", "github-release.mjs"],
|
|
24
24
|
["cargo-target", "cargo-target.mjs"],
|
|
25
|
+
["ownership", "ownership-command.mjs"],
|
|
25
26
|
]);
|
|
26
27
|
|
|
27
28
|
const args = process.argv.slice(2);
|
|
@@ -157,8 +158,10 @@ Commands:
|
|
|
157
158
|
Upload one sealed release; never builds
|
|
158
159
|
release Compatibility alias for build
|
|
159
160
|
publish Compatibility alias for upload
|
|
160
|
-
publish npm --package @rightkit/<name> [--dry-run] Gate preflight for npm publish
|
|
161
|
-
publish npm --package @rightkit/<name>
|
|
161
|
+
publish npm --package @rightkit/<name> [--dry-run] Gate preflight for npm publish
|
|
162
|
+
publish npm --package @rightkit/<name> --publish --tarball <gated.tgz>
|
|
163
|
+
Publish the gated tarball with the Keychain token
|
|
164
|
+
(npm-rightkit-publish) and verify registry bytes
|
|
162
165
|
publish cargo --crate <name> [--dry-run] Test, inspect, scan, and publish one crates.io package
|
|
163
166
|
publish swift [--repo <git-url>] [--dry-run] Test, tag, resolve a fresh exact-pin consumer, and publish RightKitSwift to its git repo
|
|
164
167
|
model promote --authority heardright --config <file> [--dry-run]
|
|
@@ -167,6 +170,7 @@ Commands:
|
|
|
167
170
|
doctor [--platform mac|win] Inspect one app's release config
|
|
168
171
|
doctor --all Verify all Right Suite app release contracts
|
|
169
172
|
suite-doctor Verify all local Right Suite repositories
|
|
173
|
+
ownership [--app <dir>] [--json] Fail on app copies of RightKit-owned capabilities
|
|
170
174
|
cache status [--json] Inspect the shared local build cache
|
|
171
175
|
cache prune [--dry-run|--apply] [--json] Plan or apply safe shared-target pruning
|
|
172
176
|
cache migrate --config <file> [--dry-run|--apply] [--json]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// right-release ownership: check an app against RightKit's capability ownership, or rebuild
|
|
3
|
+
// the shipped manifest from each package's declaration.
|
|
4
|
+
//
|
|
5
|
+
// right-release ownership [--app <dir>] [--json]
|
|
6
|
+
// right-release ownership --write-manifest [--rightkit <tools/rightkit>]
|
|
7
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { aggregateOwnership, checkApp, formatTable, MANIFEST_FILE } from "./ownership.mjs";
|
|
11
|
+
|
|
12
|
+
const packageRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const shipped = path.join(packageRoot, MANIFEST_FILE);
|
|
14
|
+
|
|
15
|
+
function flag(args, name) {
|
|
16
|
+
const i = args.indexOf(name);
|
|
17
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function main(args) {
|
|
21
|
+
if (args.includes("--write-manifest")) {
|
|
22
|
+
const root = path.resolve(flag(args, "--rightkit") ?? path.join(packageRoot, "..", ".."));
|
|
23
|
+
const manifest = aggregateOwnership(root);
|
|
24
|
+
writeFileSync(shipped, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
25
|
+
console.log(`[right-release] ownership manifest: ${manifest.capabilities.length} capabilities -> ${shipped}`);
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
const app = path.resolve(flag(args, "--app") ?? process.cwd());
|
|
29
|
+
const manifest = JSON.parse(readFileSync(shipped, "utf8"));
|
|
30
|
+
const result = checkApp(app, manifest);
|
|
31
|
+
if (args.includes("--json")) {
|
|
32
|
+
console.log(JSON.stringify({ ok: result.ok, rows: result.rows, violations: result.violations, problems: result.problems }, null, 2));
|
|
33
|
+
} else {
|
|
34
|
+
console.log(`RightKit ownership: ${path.basename(app)}`);
|
|
35
|
+
console.log(formatTable(result.rows));
|
|
36
|
+
for (const v of result.violations) {
|
|
37
|
+
console.log(` OWN COPY ${v.fingerprint} (${v.package}) ${v.file}${v.line ? `:${v.line}` : ""} ${v.text}`);
|
|
38
|
+
}
|
|
39
|
+
for (const p of result.problems) console.log(` EXCEPTION ${p}`);
|
|
40
|
+
console.log(result.ok ? "[right-release] ownership passed" : "[right-release] ownership FAILED: move these to RightKit or add a reviewed, dated exception");
|
|
41
|
+
}
|
|
42
|
+
return result.ok ? 0 : 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
46
|
+
process.exitCode = main(process.argv.slice(2));
|
|
47
|
+
}
|
package/ownership.mjs
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// RightKit ownership gate: no Right Suite app keeps its own copy of a capability RightKit owns.
|
|
2
|
+
//
|
|
3
|
+
// Each RightKit package declares what it owns in `rightkit-ownership.json` beside its manifest
|
|
4
|
+
// (capability + fingerprints of a reimplementation). `aggregateOwnership` folds those into the
|
|
5
|
+
// shipped `rightkit-ownership.json`, so an app is always checked against the data of the
|
|
6
|
+
// RightKit version it pins. `scanApp` reports fingerprint matches in an app repository; each
|
|
7
|
+
// must be covered by a live, specific entry in the app's `.rightkit/ownership-exceptions.json`
|
|
8
|
+
// (the migration backlog) or the gate fails.
|
|
9
|
+
//
|
|
10
|
+
// Fingerprint types:
|
|
11
|
+
// dependency direct dependency by name (cargo | npm | swiftpm). Transitive use through a
|
|
12
|
+
// RightKit package is never flagged.
|
|
13
|
+
// source regex over code files (comments-only lines, docs, tests fixtures, vendored and
|
|
14
|
+
// generated trees are skipped).
|
|
15
|
+
import { spawnSync } from "./background-process.mjs";
|
|
16
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
|
|
19
|
+
export const MANIFEST_FILE = "rightkit-ownership.json";
|
|
20
|
+
export const EXCEPTIONS_FILE = path.join(".rightkit", "ownership-exceptions.json");
|
|
21
|
+
|
|
22
|
+
const SKIP_DIRS = new Set([
|
|
23
|
+
".git", "node_modules", "target", "dist", "build", "out", ".next", ".turbo", ".cache",
|
|
24
|
+
"vendor", "third_party", "Pods", "DerivedData", ".build", "generated", "fixtures",
|
|
25
|
+
"test-fixtures", "__fixtures__", ".rightkit", ".audit",
|
|
26
|
+
]);
|
|
27
|
+
const CODE_EXT = new Set([".rs", ".ts", ".tsx", ".js", ".mjs", ".cjs", ".jsx", ".swift", ".m", ".mm", ".c", ".cc", ".cpp", ".h", ".py", ".go", ".kt", ".java", ".cs"]);
|
|
28
|
+
const MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
29
|
+
|
|
30
|
+
/** Collect every package's ownership declaration under a RightKit checkout. */
|
|
31
|
+
export function aggregateOwnership(rightkitRoot) {
|
|
32
|
+
const entries = [];
|
|
33
|
+
for (const group of ["crates", "packages"]) {
|
|
34
|
+
const dir = path.join(rightkitRoot, group);
|
|
35
|
+
if (!existsSync(dir)) continue;
|
|
36
|
+
for (const name of readdirSync(dir).sort()) {
|
|
37
|
+
const file = path.join(dir, name, MANIFEST_FILE);
|
|
38
|
+
if (!existsSync(file)) continue;
|
|
39
|
+
const declared = JSON.parse(readFileSync(file, "utf8"));
|
|
40
|
+
if (declared.schema !== undefined) continue; // the shipped aggregate itself
|
|
41
|
+
for (const capability of declared.capabilities ?? []) {
|
|
42
|
+
entries.push({ package: declared.package, ...capability });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const manifest = { schema: 1, capabilities: entries };
|
|
47
|
+
validateManifest(manifest);
|
|
48
|
+
return manifest;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function validateManifest(manifest) {
|
|
52
|
+
const ids = new Set();
|
|
53
|
+
for (const c of manifest.capabilities) {
|
|
54
|
+
if (!c.package || !c.capability || !Array.isArray(c.fingerprints) || c.fingerprints.length === 0) {
|
|
55
|
+
throw new Error(`ownership: incomplete capability ${JSON.stringify(c).slice(0, 120)}`);
|
|
56
|
+
}
|
|
57
|
+
for (const f of c.fingerprints) {
|
|
58
|
+
if (!f.id || ids.has(f.id)) throw new Error(`ownership: missing or duplicate fingerprint id ${f.id}`);
|
|
59
|
+
ids.add(f.id);
|
|
60
|
+
if (f.type === "dependency") {
|
|
61
|
+
if (!["cargo", "npm", "swiftpm"].includes(f.ecosystem) || !f.name) throw new Error(`ownership: bad dependency fingerprint ${f.id}`);
|
|
62
|
+
} else if (f.type === "source") {
|
|
63
|
+
new RegExp(f.pattern); // throws on a bad pattern
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error(`ownership: unknown fingerprint type ${f.type} (${f.id})`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function skippedDir(name) {
|
|
72
|
+
return SKIP_DIRS.has(name) || name.startsWith(".") || /^(node_modules|target)/.test(name);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const RIGHTKIT_MARKER = `packages/release/${MANIFEST_FILE}`;
|
|
76
|
+
|
|
77
|
+
// A nested repository is its own app with its own exceptions file, and RightKit's own sources
|
|
78
|
+
// are the owner, never a copy.
|
|
79
|
+
function walkTree(root, visit, rel = "") {
|
|
80
|
+
let entries;
|
|
81
|
+
try { entries = readdirSync(path.join(root, rel), { withFileTypes: true }); } catch { return; }
|
|
82
|
+
for (const e of entries) {
|
|
83
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
84
|
+
if (e.isDirectory()) {
|
|
85
|
+
if (!skippedDir(e.name) && !existsSync(path.join(root, r, ".git"))
|
|
86
|
+
&& !existsSync(path.join(root, r, RIGHTKIT_MARKER))) walkTree(root, visit, r);
|
|
87
|
+
} else if (e.isFile()) {
|
|
88
|
+
visit(r);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Tracked plus untracked-but-not-ignored files. Git never descends into nested repositories,
|
|
94
|
+
// and honoring .gitignore keeps model, data and build trees out of the scan.
|
|
95
|
+
function gitFiles(root) {
|
|
96
|
+
if (!existsSync(path.join(root, ".git"))) return null;
|
|
97
|
+
const r = spawnSync("git", ["-C", root, "ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
98
|
+
encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true,
|
|
99
|
+
});
|
|
100
|
+
if (r.status !== 0) return null;
|
|
101
|
+
const files = r.stdout.split("\0").filter((f) => f && !f.endsWith("/"));
|
|
102
|
+
const rightkitRoots = files.filter((f) => f === RIGHTKIT_MARKER || f.endsWith(`/${RIGHTKIT_MARKER}`))
|
|
103
|
+
.map((f) => f.slice(0, f.length - RIGHTKIT_MARKER.length));
|
|
104
|
+
return files.filter((f) => !rightkitRoots.some((p) => f.startsWith(p))
|
|
105
|
+
&& !f.split("/").slice(0, -1).some(skippedDir));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function walk(root, visit) {
|
|
109
|
+
const files = gitFiles(root);
|
|
110
|
+
if (files) files.forEach((f) => visit(f));
|
|
111
|
+
else walkTree(root, visit);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Direct dependencies declared by an app repository: [{ecosystem, name, file}]. */
|
|
115
|
+
export function directDependencies(appRoot) {
|
|
116
|
+
const deps = [];
|
|
117
|
+
walk(appRoot, (rel) => {
|
|
118
|
+
const base = path.posix.basename(rel);
|
|
119
|
+
const abs = path.join(appRoot, rel);
|
|
120
|
+
if (base === "Cargo.toml") {
|
|
121
|
+
let section = "";
|
|
122
|
+
for (const line of readFileSync(abs, "utf8").split(/\r?\n/)) {
|
|
123
|
+
const header = line.match(/^\s*\[([^\]]+)\]/);
|
|
124
|
+
if (header) { section = header[1]; continue; }
|
|
125
|
+
const dotted = section.match(/(^|\.)(dev-|build-)?dependencies\.([A-Za-z0-9_-]+)$/);
|
|
126
|
+
if (dotted) { deps.push({ ecosystem: "cargo", name: dotted[3], file: rel }); section = "~"; continue; }
|
|
127
|
+
if (!/(^|\.)((dev|build)-)?dependencies$/.test(section)) continue;
|
|
128
|
+
const m = line.match(/^\s*([A-Za-z0-9_-]+)\s*=/);
|
|
129
|
+
if (m) deps.push({ ecosystem: "cargo", name: m[1], file: rel });
|
|
130
|
+
}
|
|
131
|
+
} else if (base === "package.json") {
|
|
132
|
+
let pkg;
|
|
133
|
+
try { pkg = JSON.parse(readFileSync(abs, "utf8")); } catch { return; }
|
|
134
|
+
for (const field of ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"]) {
|
|
135
|
+
for (const name of Object.keys(pkg[field] ?? {})) deps.push({ ecosystem: "npm", name, file: rel });
|
|
136
|
+
}
|
|
137
|
+
} else if (base === "Package.swift") {
|
|
138
|
+
for (const m of readFileSync(abs, "utf8").matchAll(/\.package\([^)]*url:\s*"([^"]+)"/g)) {
|
|
139
|
+
const name = m[1].replace(/\.git$/, "").split("/").pop();
|
|
140
|
+
deps.push({ ecosystem: "swiftpm", name, file: rel });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return deps;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isCommentOnly(line) {
|
|
148
|
+
const t = line.trim();
|
|
149
|
+
return t.startsWith("//") || t.startsWith("#") || t.startsWith("*") || t.startsWith("/*") || t.startsWith("--");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** All fingerprint matches in an app: [{fingerprint, capability, package, file, line, text}]. */
|
|
153
|
+
export function scanApp(appRoot, manifest) {
|
|
154
|
+
const findings = [];
|
|
155
|
+
const deps = directDependencies(appRoot);
|
|
156
|
+
const sources = [];
|
|
157
|
+
for (const c of manifest.capabilities) {
|
|
158
|
+
for (const f of c.fingerprints) {
|
|
159
|
+
if (f.type === "dependency") {
|
|
160
|
+
for (const d of deps) {
|
|
161
|
+
if (d.ecosystem === f.ecosystem && d.name === f.name) {
|
|
162
|
+
findings.push({ fingerprint: f.id, capability: c.capability, package: c.package, file: d.file, line: 0, text: `${d.ecosystem} dependency ${d.name}` });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
sources.push({ c, f, re: new RegExp(f.pattern, f.flags ?? "") });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (sources.length) {
|
|
171
|
+
// One combined pass rejects almost every file and line before the per-fingerprint tests.
|
|
172
|
+
const any = new RegExp(sources.map(({ f }) => `(?:${f.pattern})`).join("|"));
|
|
173
|
+
walk(appRoot, (rel) => {
|
|
174
|
+
if (!CODE_EXT.has(path.posix.extname(rel))) return;
|
|
175
|
+
const abs = path.join(appRoot, rel);
|
|
176
|
+
try { if (statSync(abs).size > MAX_FILE_BYTES) return; } catch { return; }
|
|
177
|
+
const text = readFileSync(abs, "utf8");
|
|
178
|
+
if (!any.test(text)) return;
|
|
179
|
+
const lines = text.split(/\r?\n/);
|
|
180
|
+
lines.forEach((line, i) => {
|
|
181
|
+
if (!any.test(line) || isCommentOnly(line)) return;
|
|
182
|
+
for (const { c, f, re } of sources) {
|
|
183
|
+
if (re.test(line)) findings.push({ fingerprint: f.id, capability: c.capability, package: c.package, file: rel, line: i + 1, text: line.trim().slice(0, 160) });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return findings;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function globToRegExp(glob) {
|
|
192
|
+
let re = "";
|
|
193
|
+
for (let i = 0; i < glob.length; i++) {
|
|
194
|
+
const ch = glob[i];
|
|
195
|
+
if (ch === "*" && glob[i + 1] === "*") { re += ".*"; i++; if (glob[i + 1] === "/") i++; }
|
|
196
|
+
else if (ch === "*") re += "[^/]*";
|
|
197
|
+
else if (ch === "?") re += "[^/]";
|
|
198
|
+
else re += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
199
|
+
}
|
|
200
|
+
return new RegExp(`^${re}$`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Load and validate an app's exceptions (the migration backlog). */
|
|
204
|
+
export function loadExceptions(appRoot) {
|
|
205
|
+
const file = path.join(appRoot, EXCEPTIONS_FILE);
|
|
206
|
+
if (!existsSync(file)) return [];
|
|
207
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
208
|
+
return (parsed.exceptions ?? []).map((e, i) => ({ ...e, index: i }));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Apply exceptions. Returns {violations, covered, problems}: uncovered findings, findings an
|
|
213
|
+
* exception covers, and exception defects (expired, stale, wildcard-only, incomplete).
|
|
214
|
+
*/
|
|
215
|
+
export function evaluate(findings, exceptions, { today = new Date().toISOString().slice(0, 10) } = {}) {
|
|
216
|
+
const problems = [];
|
|
217
|
+
const live = [];
|
|
218
|
+
for (const e of exceptions) {
|
|
219
|
+
const where = `exception #${e.index} (${e.fingerprint ?? "?"})`;
|
|
220
|
+
const missing = ["fingerprint", "paths", "reason", "owner", "approvedBy"].filter((k) => !e[k] || (Array.isArray(e[k]) && e[k].length === 0));
|
|
221
|
+
if (missing.length) { problems.push(`${where}: missing ${missing.join(", ")}`); continue; }
|
|
222
|
+
if (!e.expires && !e.tracking) { problems.push(`${where}: needs an expires date or a tracking item`); continue; }
|
|
223
|
+
if (e.paths.some((p) => /^\*+(\/\*+)*$/.test(p))) { problems.push(`${where}: wildcard-only paths are not allowed`); continue; }
|
|
224
|
+
if (e.expires && e.expires < today) { problems.push(`${where}: expired ${e.expires}`); continue; }
|
|
225
|
+
live.push({ ...e, matchers: e.paths.map(globToRegExp), used: false });
|
|
226
|
+
}
|
|
227
|
+
const violations = [];
|
|
228
|
+
const covered = [];
|
|
229
|
+
for (const f of findings) {
|
|
230
|
+
const e = live.find((x) => x.fingerprint === f.fingerprint && x.matchers.some((m) => m.test(f.file)));
|
|
231
|
+
if (e) { e.used = true; covered.push({ ...f, exception: e }); } else violations.push(f);
|
|
232
|
+
}
|
|
233
|
+
for (const e of live) if (!e.used) problems.push(`exception #${e.index} (${e.fingerprint}): stale, matches nothing; delete it`);
|
|
234
|
+
return { violations, covered, problems };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** One row per capability: uses RightKit | own copy (exception, expiry) | not needed. */
|
|
238
|
+
export function capabilityTable(manifest, deps, result) {
|
|
239
|
+
const rows = [];
|
|
240
|
+
const byCapability = new Map();
|
|
241
|
+
for (const c of manifest.capabilities) {
|
|
242
|
+
if (!byCapability.has(c.capability)) byCapability.set(c.capability, c.package);
|
|
243
|
+
}
|
|
244
|
+
for (const [capability, owner] of byCapability) {
|
|
245
|
+
const uses = deps.some((d) => d.name === owner || d.name === owner.replace(/^rightkit-/, "@rightkit/"));
|
|
246
|
+
const own = result.violations.filter((v) => v.capability === capability);
|
|
247
|
+
const excepted = result.covered.filter((v) => v.capability === capability);
|
|
248
|
+
let status;
|
|
249
|
+
if (own.length) status = `OWN COPY, NOT EXCEPTED (${own.length})`;
|
|
250
|
+
else if (excepted.length) {
|
|
251
|
+
const e = excepted[0].exception;
|
|
252
|
+
status = `own copy, excepted until ${e.expires ?? e.tracking} (${excepted.length})`;
|
|
253
|
+
} else status = uses ? "uses RightKit" : "not needed";
|
|
254
|
+
rows.push({ capability, owner, uses, status });
|
|
255
|
+
}
|
|
256
|
+
return rows;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function formatTable(rows) {
|
|
260
|
+
const w1 = Math.max(10, ...rows.map((r) => r.capability.length));
|
|
261
|
+
const w2 = Math.max(7, ...rows.map((r) => r.owner.length));
|
|
262
|
+
const lines = [`${"capability".padEnd(w1)} ${"owner".padEnd(w2)} status`];
|
|
263
|
+
for (const r of rows) lines.push(`${r.capability.padEnd(w1)} ${r.owner.padEnd(w2)} ${r.status}`);
|
|
264
|
+
return lines.join("\n");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Full check for one app. `ok` is false on any uncovered copy or exception defect. */
|
|
268
|
+
export function checkApp(appRoot, manifest, options = {}) {
|
|
269
|
+
const findings = scanApp(appRoot, manifest);
|
|
270
|
+
const result = evaluate(findings, loadExceptions(appRoot), options);
|
|
271
|
+
const rows = capabilityTable(manifest, directDependencies(appRoot), result);
|
|
272
|
+
return { ok: result.violations.length === 0 && result.problems.length === 0, rows, ...result };
|
|
273
|
+
}
|
package/package.json
CHANGED
package/preflight.mjs
CHANGED
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
|
|
21
21
|
import { existsSync, lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
22
22
|
import path from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
23
24
|
import { spawnSync } from "./background-process.mjs";
|
|
24
25
|
import { brokerManagedHost } from "./cargo-contract.mjs";
|
|
26
|
+
import { checkApp, MANIFEST_FILE } from "./ownership.mjs";
|
|
25
27
|
|
|
26
28
|
export const PREFLIGHT_OK = "ok";
|
|
27
29
|
export const PREFLIGHT_WARN = "warn";
|
|
@@ -176,6 +178,7 @@ export function collectPreflight({
|
|
|
176
178
|
env = process.env,
|
|
177
179
|
versionProbe = commandVersion,
|
|
178
180
|
isBrokerManagedHost = brokerManagedHost,
|
|
181
|
+
ownership = true,
|
|
179
182
|
} = {}) {
|
|
180
183
|
const checks = [];
|
|
181
184
|
const windows = platform === "win" || platform === "win32";
|
|
@@ -287,9 +290,35 @@ export function collectPreflight({
|
|
|
287
290
|
}
|
|
288
291
|
}
|
|
289
292
|
|
|
293
|
+
if (repoRoot && ownership) {
|
|
294
|
+
// An app living inside the RightKit workspace repository is scoped to its own directory.
|
|
295
|
+
const workspace = existsSync(path.join(repoRoot, "tools", "rightkit", "packages", "release"));
|
|
296
|
+
checks.push(ownershipCheck(workspace && appRoot ? appRoot : repoRoot));
|
|
297
|
+
}
|
|
298
|
+
|
|
290
299
|
return checks;
|
|
291
300
|
}
|
|
292
301
|
|
|
302
|
+
/** Release-blocking: the app keeps no unexcepted copy of a RightKit-owned capability. */
|
|
303
|
+
export function ownershipCheck(repoRoot, manifestPath = path.join(path.dirname(fileURLToPath(import.meta.url)), MANIFEST_FILE)) {
|
|
304
|
+
let manifest;
|
|
305
|
+
try {
|
|
306
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
307
|
+
} catch (error) {
|
|
308
|
+
return fail("ownership", `RightKit ownership manifest unreadable: ${error.message}`, "reinstall @rightkit/release");
|
|
309
|
+
}
|
|
310
|
+
const result = checkApp(repoRoot, manifest);
|
|
311
|
+
if (result.ok) {
|
|
312
|
+
const excepted = result.covered.length;
|
|
313
|
+
return ok("ownership", excepted ? `no unexcepted copies (${excepted} findings under dated exceptions)` : "no copies of RightKit-owned capabilities");
|
|
314
|
+
}
|
|
315
|
+
const first = result.violations[0];
|
|
316
|
+
const detail = first
|
|
317
|
+
? `${result.violations.length} own cop${result.violations.length === 1 ? "y" : "ies"} of RightKit capabilities, e.g. ${first.fingerprint} at ${first.file}${first.line ? `:${first.line}` : ""}`
|
|
318
|
+
: result.problems[0];
|
|
319
|
+
return fail("ownership", detail, "run: right-release ownership (move to RightKit, or add a reviewed, dated exception)");
|
|
320
|
+
}
|
|
321
|
+
|
|
293
322
|
export function formatPreflight(checks) {
|
|
294
323
|
const symbol = { [PREFLIGHT_OK]: "ok ", [PREFLIGHT_WARN]: "WARN", [PREFLIGHT_FAIL]: "FAIL" };
|
|
295
324
|
const lines = checks.map((check) => {
|
package/publication-gate.mjs
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
// evidence file names the exact current commit SHA (clean tree for the package path) and carries
|
|
4
4
|
// passing host run receipts from that same SHA: macOS and Windows, or macOS only for Swift.
|
|
5
5
|
// Format: docs/RIGHTKIT-SHARED-PACKAGES.md, "Publication gate" -> "Evidence file format".
|
|
6
|
+
import crypto from "node:crypto";
|
|
6
7
|
import fs from "node:fs";
|
|
8
|
+
import os from "node:os";
|
|
7
9
|
import path from "node:path";
|
|
8
10
|
import { spawnSync } from "./background-process.mjs";
|
|
9
11
|
import { fileURLToPath } from "node:url";
|
|
@@ -43,7 +45,11 @@ export function checkPublicationGate({ kind, name, version, packagePath, evidenc
|
|
|
43
45
|
if (!SHA.test(String(evidence.commit ?? ""))) missing.push("evidence commit is not a full 40-character SHA");
|
|
44
46
|
else if (head && evidence.commit !== head) missing.push(`evidence commit ${evidence.commit} != current HEAD ${head}`);
|
|
45
47
|
const runs = Array.isArray(evidence.runs) ? evidence.runs : [];
|
|
46
|
-
|
|
48
|
+
// npm UI packages may ship on macOS evidence first when Adrian decides so; the decision must be
|
|
49
|
+
// recorded in the evidence (hostPolicy "darwin-only" + a non-empty hostPolicyReason).
|
|
50
|
+
const darwinOnly = kind === "npm" && evidence.hostPolicy === "darwin-only"
|
|
51
|
+
&& typeof evidence.hostPolicyReason === "string" && evidence.hostPolicyReason.trim() !== "";
|
|
52
|
+
for (const host of darwinOnly ? ["darwin"] : requiredHosts(kind)) {
|
|
47
53
|
const label = host === "darwin" ? "macOS" : "Windows";
|
|
48
54
|
const matching = runs.filter((run) => run?.host?.os === host);
|
|
49
55
|
if (!matching.length) { missing.push(`no ${label} host run receipt`); continue; }
|
|
@@ -68,21 +74,76 @@ export function enforcePublicationGate(options, { dryRun = false, write = (line)
|
|
|
68
74
|
throw new Error(report);
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
//
|
|
72
|
-
export
|
|
73
|
-
|
|
77
|
+
// Keychain item holding a granular npm token scoped to @rightkit with 2FA bypass (created by Adrian).
|
|
78
|
+
export const NPM_KEYCHAIN_SERVICE = "npm-rightkit-publish";
|
|
79
|
+
|
|
80
|
+
// `right-release publish npm --package <name> [--evidence <file>] [--dry-run]` runs the gate only.
|
|
81
|
+
// Add `--publish --tarball <file>` to publish that exact gated tarball: its sha256 must equal the
|
|
82
|
+
// evidence artifactSha256, the token is read from the macOS Keychain inside this process (never
|
|
83
|
+
// printed or passed on a command line), and the registry shasum is verified after publication.
|
|
84
|
+
export function runNpmPreflight(argv, workspaceRoot = process.cwd(), deps = {}) {
|
|
85
|
+
let pkg; let evidence; let tarball; const dryRun = argv.includes("--dry-run"); const publish = argv.includes("--publish");
|
|
74
86
|
for (let i = 0; i < argv.length; i += 1) {
|
|
75
87
|
if (argv[i] === "--package") pkg = argv[++i];
|
|
76
88
|
else if (argv[i] === "--evidence") evidence = argv[++i];
|
|
89
|
+
else if (argv[i] === "--tarball") tarball = path.resolve(argv[++i]);
|
|
77
90
|
else if (argv[i] === "--workspace") workspaceRoot = path.resolve(argv[++i]);
|
|
78
|
-
else if (argv[i] !== "--dry-run") throw new Error(`unknown publish npm option: ${argv[i]}`);
|
|
91
|
+
else if (argv[i] !== "--dry-run" && argv[i] !== "--publish") throw new Error(`unknown publish npm option: ${argv[i]}`);
|
|
79
92
|
}
|
|
80
93
|
if (!pkg || !/^@rightkit\/[a-z0-9-]+$/.test(pkg)) throw new Error("publish npm requires --package @rightkit/<name>");
|
|
81
94
|
const packagePath = path.join(workspaceRoot, "packages", pkg.slice("@rightkit/".length));
|
|
82
95
|
const manifest = JSON.parse(fs.readFileSync(path.join(packagePath, "package.json"), "utf8"));
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
96
|
+
const evidencePath = path.resolve(evidence ?? defaultEvidencePath(workspaceRoot, "npm", pkg));
|
|
97
|
+
const options = { kind: "npm", name: pkg, version: manifest.version, packagePath, evidencePath };
|
|
98
|
+
if (!publish) {
|
|
99
|
+
enforcePublicationGate(options, { dryRun });
|
|
100
|
+
process.stdout.write("[right-release] npm preflight only; add --publish --tarball <file> to publish\n");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (dryRun) throw new Error("--publish and --dry-run are mutually exclusive");
|
|
104
|
+
if (!tarball) throw new Error("--publish requires --tarball <gated .tgz>");
|
|
105
|
+
enforcePublicationGate(options);
|
|
106
|
+
publishGatedTarball({ name: pkg, version: manifest.version, tarball, evidencePath }, deps);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function publishGatedTarball({ name, version, tarball, evidencePath }, deps = {}) {
|
|
110
|
+
const run = deps.spawnSync ?? spawnSync;
|
|
111
|
+
const bytes = fs.readFileSync(tarball);
|
|
112
|
+
const evidence = JSON.parse(fs.readFileSync(evidencePath, "utf8"));
|
|
113
|
+
const sha256 = crypto.createHash("sha256").update(bytes).digest("hex");
|
|
114
|
+
if (sha256 !== evidence.artifactSha256) throw new Error(`tarball sha256 ${sha256} != evidence artifactSha256 ${evidence.artifactSha256}`);
|
|
115
|
+
const token = deps.readToken ? deps.readToken() : readKeychainToken(run);
|
|
116
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rk-npm-publish-"));
|
|
117
|
+
const userconfig = path.join(dir, "npmrc");
|
|
118
|
+
const redact = (text) => String(text ?? "").split(token).join("<redacted>");
|
|
119
|
+
try {
|
|
120
|
+
fs.writeFileSync(userconfig, "//registry.npmjs.org/:_authToken=${RIGHTKIT_NPM_TOKEN}\n", { mode: 0o600 });
|
|
121
|
+
const result = run("npm", ["publish", tarball, "--access", "public", "--registry", "https://registry.npmjs.org/", "--userconfig", userconfig],
|
|
122
|
+
{ encoding: "utf8", env: { ...process.env, RIGHTKIT_NPM_TOKEN: token }, windowsHide: true });
|
|
123
|
+
const out = redact(`${result.stdout ?? ""}${result.stderr ?? ""}`);
|
|
124
|
+
if (result.error || result.status !== 0) throw new Error(`npm publish failed for ${name}@${version}:\n${out.trim()}`);
|
|
125
|
+
process.stdout.write(`[right-release] published ${name}@${version}\n`);
|
|
126
|
+
} finally {
|
|
127
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
const sha1 = crypto.createHash("sha1").update(bytes).digest("hex");
|
|
130
|
+
const deadline = Date.now() + (deps.verifyTimeoutMs ?? 10 * 60 * 1000);
|
|
131
|
+
for (;;) {
|
|
132
|
+
const view = run("npm", ["view", `${name}@${version}`, "dist.shasum", "--prefer-online", "--registry", "https://registry.npmjs.org/"], { encoding: "utf8", windowsHide: true });
|
|
133
|
+
const registry = (view.stdout ?? "").trim();
|
|
134
|
+
if (registry === sha1) { process.stdout.write(`[right-release] registry bytes verified for ${name}@${version} (shasum ${sha1})\n`); return; }
|
|
135
|
+
if (registry && registry !== sha1) throw new Error(`registry shasum ${registry} != gated tarball ${sha1} for ${name}@${version}`);
|
|
136
|
+
if (Date.now() > deadline) throw new Error(`${name}@${version} not visible on the registry after publish; verify later with npm view`);
|
|
137
|
+
(deps.sleep ?? ((ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)))(15000);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readKeychainToken(run) {
|
|
142
|
+
if (process.platform !== "darwin") throw new Error(`npm publish token lookup is implemented for the macOS Keychain (${NPM_KEYCHAIN_SERVICE}) only`);
|
|
143
|
+
const result = run("security", ["find-generic-password", "-s", NPM_KEYCHAIN_SERVICE, "-w"], { encoding: "utf8", windowsHide: true });
|
|
144
|
+
const token = (result.stdout ?? "").trim();
|
|
145
|
+
if (result.status !== 0 || !token) throw new Error(`Keychain item ${NPM_KEYCHAIN_SERVICE} not found; create it with: security add-generic-password -a "$USER" -s ${NPM_KEYCHAIN_SERVICE} -w`);
|
|
146
|
+
return token;
|
|
86
147
|
}
|
|
87
148
|
|
|
88
149
|
if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url))) {
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"capabilities": [
|
|
4
|
+
{
|
|
5
|
+
"package": "rightkit-browser",
|
|
6
|
+
"capability": "browser-automation",
|
|
7
|
+
"summary": "Chromium over CDP with admission, SSRF policy, profiles and owned processes",
|
|
8
|
+
"fingerprints": [
|
|
9
|
+
{
|
|
10
|
+
"id": "browser.cargo.chromiumoxide",
|
|
11
|
+
"type": "dependency",
|
|
12
|
+
"ecosystem": "cargo",
|
|
13
|
+
"name": "chromiumoxide"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "browser.cargo.headless_chrome",
|
|
17
|
+
"type": "dependency",
|
|
18
|
+
"ecosystem": "cargo",
|
|
19
|
+
"name": "headless_chrome"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "browser.cargo.fantoccini",
|
|
23
|
+
"type": "dependency",
|
|
24
|
+
"ecosystem": "cargo",
|
|
25
|
+
"name": "fantoccini"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "browser.cargo.thirtyfour",
|
|
29
|
+
"type": "dependency",
|
|
30
|
+
"ecosystem": "cargo",
|
|
31
|
+
"name": "thirtyfour"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "browser.npm.puppeteer",
|
|
35
|
+
"type": "dependency",
|
|
36
|
+
"ecosystem": "npm",
|
|
37
|
+
"name": "puppeteer"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "browser.npm.puppeteer-core",
|
|
41
|
+
"type": "dependency",
|
|
42
|
+
"ecosystem": "npm",
|
|
43
|
+
"name": "puppeteer-core"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "browser.npm.playwright-core",
|
|
47
|
+
"type": "dependency",
|
|
48
|
+
"ecosystem": "npm",
|
|
49
|
+
"name": "playwright-core"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"package": "rightkit-db",
|
|
55
|
+
"capability": "sqlite-storage",
|
|
56
|
+
"summary": "SQLite open profiles, WAL with verified fallback, keys, migrations, backup, recovery",
|
|
57
|
+
"fingerprints": [
|
|
58
|
+
{
|
|
59
|
+
"id": "db.cargo.rusqlite",
|
|
60
|
+
"type": "dependency",
|
|
61
|
+
"ecosystem": "cargo",
|
|
62
|
+
"name": "rusqlite"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "db.cargo.sqlx",
|
|
66
|
+
"type": "dependency",
|
|
67
|
+
"ecosystem": "cargo",
|
|
68
|
+
"name": "sqlx"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "db.npm.better-sqlite3",
|
|
72
|
+
"type": "dependency",
|
|
73
|
+
"ecosystem": "npm",
|
|
74
|
+
"name": "better-sqlite3"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"package": "rightkit-embed",
|
|
80
|
+
"capability": "embeddings",
|
|
81
|
+
"summary": "Local text embeddings",
|
|
82
|
+
"fingerprints": [
|
|
83
|
+
{
|
|
84
|
+
"id": "embed.cargo.fastembed",
|
|
85
|
+
"type": "dependency",
|
|
86
|
+
"ecosystem": "cargo",
|
|
87
|
+
"name": "fastembed"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"package": "rightkit-mcp",
|
|
93
|
+
"capability": "mcp",
|
|
94
|
+
"summary": "MCP client/server transports, trust gate, server pool",
|
|
95
|
+
"fingerprints": [
|
|
96
|
+
{
|
|
97
|
+
"id": "mcp.cargo.rmcp",
|
|
98
|
+
"type": "dependency",
|
|
99
|
+
"ecosystem": "cargo",
|
|
100
|
+
"name": "rmcp"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "mcp.npm.sdk",
|
|
104
|
+
"type": "dependency",
|
|
105
|
+
"ecosystem": "npm",
|
|
106
|
+
"name": "@modelcontextprotocol/sdk"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "mcp.protocol.initialized",
|
|
110
|
+
"type": "source",
|
|
111
|
+
"pattern": "[\"']notifications/initialized[\"']"
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"package": "rightkit-oauth",
|
|
117
|
+
"capability": "oauth-sign-in",
|
|
118
|
+
"summary": "OAuth sign-in: loopback PKCE, device flow, provider endpoints, refresh",
|
|
119
|
+
"fingerprints": [
|
|
120
|
+
{
|
|
121
|
+
"id": "oauth.cargo.oauth2",
|
|
122
|
+
"type": "dependency",
|
|
123
|
+
"ecosystem": "cargo",
|
|
124
|
+
"name": "oauth2"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "oauth.cargo.openidconnect",
|
|
128
|
+
"type": "dependency",
|
|
129
|
+
"ecosystem": "cargo",
|
|
130
|
+
"name": "openidconnect"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "oauth.cargo.yup-oauth2",
|
|
134
|
+
"type": "dependency",
|
|
135
|
+
"ecosystem": "cargo",
|
|
136
|
+
"name": "yup-oauth2"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"id": "oauth.npm.simple-oauth2",
|
|
140
|
+
"type": "dependency",
|
|
141
|
+
"ecosystem": "npm",
|
|
142
|
+
"name": "simple-oauth2"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "oauth.npm.oauth4webapi",
|
|
146
|
+
"type": "dependency",
|
|
147
|
+
"ecosystem": "npm",
|
|
148
|
+
"name": "oauth4webapi"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "oauth.endpoint.github",
|
|
152
|
+
"type": "source",
|
|
153
|
+
"pattern": "github\\.com/login/(oauth/(authorize|access_token)|device/code)"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "oauth.endpoint.google",
|
|
157
|
+
"type": "source",
|
|
158
|
+
"pattern": "accounts\\.google\\.com/o/oauth2|oauth2\\.googleapis\\.com/(token|device/code)"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "oauth.endpoint.openai",
|
|
162
|
+
"type": "source",
|
|
163
|
+
"pattern": "auth\\.openai\\.com/oauth/(authorize|token)"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "oauth.endpoint.apple",
|
|
167
|
+
"type": "source",
|
|
168
|
+
"pattern": "appleid\\.apple\\.com/auth/(authorize|token)"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"id": "oauth.endpoint.cloudflare",
|
|
172
|
+
"type": "source",
|
|
173
|
+
"pattern": "dash\\.cloudflare\\.com/oauth2/(auth|token)"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"id": "oauth.device-grant",
|
|
177
|
+
"type": "source",
|
|
178
|
+
"pattern": "urn:ietf:params:oauth:grant-type:device_code"
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"package": "rightkit-ort",
|
|
184
|
+
"capability": "onnx-runtime",
|
|
185
|
+
"summary": "ONNX Runtime selection, execution providers, dylib resolution and global pools",
|
|
186
|
+
"fingerprints": [
|
|
187
|
+
{
|
|
188
|
+
"id": "ort.cargo.ort",
|
|
189
|
+
"type": "dependency",
|
|
190
|
+
"ecosystem": "cargo",
|
|
191
|
+
"name": "ort"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"id": "ort.npm.onnxruntime-node",
|
|
195
|
+
"type": "dependency",
|
|
196
|
+
"ecosystem": "npm",
|
|
197
|
+
"name": "onnxruntime-node"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"id": "ort.env.dylib",
|
|
201
|
+
"type": "source",
|
|
202
|
+
"pattern": "ORT_DYLIB_PATH"
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"package": "rightkit-process",
|
|
208
|
+
"capability": "owned-processes",
|
|
209
|
+
"summary": "Child processes owned as a tree: process groups / kill-on-close jobs, hidden windows",
|
|
210
|
+
"fingerprints": [
|
|
211
|
+
{
|
|
212
|
+
"id": "process.cargo.process-wrap",
|
|
213
|
+
"type": "dependency",
|
|
214
|
+
"ecosystem": "cargo",
|
|
215
|
+
"name": "process-wrap"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "process.cargo.command-group",
|
|
219
|
+
"type": "dependency",
|
|
220
|
+
"ecosystem": "cargo",
|
|
221
|
+
"name": "command-group"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"id": "process.api.kill-on-job-close",
|
|
225
|
+
"type": "source",
|
|
226
|
+
"pattern": "JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE"
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"package": "rightkit-sandbox",
|
|
232
|
+
"capability": "os-sandbox",
|
|
233
|
+
"summary": "Seatbelt / Landlock+seccomp / AppContainer confinement, PTY leases, resource authority",
|
|
234
|
+
"fingerprints": [
|
|
235
|
+
{
|
|
236
|
+
"id": "sandbox.cargo.landlock",
|
|
237
|
+
"type": "dependency",
|
|
238
|
+
"ecosystem": "cargo",
|
|
239
|
+
"name": "landlock"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"id": "sandbox.cargo.seccompiler",
|
|
243
|
+
"type": "dependency",
|
|
244
|
+
"ecosystem": "cargo",
|
|
245
|
+
"name": "seccompiler"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"id": "sandbox.api.appcontainer",
|
|
249
|
+
"type": "source",
|
|
250
|
+
"pattern": "\\b(CreateAppContainerProfile|DeriveAppContainerSidFromAppContainerName|PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES)\\b"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"id": "sandbox.api.seatbelt",
|
|
254
|
+
"type": "source",
|
|
255
|
+
"pattern": "\\bsandbox_init\\b|sandbox-exec\\b"
|
|
256
|
+
}
|
|
257
|
+
]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"package": "rightkit-secrets",
|
|
261
|
+
"capability": "os-credential-store",
|
|
262
|
+
"summary": "Keychain / Credential Manager access with per-app namespaces and test guards",
|
|
263
|
+
"fingerprints": [
|
|
264
|
+
{
|
|
265
|
+
"id": "secrets.cargo.keyring",
|
|
266
|
+
"type": "dependency",
|
|
267
|
+
"ecosystem": "cargo",
|
|
268
|
+
"name": "keyring"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"id": "secrets.cargo.security-framework",
|
|
272
|
+
"type": "dependency",
|
|
273
|
+
"ecosystem": "cargo",
|
|
274
|
+
"name": "security-framework"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"id": "secrets.npm.keytar",
|
|
278
|
+
"type": "dependency",
|
|
279
|
+
"ecosystem": "npm",
|
|
280
|
+
"name": "keytar"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"id": "secrets.api.wincred",
|
|
284
|
+
"type": "source",
|
|
285
|
+
"pattern": "\\bCred(Write|Read|Delete|Enumerate)W?\\b"
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"id": "secrets.api.secitem",
|
|
289
|
+
"type": "source",
|
|
290
|
+
"pattern": "\\bSecItem(Add|CopyMatching|Update|Delete)\\b"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"package": "rightkit-shell",
|
|
296
|
+
"capability": "desktop-shell",
|
|
297
|
+
"summary": "Tauri shell: tray, autostart, deep links, single instance, window state, notifications",
|
|
298
|
+
"fingerprints": [
|
|
299
|
+
{
|
|
300
|
+
"id": "shell.cargo.tauri-plugin-autostart",
|
|
301
|
+
"type": "dependency",
|
|
302
|
+
"ecosystem": "cargo",
|
|
303
|
+
"name": "tauri-plugin-autostart"
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
"id": "shell.cargo.tauri-plugin-deep-link",
|
|
307
|
+
"type": "dependency",
|
|
308
|
+
"ecosystem": "cargo",
|
|
309
|
+
"name": "tauri-plugin-deep-link"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"id": "shell.cargo.tauri-plugin-single-instance",
|
|
313
|
+
"type": "dependency",
|
|
314
|
+
"ecosystem": "cargo",
|
|
315
|
+
"name": "tauri-plugin-single-instance"
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"id": "shell.cargo.tauri-plugin-window-state",
|
|
319
|
+
"type": "dependency",
|
|
320
|
+
"ecosystem": "cargo",
|
|
321
|
+
"name": "tauri-plugin-window-state"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"id": "shell.cargo.tauri-plugin-notification",
|
|
325
|
+
"type": "dependency",
|
|
326
|
+
"ecosystem": "cargo",
|
|
327
|
+
"name": "tauri-plugin-notification"
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
"id": "shell.cargo.auto-launch",
|
|
331
|
+
"type": "dependency",
|
|
332
|
+
"ecosystem": "cargo",
|
|
333
|
+
"name": "auto-launch"
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"package": "rightkit-watch",
|
|
339
|
+
"capability": "file-watching",
|
|
340
|
+
"summary": "Coalesced, ignore-aware file watching with rescan recovery",
|
|
341
|
+
"fingerprints": [
|
|
342
|
+
{
|
|
343
|
+
"id": "watch.cargo.notify",
|
|
344
|
+
"type": "dependency",
|
|
345
|
+
"ecosystem": "cargo",
|
|
346
|
+
"name": "notify"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"id": "watch.cargo.notify-debouncer-full",
|
|
350
|
+
"type": "dependency",
|
|
351
|
+
"ecosystem": "cargo",
|
|
352
|
+
"name": "notify-debouncer-full"
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"id": "watch.npm.chokidar",
|
|
356
|
+
"type": "dependency",
|
|
357
|
+
"ecosystem": "npm",
|
|
358
|
+
"name": "chokidar"
|
|
359
|
+
}
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
]
|
|
363
|
+
}
|
package/rightkit-versions.json
CHANGED
|
@@ -20,16 +20,16 @@
|
|
|
20
20
|
"@rightkit/hooks": "0.1.1",
|
|
21
21
|
"@rightkit/i18n": "0.1.0",
|
|
22
22
|
"@rightkit/legal": "0.3.1",
|
|
23
|
-
"@rightkit/legal-ui": "0.1.
|
|
23
|
+
"@rightkit/legal-ui": "0.1.3",
|
|
24
24
|
"@rightkit/license": "0.1.8",
|
|
25
25
|
"@rightkit/logs": "0.1.4",
|
|
26
|
-
"@rightkit/media-ui": "0.2.
|
|
27
|
-
"@rightkit/onboarding": "0.2.
|
|
26
|
+
"@rightkit/media-ui": "0.2.5",
|
|
27
|
+
"@rightkit/onboarding": "0.2.1",
|
|
28
28
|
"@rightkit/pdf-ui": "0.1.0",
|
|
29
29
|
"@rightkit/platform-ui": "0.1.2",
|
|
30
30
|
"@rightkit/qa": "0.2.1",
|
|
31
|
-
"@rightkit/release": "0.2.
|
|
32
|
-
"@rightkit/settings-ui": "0.2.
|
|
31
|
+
"@rightkit/release": "0.2.117",
|
|
32
|
+
"@rightkit/settings-ui": "0.2.2",
|
|
33
33
|
"@rightkit/shell": "0.1.0",
|
|
34
34
|
"@rightkit/tauri": "0.1.1",
|
|
35
35
|
"@rightkit/theme": "0.2.0",
|
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
],
|
|
70
70
|
"@rightkit/legal-ui": [
|
|
71
71
|
"0.1.0",
|
|
72
|
-
"0.1.1"
|
|
72
|
+
"0.1.1",
|
|
73
|
+
"0.1.2"
|
|
73
74
|
],
|
|
74
75
|
"@rightkit/hooks": [
|
|
75
76
|
"0.1.0"
|
|
@@ -135,7 +136,9 @@
|
|
|
135
136
|
"0.2.111",
|
|
136
137
|
"0.2.112",
|
|
137
138
|
"0.2.113",
|
|
138
|
-
"0.2.114"
|
|
139
|
+
"0.2.114",
|
|
140
|
+
"0.2.115",
|
|
141
|
+
"0.2.116"
|
|
139
142
|
],
|
|
140
143
|
"@rightkit/qa": [
|
|
141
144
|
"0.1.0",
|
|
@@ -148,16 +151,20 @@
|
|
|
148
151
|
"0.1.0",
|
|
149
152
|
"0.2.0",
|
|
150
153
|
"0.2.1",
|
|
151
|
-
"0.2.2"
|
|
154
|
+
"0.2.2",
|
|
155
|
+
"0.2.3",
|
|
156
|
+
"0.2.4"
|
|
152
157
|
],
|
|
153
158
|
"@rightkit/theme": [
|
|
154
159
|
"0.1.0"
|
|
155
160
|
],
|
|
156
161
|
"@rightkit/settings-ui": [
|
|
157
|
-
"0.1.0"
|
|
162
|
+
"0.1.0",
|
|
163
|
+
"0.2.0"
|
|
158
164
|
],
|
|
159
165
|
"@rightkit/onboarding": [
|
|
160
|
-
"0.1.0"
|
|
166
|
+
"0.1.0",
|
|
167
|
+
"0.2.0"
|
|
161
168
|
],
|
|
162
169
|
"@rightkit/doc-render": [
|
|
163
170
|
"0.1.0"
|
|
@@ -167,19 +174,30 @@
|
|
|
167
174
|
]
|
|
168
175
|
},
|
|
169
176
|
"cargo": {
|
|
177
|
+
"rightkit-audio": "0.1.0",
|
|
178
|
+
"rightkit-browser": "0.1.0",
|
|
179
|
+
"rightkit-cache": "0.1.0",
|
|
180
|
+
"rightkit-crash": "0.1.0",
|
|
181
|
+
"rightkit-db": "0.1.0",
|
|
182
|
+
"rightkit-framed-sidecar": "0.1.0",
|
|
183
|
+
"rightkit-fs": "0.1.0",
|
|
170
184
|
"rightkit-license": "0.1.2",
|
|
171
185
|
"rightkit-logs": "0.1.0",
|
|
186
|
+
"rightkit-media": "0.1.0",
|
|
187
|
+
"rightkit-pdf": "0.1.0",
|
|
188
|
+
"rightkit-playback": "0.1.0",
|
|
172
189
|
"rightkit-process": "0.1.0",
|
|
190
|
+
"rightkit-search": "0.1.0",
|
|
173
191
|
"rightkit-tauri": "0.1.0"
|
|
174
192
|
},
|
|
175
193
|
"stagedCargo": {
|
|
176
194
|
"rightkit-automation": "0.1.0",
|
|
177
195
|
"rightkit-audio": "0.1.0",
|
|
178
|
-
"rightkit-browser": "0.1.
|
|
196
|
+
"rightkit-browser": "0.1.1",
|
|
179
197
|
"rightkit-cache": "0.1.0",
|
|
180
|
-
"rightkit-control": "0.1.
|
|
198
|
+
"rightkit-control": "0.1.1",
|
|
181
199
|
"rightkit-crash": "0.1.0",
|
|
182
|
-
"rightkit-db": "0.
|
|
200
|
+
"rightkit-db": "0.2.0",
|
|
183
201
|
"rightkit-framed-sidecar": "0.1.0",
|
|
184
202
|
"rightkit-fs": "0.1.0",
|
|
185
203
|
"rightkit-jobs": "0.1.0",
|
|
@@ -188,7 +206,7 @@
|
|
|
188
206
|
"rightkit-models": "0.1.0",
|
|
189
207
|
"rightkit-pdf": "0.1.0",
|
|
190
208
|
"rightkit-playback": "0.1.0",
|
|
191
|
-
"rightkit-process": "0.2.
|
|
209
|
+
"rightkit-process": "0.2.1",
|
|
192
210
|
"rightkit-qa": "0.1.0",
|
|
193
211
|
"rightkit-sandbox": "0.1.0",
|
|
194
212
|
"rightkit-search": "0.1.0",
|