@magnusekdahl/parallix 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agents/agents.js +737 -838
- package/lib/agents/agents.ts +930 -0
- package/lib/agents/claude-telemetry.js +149 -154
- package/lib/agents/claude-telemetry.ts +233 -0
- package/lib/agents/claude.js +121 -131
- package/lib/agents/claude.ts +156 -0
- package/lib/agents/codex-telemetry.js +176 -164
- package/lib/agents/codex-telemetry.ts +205 -0
- package/lib/agents/codex.js +168 -201
- package/lib/agents/codex.ts +236 -0
- package/lib/agents/limit-hit.js +216 -226
- package/lib/agents/limit-hit.ts +262 -0
- package/lib/agents/mistral-telemetry.js +7 -10
- package/lib/agents/mistral-telemetry.ts +44 -0
- package/lib/agents/mistral.js +40 -58
- package/lib/agents/mistral.ts +84 -0
- package/lib/agents/opencode-export.js +126 -127
- package/lib/agents/opencode-export.ts +160 -0
- package/lib/agents/opencode-telemetry.js +282 -261
- package/lib/agents/opencode-telemetry.ts +350 -0
- package/lib/agents/opencode.js +263 -305
- package/lib/agents/opencode.ts +370 -0
- package/lib/agents/stage-telemetry.js +23 -24
- package/lib/agents/stage-telemetry.ts +47 -0
- package/lib/commands/integrate.js +9 -3
- package/lib/core/fmt.js +140 -211
- package/lib/core/fmt.ts +189 -0
- package/lib/core/git.js +121 -102
- package/lib/core/git.ts +146 -0
- package/lib/core/gitignore.js +59 -106
- package/lib/core/gitignore.ts +103 -0
- package/lib/core/mission-utils.js +833 -875
- package/lib/core/mission-utils.ts +998 -0
- package/lib/core/persistent-data-migration.js +247 -205
- package/lib/core/persistent-data-migration.ts +255 -0
- package/lib/core/product-config.js +400 -476
- package/lib/core/product-config.ts +518 -0
- package/lib/core/runtime-matrix.js +41 -87
- package/lib/core/runtime-matrix.ts +84 -0
- package/lib/core/spawn-tee.js +152 -192
- package/lib/core/spawn-tee.ts +202 -0
- package/lib/core/state-map.js +92 -92
- package/lib/core/state-map.ts +115 -0
- package/lib/core/storage.js +101 -129
- package/lib/core/storage.ts +182 -0
- package/lib/core/verification.js +148 -138
- package/lib/core/verification.ts +200 -0
- package/package.json +4 -2
- package/prompts/draft.md +1 -0
- package/prompts/portfolio.md +1 -1
- package/px.js +0 -2
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface ResolveParallixHomeOptions {
|
|
6
|
+
ensureDir?: boolean;
|
|
7
|
+
platform?: string;
|
|
8
|
+
env?: Record<string, string>;
|
|
9
|
+
homedir?: () => string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ReadJsonResult<T = unknown> {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
error: unknown;
|
|
15
|
+
data: T | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IsInitializedResult {
|
|
19
|
+
isInitialized: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the parallix-owned persistent-data root.
|
|
24
|
+
*
|
|
25
|
+
* Precedence:
|
|
26
|
+
* 1. PARALLIX_HOME env var (highest priority)
|
|
27
|
+
* 2. Platform-specific base:
|
|
28
|
+
* - Linux: $HOME/.local/state/parallix
|
|
29
|
+
* - macOS: ~/Library/Application Support/parallix
|
|
30
|
+
* - Windows: %LOCALAPPDATA%/parallix
|
|
31
|
+
* - Fallback: $HOME/.parallix
|
|
32
|
+
*
|
|
33
|
+
* This function guarantees the directory exists (creates it + parents on first
|
|
34
|
+
* call with `ensureDir: true`). Read-side callers may omit ensureDir so they
|
|
35
|
+
* fail gracefully when PARALLIX_HOME has never been initialized.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveParallixHome(
|
|
38
|
+
options: ResolveParallixHomeOptions | boolean | string = {}
|
|
39
|
+
): string {
|
|
40
|
+
if (typeof options === 'boolean' || typeof options === 'string') {
|
|
41
|
+
// Legacy shim: resolveParallixHome(true) === { ensureDir: true }
|
|
42
|
+
options = { ensureDir: Boolean(options) };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const opts = options as ResolveParallixHomeOptions;
|
|
46
|
+
const {
|
|
47
|
+
ensureDir = false,
|
|
48
|
+
platform = process.platform,
|
|
49
|
+
env = process.env,
|
|
50
|
+
homedir = os.homedir
|
|
51
|
+
} = opts;
|
|
52
|
+
|
|
53
|
+
let home: string;
|
|
54
|
+
|
|
55
|
+
// --- env override (highest precedence) ---
|
|
56
|
+
if (env.PARALLIX_HOME && typeof env.PARALLIX_HOME === 'string' && env.PARALLIX_HOME.trim().length > 0) {
|
|
57
|
+
home = path.resolve(env.PARALLIX_HOME);
|
|
58
|
+
} else if (platform === 'linux') {
|
|
59
|
+
home = path.join(homedir(), '.local', 'state', 'parallix');
|
|
60
|
+
} else if (platform === 'darwin') {
|
|
61
|
+
home = path.join(homedir(), 'Library', 'Application Support', 'parallix');
|
|
62
|
+
} else if (platform === 'win32') {
|
|
63
|
+
const localAppData = env.LOCALAPPDATA;
|
|
64
|
+
if (localAppData && typeof localAppData === 'string' && localAppData.trim().length > 0) {
|
|
65
|
+
home = path.join(localAppData.trim(), 'parallix');
|
|
66
|
+
} else {
|
|
67
|
+
home = path.join(homedir(), '.parallix');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
// WSL, CI, other UNIX variants
|
|
71
|
+
home = path.join(homedir(), '.parallix');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
home = path.resolve(home);
|
|
75
|
+
|
|
76
|
+
if (ensureDir) {
|
|
77
|
+
fs.mkdirSync(home, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return home;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the effective stats CSV path.
|
|
85
|
+
*
|
|
86
|
+
* Returns `<PARALLIX_HOME>/stats.csv` (creating PARALLIX_HOME if `ensureDir`
|
|
87
|
+
* is true). Callers that pass an explicit `--csv-file` path bypass this
|
|
88
|
+
* resolver entirely — the caller supplies the path before calling here.
|
|
89
|
+
*/
|
|
90
|
+
export interface ResolveStatsOptions {
|
|
91
|
+
ensureDir?: boolean;
|
|
92
|
+
warn?: (...args: unknown[]) => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function resolveStatsPath(options: ResolveStatsOptions = {}): string {
|
|
96
|
+
const opts = options as ResolveStatsOptions;
|
|
97
|
+
const home = resolveParallixHome({ ensureDir: opts.ensureDir !== false });
|
|
98
|
+
return path.join(home, 'stats.csv');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the effective agent blocklist path.
|
|
103
|
+
*
|
|
104
|
+
* Returns `<PARALLIX_HOME>/agents.local.json`. Callers that pass an
|
|
105
|
+
* explicit `targetPath` bypass this resolver.
|
|
106
|
+
*/
|
|
107
|
+
export function resolveAgentsLocalPath(options?: ResolveParallixHomeOptions | string): string {
|
|
108
|
+
if (typeof options === 'string') {
|
|
109
|
+
return path.resolve(options);
|
|
110
|
+
}
|
|
111
|
+
const opts = options as ResolveParallixHomeOptions | undefined;
|
|
112
|
+
const home = resolveParallixHome({ ensureDir: opts?.ensureDir !== false });
|
|
113
|
+
return path.join(home, 'agents.local.json');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Read a JSON file that lives under PARALLIX_HOME.
|
|
118
|
+
*
|
|
119
|
+
* Returns `{ ok: false, error }` when the file does not exist or is not
|
|
120
|
+
* valid JSON — callers should treat absence as "no local overrides".
|
|
121
|
+
* Malformed JSON returns `{ ok: false, error }` rather than throwing so
|
|
122
|
+
* callers can decide whether this is a hard failure.
|
|
123
|
+
*/
|
|
124
|
+
export function readJson<T = unknown>(pathOrResolution: string | (() => string)): ReadJsonResult<T> {
|
|
125
|
+
let filePath: string;
|
|
126
|
+
if (typeof pathOrResolution === 'function') {
|
|
127
|
+
filePath = pathOrResolution();
|
|
128
|
+
} else {
|
|
129
|
+
filePath = pathOrResolution;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
133
|
+
return { ok: false, error: null, data: null };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
138
|
+
const data = JSON.parse(raw) as T;
|
|
139
|
+
return { ok: true, error: null, data };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
return { ok: false, error: err, data: null };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Write JSON to a path under PARALLIX_HOME (or an explicit path).
|
|
147
|
+
* Creates parent directories as needed.
|
|
148
|
+
*/
|
|
149
|
+
export function writeJson(filePath: string | (() => string), data: unknown): string {
|
|
150
|
+
if (typeof filePath === 'function') {
|
|
151
|
+
filePath = filePath();
|
|
152
|
+
}
|
|
153
|
+
writeFileAtomic(filePath, `${JSON.stringify(data, null, 2)}\n`);
|
|
154
|
+
return filePath;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function writeFileAtomic(filePath: string, content: string): void {
|
|
158
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
159
|
+
const tempPath = path.join(
|
|
160
|
+
path.dirname(filePath),
|
|
161
|
+
`.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`
|
|
162
|
+
);
|
|
163
|
+
try {
|
|
164
|
+
fs.writeFileSync(tempPath, content, 'utf8');
|
|
165
|
+
fs.renameSync(tempPath, filePath);
|
|
166
|
+
} finally {
|
|
167
|
+
if (fs.existsSync(tempPath)) {fs.unlinkSync(tempPath);}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check whether PARALLIX_HOME has been initialized (directory exists).
|
|
173
|
+
* Does NOT create the directory.
|
|
174
|
+
*/
|
|
175
|
+
export function isInitialized(): boolean {
|
|
176
|
+
const home = resolveParallixHome({ ensureDir: false });
|
|
177
|
+
try {
|
|
178
|
+
return fs.statSync(home).isDirectory();
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
package/lib/core/verification.js
CHANGED
|
@@ -1,166 +1,176 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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.NO_GATE_NOTICE = exports.DEFAULT_AREA = void 0;
|
|
37
|
+
exports.resolveVerificationAdapter = resolveVerificationAdapter;
|
|
38
|
+
exports.formatVerificationCommand = formatVerificationCommand;
|
|
39
|
+
exports.runVerificationGate = runVerificationGate;
|
|
40
|
+
exports.readPublishedTreeState = readPublishedTreeState;
|
|
41
|
+
exports.captureVerifiedTreeProof = captureVerifiedTreeProof;
|
|
42
|
+
exports.assertVerifiedTreeProof = assertVerifiedTreeProof;
|
|
43
|
+
exports.default = runWorkflow;
|
|
44
|
+
const git_js_1 = require("./git.js");
|
|
45
|
+
const product_config_js_1 = require("./product-config.js");
|
|
46
|
+
const fmt_js_1 = require("./fmt.js");
|
|
47
|
+
const fsMod = __importStar(require("node:fs"));
|
|
7
48
|
// parallix targets arbitrary repositories, so there is no universal gate
|
|
8
49
|
// command. When adapters.verification.command is not configured, verification
|
|
9
50
|
// is a no-op pass ("no validation"). A repository opts into a real gate by
|
|
10
51
|
// declaring the command in workflow.config.json.
|
|
11
|
-
|
|
52
|
+
exports.DEFAULT_AREA = 'docs';
|
|
12
53
|
// Shell-safe no-op so this is harmless if pasted into a command sequence: `:` is
|
|
13
54
|
// the bash null command and `#` comments the explanation.
|
|
14
|
-
|
|
15
|
-
|
|
55
|
+
exports.NO_GATE_NOTICE = ': # no verification gate configured (set adapters.verification.command)';
|
|
16
56
|
function resolveVerificationAdapter(rootDir = process.cwd()) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
57
|
+
const config = (0, product_config_js_1.loadAdapterConfig)(rootDir);
|
|
58
|
+
const verification = config.verification || {};
|
|
59
|
+
const command = typeof verification.command === 'string' && verification.command.trim()
|
|
60
|
+
? verification.command.trim()
|
|
61
|
+
: null;
|
|
62
|
+
const defaultArea = typeof verification.defaultArea === 'string' && verification.defaultArea.trim()
|
|
63
|
+
? verification.defaultArea.trim()
|
|
64
|
+
: exports.DEFAULT_AREA;
|
|
65
|
+
return { command, defaultArea };
|
|
26
66
|
}
|
|
27
|
-
|
|
28
67
|
/** @param {string} [area] @param {string} [rootDir] */
|
|
29
68
|
function formatVerificationCommand(area, rootDir = process.cwd()) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
69
|
+
const { command, defaultArea } = resolveVerificationAdapter(rootDir);
|
|
70
|
+
const effectiveArea = area || defaultArea;
|
|
71
|
+
if (!command) {
|
|
72
|
+
return exports.NO_GATE_NOTICE;
|
|
73
|
+
}
|
|
74
|
+
return command.replaceAll('{{area}}', effectiveArea || exports.DEFAULT_AREA);
|
|
36
75
|
}
|
|
37
|
-
|
|
38
76
|
/** @param {string} [area] @param {{rootDir?: string, log?: Function, stdio?: string, runFn?: Function}} [options] */
|
|
39
77
|
function runVerificationGate(area, options = {}) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const stdio = opts.stdio || 'inherit';
|
|
54
|
-
const runFn = opts.runFn || run;
|
|
55
|
-
return runFn('bash', ['-lc', command.replaceAll('{{area}}', effectiveArea)], { cwd: rootDir, stdio });
|
|
78
|
+
const opts = options;
|
|
79
|
+
const rootDir = opts.rootDir || process.cwd();
|
|
80
|
+
const { command, defaultArea } = resolveVerificationAdapter(rootDir);
|
|
81
|
+
const effectiveArea = area || defaultArea;
|
|
82
|
+
if (!command) {
|
|
83
|
+
const info = opts.log || fmt_js_1.log.info;
|
|
84
|
+
info(`No verification gate configured for area: ${effectiveArea}; default is no validation. `
|
|
85
|
+
+ 'Set adapters.verification.command in workflow.config.json to enforce one.');
|
|
86
|
+
return { status: 0 };
|
|
87
|
+
}
|
|
88
|
+
const stdio = opts.stdio || 'inherit';
|
|
89
|
+
const runFn = opts.runFn || git_js_1.run;
|
|
90
|
+
return runFn('bash', ['-lc', command.replaceAll('{{area}}', effectiveArea)], { cwd: rootDir, stdio });
|
|
56
91
|
}
|
|
57
|
-
|
|
58
92
|
/** @param {string} rootDir @param {{gitRunner?: GitFn}} [options] */
|
|
59
93
|
function readPublishedTreeState(rootDir, options = {}) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return { ok: true, rootDir: resolvedRoot, commit, tree };
|
|
94
|
+
const gitRunner = options.gitRunner || git_js_1.git;
|
|
95
|
+
const resolvedRoot = fsMod.realpathSync(rootDir);
|
|
96
|
+
const commitResult = gitRunner(['-C', resolvedRoot, 'rev-parse', 'HEAD']);
|
|
97
|
+
const treeResult = gitRunner(['-C', resolvedRoot, 'rev-parse', 'HEAD^{tree}']);
|
|
98
|
+
const commit = commitResult.stdout ? commitResult.stdout.trim() : '';
|
|
99
|
+
const tree = treeResult.stdout ? treeResult.stdout.trim() : '';
|
|
100
|
+
if (commitResult.status !== 0 || treeResult.status !== 0 || !commit || !tree) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
error: `could not resolve current published tree for ${resolvedRoot}`
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return { ok: true, rootDir: resolvedRoot, commit, tree };
|
|
76
107
|
}
|
|
77
|
-
|
|
78
108
|
/** @param {string} [area] @param {string} [rootDir] @param {{gitRunner?: GitFn, runFn?: Function, stdio?: string}} [options] */
|
|
79
109
|
function captureVerifiedTreeProof(area, rootDir = process.cwd(), options = {}) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
const { gitRunner = git_js_1.git, runFn = git_js_1.run, stdio = 'inherit' } = options;
|
|
111
|
+
const before = readPublishedTreeState(rootDir, { gitRunner });
|
|
112
|
+
if (!before.ok) {
|
|
113
|
+
return before;
|
|
114
|
+
}
|
|
115
|
+
const verification = runVerificationGate(area, {
|
|
116
|
+
rootDir: before.rootDir,
|
|
117
|
+
runFn,
|
|
118
|
+
stdio
|
|
119
|
+
});
|
|
120
|
+
if (verification.status !== 0) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
error: `verification gate failed for ${before.rootDir} with exit code ${verification.status}`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const after = readPublishedTreeState(before.rootDir, { gitRunner });
|
|
127
|
+
if (!after.ok) {
|
|
128
|
+
return after;
|
|
129
|
+
}
|
|
130
|
+
if (after.commit !== before.commit || after.tree !== before.tree) {
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
error: `verification proof became stale while publishing ${before.rootDir}`
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const { command, defaultArea } = resolveVerificationAdapter(before.rootDir);
|
|
137
|
+
const effectiveArea = area || defaultArea;
|
|
104
138
|
return {
|
|
105
|
-
|
|
106
|
-
|
|
139
|
+
ok: true,
|
|
140
|
+
proof: {
|
|
141
|
+
rootDir: before.rootDir,
|
|
142
|
+
area: effectiveArea,
|
|
143
|
+
command: command || null,
|
|
144
|
+
commit: after.commit,
|
|
145
|
+
tree: after.tree,
|
|
146
|
+
verifiedAt: new Date().toISOString()
|
|
147
|
+
}
|
|
107
148
|
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const { command, defaultArea } = resolveVerificationAdapter(before.rootDir);
|
|
111
|
-
const effectiveArea = area || defaultArea;
|
|
112
|
-
|
|
113
|
-
return {
|
|
114
|
-
ok: true,
|
|
115
|
-
proof: {
|
|
116
|
-
rootDir: before.rootDir,
|
|
117
|
-
area: effectiveArea,
|
|
118
|
-
command: command || null,
|
|
119
|
-
commit: after.commit,
|
|
120
|
-
tree: after.tree,
|
|
121
|
-
verifiedAt: new Date().toISOString()
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
149
|
}
|
|
125
|
-
|
|
126
150
|
/** @param {{rootDir?: string, commit?: string, tree?: string}} proof @param {string} [rootDir] @param {{gitRunner?: GitFn}} [opts] */
|
|
127
151
|
function assertVerifiedTreeProof(proof, rootDir = process.cwd(), opts = {}) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return { ok: true, proof: current };
|
|
152
|
+
if (!proof || typeof proof !== 'object') {
|
|
153
|
+
return { ok: false, error: 'missing verification proof' };
|
|
154
|
+
}
|
|
155
|
+
const o = opts;
|
|
156
|
+
const gitRunner = o.gitRunner || git_js_1.git;
|
|
157
|
+
const current = readPublishedTreeState(rootDir, { gitRunner });
|
|
158
|
+
if (!current.ok) {
|
|
159
|
+
return current;
|
|
160
|
+
}
|
|
161
|
+
if (proof.rootDir !== current.rootDir) {
|
|
162
|
+
return { ok: false, error: `verification proof was captured from a different checkout: ${proof.rootDir}` };
|
|
163
|
+
}
|
|
164
|
+
if (proof.commit !== current.commit || proof.tree !== current.tree) {
|
|
165
|
+
return { ok: false, error: 'verification proof does not match the tree being published' };
|
|
166
|
+
}
|
|
167
|
+
return { ok: true, proof: current };
|
|
146
168
|
}
|
|
147
|
-
|
|
148
169
|
/** @param {string[]} args @param {{log?: Function}} [options] */
|
|
149
170
|
function runWorkflow(args, options = {}) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return runVerificationGate(area, { stdio: 'inherit' });
|
|
171
|
+
const opts = options;
|
|
172
|
+
const logFn = opts.log || fmt_js_1.log.plain;
|
|
173
|
+
const area = args[0] || process.env.VERIFY_AREA || exports.DEFAULT_AREA;
|
|
174
|
+
logFn(`Running verification gate for area: ${area}...`);
|
|
175
|
+
return runVerificationGate(area, { stdio: 'inherit' });
|
|
156
176
|
}
|
|
157
|
-
|
|
158
|
-
module.exports = runWorkflow;
|
|
159
|
-
module.exports.DEFAULT_AREA = DEFAULT_AREA;
|
|
160
|
-
module.exports.NO_GATE_NOTICE = NO_GATE_NOTICE;
|
|
161
|
-
module.exports.formatVerificationCommand = formatVerificationCommand;
|
|
162
|
-
module.exports.resolveVerificationAdapter = resolveVerificationAdapter;
|
|
163
|
-
module.exports.runVerificationGate = runVerificationGate;
|
|
164
|
-
module.exports.readPublishedTreeState = readPublishedTreeState;
|
|
165
|
-
module.exports.captureVerifiedTreeProof = captureVerifiedTreeProof;
|
|
166
|
-
module.exports.assertVerifiedTreeProof = assertVerifiedTreeProof;
|