zephyr-agent
Version:
Zephyr plugin agent
451 lines (450 loc) • 19 kB
JavaScript
;
var __webpack_require__ = {};
(()=>{
__webpack_require__.d = (exports1, getters, values)=>{
var define = (defs, kind)=>{
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
enumerable: true,
[kind]: defs[key]
});
};
define(getters, "get");
define(values, "value");
};
})();
(()=>{
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
})();
(()=>{
__webpack_require__.r = (exports1)=>{
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
value: 'Module'
});
Object.defineProperty(exports1, '__esModule', {
value: true
});
};
})();
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
attributionRepository: ()=>attributionRepository,
captureSource: ()=>captureSource,
comparisonFile: ()=>comparisonFile,
finishSourceCapture: ()=>finishSourceCapture,
loadSourceRecord: ()=>loadSourceRecord,
loadVersionAttribution: ()=>loadVersionAttribution,
readAttributionConfig: ()=>external_config_js_namespaceObject.readAttributionConfig,
sourceCommitBaseline: ()=>sourceCommitBaseline
});
const external_node_child_process_namespaceObject = require("node:child_process");
const external_node_crypto_namespaceObject = require("node:crypto");
const external_node_fs_namespaceObject = require("node:fs");
const external_node_path_namespaceObject = require("node:path");
const external_git_ai_js_namespaceObject = require("./git-ai.js");
const external_sessions_js_namespaceObject = require("./sessions.js");
const external_config_js_namespaceObject = require("./config.js");
const digest = (data)=>(0, external_node_crypto_namespaceObject.createHash)('sha256').update(data).digest('hex');
function git(root, args, encoding) {
const options = {
cwd: root,
timeout: 5000,
maxBuffer: 67108864,
stdio: [
'ignore',
'pipe',
'ignore'
]
};
return encoding ? (0, external_node_child_process_namespaceObject.execFileSync)('git', args, {
...options,
encoding
}) : (0, external_node_child_process_namespaceObject.execFileSync)('git', args, options);
}
function attributionRepository(directory) {
const root = git((0, external_node_path_namespaceObject.resolve)(directory), [
'rev-parse',
'--show-toplevel'
], 'utf8').trim();
const gitDir = git(root, [
'rev-parse',
'--absolute-git-dir'
], 'utf8').trim();
return {
root,
gitDir
};
}
function excluded(file, config) {
return file.split('/').some((part)=>[
'node_modules',
'.git',
'dist',
'build',
'.next',
'.nuxt',
'.output',
'.turbo',
'coverage',
'.npmrc',
'.netrc'
].includes(part) || /^\.env(?:\.|$)/.test(part) || /\.(?:pem|key|p12|pfx)$/i.test(part)) || (config.exclude ?? []).some((prefix)=>file === prefix || file.startsWith(`${prefix.replace(/\/$/, '')}/`));
}
function privateDirectory(gitDir) {
const directory = (0, external_node_path_namespaceObject.join)(gitDir, 'zephyr-attribution');
(0, external_node_fs_namespaceObject.mkdirSync)(directory, {
recursive: true,
mode: 448
});
return directory;
}
function captureSource(directory, phase = 'publication') {
let repository;
try {
repository = attributionRepository(directory);
} catch {
return;
}
const { root, gitDir } = repository;
try {
const config = (0, external_config_js_namespaceObject.readAttributionConfig)(root, gitDir);
if (!config?.enabled) return;
let baseCommit = '';
try {
baseCommit = git(root, [
'rev-parse',
'--verify',
'HEAD'
], 'utf8').trim();
} catch {}
const paths = [
...new Set(git(root, [
'ls-files',
'-z',
'--cached',
'--others',
'--exclude-standard'
], 'utf8').split('\0').filter(Boolean))
].filter((file)=>!excluded(file, config)).sort();
if (paths.length > 20000) throw new Error('Source capture exceeds 20,000 files; configure exclude prefixes');
const head = new Map(commitEntries(root, baseCommit, config).map((entry)=>[
entry.file,
entry.oid
]));
const working = (0, external_git_ai_js_namespaceObject.readGitAiWorkingAttribution)(gitDir, baseCommit);
const metadata = (0, external_sessions_js_namespaceObject.readSessionMetadata)(gitDir);
const sessions = Object.create(null);
const files = Object.create(null);
let bytes = 0;
for (const file of paths){
const absolute = (0, external_node_path_namespaceObject.resolve)(root, file);
if ((0, external_node_path_namespaceObject.isAbsolute)((0, external_node_path_namespaceObject.relative)(root, absolute)) || (0, external_node_path_namespaceObject.relative)(root, absolute).startsWith(`..${external_node_path_namespaceObject.sep}`)) throw new Error('Source path escapes repository');
const parents = file.split('/').slice(0, -1);
let parent = root;
for (const part of parents){
parent = (0, external_node_path_namespaceObject.join)(parent, part);
if ((0, external_node_fs_namespaceObject.existsSync)(parent) && (0, external_node_fs_namespaceObject.lstatSync)(parent).isSymbolicLink()) throw new Error('Source parent is a symlink');
}
let stat;
try {
stat = (0, external_node_fs_namespaceObject.lstatSync)(absolute);
} catch (error) {
if ('ENOENT' === error.code) continue;
throw error;
}
if (stat.isDirectory()) throw new Error(`Submodule or directory entry needs an exclusion: ${file}`);
if (!stat.isFile() && !stat.isSymbolicLink()) throw new Error(`Unsupported source entry: ${file}`);
bytes += stat.size;
if (bytes > 52428800) throw new Error('Source capture exceeds 50 MiB; configure exclude prefixes');
const content = stat.isSymbolicLink() ? Buffer.from((0, external_node_fs_namespaceObject.readlinkSync)(absolute)) : (0, external_node_fs_namespaceObject.readFileSync)(absolute);
const hash = digest(content);
const mode = stat.isSymbolicLink() ? '120000' : 73 & stat.mode ? '100755' : '100644';
const attribution = (0, external_git_ai_js_namespaceObject.workingRanges)(working.entries.get(file), hash, working.authors);
const oid = head.get(file);
const committed = void 0 === attribution && !working.entries.has(file) && Boolean(oid && blobId(content, oid.length) === oid) && !stat.isSymbolicLink();
for (const range of attribution ?? []){
const match = (0, external_sessions_js_namespaceObject.sessionForOrigin)(range.origin, metadata);
if (!match) continue;
sessions[match.key] = match.session;
const session = match.session;
range.origin = {
...range.origin,
agent: session.agent,
harness: session.harness,
provider: session.provider,
reasoningEffort: session.reasoningEffort,
turn: session.turn,
turnAssociation: session.turnAssociation,
promptInitiator: session.promptInitiator
};
}
files[file] = {
hash,
mode,
content: content.toString('base64'),
attribution: attribution ?? [],
...committed ? {
committed: true
} : {}
};
}
const fingerprint = digest(JSON.stringify(Object.entries(files).map(([file, value])=>[
file,
value.mode,
value.hash
])));
let workspaceHuman;
try {
workspaceHuman = git(root, [
'var',
'GIT_AUTHOR_IDENT'
], 'utf8').trim().replace(/ \d+ [+-]\d{4}$/, '');
} catch {}
const record = {
schemaVersion: 1,
id: `source-${(0, external_node_crypto_namespaceObject.randomUUID)()}`,
fingerprint,
baseCommit,
dirty: Boolean(git(root, [
'status',
'--porcelain',
'--untracked-files=normal'
], 'utf8').trim()),
capturedAt: new Date().toISOString(),
workspaceHuman,
sessions,
files
};
(0, external_node_fs_namespaceObject.writeFileSync)((0, external_node_path_namespaceObject.join)(privateDirectory(gitDir), `${record.id}.json`), JSON.stringify(record), {
flag: 'wx',
mode: 384
});
return {
...repository,
phase,
record,
storage: config.storage ?? 'local'
};
} catch (error) {
return {
...repository,
phase,
reason: error.message
};
}
}
function finishSourceCapture(directory, start, version) {
const end = captureSource(directory);
if (!end && !start) return;
const record = end?.record;
if (!record) return {
schemaVersion: 1,
status: 'unavailable',
reason: end?.reason ?? 'Attribution disabled during build',
scope: 'git-working-tree',
consistency: 'unavailable',
identity: 'self-reported'
};
const summary = {
schemaVersion: 1,
status: 'captured',
scope: 'git-working-tree',
identity: 'self-reported',
storage: end.storage ?? 'local',
sourceId: record.id,
sourceFingerprint: record.fingerprint,
baseCommit: record.baseCommit,
dirty: record.dirty,
capturedAt: record.capturedAt,
workspaceHuman: record.workspaceHuman,
sessions: record.sessions,
reason: start?.reason,
startSourceId: start?.record?.id,
startSourceFingerprint: start?.record?.fingerprint,
consistency: start?.reason ? 'unavailable' : start?.record && 'build-start' === start.phase ? start.record.fingerprint === record.fingerprint ? 'boundary-match' : 'changed-during-build' : 'publication-only',
files: Object.fromEntries(Object.entries(record.files).map(([file, value])=>[
file,
{
hash: value.hash,
mode: value.mode,
attribution: value.attribution
}
]))
};
try {
(0, external_node_fs_namespaceObject.writeFileSync)((0, external_node_path_namespaceObject.join)(privateDirectory(end.gitDir), `version-${digest(version)}.json`), JSON.stringify({
version,
summary
}), {
flag: 'wx',
mode: 384
});
} catch (error) {
return {
schemaVersion: 1,
status: 'unavailable',
scope: 'git-working-tree',
consistency: 'unavailable',
identity: 'self-reported',
reason: 'EEXIST' === error.code ? `A source receipt already exists for version ${version}` : 'Unable to save the private version receipt'
};
}
return summary;
}
function loadSourceRecord(directory, id) {
const { gitDir } = attributionRepository(directory);
const storage = privateDirectory(gitDir);
let sourceId = id;
if (!/^source-[a-f0-9-]{36}$/.test(id)) {
const receipt = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)((0, external_node_path_namespaceObject.join)(storage, `version-${digest(id)}.json`), 'utf8'));
sourceId = receipt.summary.sourceId;
}
if (!/^source-[a-f0-9-]{36}$/.test(sourceId)) throw new Error('Invalid source record id');
const record = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)((0, external_node_path_namespaceObject.join)(storage, `${sourceId}.json`), 'utf8'));
if (1 !== record.schemaVersion || record.id !== sourceId) throw new Error('Unsupported source record');
return record;
}
function loadVersionAttribution(directory, version) {
const { gitDir } = attributionRepository(directory);
const receipt = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)((0, external_node_path_namespaceObject.join)(privateDirectory(gitDir), `version-${digest(version)}.json`), 'utf8'));
if (receipt.version !== version || receipt.summary?.schemaVersion !== 1) throw new Error('Invalid private version receipt');
return receipt.summary;
}
function blobId(content, oidLength) {
return (0, external_node_crypto_namespaceObject.createHash)(64 === oidLength ? 'sha256' : 'sha1').update(`blob ${content.length}\0`).update(content).digest('hex');
}
function commitEntries(root, commit, config) {
const entries = (commit ? git(root, [
'ls-tree',
'-rz',
'--full-tree',
commit
], 'utf8') : '').split('\0').filter(Boolean).map((entry)=>{
const tab = entry.indexOf('\t');
const [mode, type, oid] = entry.slice(0, tab).split(' ');
return {
mode,
type,
oid,
file: entry.slice(tab + 1)
};
}).filter(({ file })=>!excluded(file, config));
if (entries.length > 20000) throw new Error('Git baseline exceeds 20,000 entries');
if (entries.some(({ type })=>'blob' !== type)) throw new Error('Git baseline contains a submodule; configure exclusions');
return entries;
}
function comparisonFile(directory, record, file) {
const value = record.files[file];
if (!value?.committed || !record.baseCommit || value.attribution.length) return value;
const { root, gitDir } = attributionRepository(directory);
const config = (0, external_config_js_namespaceObject.readAttributionConfig)(root, gitDir);
return {
...value,
attribution: (0, external_git_ai_js_namespaceObject.committedRanges)(root, config?.gitAiPath ?? 'git-ai', file, record.baseCommit)
};
}
function sourceCommitBaseline(directory, captured) {
const { root, gitDir } = attributionRepository(directory);
const config = (0, external_config_js_namespaceObject.readAttributionConfig)(root, gitDir);
if (!config?.enabled) throw new Error('Attribution is disabled');
const files = Object.create(null);
const entries = commitEntries(root, captured.baseCommit, config);
const reused = new Map();
const missing = new Set();
for (const { file, oid } of entries){
const current = captured.files[file];
const content = current && Buffer.from(current.content, 'base64');
if (content && blobId(content, oid.length) === oid) reused.set(oid, content);
else missing.add(oid);
}
const blobs = new Map(reused);
const requested = [
...missing
].filter((oid)=>!blobs.has(oid));
if (requested.length) {
const output = (0, external_node_child_process_namespaceObject.execFileSync)('git', [
'cat-file',
'--batch'
], {
cwd: root,
input: requested.join('\n') + '\n',
timeout: 5000,
maxBuffer: 67108864,
stdio: [
'pipe',
'pipe',
'ignore'
]
});
let offset = 0;
let fetched = 0;
for (const oid of requested){
const end = output.indexOf(10, offset);
const header = output.subarray(offset, end).toString('ascii');
const match = /^([a-f0-9]{40,64}) blob (\d+)$/.exec(header);
if (end < 0 || !match || match[1] !== oid) throw new Error('Invalid Git blob batch');
const size = Number(match[2]);
fetched += size;
if (fetched > 52428800) throw new Error('Git baseline exceeds 50 MiB');
offset = end + 1;
if (!Number.isSafeInteger(size) || offset + size >= output.length || 10 !== output[offset + size]) throw new Error('Incomplete Git blob batch');
blobs.set(oid, output.subarray(offset, offset + size));
offset += size + 1;
}
}
let bytes = 0;
for (const { mode, oid, file } of entries){
const content = blobs.get(oid);
bytes += content.length;
if (bytes > 52428800) throw new Error('Git baseline exceeds 50 MiB');
const hash = digest(content);
const current = captured.files[file];
const unchanged = hash === current?.hash;
files[file] = {
hash,
mode,
content: content.toString('base64'),
attribution: unchanged ? current.attribution : [],
...unchanged ? current.committed ? {
committed: true
} : {} : {
committed: '120000' !== mode
}
};
}
return {
schemaVersion: 1,
id: `head-${captured.baseCommit || 'unborn'}`,
fingerprint: digest(JSON.stringify(Object.entries(files).map(([file, value])=>[
file,
value.mode,
value.hash
]))),
baseCommit: captured.baseCommit,
dirty: false,
capturedAt: captured.capturedAt,
files
};
}
exports.attributionRepository = __webpack_exports__.attributionRepository;
exports.captureSource = __webpack_exports__.captureSource;
exports.comparisonFile = __webpack_exports__.comparisonFile;
exports.finishSourceCapture = __webpack_exports__.finishSourceCapture;
exports.loadSourceRecord = __webpack_exports__.loadSourceRecord;
exports.loadVersionAttribution = __webpack_exports__.loadVersionAttribution;
exports.readAttributionConfig = __webpack_exports__.readAttributionConfig;
exports.sourceCommitBaseline = __webpack_exports__.sourceCommitBaseline;
for(var __rspack_i in __webpack_exports__)if (-1 === [
"attributionRepository",
"captureSource",
"comparisonFile",
"finishSourceCapture",
"loadSourceRecord",
"loadVersionAttribution",
"readAttributionConfig",
"sourceCommitBaseline"
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
Object.defineProperty(exports, '__esModule', {
value: true
});
//# sourceMappingURL=source.js.map