@hasna/instructions 0.4.8 → 0.4.9
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/README.md +35 -0
- package/dist/cli/index.js +449 -218
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +417 -186
- package/dist/lib/apply.d.ts +7 -0
- package/dist/lib/apply.d.ts.map +1 -1
- package/dist/lib/global-agent-rules-standard.d.ts +106 -0
- package/dist/lib/global-agent-rules-standard.d.ts.map +1 -1
- package/dist/lib/global-agent-rules-stored-content.test.d.ts +2 -0
- package/dist/lib/global-agent-rules-stored-content.test.d.ts.map +1 -0
- package/dist/lib/session-render-contract.d.ts +10 -0
- package/dist/lib/session-render-contract.d.ts.map +1 -1
- package/dist/lib/session-render-ownership.d.ts +23 -0
- package/dist/lib/session-render-ownership.d.ts.map +1 -0
- package/dist/lib/session-render-ownership.test.d.ts +2 -0
- package/dist/lib/session-render-ownership.test.d.ts.map +1 -0
- package/dist/lib/session-render.d.ts +24 -1
- package/dist/lib/session-render.d.ts.map +1 -1
- package/dist/mcp/index.js +214 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1100,11 +1100,11 @@ function resolveConfigStore(env = process.env) {
|
|
|
1100
1100
|
return cloud ? new CloudConfigStore(cloud) : new LocalConfigStore;
|
|
1101
1101
|
}
|
|
1102
1102
|
// src/status.ts
|
|
1103
|
-
import { existsSync as
|
|
1103
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
1104
1104
|
|
|
1105
1105
|
// src/lib/apply.ts
|
|
1106
|
-
import { existsSync as
|
|
1107
|
-
import { basename as basename4, dirname as
|
|
1106
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1107
|
+
import { basename as basename4, dirname as dirname4, join as join6, resolve as resolve3 } from "path";
|
|
1108
1108
|
import { homedir as homedir3 } from "os";
|
|
1109
1109
|
|
|
1110
1110
|
// src/lib/config-agents.ts
|
|
@@ -1127,18 +1127,23 @@ function retiredOrUnsupportedAgentReason(agent) {
|
|
|
1127
1127
|
}
|
|
1128
1128
|
|
|
1129
1129
|
// src/lib/session-render.ts
|
|
1130
|
-
import { createHash as
|
|
1130
|
+
import { createHash as createHash3 } from "crypto";
|
|
1131
1131
|
import { existsSync as existsSync4, readFileSync as readFileSync2, realpathSync, statSync as statSync2 } from "fs";
|
|
1132
1132
|
import { homedir as homedir2 } from "os";
|
|
1133
1133
|
import { basename as basename3, dirname as dirname2, extname as extname2, isAbsolute as isAbsolute2, join as join4, parse as parse2, posix, relative as relative2, resolve as resolve2 } from "path";
|
|
1134
1134
|
|
|
1135
1135
|
// src/lib/global-agent-rules-standard.ts
|
|
1136
|
+
import { createHash } from "crypto";
|
|
1136
1137
|
var GLOBAL_AGENT_RULES_STANDARD_SLUG = "global-agent-rules-standard";
|
|
1137
1138
|
var AGENT_OPERATING_RULES_SOURCE_SET_ID = "hasna-global-agent-rules-standard";
|
|
1138
1139
|
var AGENT_OPERATING_RULES_SOURCE_ID = "hasna-agent-operating-rules";
|
|
1140
|
+
var AGENT_OPERATING_RULES_ROLE = "agent-operating-rules";
|
|
1139
1141
|
var AGENT_OPERATING_RULES_VERSION = "1.1.6";
|
|
1140
1142
|
var AGENT_OPERATING_RULES_SOURCE_SET_VERSION = "2026-07-23";
|
|
1141
1143
|
var AGENT_OPERATING_RULES_SENTINEL = "<!-- hasna:agent-operating-rules v=1.1.6 -->";
|
|
1144
|
+
var AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY = "hasna:agent-operating-rules";
|
|
1145
|
+
var AGENT_OPERATING_RULES_SENTINEL_PATTERN = /<!--\s*hasna:agent-operating-rules\s+v=([0-9]+\.[0-9]+\.[0-9]+)\s*-->/i;
|
|
1146
|
+
var AGENT_OPERATING_RULES_HEADING_PATTERN = /^#\s*Hasna Agent Operating Rules\s+\u2014\s+v[0-9]+\.[0-9]+\.[0-9]+\s+\(([0-9]{4}-[0-9]{2}-[0-9]{2})\)/;
|
|
1142
1147
|
var AGENT_OPERATING_RULES_PAYLOAD_SHA256 = "8b236086b82e94490516e0b00dffa03fb5f6841b68d95f80fc3e3c8fb7087420";
|
|
1143
1148
|
var AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256 = "b8e89cdb49e207e5b497ac51384d67022b94fe5645cc9273db60384eb2c2fb32";
|
|
1144
1149
|
var SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE = "hasna-agent-operating-rules/scoped-operational-control/v1";
|
|
@@ -1170,7 +1175,7 @@ var AGENT_OPERATING_RULES_PROVENANCE = {
|
|
|
1170
1175
|
};
|
|
1171
1176
|
var AGENT_OPERATING_RULES_METADATA = {
|
|
1172
1177
|
sourceSet: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
1173
|
-
role:
|
|
1178
|
+
role: AGENT_OPERATING_RULES_ROLE,
|
|
1174
1179
|
rulesVersion: AGENT_OPERATING_RULES_VERSION,
|
|
1175
1180
|
sourceSetVersion: AGENT_OPERATING_RULES_SOURCE_SET_VERSION,
|
|
1176
1181
|
plan: GLOBAL_AGENT_RULES_STANDARD_SLUG,
|
|
@@ -1179,7 +1184,7 @@ var AGENT_OPERATING_RULES_METADATA = {
|
|
|
1179
1184
|
upstreamFileSha256: AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256,
|
|
1180
1185
|
upstreamExportId: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
1181
1186
|
upstreamSourceId: AGENT_OPERATING_RULES_SOURCE_ID,
|
|
1182
|
-
sentinel:
|
|
1187
|
+
sentinel: AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY,
|
|
1183
1188
|
policyReferences: {
|
|
1184
1189
|
incidentRecovery: SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE
|
|
1185
1190
|
}
|
|
@@ -1219,37 +1224,120 @@ var GLOBAL_AGENT_RULES_STANDARD_CONTENT = [
|
|
|
1219
1224
|
].join(`
|
|
1220
1225
|
`) + `
|
|
1221
1226
|
`;
|
|
1222
|
-
|
|
1223
|
-
|
|
1227
|
+
function parseAgentOperatingRulesVersion(content) {
|
|
1228
|
+
return content ? AGENT_OPERATING_RULES_SENTINEL_PATTERN.exec(content)?.[1] ?? null : null;
|
|
1229
|
+
}
|
|
1230
|
+
function compareAgentOperatingRulesVersions(left, right) {
|
|
1231
|
+
const leftParts = left.split(".").map(Number);
|
|
1232
|
+
const rightParts = right.split(".").map(Number);
|
|
1233
|
+
for (let i = 0;i < 3; i++) {
|
|
1234
|
+
const diff = (leftParts[i] ?? 0) - (rightParts[i] ?? 0);
|
|
1235
|
+
if (diff !== 0)
|
|
1236
|
+
return diff;
|
|
1237
|
+
}
|
|
1238
|
+
return 0;
|
|
1239
|
+
}
|
|
1240
|
+
function payloadDate(content) {
|
|
1241
|
+
const canonical = new RegExp(AGENT_OPERATING_RULES_HEADING_PATTERN.source, "m").exec(content)?.[1];
|
|
1242
|
+
if (canonical)
|
|
1243
|
+
return canonical;
|
|
1244
|
+
const heading = /^#[^\S\n].*$/m.exec(content)?.[0];
|
|
1245
|
+
return heading ? /\b([0-9]{4}-[0-9]{2}-[0-9]{2})\b/.exec(heading)?.[1] ?? null : null;
|
|
1246
|
+
}
|
|
1247
|
+
function sha256(content) {
|
|
1248
|
+
return createHash("sha256").update(content).digest("hex");
|
|
1249
|
+
}
|
|
1250
|
+
function resolveAgentOperatingRulesPayload(storedContent) {
|
|
1251
|
+
const stored = storedContent ?? "";
|
|
1252
|
+
const storedVersion = parseAgentOperatingRulesVersion(stored);
|
|
1253
|
+
const baselineOrder = storedVersion === null ? null : compareAgentOperatingRulesVersions(storedVersion, AGENT_OPERATING_RULES_VERSION);
|
|
1254
|
+
const storedIsCurrent = baselineOrder !== null && (baselineOrder > 0 || baselineOrder === 0 && sha256(stored) === AGENT_OPERATING_RULES_PAYLOAD_SHA256);
|
|
1255
|
+
const content = storedIsCurrent ? stored : GLOBAL_AGENT_RULES_STANDARD_CONTENT;
|
|
1256
|
+
const origin = storedIsCurrent ? "stored-config" : "embedded-baseline";
|
|
1257
|
+
const matchesEmbeddedBaseline = content === GLOBAL_AGENT_RULES_STANDARD_CONTENT;
|
|
1258
|
+
const integrity = matchesEmbeddedBaseline ? "pinned-digest" : "unverified-self-declared";
|
|
1259
|
+
const version = storedIsCurrent ? storedVersion : AGENT_OPERATING_RULES_VERSION;
|
|
1260
|
+
const payloadSha256 = matchesEmbeddedBaseline ? AGENT_OPERATING_RULES_PAYLOAD_SHA256 : sha256(content);
|
|
1261
|
+
const sourceSetVersion = matchesEmbeddedBaseline ? AGENT_OPERATING_RULES_SOURCE_SET_VERSION : payloadDate(content);
|
|
1262
|
+
const upstreamPin = matchesEmbeddedBaseline ? {
|
|
1263
|
+
upstreamRepository: AGENT_OPERATING_RULES_UPSTREAM.repository,
|
|
1264
|
+
upstreamCommit: AGENT_OPERATING_RULES_UPSTREAM.commit,
|
|
1265
|
+
upstreamPath: AGENT_OPERATING_RULES_UPSTREAM.path,
|
|
1266
|
+
upstreamFileSha256: AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256
|
|
1267
|
+
} : {};
|
|
1268
|
+
const policyReference = content.includes(SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE) ? { policyReference: SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE } : {};
|
|
1269
|
+
return {
|
|
1270
|
+
content,
|
|
1271
|
+
version,
|
|
1272
|
+
origin,
|
|
1273
|
+
matchesEmbeddedBaseline,
|
|
1274
|
+
integrity,
|
|
1275
|
+
provenance: {
|
|
1276
|
+
source: AGENT_OPERATING_RULES_PROVENANCE.source,
|
|
1277
|
+
payloadOrigin: origin,
|
|
1278
|
+
payloadIntegrity: integrity,
|
|
1279
|
+
...upstreamPin,
|
|
1280
|
+
upstreamExportId: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
1281
|
+
upstreamSourceId: AGENT_OPERATING_RULES_SOURCE_ID,
|
|
1282
|
+
selectedPayloadSha256: payloadSha256,
|
|
1283
|
+
rulesVersion: version,
|
|
1284
|
+
sourceSetVersion,
|
|
1285
|
+
...policyReference
|
|
1286
|
+
},
|
|
1287
|
+
metadata: {
|
|
1288
|
+
sourceSet: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
1289
|
+
role: AGENT_OPERATING_RULES_METADATA.role,
|
|
1290
|
+
payloadOrigin: origin,
|
|
1291
|
+
payloadIntegrity: integrity,
|
|
1292
|
+
rulesVersion: version,
|
|
1293
|
+
sourceSetVersion,
|
|
1294
|
+
plan: GLOBAL_AGENT_RULES_STANDARD_SLUG,
|
|
1295
|
+
contentSha256: payloadSha256,
|
|
1296
|
+
selectedPayloadSha256: payloadSha256,
|
|
1297
|
+
...matchesEmbeddedBaseline ? { upstreamFileSha256: AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256 } : {},
|
|
1298
|
+
upstreamExportId: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
1299
|
+
upstreamSourceId: AGENT_OPERATING_RULES_SOURCE_ID,
|
|
1300
|
+
sentinel: AGENT_OPERATING_RULES_METADATA.sentinel,
|
|
1301
|
+
...policyReference.policyReference ? { policyReferences: { incidentRecovery: SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE } } : {}
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
function standardConfigInput(payload) {
|
|
1306
|
+
return {
|
|
1224
1307
|
name: "Global Agent Rules Standard",
|
|
1225
1308
|
category: "rules",
|
|
1226
1309
|
agent: "global",
|
|
1227
1310
|
format: "markdown",
|
|
1228
|
-
content:
|
|
1311
|
+
content: payload.content,
|
|
1229
1312
|
kind: "reference",
|
|
1230
|
-
description: `Managed Hasna agent operating rules v${
|
|
1313
|
+
description: payload.matchesEmbeddedBaseline ? `Managed Hasna agent operating rules v${payload.version}; accepted source ${AGENT_OPERATING_RULES_UPSTREAM.repository}@${AGENT_OPERATING_RULES_UPSTREAM.commit}:${AGENT_OPERATING_RULES_UPSTREAM.path}` : `Managed Hasna agent operating rules v${payload.version}; stored payload sha256 ${payload.metadata["contentSha256"]}`,
|
|
1231
1314
|
tags: [
|
|
1232
1315
|
"global-agent-rules",
|
|
1233
1316
|
"system-prompt",
|
|
1234
1317
|
"coding-agent-rules",
|
|
1235
1318
|
"agent-operating-rules",
|
|
1236
|
-
`rules-version:${
|
|
1237
|
-
`source-commit:${AGENT_OPERATING_RULES_UPSTREAM.commit}`
|
|
1319
|
+
`rules-version:${payload.version}`,
|
|
1320
|
+
...payload.matchesEmbeddedBaseline ? [`source-commit:${AGENT_OPERATING_RULES_UPSTREAM.commit}`] : []
|
|
1238
1321
|
]
|
|
1239
1322
|
};
|
|
1323
|
+
}
|
|
1324
|
+
async function ensureGlobalAgentRulesStandardConfig(store = resolveConfigStore()) {
|
|
1325
|
+
let existing;
|
|
1240
1326
|
try {
|
|
1241
|
-
|
|
1242
|
-
if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind || JSON.stringify(existing.tags) !== JSON.stringify(input.tags)) {
|
|
1243
|
-
return await store.updateConfig(existing.id, input);
|
|
1244
|
-
}
|
|
1245
|
-
return existing;
|
|
1327
|
+
existing = await store.getConfig(GLOBAL_AGENT_RULES_STANDARD_SLUG);
|
|
1246
1328
|
} catch {
|
|
1247
|
-
return await store.createConfig(
|
|
1329
|
+
return await store.createConfig(standardConfigInput(resolveAgentOperatingRulesPayload(null)));
|
|
1330
|
+
}
|
|
1331
|
+
const payload = resolveAgentOperatingRulesPayload(existing.content);
|
|
1332
|
+
const input = standardConfigInput(payload);
|
|
1333
|
+
if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind || JSON.stringify(existing.tags) !== JSON.stringify(input.tags)) {
|
|
1334
|
+
return await store.updateConfig(existing.id, input);
|
|
1248
1335
|
}
|
|
1336
|
+
return existing;
|
|
1249
1337
|
}
|
|
1250
1338
|
|
|
1251
1339
|
// src/lib/project-context.ts
|
|
1252
|
-
import { createHash, randomUUID as randomUUID3 } from "crypto";
|
|
1340
|
+
import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
|
|
1253
1341
|
import { execFileSync } from "child_process";
|
|
1254
1342
|
import { dlopen, FFIType } from "bun:ffi";
|
|
1255
1343
|
import {
|
|
@@ -5432,6 +5520,10 @@ function hasSecrets(content, format) {
|
|
|
5432
5520
|
var CODEWITH_NATIVE_IMPORTS_ENV = "HASNA_CONFIGS_CODEWITH_NATIVE_IMPORTS";
|
|
5433
5521
|
var SESSION_RENDER_MANAGED_MARKER = "Managed by @hasna/configs session render";
|
|
5434
5522
|
var SESSION_RENDER_SCHEMA = "hasna.configs.session-render/v1";
|
|
5523
|
+
var SESSION_RENDER_MANAGED_NAMESPACE = ".hasna";
|
|
5524
|
+
var SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR = `${SESSION_RENDER_MANAGED_NAMESPACE}/instructions`;
|
|
5525
|
+
var SESSION_RENDER_MANIFEST_RELATIVE_PATH = `${SESSION_RENDER_MANAGED_NAMESPACE}/session-render-manifest.json`;
|
|
5526
|
+
var SESSION_RENDER_SNAPSHOT_RELATIVE_DIR = `${SESSION_RENDER_MANAGED_NAMESPACE}/session-render-snapshots`;
|
|
5435
5527
|
var SESSION_INSTRUCTION_LAYERS = [
|
|
5436
5528
|
"global",
|
|
5437
5529
|
"tool",
|
|
@@ -5608,7 +5700,7 @@ class ProjectContextHashRace extends Error {
|
|
|
5608
5700
|
}
|
|
5609
5701
|
function computeProjectContextSourceHash(value) {
|
|
5610
5702
|
const normalized = removeHashForFingerprint(value);
|
|
5611
|
-
return `sha256:${
|
|
5703
|
+
return `sha256:${sha2562(stableStringify(normalized))}`;
|
|
5612
5704
|
}
|
|
5613
5705
|
function parseProjectContextBundle(input) {
|
|
5614
5706
|
let encoded;
|
|
@@ -5778,7 +5870,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
5778
5870
|
const files = input.files.map((file) => file === index ? {
|
|
5779
5871
|
...file,
|
|
5780
5872
|
content,
|
|
5781
|
-
sha256:
|
|
5873
|
+
sha256: sha2562(content),
|
|
5782
5874
|
sourceIds: [...new Set([...file.sourceIds, "project-context-bundle"])]
|
|
5783
5875
|
} : file);
|
|
5784
5876
|
if (observedHashes.some((observed) => currentFileHash(observed.path, workspaceRoot) !== observed.sha256)) {
|
|
@@ -5979,7 +6071,7 @@ function assertRenderedOutputsStable(plan, cacheContent, sessionOutput) {
|
|
|
5979
6071
|
sessionOutput
|
|
5980
6072
|
];
|
|
5981
6073
|
for (const output of outputs) {
|
|
5982
|
-
if (currentFileHash(output.path, plan.workspace_root) !==
|
|
6074
|
+
if (currentFileHash(output.path, plan.workspace_root) !== sha2562(output.content)) {
|
|
5983
6075
|
throw new ProjectContextHashRace(`managed path changed before manifest commit: ${relativePosix(plan.workspace_root, output.path)}`);
|
|
5984
6076
|
}
|
|
5985
6077
|
}
|
|
@@ -6199,7 +6291,7 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
6199
6291
|
return null;
|
|
6200
6292
|
const files = Array.isArray(manifest["files"]) ? manifest["files"] : [];
|
|
6201
6293
|
const codewith = files.find((file) => isRecord(file) && file["relativePath"] === "CODEWITH.md");
|
|
6202
|
-
if (!isRecord(codewith) || codewith["sha256"] !==
|
|
6294
|
+
if (!isRecord(codewith) || codewith["sha256"] !== sha2562(content)) {
|
|
6203
6295
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "legacy /dev/fd session manifest does not match CODEWITH.md");
|
|
6204
6296
|
}
|
|
6205
6297
|
const section = /^## Workspace\r?\n/gm.exec(content);
|
|
@@ -6269,14 +6361,14 @@ function buildManifest(plan, now2) {
|
|
|
6269
6361
|
path: plan.fragment_path,
|
|
6270
6362
|
relativePath: PROJECT_CONTEXT_FRAGMENT_PATH,
|
|
6271
6363
|
role: "fragment",
|
|
6272
|
-
sha256:
|
|
6364
|
+
sha256: sha2562(plan.fragment),
|
|
6273
6365
|
sourceIds: ["project-context-bundle"]
|
|
6274
6366
|
},
|
|
6275
6367
|
{
|
|
6276
6368
|
path: plan.target_path,
|
|
6277
6369
|
relativePath: plan.target_relative_path,
|
|
6278
6370
|
role: "index",
|
|
6279
|
-
sha256:
|
|
6371
|
+
sha256: sha2562(plan.target_content),
|
|
6280
6372
|
sourceIds: ["project-context-bundle"]
|
|
6281
6373
|
}
|
|
6282
6374
|
];
|
|
@@ -6353,7 +6445,7 @@ function buildSessionCompatibilityManifest(plan, now2) {
|
|
|
6353
6445
|
path: plan.target_path,
|
|
6354
6446
|
relativePath: targetRelativePath,
|
|
6355
6447
|
role: "index",
|
|
6356
|
-
sha256:
|
|
6448
|
+
sha256: sha2562(plan.target_content),
|
|
6357
6449
|
sourceIds: [...new Set([...previousSourceIds ?? [], "project-context-bundle"])]
|
|
6358
6450
|
};
|
|
6359
6451
|
const targetOwner = isRecord(existing["targetOwner"]) ? existing["targetOwner"] : {};
|
|
@@ -6381,7 +6473,7 @@ function buildSessionCompatibilityManifest(plan, now2) {
|
|
|
6381
6473
|
blockers: [],
|
|
6382
6474
|
generatedAt: now2.toISOString(),
|
|
6383
6475
|
env: sanitizeLegacyEnvironment(existing["env"]),
|
|
6384
|
-
sourceHash:
|
|
6476
|
+
sourceHash: sha2562(stableStringify({ previous: typeof existing["sourceHash"] === "string" ? existing["sourceHash"] : null, projectContext: plan.bundle.hash })),
|
|
6385
6477
|
sources,
|
|
6386
6478
|
skippedSources: sanitizeLegacySkippedSources(existing["skippedSources"]),
|
|
6387
6479
|
files: [...files.filter((file) => file["relativePath"] !== targetRelativePath), updatedTarget],
|
|
@@ -6584,7 +6676,7 @@ function projectContextManifestSource(cachePath, runtime, bundle) {
|
|
|
6584
6676
|
nonOverridable: true,
|
|
6585
6677
|
replacementScope: "project-context",
|
|
6586
6678
|
rules: [],
|
|
6587
|
-
renderedPayloadSha256:
|
|
6679
|
+
renderedPayloadSha256: sha2562(JSON.stringify(bundle)),
|
|
6588
6680
|
provenance: {
|
|
6589
6681
|
schema: PROJECT_CONTEXT_SCHEMA,
|
|
6590
6682
|
projectId: bundle.project.id,
|
|
@@ -6706,7 +6798,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
6706
6798
|
let fd = null;
|
|
6707
6799
|
let preserveTemp = false;
|
|
6708
6800
|
let directoryChanged = false;
|
|
6709
|
-
const desiredHash =
|
|
6801
|
+
const desiredHash = sha2562(content);
|
|
6710
6802
|
try {
|
|
6711
6803
|
fd = anchoredOpenExclusive(directory, tempName, previousMode);
|
|
6712
6804
|
writeFileSync(fd, content, { encoding: "utf8" });
|
|
@@ -6816,7 +6908,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
6816
6908
|
}
|
|
6817
6909
|
}
|
|
6818
6910
|
function atomicWritePortable(path, content, workspaceRoot, defaultMode, expectedHash, beforeInstall, maxObservedBytes, allowReplacement = false) {
|
|
6819
|
-
const desiredHash =
|
|
6911
|
+
const desiredHash = sha2562(content);
|
|
6820
6912
|
const currentHash = portableFileHash(path, workspaceRoot, maxObservedBytes);
|
|
6821
6913
|
if (expectedHash === undefined && currentHash === desiredHash)
|
|
6822
6914
|
return;
|
|
@@ -6890,7 +6982,7 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
6890
6982
|
const dir = dirname(path);
|
|
6891
6983
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
6892
6984
|
const tempPath = join3(dir, `.project-context-${randomUUID3()}.tmp`);
|
|
6893
|
-
const desiredHash =
|
|
6985
|
+
const desiredHash = sha2562(content);
|
|
6894
6986
|
let fd = null;
|
|
6895
6987
|
let tempIdentity = null;
|
|
6896
6988
|
try {
|
|
@@ -6941,7 +7033,7 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
6941
7033
|
if (maxObservedBytes !== null && stat.size > maxObservedBytes) {
|
|
6942
7034
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePosix(workspaceRoot, path)}`);
|
|
6943
7035
|
}
|
|
6944
|
-
return
|
|
7036
|
+
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
6945
7037
|
}
|
|
6946
7038
|
function writeProjectContextCoordinatedFile(input) {
|
|
6947
7039
|
atomicWriteFile(resolve(input.path), input.content, assertSafeWorkspaceRoot(input.workspace_root), input.default_mode ?? 420, input.expected_hash, undefined, false, undefined, input.force_portable_file_ops ?? false, input.max_observed_bytes, input.allow_portable_replacement ?? false);
|
|
@@ -7115,7 +7207,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
7115
7207
|
return {
|
|
7116
7208
|
dev: stat.dev,
|
|
7117
7209
|
ino: stat.ino,
|
|
7118
|
-
hash:
|
|
7210
|
+
hash: createHash2("sha256").update(readFileSync(fd)).digest("hex"),
|
|
7119
7211
|
mode: stat.mode & 511
|
|
7120
7212
|
};
|
|
7121
7213
|
} finally {
|
|
@@ -7295,7 +7387,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
7295
7387
|
process_start_id: processStartIdentityLookup(process.pid)
|
|
7296
7388
|
})}
|
|
7297
7389
|
`;
|
|
7298
|
-
openedContentHash =
|
|
7390
|
+
openedContentHash = sha2562(content);
|
|
7299
7391
|
writeFileSync(fd, content);
|
|
7300
7392
|
fsyncSync(fd);
|
|
7301
7393
|
try {
|
|
@@ -7346,7 +7438,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
7346
7438
|
const current = lstatSync(lockPath);
|
|
7347
7439
|
if (current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino)
|
|
7348
7440
|
return;
|
|
7349
|
-
if (expectedHash !== undefined &&
|
|
7441
|
+
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
7350
7442
|
return;
|
|
7351
7443
|
rmSync2(lockPath);
|
|
7352
7444
|
fsyncDirectory(resolve(lockPath, ".."));
|
|
@@ -7363,7 +7455,7 @@ function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentity
|
|
|
7363
7455
|
} catch {
|
|
7364
7456
|
return null;
|
|
7365
7457
|
}
|
|
7366
|
-
const contentHash =
|
|
7458
|
+
const contentHash = sha2562(content);
|
|
7367
7459
|
if (currentFileHash(lockPath, workspaceRoot) !== contentHash)
|
|
7368
7460
|
return null;
|
|
7369
7461
|
let pid = null;
|
|
@@ -7525,7 +7617,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
7525
7617
|
created_at: new Date().toISOString()
|
|
7526
7618
|
})}
|
|
7527
7619
|
`;
|
|
7528
|
-
releaseHash =
|
|
7620
|
+
releaseHash = sha2562(releaseContent);
|
|
7529
7621
|
writeFileSync(releaseFd, releaseContent);
|
|
7530
7622
|
fsyncSync(releaseFd);
|
|
7531
7623
|
closeSync(releaseFd);
|
|
@@ -7764,7 +7856,7 @@ function currentFileHash(path, workspaceRoot) {
|
|
|
7764
7856
|
return null;
|
|
7765
7857
|
const relativePath = relativePosix(workspaceRoot, path);
|
|
7766
7858
|
const maxBytes = relativePath === ".hasna/session-render-manifest.json" || relativePath === ".codewith/.hasna/session-render-manifest.json" ? SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES : 256 * 1024;
|
|
7767
|
-
return
|
|
7859
|
+
return sha2562(readUtf8RegularFile(path, workspaceRoot, maxBytes));
|
|
7768
7860
|
}
|
|
7769
7861
|
function hashesStillMatch(expected, workspaceRoot) {
|
|
7770
7862
|
for (const [path, hash] of expected) {
|
|
@@ -7933,8 +8025,8 @@ function removeHashForFingerprint(value) {
|
|
|
7933
8025
|
}
|
|
7934
8026
|
return copy;
|
|
7935
8027
|
}
|
|
7936
|
-
function
|
|
7937
|
-
return
|
|
8028
|
+
function sha2562(content) {
|
|
8029
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
7938
8030
|
}
|
|
7939
8031
|
function isRecord(value) {
|
|
7940
8032
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -8078,7 +8170,7 @@ var CODEWITH_FLATTENED_ADAPTER = {
|
|
|
8078
8170
|
tool: "codewith",
|
|
8079
8171
|
mode: "flattened-markdown",
|
|
8080
8172
|
indexFile: "CODEWITH.md",
|
|
8081
|
-
managedDir:
|
|
8173
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
8082
8174
|
envVar: "CODEWITH_HOME",
|
|
8083
8175
|
nativeImports: false,
|
|
8084
8176
|
description: "Codewith CODEWITH.md flattened until native @ imports are implemented in Codewith."
|
|
@@ -8087,7 +8179,7 @@ var CODEWITH_NATIVE_ADAPTER = {
|
|
|
8087
8179
|
tool: "codewith",
|
|
8088
8180
|
mode: "native-imports",
|
|
8089
8181
|
indexFile: "CODEWITH.md",
|
|
8090
|
-
managedDir:
|
|
8182
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
8091
8183
|
envVar: "CODEWITH_HOME",
|
|
8092
8184
|
nativeImports: true,
|
|
8093
8185
|
description: "Codewith CODEWITH.md with gated @ imports into managed fragments."
|
|
@@ -8097,7 +8189,7 @@ var SESSION_TOOL_ADAPTERS = {
|
|
|
8097
8189
|
tool: "claude",
|
|
8098
8190
|
mode: "native-imports",
|
|
8099
8191
|
indexFile: "CLAUDE.md",
|
|
8100
|
-
managedDir:
|
|
8192
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
8101
8193
|
envVar: "CLAUDE_CONFIG_DIR",
|
|
8102
8194
|
nativeImports: true,
|
|
8103
8195
|
description: "Claude Code CLAUDE.md with @ imports into managed fragments."
|
|
@@ -8106,7 +8198,7 @@ var SESSION_TOOL_ADAPTERS = {
|
|
|
8106
8198
|
tool: "codex",
|
|
8107
8199
|
mode: "flattened-markdown",
|
|
8108
8200
|
indexFile: "AGENTS.md",
|
|
8109
|
-
managedDir:
|
|
8201
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
8110
8202
|
envVar: "CODEX_HOME",
|
|
8111
8203
|
nativeImports: false,
|
|
8112
8204
|
description: "Codex AGENTS.md flattened instruction file."
|
|
@@ -8123,7 +8215,7 @@ var SESSION_TOOL_ADAPTERS = {
|
|
|
8123
8215
|
mode: "opencode-instructions",
|
|
8124
8216
|
indexFile: "AGENTS.md",
|
|
8125
8217
|
configFile: "opencode.json",
|
|
8126
|
-
managedDir:
|
|
8218
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
8127
8219
|
envVar: "OPENCODE_CONFIG_DIR",
|
|
8128
8220
|
nativeImports: false,
|
|
8129
8221
|
description: "OpenCode AGENTS.md plus opencode.json instructions pointing at managed fragments."
|
|
@@ -8132,7 +8224,7 @@ var SESSION_TOOL_ADAPTERS = {
|
|
|
8132
8224
|
tool: "aicopilot",
|
|
8133
8225
|
mode: "flattened-markdown",
|
|
8134
8226
|
indexFile: "AICOPILOT.md",
|
|
8135
|
-
managedDir:
|
|
8227
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
8136
8228
|
envVar: "AICOPILOT_CONFIG_DIR",
|
|
8137
8229
|
nativeImports: false,
|
|
8138
8230
|
description: "AI Copilot AICOPILOT.md flattened instruction file."
|
|
@@ -8155,6 +8247,19 @@ var SESSION_TOOL_ADAPTERS = {
|
|
|
8155
8247
|
},
|
|
8156
8248
|
codewith: CODEWITH_FLATTENED_ADAPTER
|
|
8157
8249
|
};
|
|
8250
|
+
var SESSION_RENDER_MANAGED_DIRS = [
|
|
8251
|
+
...new Set([
|
|
8252
|
+
CODEWITH_FLATTENED_ADAPTER,
|
|
8253
|
+
CODEWITH_NATIVE_ADAPTER,
|
|
8254
|
+
...Object.values(SESSION_TOOL_ADAPTERS)
|
|
8255
|
+
].map((adapter) => adapter.managedDir))
|
|
8256
|
+
];
|
|
8257
|
+
var SESSION_RENDER_SHARED_MANAGED_DIRS = [".cursor/rules"];
|
|
8258
|
+
var SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS = [
|
|
8259
|
+
...SESSION_RENDER_MANAGED_DIRS.filter((dir) => !SESSION_RENDER_SHARED_MANAGED_DIRS.includes(dir)),
|
|
8260
|
+
SESSION_RENDER_MANIFEST_RELATIVE_PATH,
|
|
8261
|
+
SESSION_RENDER_SNAPSHOT_RELATIVE_DIR
|
|
8262
|
+
];
|
|
8158
8263
|
var SESSION_LAYER_RANK = {
|
|
8159
8264
|
global: 10,
|
|
8160
8265
|
tool: 20,
|
|
@@ -8184,11 +8289,11 @@ function ensureTrailingNewline3(content) {
|
|
|
8184
8289
|
`) ? content : `${content}
|
|
8185
8290
|
`;
|
|
8186
8291
|
}
|
|
8187
|
-
function
|
|
8188
|
-
return
|
|
8292
|
+
function sha2563(content) {
|
|
8293
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
8189
8294
|
}
|
|
8190
8295
|
function fingerprint(value) {
|
|
8191
|
-
return
|
|
8296
|
+
return sha2563(JSON.stringify(value));
|
|
8192
8297
|
}
|
|
8193
8298
|
function canonicalFingerprintValue(value) {
|
|
8194
8299
|
if (Array.isArray(value))
|
|
@@ -8254,18 +8359,59 @@ function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
|
8254
8359
|
relativePath: safeRelativePath,
|
|
8255
8360
|
role,
|
|
8256
8361
|
content: normalizedContent,
|
|
8257
|
-
sha256:
|
|
8362
|
+
sha256: sha2563(normalizedContent),
|
|
8258
8363
|
sourceIds
|
|
8259
8364
|
};
|
|
8260
8365
|
}
|
|
8366
|
+
function claimsAgentOperatingRulesPolicy(source, content) {
|
|
8367
|
+
if (!AGENT_OPERATING_RULES_SENTINEL_PATTERN.test(content))
|
|
8368
|
+
return false;
|
|
8369
|
+
if (source.nonOverridable === true)
|
|
8370
|
+
return true;
|
|
8371
|
+
if (source.id === GLOBAL_AGENT_RULES_STANDARD_SLUG || source.id === AGENT_OPERATING_RULES_SOURCE_ID)
|
|
8372
|
+
return true;
|
|
8373
|
+
if (source.metadata?.["role"] === AGENT_OPERATING_RULES_ROLE)
|
|
8374
|
+
return true;
|
|
8375
|
+
return AGENT_OPERATING_RULES_HEADING_PATTERN.test(content.trimStart());
|
|
8376
|
+
}
|
|
8377
|
+
function applyAgentOperatingRulesFloor(source, content) {
|
|
8378
|
+
const unchanged = {
|
|
8379
|
+
content,
|
|
8380
|
+
provenance: source.provenance ?? null,
|
|
8381
|
+
metadata: source.metadata ?? null
|
|
8382
|
+
};
|
|
8383
|
+
if (!claimsAgentOperatingRulesPolicy(source, content))
|
|
8384
|
+
return unchanged;
|
|
8385
|
+
const payload = resolveAgentOperatingRulesPayload(content);
|
|
8386
|
+
if (payload.content === content) {
|
|
8387
|
+
return {
|
|
8388
|
+
content,
|
|
8389
|
+
provenance: { ...source.provenance ?? {}, payloadIntegrity: payload.integrity },
|
|
8390
|
+
metadata: { ...source.metadata ?? {}, payloadIntegrity: payload.integrity }
|
|
8391
|
+
};
|
|
8392
|
+
}
|
|
8393
|
+
const floored = {
|
|
8394
|
+
payloadFloorApplied: true,
|
|
8395
|
+
flooredFromRulesVersion: parseAgentOperatingRulesVersion(content),
|
|
8396
|
+
flooredFromPayloadSha256: sha2563(content)
|
|
8397
|
+
};
|
|
8398
|
+
return {
|
|
8399
|
+
content: payload.content,
|
|
8400
|
+
provenance: { ...source.provenance ?? {}, ...payload.provenance, ...floored },
|
|
8401
|
+
metadata: { ...source.metadata ?? {}, ...payload.metadata, ...floored }
|
|
8402
|
+
};
|
|
8403
|
+
}
|
|
8261
8404
|
function normalizeSources(sources, tool, allowEmptySources) {
|
|
8262
8405
|
const normalized = sources.map((source, index) => {
|
|
8263
8406
|
if (!source.id.trim())
|
|
8264
8407
|
throw new Error("Session instruction source id is required.");
|
|
8265
|
-
const
|
|
8408
|
+
const floored = applyAgentOperatingRulesFloor(source, source.content ?? "");
|
|
8409
|
+
const content = filterProviderOnlyBlocks(floored.content, tool);
|
|
8266
8410
|
const normalized2 = {
|
|
8267
8411
|
...source,
|
|
8268
8412
|
content,
|
|
8413
|
+
provenance: floored.provenance,
|
|
8414
|
+
metadata: floored.metadata,
|
|
8269
8415
|
normalizedId: slug(source.id),
|
|
8270
8416
|
resolvedLabel: source.label ?? source.id,
|
|
8271
8417
|
resolvedLayer: source.layer === undefined ? "agent" : normalizeSessionInstructionLayer(source.layer),
|
|
@@ -8288,30 +8434,36 @@ function deduplicateSemanticPolicySources(sources) {
|
|
|
8288
8434
|
const selected = [];
|
|
8289
8435
|
const policySources = new Map;
|
|
8290
8436
|
for (const source of sources) {
|
|
8291
|
-
const sentinel = source.content.match(
|
|
8437
|
+
const sentinel = source.content.match(AGENT_OPERATING_RULES_SENTINEL_PATTERN);
|
|
8292
8438
|
if (!sentinel) {
|
|
8293
8439
|
selected.push(source);
|
|
8294
8440
|
continue;
|
|
8295
8441
|
}
|
|
8296
|
-
const
|
|
8442
|
+
const version = sentinel[1];
|
|
8443
|
+
const key = AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY;
|
|
8297
8444
|
const normalizedContent = source.content.replace(/\r\n/g, `
|
|
8298
8445
|
`).trim();
|
|
8299
8446
|
const existing = policySources.get(key);
|
|
8300
8447
|
if (!existing) {
|
|
8301
|
-
policySources.set(key, { index: selected.length, normalizedContent });
|
|
8448
|
+
policySources.set(key, { index: selected.length, version, normalizedContent });
|
|
8302
8449
|
selected.push(source);
|
|
8303
8450
|
continue;
|
|
8304
8451
|
}
|
|
8305
|
-
|
|
8306
|
-
|
|
8452
|
+
const versionOrder = compareAgentOperatingRulesVersions(version, existing.version);
|
|
8453
|
+
if (versionOrder === 0 && existing.normalizedContent !== normalizedContent) {
|
|
8454
|
+
throw new Error(`Conflicting semantic policy sources declare ${key}/v${version} with different content.`);
|
|
8307
8455
|
}
|
|
8308
8456
|
const current = selected[existing.index];
|
|
8309
|
-
|
|
8457
|
+
const priorityOrder = semanticPolicySourcePriority(source) - semanticPolicySourcePriority(current);
|
|
8458
|
+
if (priorityOrder < 0)
|
|
8459
|
+
continue;
|
|
8460
|
+
if (priorityOrder === 0 && versionOrder <= 0)
|
|
8310
8461
|
continue;
|
|
8311
8462
|
selected[existing.index] = {
|
|
8312
8463
|
...source,
|
|
8313
8464
|
resolvedOrder: current.resolvedOrder
|
|
8314
8465
|
};
|
|
8466
|
+
policySources.set(key, { index: existing.index, version, normalizedContent });
|
|
8315
8467
|
}
|
|
8316
8468
|
return selected;
|
|
8317
8469
|
}
|
|
@@ -8321,7 +8473,7 @@ function semanticPolicySourcePriority(source) {
|
|
|
8321
8473
|
priority += 4;
|
|
8322
8474
|
if (source.id === GLOBAL_AGENT_RULES_STANDARD_SLUG)
|
|
8323
8475
|
priority += 2;
|
|
8324
|
-
if (source.metadata?.["role"] ===
|
|
8476
|
+
if (source.metadata?.["role"] === AGENT_OPERATING_RULES_ROLE)
|
|
8325
8477
|
priority += 1;
|
|
8326
8478
|
return priority;
|
|
8327
8479
|
}
|
|
@@ -8810,7 +8962,7 @@ function planSessionRender(input) {
|
|
|
8810
8962
|
globs: rule.globs ?? [],
|
|
8811
8963
|
hash: rule.hash ?? null
|
|
8812
8964
|
})),
|
|
8813
|
-
renderedPayloadSha256:
|
|
8965
|
+
renderedPayloadSha256: sha2563(source.content),
|
|
8814
8966
|
provenance: source.provenance ?? null,
|
|
8815
8967
|
metadata: source.metadata ?? null
|
|
8816
8968
|
})),
|
|
@@ -8828,8 +8980,8 @@ function planSessionRender(input) {
|
|
|
8828
8980
|
...input.providerConfig ? {
|
|
8829
8981
|
providerConfig: {
|
|
8830
8982
|
sourceId: input.providerConfig.sourceId,
|
|
8831
|
-
selectedPayloadSha256:
|
|
8832
|
-
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ??
|
|
8983
|
+
selectedPayloadSha256: sha2563(input.providerConfig.content),
|
|
8984
|
+
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2563(input.providerConfig.content),
|
|
8833
8985
|
selected: !existsSync4(joinTarget(targetHome, adapter.configFile))
|
|
8834
8986
|
}
|
|
8835
8987
|
} : {},
|
|
@@ -8873,15 +9025,16 @@ function sourceFromFilePath(path, content, order = 0) {
|
|
|
8873
9025
|
}
|
|
8874
9026
|
function sourceFromConfig(config, order = 0, layer) {
|
|
8875
9027
|
const isAgentOperatingRules = config.slug === GLOBAL_AGENT_RULES_STANDARD_SLUG;
|
|
9028
|
+
const rules = isAgentOperatingRules ? resolveAgentOperatingRulesPayload(config.content) : null;
|
|
8876
9029
|
return {
|
|
8877
9030
|
id: config.slug,
|
|
8878
9031
|
label: config.name,
|
|
8879
|
-
content:
|
|
9032
|
+
content: rules ? rules.content : config.content,
|
|
8880
9033
|
layer: layer ?? (config.agent === "global" ? "global" : "agent"),
|
|
8881
9034
|
order,
|
|
8882
9035
|
path: config.target_path ?? undefined,
|
|
8883
|
-
provenance:
|
|
8884
|
-
...
|
|
9036
|
+
provenance: rules ? {
|
|
9037
|
+
...rules.provenance,
|
|
8885
9038
|
configSlug: config.slug,
|
|
8886
9039
|
configAgent: config.agent
|
|
8887
9040
|
} : {
|
|
@@ -8889,7 +9042,7 @@ function sourceFromConfig(config, order = 0, layer) {
|
|
|
8889
9042
|
configSlug: config.slug,
|
|
8890
9043
|
configAgent: config.agent
|
|
8891
9044
|
},
|
|
8892
|
-
metadata:
|
|
9045
|
+
metadata: rules ? { ...rules.metadata } : null,
|
|
8893
9046
|
nonOverridable: isAgentOperatingRules
|
|
8894
9047
|
};
|
|
8895
9048
|
}
|
|
@@ -8927,7 +9080,7 @@ function selectProfileConfigsForSessionRender(configs, tool) {
|
|
|
8927
9080
|
const selectedSources = [];
|
|
8928
9081
|
const equivalentSources = new Map;
|
|
8929
9082
|
for (const candidate of sources) {
|
|
8930
|
-
const key =
|
|
9083
|
+
const key = sha2563(candidate.source.content);
|
|
8931
9084
|
const existing = equivalentSources.get(key);
|
|
8932
9085
|
if (!existing) {
|
|
8933
9086
|
equivalentSources.set(key, {
|
|
@@ -9273,6 +9426,77 @@ function asStringArray(value) {
|
|
|
9273
9426
|
return value.filter((item) => typeof item === "string");
|
|
9274
9427
|
}
|
|
9275
9428
|
|
|
9429
|
+
// src/lib/session-render-ownership.ts
|
|
9430
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
9431
|
+
import { dirname as dirname3, join as join5, parse as parse3, relative as relative3, sep } from "path";
|
|
9432
|
+
var MANIFEST_ANCESTOR_LIMIT = 24;
|
|
9433
|
+
var MANAGED_PATH_SEGMENTS = SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS.map((managedPath) => managedPath.split("/").filter(Boolean));
|
|
9434
|
+
var manifestCache = new Map;
|
|
9435
|
+
function toSegments(absolutePath2) {
|
|
9436
|
+
return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
9437
|
+
}
|
|
9438
|
+
function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
9439
|
+
const segments = toSegments(absolutePath2);
|
|
9440
|
+
return MANAGED_PATH_SEGMENTS.some((managed) => {
|
|
9441
|
+
if (managed.length === 0 || managed.length > segments.length)
|
|
9442
|
+
return false;
|
|
9443
|
+
for (let start = 0;start + managed.length <= segments.length; start += 1) {
|
|
9444
|
+
if (managed.every((segment, offset) => segments[start + offset] === segment))
|
|
9445
|
+
return true;
|
|
9446
|
+
}
|
|
9447
|
+
return false;
|
|
9448
|
+
});
|
|
9449
|
+
}
|
|
9450
|
+
function readManifestRelativePaths(manifestPath) {
|
|
9451
|
+
let stats;
|
|
9452
|
+
try {
|
|
9453
|
+
if (!existsSync5(manifestPath))
|
|
9454
|
+
return null;
|
|
9455
|
+
stats = statSync3(manifestPath);
|
|
9456
|
+
} catch {
|
|
9457
|
+
return null;
|
|
9458
|
+
}
|
|
9459
|
+
const cached = manifestCache.get(manifestPath);
|
|
9460
|
+
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
|
|
9461
|
+
return cached.relativePaths;
|
|
9462
|
+
}
|
|
9463
|
+
let manifest;
|
|
9464
|
+
try {
|
|
9465
|
+
manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
|
|
9466
|
+
} catch {
|
|
9467
|
+
return null;
|
|
9468
|
+
}
|
|
9469
|
+
if (manifest?.schema !== SESSION_RENDER_SCHEMA || !Array.isArray(manifest.files))
|
|
9470
|
+
return null;
|
|
9471
|
+
const writerId = manifest.targetOwner?.writer?.id;
|
|
9472
|
+
if (writerId !== undefined && writerId !== SESSION_RENDERER_OWNER_ID)
|
|
9473
|
+
return null;
|
|
9474
|
+
const relativePaths = new Set(manifest.files.map((file) => file?.relativePath).filter((relativePath) => typeof relativePath === "string").map((relativePath) => relativePath.replaceAll("\\", "/")));
|
|
9475
|
+
manifestCache.set(manifestPath, { mtimeMs: stats.mtimeMs, size: stats.size, relativePaths });
|
|
9476
|
+
return relativePaths;
|
|
9477
|
+
}
|
|
9478
|
+
function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
9479
|
+
const root = parse3(absolutePath2).root;
|
|
9480
|
+
let home = dirname3(absolutePath2);
|
|
9481
|
+
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
9482
|
+
const manifestPath = join5(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
9483
|
+
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
9484
|
+
if (relativePaths) {
|
|
9485
|
+
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
9486
|
+
if (relativePaths.has(claimed))
|
|
9487
|
+
return true;
|
|
9488
|
+
}
|
|
9489
|
+
const parent = dirname3(home);
|
|
9490
|
+
if (parent === home || home === root)
|
|
9491
|
+
break;
|
|
9492
|
+
home = parent;
|
|
9493
|
+
}
|
|
9494
|
+
return false;
|
|
9495
|
+
}
|
|
9496
|
+
function sessionRenderOwnsPath(absolutePath2) {
|
|
9497
|
+
return pathIsSessionRenderManagedDir(absolutePath2) || sessionRenderManifestClaimsPath(absolutePath2);
|
|
9498
|
+
}
|
|
9499
|
+
|
|
9276
9500
|
// src/lib/apply.ts
|
|
9277
9501
|
function getConfigHome() {
|
|
9278
9502
|
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir3();
|
|
@@ -9291,14 +9515,14 @@ function normalizeTargetPath(p) {
|
|
|
9291
9515
|
let current = expanded;
|
|
9292
9516
|
const missingSegments = [];
|
|
9293
9517
|
while (true) {
|
|
9294
|
-
if (
|
|
9518
|
+
if (existsSync6(current)) {
|
|
9295
9519
|
try {
|
|
9296
9520
|
return resolve3(realpathSync2(current), ...missingSegments);
|
|
9297
9521
|
} catch {
|
|
9298
9522
|
return expanded;
|
|
9299
9523
|
}
|
|
9300
9524
|
}
|
|
9301
|
-
const parent =
|
|
9525
|
+
const parent = dirname4(current);
|
|
9302
9526
|
const name = basename4(current);
|
|
9303
9527
|
if (parent === current)
|
|
9304
9528
|
return expanded;
|
|
@@ -9320,11 +9544,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
9320
9544
|
throw new ConfigApplyError(`Antigravity rule file ${renderedTargetPath} is ${renderedContent.length} characters; split it before applying because Antigravity limits rule files to ${ANTIGRAVITY_RULE_FILE_CHAR_LIMIT} characters.`);
|
|
9321
9545
|
}
|
|
9322
9546
|
const path = expandPath(renderedTargetPath);
|
|
9323
|
-
const previousContent =
|
|
9547
|
+
const previousContent = existsSync6(path) ? readFileSync4(path, "utf-8") : null;
|
|
9324
9548
|
const changed = previousContent !== renderedContent;
|
|
9325
9549
|
if (!opts.dryRun) {
|
|
9326
|
-
const dir =
|
|
9327
|
-
if (!
|
|
9550
|
+
const dir = dirname4(path);
|
|
9551
|
+
if (!existsSync6(dir)) {
|
|
9328
9552
|
mkdirSync3(dir, { recursive: true });
|
|
9329
9553
|
}
|
|
9330
9554
|
if (previousContent !== null && changed) {
|
|
@@ -9627,35 +9851,37 @@ function sessionRendererOwnsTarget(targetPath, opts) {
|
|
|
9627
9851
|
return sessionRendererOwnsCanonicalTarget(canonicalApplyTargetPath(targetPath, opts), opts);
|
|
9628
9852
|
}
|
|
9629
9853
|
function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
9854
|
+
if (opts.allowSessionRendererOwned)
|
|
9855
|
+
return false;
|
|
9630
9856
|
const homes = new Set([
|
|
9631
9857
|
getConfigHome(),
|
|
9632
9858
|
opts.vars?.["HOME_DIR"]
|
|
9633
9859
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
9634
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
9860
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join6(home, ...relativePath.split("/"))))))
|
|
9635
9861
|
return true;
|
|
9636
|
-
return normalized
|
|
9862
|
+
return sessionRenderOwnsPath(normalized);
|
|
9637
9863
|
}
|
|
9638
9864
|
|
|
9639
9865
|
// src/lib/package-version.ts
|
|
9640
|
-
import { existsSync as
|
|
9641
|
-
import { dirname as
|
|
9866
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
9867
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
9642
9868
|
import { fileURLToPath } from "url";
|
|
9643
9869
|
var cached = null;
|
|
9644
9870
|
function getPackageVersion() {
|
|
9645
9871
|
if (cached)
|
|
9646
9872
|
return cached;
|
|
9647
9873
|
try {
|
|
9648
|
-
let dir =
|
|
9874
|
+
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
9649
9875
|
for (let i = 0;i < 8; i++) {
|
|
9650
|
-
const pkgPath =
|
|
9651
|
-
if (
|
|
9652
|
-
const pkg = JSON.parse(
|
|
9876
|
+
const pkgPath = join7(dir, "package.json");
|
|
9877
|
+
if (existsSync7(pkgPath)) {
|
|
9878
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
9653
9879
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
9654
9880
|
cached = pkg.version;
|
|
9655
9881
|
return cached;
|
|
9656
9882
|
}
|
|
9657
9883
|
}
|
|
9658
|
-
const parent =
|
|
9884
|
+
const parent = dirname5(dir);
|
|
9659
9885
|
if (parent === dir)
|
|
9660
9886
|
break;
|
|
9661
9887
|
dir = parent;
|
|
@@ -9711,11 +9937,11 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
9711
9937
|
continue;
|
|
9712
9938
|
knownTargets += 1;
|
|
9713
9939
|
const targetPath = expandPath(config.target_path);
|
|
9714
|
-
if (!
|
|
9940
|
+
if (!existsSync8(targetPath)) {
|
|
9715
9941
|
missingTargets += 1;
|
|
9716
9942
|
continue;
|
|
9717
9943
|
}
|
|
9718
|
-
const disk =
|
|
9944
|
+
const disk = readFileSync6(targetPath, "utf-8");
|
|
9719
9945
|
const { content: redactedDisk } = redactContent(disk, config.format);
|
|
9720
9946
|
if (redactedDisk !== config.content) {
|
|
9721
9947
|
driftedTargets += 1;
|
|
@@ -9858,16 +10084,16 @@ var PG_MIGRATIONS = [
|
|
|
9858
10084
|
`ALTER TABLE configs ADD COLUMN IF NOT EXISTS outputs TEXT NOT NULL DEFAULT '[]'`
|
|
9859
10085
|
];
|
|
9860
10086
|
// src/lib/session-apply.ts
|
|
9861
|
-
import { createHash as
|
|
10087
|
+
import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
|
|
9862
10088
|
import {
|
|
9863
|
-
existsSync as
|
|
10089
|
+
existsSync as existsSync9,
|
|
9864
10090
|
lstatSync as lstatSync2,
|
|
9865
10091
|
mkdirSync as mkdirSync4,
|
|
9866
|
-
readFileSync as
|
|
10092
|
+
readFileSync as readFileSync7,
|
|
9867
10093
|
readdirSync,
|
|
9868
|
-
statSync as
|
|
10094
|
+
statSync as statSync4
|
|
9869
10095
|
} from "fs";
|
|
9870
|
-
import { dirname as
|
|
10096
|
+
import { dirname as dirname6, isAbsolute as isAbsolute3, join as join8, parse as parse4, relative as relative4, resolve as resolve4 } from "path";
|
|
9871
10097
|
class SessionApplyError extends Error {
|
|
9872
10098
|
constructor(message) {
|
|
9873
10099
|
super(message);
|
|
@@ -9947,13 +10173,13 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
9947
10173
|
};
|
|
9948
10174
|
}
|
|
9949
10175
|
function ensureSessionTargetHome(targetHome) {
|
|
9950
|
-
if (!
|
|
10176
|
+
if (!existsSync9(targetHome))
|
|
9951
10177
|
mkdirSync4(targetHome, { recursive: true, mode: 448 });
|
|
9952
10178
|
assertSafeTargetHome(targetHome);
|
|
9953
10179
|
}
|
|
9954
10180
|
function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
9955
10181
|
const safeTargetHome = assertSafeTargetHome(targetHome);
|
|
9956
|
-
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(
|
|
10182
|
+
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative4(safeTargetHome, resolve4(manifestPath)), safeTargetHome) : resolve4(safeTargetHome, ".hasna", "session-render-manifest.json");
|
|
9957
10183
|
const checkedAt = new Date().toISOString();
|
|
9958
10184
|
const previousManifest = readPreviousManifest(resolvedManifestPath);
|
|
9959
10185
|
if (!previousManifest) {
|
|
@@ -9970,7 +10196,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
9970
10196
|
const drifted = [];
|
|
9971
10197
|
for (const file of previousManifest.files) {
|
|
9972
10198
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
9973
|
-
if (!
|
|
10199
|
+
if (!existsSync9(target)) {
|
|
9974
10200
|
missing.push({
|
|
9975
10201
|
path: target,
|
|
9976
10202
|
relativePath: file.relativePath,
|
|
@@ -9980,7 +10206,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
9980
10206
|
});
|
|
9981
10207
|
continue;
|
|
9982
10208
|
}
|
|
9983
|
-
const actualSha256 =
|
|
10209
|
+
const actualSha256 = sha2564(readFileSync7(target, "utf-8"));
|
|
9984
10210
|
if (actualSha256 !== file.sha256) {
|
|
9985
10211
|
drifted.push({
|
|
9986
10212
|
path: target,
|
|
@@ -10004,7 +10230,7 @@ function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
|
|
|
10004
10230
|
const snapshot = readSessionRenderSnapshot(snapshotPath);
|
|
10005
10231
|
const targetHome = assertSafeTargetHome(snapshot.targetHome);
|
|
10006
10232
|
const resolvedSnapshotPath = resolve4(snapshotPath);
|
|
10007
|
-
const snapshotRelativePath =
|
|
10233
|
+
const snapshotRelativePath = relative4(targetHome, resolvedSnapshotPath);
|
|
10008
10234
|
if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute3(snapshotRelativePath)) {
|
|
10009
10235
|
throw new SessionApplyError("Session snapshot must be stored inside its target home.");
|
|
10010
10236
|
}
|
|
@@ -10122,18 +10348,18 @@ function requiredRestoreHash(file) {
|
|
|
10122
10348
|
}
|
|
10123
10349
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
10124
10350
|
const resolved = resolve4(snapshotPath);
|
|
10125
|
-
if (!
|
|
10351
|
+
if (!existsSync9(resolved))
|
|
10126
10352
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
10127
10353
|
const stat = lstatSync2(resolved);
|
|
10128
10354
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
10129
10355
|
throw new SessionApplyError(`Session snapshot is not a regular file: ${snapshotPath}`);
|
|
10130
10356
|
}
|
|
10131
|
-
if (
|
|
10357
|
+
if (statSync4(resolved).size > 32 * 1024 * 1024) {
|
|
10132
10358
|
throw new SessionApplyError(`Session snapshot exceeds the 32 MiB restore limit: ${snapshotPath}`);
|
|
10133
10359
|
}
|
|
10134
10360
|
let parsed;
|
|
10135
10361
|
try {
|
|
10136
|
-
parsed = JSON.parse(
|
|
10362
|
+
parsed = JSON.parse(readFileSync7(resolved, "utf8"));
|
|
10137
10363
|
} catch {
|
|
10138
10364
|
throw new SessionApplyError(`Session snapshot is not valid JSON: ${snapshotPath}`);
|
|
10139
10365
|
}
|
|
@@ -10155,7 +10381,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
10155
10381
|
const previousManifest = snapshot.previousManifest;
|
|
10156
10382
|
const previousFiles = new Map;
|
|
10157
10383
|
for (const file of snapshot.files) {
|
|
10158
|
-
if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" ||
|
|
10384
|
+
if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha2564(file.content) !== file.sha256) {
|
|
10159
10385
|
throw new SessionApplyError(`Session snapshot previous file metadata is invalid: ${snapshotPath}`);
|
|
10160
10386
|
}
|
|
10161
10387
|
resolveSnapshotFilePath(file.relativePath, file.path, targetHome);
|
|
@@ -10203,7 +10429,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
10203
10429
|
function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
|
|
10204
10430
|
assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
|
|
10205
10431
|
const manifestPath = resolve4(snapshot.manifestPath);
|
|
10206
|
-
const manifestRelativePath =
|
|
10432
|
+
const manifestRelativePath = relative4(targetHome, manifestPath).replaceAll("\\", "/");
|
|
10207
10433
|
resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
|
|
10208
10434
|
const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
|
|
10209
10435
|
if (manifestSha256 === null) {
|
|
@@ -10211,7 +10437,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
10211
10437
|
}
|
|
10212
10438
|
let parsedManifest;
|
|
10213
10439
|
try {
|
|
10214
|
-
parsedManifest = JSON.parse(
|
|
10440
|
+
parsedManifest = JSON.parse(readFileSync7(manifestPath, "utf8"));
|
|
10215
10441
|
} catch {
|
|
10216
10442
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is not valid JSON: ${snapshotPath}`);
|
|
10217
10443
|
}
|
|
@@ -10303,15 +10529,15 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
10303
10529
|
if (!Number.isFinite(createdAtMs)) {
|
|
10304
10530
|
throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
|
|
10305
10531
|
}
|
|
10306
|
-
for (const entry of readdirSync(
|
|
10307
|
-
const candidatePath = resolve4(
|
|
10532
|
+
for (const entry of readdirSync(dirname6(snapshotPath))) {
|
|
10533
|
+
const candidatePath = resolve4(dirname6(snapshotPath), entry);
|
|
10308
10534
|
if (candidatePath === resolve4(snapshotPath) || !entry.endsWith(".json"))
|
|
10309
10535
|
continue;
|
|
10310
10536
|
const candidateStat = lstatSync2(candidatePath);
|
|
10311
10537
|
if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
|
|
10312
10538
|
continue;
|
|
10313
10539
|
try {
|
|
10314
|
-
const candidate = JSON.parse(
|
|
10540
|
+
const candidate = JSON.parse(readFileSync7(candidatePath, "utf8"));
|
|
10315
10541
|
const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
|
|
10316
10542
|
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve4(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
|
|
10317
10543
|
throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
|
|
@@ -10371,7 +10597,7 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
10371
10597
|
return "create";
|
|
10372
10598
|
}
|
|
10373
10599
|
if (file.role === "manifest" && previousManifest) {
|
|
10374
|
-
const previousManifestSha256 =
|
|
10600
|
+
const previousManifestSha256 = sha2564(`${JSON.stringify(previousManifest, null, 2)}
|
|
10375
10601
|
`);
|
|
10376
10602
|
if (previousManifestSha256 !== file.sha256) {
|
|
10377
10603
|
throw new SessionApplyError(`Cannot infer legacy v1 manifest action without a before-image: ${file.relativePath}`);
|
|
@@ -10389,8 +10615,8 @@ function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
|
10389
10615
|
}
|
|
10390
10616
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
10391
10617
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
10392
|
-
const previousContent =
|
|
10393
|
-
const previousSha256 = previousContent === null ? null :
|
|
10618
|
+
const previousContent = existsSync9(target) ? readFileSync7(target, "utf-8") : null;
|
|
10619
|
+
const previousSha256 = previousContent === null ? null : sha2564(previousContent);
|
|
10394
10620
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
10395
10621
|
const changed = previousContent !== file.content;
|
|
10396
10622
|
if (previousContent !== null && !options.force && !previouslyManaged) {
|
|
@@ -10472,10 +10698,10 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
10472
10698
|
}
|
|
10473
10699
|
function planStaleFileResult(file, targetHome, options) {
|
|
10474
10700
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
10475
|
-
if (!
|
|
10701
|
+
if (!existsSync9(target))
|
|
10476
10702
|
return null;
|
|
10477
|
-
const previousContent =
|
|
10478
|
-
const previousSha256 =
|
|
10703
|
+
const previousContent = readFileSync7(target, "utf-8");
|
|
10704
|
+
const previousSha256 = sha2564(previousContent);
|
|
10479
10705
|
if (!options.force && previousSha256 !== file.sha256) {
|
|
10480
10706
|
return {
|
|
10481
10707
|
path: target,
|
|
@@ -10520,7 +10746,7 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
|
|
|
10520
10746
|
}
|
|
10521
10747
|
function resolvePlannedFilePath(plan, file, targetHome) {
|
|
10522
10748
|
const target = resolve4(targetHome, ...file.relativePath.split("/"));
|
|
10523
|
-
const rel =
|
|
10749
|
+
const rel = relative4(targetHome, target);
|
|
10524
10750
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute3(rel)) {
|
|
10525
10751
|
throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
|
|
10526
10752
|
}
|
|
@@ -10532,7 +10758,7 @@ function resolvePlannedFilePath(plan, file, targetHome) {
|
|
|
10532
10758
|
}
|
|
10533
10759
|
function resolveManifestRelativePath(relativePath, targetHome) {
|
|
10534
10760
|
const target = resolve4(targetHome, ...relativePath.split(/[\\/]+/));
|
|
10535
|
-
const rel =
|
|
10761
|
+
const rel = relative4(targetHome, target);
|
|
10536
10762
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute3(rel)) {
|
|
10537
10763
|
throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
|
|
10538
10764
|
}
|
|
@@ -10540,10 +10766,10 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
10540
10766
|
return target;
|
|
10541
10767
|
}
|
|
10542
10768
|
function readPreviousManifest(path) {
|
|
10543
|
-
if (!
|
|
10769
|
+
if (!existsSync9(path))
|
|
10544
10770
|
return null;
|
|
10545
10771
|
try {
|
|
10546
|
-
const parsed = JSON.parse(
|
|
10772
|
+
const parsed = JSON.parse(readFileSync7(path, "utf-8"));
|
|
10547
10773
|
if (parsed.schema !== SESSION_RENDER_SCHEMA)
|
|
10548
10774
|
return null;
|
|
10549
10775
|
if (!Array.isArray(parsed.files))
|
|
@@ -10577,18 +10803,18 @@ function applyPlannedFile(plan, file, targetHome, resultsByPath, coordination, a
|
|
|
10577
10803
|
function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
10578
10804
|
const actualHash = currentSessionFileHash(path, targetHome);
|
|
10579
10805
|
if (actualHash !== expectedHash) {
|
|
10580
|
-
throw new SessionApplyError(`Session apply path changed after planning: ${
|
|
10806
|
+
throw new SessionApplyError(`Session apply path changed after planning: ${relative4(targetHome, path)}`);
|
|
10581
10807
|
}
|
|
10582
10808
|
}
|
|
10583
10809
|
function currentSessionFileHash(path, targetHome) {
|
|
10584
10810
|
assertNoSymlinkSegments2(targetHome, path);
|
|
10585
|
-
if (!
|
|
10811
|
+
if (!existsSync9(path))
|
|
10586
10812
|
return null;
|
|
10587
10813
|
const stat = lstatSync2(path);
|
|
10588
10814
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
10589
10815
|
throw new SessionApplyError(`Session apply path is not a regular file: ${path}`);
|
|
10590
10816
|
}
|
|
10591
|
-
return
|
|
10817
|
+
return sha2564(readFileSync7(path, "utf-8"));
|
|
10592
10818
|
}
|
|
10593
10819
|
function requiredPreviousHash(result) {
|
|
10594
10820
|
if (result.previousSha256 === null) {
|
|
@@ -10597,13 +10823,13 @@ function requiredPreviousHash(result) {
|
|
|
10597
10823
|
return result.previousSha256;
|
|
10598
10824
|
}
|
|
10599
10825
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
10600
|
-
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) =>
|
|
10601
|
-
const content =
|
|
10826
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync9(result.path)).map((result) => {
|
|
10827
|
+
const content = readFileSync7(result.path, "utf-8");
|
|
10602
10828
|
return {
|
|
10603
10829
|
path: result.path,
|
|
10604
10830
|
relativePath: result.relativePath,
|
|
10605
10831
|
role: result.role,
|
|
10606
|
-
sha256:
|
|
10832
|
+
sha256: sha2564(content),
|
|
10607
10833
|
content
|
|
10608
10834
|
};
|
|
10609
10835
|
});
|
|
@@ -10654,42 +10880,42 @@ function assertSafeTargetHome(targetHome) {
|
|
|
10654
10880
|
if (!isAbsolute3(targetHome))
|
|
10655
10881
|
throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
|
|
10656
10882
|
const normalized = resolve4(targetHome);
|
|
10657
|
-
if (normalized ===
|
|
10883
|
+
if (normalized === parse4(normalized).root) {
|
|
10658
10884
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
10659
10885
|
}
|
|
10660
10886
|
assertNoSymlinkAncestors2(normalized);
|
|
10661
|
-
if (
|
|
10887
|
+
if (existsSync9(normalized) && lstatSync2(normalized).isSymbolicLink()) {
|
|
10662
10888
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
10663
10889
|
}
|
|
10664
10890
|
return normalized;
|
|
10665
10891
|
}
|
|
10666
10892
|
function assertNoSymlinkSegments2(root, target) {
|
|
10667
10893
|
assertNoSymlinkAncestors2(root);
|
|
10668
|
-
const rel =
|
|
10894
|
+
const rel = relative4(root, target);
|
|
10669
10895
|
let current = root;
|
|
10670
10896
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
10671
|
-
current =
|
|
10672
|
-
if (
|
|
10897
|
+
current = join8(current, segment);
|
|
10898
|
+
if (existsSync9(current) && lstatSync2(current).isSymbolicLink()) {
|
|
10673
10899
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
10674
10900
|
}
|
|
10675
10901
|
}
|
|
10676
10902
|
}
|
|
10677
10903
|
function assertNoSymlinkAncestors2(path) {
|
|
10678
10904
|
const normalized = resolve4(path);
|
|
10679
|
-
const parsed =
|
|
10905
|
+
const parsed = parse4(normalized);
|
|
10680
10906
|
let current = parsed.root;
|
|
10681
|
-
const rel =
|
|
10907
|
+
const rel = relative4(parsed.root, normalized);
|
|
10682
10908
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
10683
|
-
current =
|
|
10684
|
-
if (!
|
|
10909
|
+
current = join8(current, segment);
|
|
10910
|
+
if (!existsSync9(current))
|
|
10685
10911
|
return;
|
|
10686
10912
|
if (lstatSync2(current).isSymbolicLink()) {
|
|
10687
10913
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
10688
10914
|
}
|
|
10689
10915
|
}
|
|
10690
10916
|
}
|
|
10691
|
-
function
|
|
10692
|
-
return
|
|
10917
|
+
function sha2564(content) {
|
|
10918
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
10693
10919
|
}
|
|
10694
10920
|
// src/lib/project-dashboard-standard.ts
|
|
10695
10921
|
var PROJECT_DASHBOARD_STANDARD_SLUG = "agent-managed-project-dashboard-standard";
|
|
@@ -10987,12 +11213,12 @@ async function ensureDangerousOperationGuardStandardConfig(store = resolveConfig
|
|
|
10987
11213
|
}
|
|
10988
11214
|
}
|
|
10989
11215
|
// src/lib/sync.ts
|
|
10990
|
-
import { existsSync as
|
|
10991
|
-
import { basename as basename5, extname as extname3, join as
|
|
11216
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
11217
|
+
import { basename as basename5, extname as extname3, join as join10 } from "path";
|
|
10992
11218
|
|
|
10993
11219
|
// src/lib/sync-dir.ts
|
|
10994
|
-
import { existsSync as
|
|
10995
|
-
import { join as
|
|
11220
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
11221
|
+
import { join as join9, relative as relative5 } from "path";
|
|
10996
11222
|
import { homedir as homedir4 } from "os";
|
|
10997
11223
|
var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
10998
11224
|
function shouldSkip(p) {
|
|
@@ -11001,9 +11227,9 @@ function shouldSkip(p) {
|
|
|
11001
11227
|
async function syncFromDir(dir, opts = {}) {
|
|
11002
11228
|
const store = opts.store ?? resolveConfigStore();
|
|
11003
11229
|
const absDir = expandPath(dir);
|
|
11004
|
-
if (!
|
|
11230
|
+
if (!existsSync10(absDir))
|
|
11005
11231
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
11006
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) =>
|
|
11232
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join9(absDir, f)).filter((f) => statSync5(f).isFile());
|
|
11007
11233
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
11008
11234
|
const home = homedir4();
|
|
11009
11235
|
const allConfigs = await store.listConfigs();
|
|
@@ -11013,7 +11239,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
11013
11239
|
continue;
|
|
11014
11240
|
}
|
|
11015
11241
|
try {
|
|
11016
|
-
const content =
|
|
11242
|
+
const content = readFileSync8(file, "utf-8");
|
|
11017
11243
|
if (content.length > 500000) {
|
|
11018
11244
|
result.skipped.push(file + " (too large)");
|
|
11019
11245
|
continue;
|
|
@@ -11022,7 +11248,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
11022
11248
|
const existing = allConfigs.find((c) => c.target_path === targetPath);
|
|
11023
11249
|
if (!existing) {
|
|
11024
11250
|
if (!opts.dryRun)
|
|
11025
|
-
await store.createConfig({ name:
|
|
11251
|
+
await store.createConfig({ name: relative5(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content });
|
|
11026
11252
|
result.added++;
|
|
11027
11253
|
} else if (existing.content !== content) {
|
|
11028
11254
|
if (!opts.dryRun)
|
|
@@ -11063,7 +11289,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
11063
11289
|
}
|
|
11064
11290
|
function walkDir(dir, files = []) {
|
|
11065
11291
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
11066
|
-
const full =
|
|
11292
|
+
const full = join9(dir, entry.name);
|
|
11067
11293
|
if (shouldSkip(full))
|
|
11068
11294
|
continue;
|
|
11069
11295
|
if (entry.isDirectory())
|
|
@@ -11115,7 +11341,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
11115
11341
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
11116
11342
|
}
|
|
11117
11343
|
function hasClaudePromptSource() {
|
|
11118
|
-
return
|
|
11344
|
+
return existsSync11(expandPath("~/.claude/CLAUDE.md"));
|
|
11119
11345
|
}
|
|
11120
11346
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
11121
11347
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -11123,7 +11349,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
11123
11349
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
11124
11350
|
return false;
|
|
11125
11351
|
const stem = basename5(absoluteTargetPath, ".mdc");
|
|
11126
|
-
return
|
|
11352
|
+
return existsSync11(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync11(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
11127
11353
|
}
|
|
11128
11354
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
11129
11355
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -11186,11 +11412,11 @@ async function syncProject(opts) {
|
|
|
11186
11412
|
const allConfigs = await store.listConfigs();
|
|
11187
11413
|
const machine = detectMachineContext();
|
|
11188
11414
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
11189
|
-
const abs =
|
|
11190
|
-
if (!
|
|
11415
|
+
const abs = join10(absDir, pf.file);
|
|
11416
|
+
if (!existsSync11(abs))
|
|
11191
11417
|
continue;
|
|
11192
11418
|
try {
|
|
11193
|
-
const rawContent =
|
|
11419
|
+
const rawContent = readFileSync9(abs, "utf-8");
|
|
11194
11420
|
if (rawContent.length > 500000) {
|
|
11195
11421
|
result.skipped.push(pf.file);
|
|
11196
11422
|
continue;
|
|
@@ -11219,15 +11445,15 @@ async function syncProject(opts) {
|
|
|
11219
11445
|
}
|
|
11220
11446
|
}
|
|
11221
11447
|
for (const ruleDir of [
|
|
11222
|
-
{ dir:
|
|
11223
|
-
{ dir:
|
|
11448
|
+
{ dir: join10(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
11449
|
+
{ dir: join10(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" }
|
|
11224
11450
|
]) {
|
|
11225
|
-
if (!
|
|
11451
|
+
if (!existsSync11(ruleDir.dir))
|
|
11226
11452
|
continue;
|
|
11227
11453
|
const mdFiles = readdirSync3(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
11228
11454
|
for (const f of mdFiles) {
|
|
11229
|
-
const abs =
|
|
11230
|
-
const raw =
|
|
11455
|
+
const abs = join10(ruleDir.dir, f);
|
|
11456
|
+
const raw = readFileSync9(abs, "utf-8");
|
|
11231
11457
|
const redacted = redactContent(raw, "markdown");
|
|
11232
11458
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
11233
11459
|
const content = machineAware.content;
|
|
@@ -11266,20 +11492,20 @@ async function syncKnown(opts = {}) {
|
|
|
11266
11492
|
for (const known of targets) {
|
|
11267
11493
|
if (known.rulesDir) {
|
|
11268
11494
|
const absDir = expandPath(known.rulesDir);
|
|
11269
|
-
if (!
|
|
11495
|
+
if (!existsSync11(absDir)) {
|
|
11270
11496
|
result.skipped.push(known.rulesDir);
|
|
11271
11497
|
continue;
|
|
11272
11498
|
}
|
|
11273
11499
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
11274
11500
|
const ruleFiles = readdirSync3(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
11275
11501
|
for (const f of ruleFiles) {
|
|
11276
|
-
const abs2 =
|
|
11502
|
+
const abs2 = join10(absDir, f);
|
|
11277
11503
|
const targetPath = abs2.replace(home, "~");
|
|
11278
11504
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
11279
11505
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
11280
11506
|
continue;
|
|
11281
11507
|
}
|
|
11282
|
-
const raw =
|
|
11508
|
+
const raw = readFileSync9(abs2, "utf-8");
|
|
11283
11509
|
const redacted = redactContent(raw, "markdown");
|
|
11284
11510
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
11285
11511
|
const content = machineAware.content;
|
|
@@ -11307,12 +11533,12 @@ async function syncKnown(opts = {}) {
|
|
|
11307
11533
|
continue;
|
|
11308
11534
|
}
|
|
11309
11535
|
const abs = expandPath(known.path);
|
|
11310
|
-
if (!
|
|
11536
|
+
if (!existsSync11(abs)) {
|
|
11311
11537
|
result.skipped.push(known.path);
|
|
11312
11538
|
continue;
|
|
11313
11539
|
}
|
|
11314
11540
|
try {
|
|
11315
|
-
const rawContent =
|
|
11541
|
+
const rawContent = readFileSync9(abs, "utf-8");
|
|
11316
11542
|
if (rawContent.length > 500000) {
|
|
11317
11543
|
result.skipped.push(known.path + " (too large)");
|
|
11318
11544
|
continue;
|
|
@@ -11400,9 +11626,9 @@ async function syncToDisk(opts = {}) {
|
|
|
11400
11626
|
}
|
|
11401
11627
|
function buildDiff(expectedContent, targetPath) {
|
|
11402
11628
|
const path = expandPath(targetPath);
|
|
11403
|
-
if (!
|
|
11629
|
+
if (!existsSync11(path))
|
|
11404
11630
|
return `(file not found on disk: ${path})`;
|
|
11405
|
-
const diskContent =
|
|
11631
|
+
const diskContent = readFileSync9(path, "utf-8");
|
|
11406
11632
|
if (diskContent === expectedContent)
|
|
11407
11633
|
return "(no diff \u2014 identical)";
|
|
11408
11634
|
const stored = expectedContent.split(`
|
|
@@ -11516,15 +11742,15 @@ function detectFormat(filePath) {
|
|
|
11516
11742
|
return "text";
|
|
11517
11743
|
}
|
|
11518
11744
|
// src/lib/export.ts
|
|
11519
|
-
import { existsSync as
|
|
11520
|
-
import { join as
|
|
11745
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
11746
|
+
import { join as join11, resolve as resolve5 } from "path";
|
|
11521
11747
|
import { tmpdir } from "os";
|
|
11522
11748
|
async function exportConfigs(outputPath, opts = {}) {
|
|
11523
11749
|
const store = opts.store ?? resolveConfigStore();
|
|
11524
11750
|
const configs = await store.listConfigs(opts.filter);
|
|
11525
11751
|
const absOutput = resolve5(outputPath);
|
|
11526
|
-
const tmpDir =
|
|
11527
|
-
const contentsDir =
|
|
11752
|
+
const tmpDir = join11(tmpdir(), `configs-export-${Date.now()}`);
|
|
11753
|
+
const contentsDir = join11(tmpDir, "contents");
|
|
11528
11754
|
try {
|
|
11529
11755
|
mkdirSync5(contentsDir, { recursive: true });
|
|
11530
11756
|
const manifest = {
|
|
@@ -11532,10 +11758,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
11532
11758
|
exported_at: new Date().toISOString(),
|
|
11533
11759
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
11534
11760
|
};
|
|
11535
|
-
writeFileSync3(
|
|
11761
|
+
writeFileSync3(join11(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
11536
11762
|
for (const config of configs) {
|
|
11537
11763
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
11538
|
-
writeFileSync3(
|
|
11764
|
+
writeFileSync3(join11(contentsDir, fileName), config.content, "utf-8");
|
|
11539
11765
|
}
|
|
11540
11766
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
11541
11767
|
stdout: "pipe",
|
|
@@ -11548,20 +11774,20 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
11548
11774
|
}
|
|
11549
11775
|
return { path: absOutput, count: configs.length };
|
|
11550
11776
|
} finally {
|
|
11551
|
-
if (
|
|
11777
|
+
if (existsSync12(tmpDir)) {
|
|
11552
11778
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
11553
11779
|
}
|
|
11554
11780
|
}
|
|
11555
11781
|
}
|
|
11556
11782
|
// src/lib/import.ts
|
|
11557
|
-
import { existsSync as
|
|
11558
|
-
import { join as
|
|
11783
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, rmSync as rmSync4 } from "fs";
|
|
11784
|
+
import { join as join12, resolve as resolve6 } from "path";
|
|
11559
11785
|
import { tmpdir as tmpdir2 } from "os";
|
|
11560
11786
|
async function importConfigs(bundlePath, opts = {}) {
|
|
11561
11787
|
const store = opts.store ?? resolveConfigStore();
|
|
11562
11788
|
const conflict = opts.conflict ?? "skip";
|
|
11563
11789
|
const absPath = resolve6(bundlePath);
|
|
11564
|
-
const tmpDir =
|
|
11790
|
+
const tmpDir = join12(tmpdir2(), `configs-import-${Date.now()}`);
|
|
11565
11791
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
11566
11792
|
try {
|
|
11567
11793
|
mkdirSync6(tmpDir, { recursive: true });
|
|
@@ -11574,15 +11800,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
11574
11800
|
const stderr = await new Response(proc.stderr).text();
|
|
11575
11801
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
11576
11802
|
}
|
|
11577
|
-
const manifestPath =
|
|
11578
|
-
if (!
|
|
11803
|
+
const manifestPath = join12(tmpDir, "manifest.json");
|
|
11804
|
+
if (!existsSync13(manifestPath))
|
|
11579
11805
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
11580
|
-
const manifest = JSON.parse(
|
|
11806
|
+
const manifest = JSON.parse(readFileSync10(manifestPath, "utf-8"));
|
|
11581
11807
|
for (const meta of manifest.configs) {
|
|
11582
11808
|
try {
|
|
11583
11809
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
11584
|
-
const contentFile =
|
|
11585
|
-
const content =
|
|
11810
|
+
const contentFile = join12(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
11811
|
+
const content = existsSync13(contentFile) ? readFileSync10(contentFile, "utf-8") : "";
|
|
11586
11812
|
let existing = null;
|
|
11587
11813
|
try {
|
|
11588
11814
|
existing = await store.getConfig(meta.slug);
|
|
@@ -11616,16 +11842,16 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
11616
11842
|
}
|
|
11617
11843
|
return result;
|
|
11618
11844
|
} finally {
|
|
11619
|
-
if (
|
|
11845
|
+
if (existsSync13(tmpDir)) {
|
|
11620
11846
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
11621
11847
|
}
|
|
11622
11848
|
}
|
|
11623
11849
|
}
|
|
11624
11850
|
// src/lib/package-manager-guard.ts
|
|
11625
11851
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
11626
|
-
import { existsSync as
|
|
11852
|
+
import { existsSync as existsSync14, lstatSync as lstatSync3, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
11627
11853
|
import { homedir as homedir5 } from "os";
|
|
11628
|
-
import { basename as basename6, dirname as
|
|
11854
|
+
import { basename as basename6, dirname as dirname7, isAbsolute as isAbsolute4, join as join13, relative as relative6, resolve as resolve7 } from "path";
|
|
11629
11855
|
var SKIP_DIRS = new Set([
|
|
11630
11856
|
".git",
|
|
11631
11857
|
"node_modules",
|
|
@@ -11667,7 +11893,7 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
11667
11893
|
const findings = [];
|
|
11668
11894
|
let scannedFiles = 0;
|
|
11669
11895
|
for (const root of roots) {
|
|
11670
|
-
if (!
|
|
11896
|
+
if (!existsSync14(root))
|
|
11671
11897
|
continue;
|
|
11672
11898
|
const stat = lstatSync3(root);
|
|
11673
11899
|
if (stat.isFile()) {
|
|
@@ -11677,14 +11903,14 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
11677
11903
|
if (text === null)
|
|
11678
11904
|
continue;
|
|
11679
11905
|
scannedFiles++;
|
|
11680
|
-
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root),
|
|
11906
|
+
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname7(root)));
|
|
11681
11907
|
continue;
|
|
11682
11908
|
}
|
|
11683
11909
|
if (!stat.isDirectory())
|
|
11684
11910
|
continue;
|
|
11685
11911
|
const tracked = trackedFiles(root);
|
|
11686
11912
|
for (const file of collectRepoFiles(root)) {
|
|
11687
|
-
const rel = toPosix(
|
|
11913
|
+
const rel = toPosix(relative6(root, file));
|
|
11688
11914
|
const isTracked = tracked.has(rel);
|
|
11689
11915
|
const text = readTextFile(file);
|
|
11690
11916
|
if (text === null)
|
|
@@ -11696,8 +11922,8 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
11696
11922
|
if (options.includeHome) {
|
|
11697
11923
|
const home = homedir5();
|
|
11698
11924
|
for (const name of HOME_FILES) {
|
|
11699
|
-
const file =
|
|
11700
|
-
if (!
|
|
11925
|
+
const file = join13(home, name);
|
|
11926
|
+
if (!existsSync14(file))
|
|
11701
11927
|
continue;
|
|
11702
11928
|
const text = readTextFile(file);
|
|
11703
11929
|
if (text === null)
|
|
@@ -11721,12 +11947,12 @@ function collectRepoFiles(root) {
|
|
|
11721
11947
|
if (entry.isDirectory()) {
|
|
11722
11948
|
if (SKIP_DIRS.has(entry.name))
|
|
11723
11949
|
continue;
|
|
11724
|
-
visit(
|
|
11950
|
+
visit(join13(dir, entry.name));
|
|
11725
11951
|
continue;
|
|
11726
11952
|
}
|
|
11727
11953
|
if (!entry.isFile())
|
|
11728
11954
|
continue;
|
|
11729
|
-
const file =
|
|
11955
|
+
const file = join13(dir, entry.name);
|
|
11730
11956
|
if (shouldScanRepoFile(file))
|
|
11731
11957
|
out.push(file);
|
|
11732
11958
|
}
|
|
@@ -11764,7 +11990,7 @@ function readTextFile(file) {
|
|
|
11764
11990
|
const stat = lstatSync3(file);
|
|
11765
11991
|
if (!stat.isFile() || stat.size > 5000000)
|
|
11766
11992
|
return null;
|
|
11767
|
-
const buf =
|
|
11993
|
+
const buf = readFileSync11(file);
|
|
11768
11994
|
if (buf.includes(0))
|
|
11769
11995
|
return null;
|
|
11770
11996
|
return buf.toString("utf-8");
|
|
@@ -11964,11 +12190,11 @@ function trackedFiles(root) {
|
|
|
11964
12190
|
}
|
|
11965
12191
|
function isTrackedFile(file) {
|
|
11966
12192
|
try {
|
|
11967
|
-
const repoRoot = execFileSync2("git", ["-C",
|
|
12193
|
+
const repoRoot = execFileSync2("git", ["-C", dirname7(file), "rev-parse", "--show-toplevel"], {
|
|
11968
12194
|
encoding: "utf-8",
|
|
11969
12195
|
stdio: ["ignore", "pipe", "ignore"]
|
|
11970
12196
|
}).trim();
|
|
11971
|
-
const rel = toPosix(
|
|
12197
|
+
const rel = toPosix(relative6(repoRoot, file));
|
|
11972
12198
|
execFileSync2("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", rel], {
|
|
11973
12199
|
stdio: ["ignore", "ignore", "ignore"]
|
|
11974
12200
|
});
|
|
@@ -11996,11 +12222,11 @@ function stripInlineComment(value) {
|
|
|
11996
12222
|
function displayPath(file, root) {
|
|
11997
12223
|
const home = homedir5();
|
|
11998
12224
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
11999
|
-
return "~/" + toPosix(
|
|
12225
|
+
return "~/" + toPosix(relative6(home, file));
|
|
12000
12226
|
if (isAbsolute4(root) && file.startsWith(root + "/"))
|
|
12001
|
-
return toPosix(
|
|
12227
|
+
return toPosix(relative6(root, file));
|
|
12002
12228
|
if (file === home || file.startsWith(home + "/"))
|
|
12003
|
-
return "~/" + toPosix(
|
|
12229
|
+
return "~/" + toPosix(relative6(home, file));
|
|
12004
12230
|
return file;
|
|
12005
12231
|
}
|
|
12006
12232
|
function toPosix(path) {
|
|
@@ -12029,6 +12255,7 @@ export {
|
|
|
12029
12255
|
resolveProfileVariables,
|
|
12030
12256
|
resolveConfigStore,
|
|
12031
12257
|
resolveCloudConfig,
|
|
12258
|
+
resolveAgentOperatingRulesPayload,
|
|
12032
12259
|
renderTemplatePreview,
|
|
12033
12260
|
renderTemplate,
|
|
12034
12261
|
renderMachineAwareContentPreview,
|
|
@@ -12039,6 +12266,7 @@ export {
|
|
|
12039
12266
|
planProjectContext,
|
|
12040
12267
|
parseTemplateVars,
|
|
12041
12268
|
parseProjectContextBundle,
|
|
12269
|
+
parseAgentOperatingRulesVersion,
|
|
12042
12270
|
now,
|
|
12043
12271
|
normalizeOsFamily,
|
|
12044
12272
|
machineContextToVariables,
|
|
@@ -12063,6 +12291,7 @@ export {
|
|
|
12063
12291
|
currentHostname2 as currentHostname,
|
|
12064
12292
|
currentArch2 as currentArch,
|
|
12065
12293
|
computeProjectContextSourceHash,
|
|
12294
|
+
compareAgentOperatingRulesVersions,
|
|
12066
12295
|
cleanSessionPathInput,
|
|
12067
12296
|
checkSessionRenderDrift,
|
|
12068
12297
|
buildOpenCodeAgentsMd,
|
|
@@ -12118,5 +12347,7 @@ export {
|
|
|
12118
12347
|
CONFIG_FORMATS,
|
|
12119
12348
|
CONFIG_CATEGORIES,
|
|
12120
12349
|
CONFIG_AGENTS,
|
|
12121
|
-
CODEWITH_NATIVE_IMPORTS_ENV
|
|
12350
|
+
CODEWITH_NATIVE_IMPORTS_ENV,
|
|
12351
|
+
AGENT_OPERATING_RULES_SENTINEL_PATTERN,
|
|
12352
|
+
AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY
|
|
12122
12353
|
};
|