@hasna/instructions 0.5.2 → 0.5.4
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 +8 -3
- package/dist/cli/index.js +429 -316
- package/dist/index.js +377 -268
- package/dist/lib/app-home.d.ts +45 -0
- package/dist/lib/app-home.d.ts.map +1 -0
- package/dist/lib/app-home.test.d.ts +2 -0
- package/dist/lib/app-home.test.d.ts.map +1 -0
- package/dist/lib/cursor-authority.d.ts +11 -2
- package/dist/lib/cursor-authority.d.ts.map +1 -1
- package/dist/lib/raw-store-root.d.ts +12 -8
- package/dist/lib/raw-store-root.d.ts.map +1 -1
- package/dist/lib/station-profile.d.ts.map +1 -1
- package/dist/mcp/index.js +194 -74
- package/dist/server/index.js +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -103,8 +103,8 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
103
103
|
|
|
104
104
|
// src/db/database.ts
|
|
105
105
|
import { Database } from "bun:sqlite";
|
|
106
|
-
import { existsSync, mkdirSync, rmSync } from "fs";
|
|
107
|
-
import { join as
|
|
106
|
+
import { existsSync as existsSync2, mkdirSync, rmSync } from "fs";
|
|
107
|
+
import { join as join3 } from "path";
|
|
108
108
|
import { randomUUID } from "crypto";
|
|
109
109
|
|
|
110
110
|
// src/lib/retired-storage-mode.ts
|
|
@@ -129,11 +129,111 @@ function assertNoLegacyStorageMode(env = process.env) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// src/lib/raw-store-root.ts
|
|
132
|
+
import { resolve as resolve2 } from "path";
|
|
133
|
+
|
|
134
|
+
// src/lib/app-home.ts
|
|
135
|
+
import { existsSync } from "fs";
|
|
136
|
+
import { homedir as homedir2 } from "os";
|
|
137
|
+
import { join as join2, resolve } from "path";
|
|
138
|
+
|
|
139
|
+
// ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
|
|
132
140
|
import { homedir } from "os";
|
|
133
|
-
import { join
|
|
141
|
+
import { join } from "path";
|
|
142
|
+
var KIND_ENV = {
|
|
143
|
+
config: "HASNA_CONFIG_HOME",
|
|
144
|
+
data: "HASNA_DATA_HOME",
|
|
145
|
+
state: "HASNA_STATE_HOME",
|
|
146
|
+
cache: "HASNA_CACHE_HOME"
|
|
147
|
+
};
|
|
148
|
+
var APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
149
|
+
function assertApp(app) {
|
|
150
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
151
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
152
|
+
}
|
|
153
|
+
if (!APP_SLUG_RE.test(app)) {
|
|
154
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function envOf(options) {
|
|
158
|
+
return options.env ?? process.env;
|
|
159
|
+
}
|
|
160
|
+
function envValue(options, kind) {
|
|
161
|
+
const value = envOf(options)[KIND_ENV[kind]];
|
|
162
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
163
|
+
}
|
|
164
|
+
function isMacOS(platform) {
|
|
165
|
+
return platform === "darwin";
|
|
166
|
+
}
|
|
167
|
+
function baseDir(kind, options) {
|
|
168
|
+
const override = envValue(options, kind);
|
|
169
|
+
if (override)
|
|
170
|
+
return override;
|
|
171
|
+
const home = options.home ?? homedir();
|
|
172
|
+
const platform = options.platform ?? process.platform;
|
|
173
|
+
if (isMacOS(platform)) {
|
|
174
|
+
switch (kind) {
|
|
175
|
+
case "config":
|
|
176
|
+
case "data":
|
|
177
|
+
return join(home, "Library", "Application Support", "Hasna");
|
|
178
|
+
case "cache":
|
|
179
|
+
return join(home, "Library", "Caches", "Hasna");
|
|
180
|
+
case "state":
|
|
181
|
+
return join(home, "Library", "Logs", "Hasna");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
switch (kind) {
|
|
185
|
+
case "config":
|
|
186
|
+
return join(home, ".config", "hasna");
|
|
187
|
+
case "data":
|
|
188
|
+
return join(home, ".local", "share", "hasna");
|
|
189
|
+
case "state":
|
|
190
|
+
return join(home, ".local", "state", "hasna");
|
|
191
|
+
case "cache":
|
|
192
|
+
return join(home, ".cache", "hasna");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function resolvePath(kind, options) {
|
|
196
|
+
assertApp(options.app);
|
|
197
|
+
const appSegment = options.internal === true ? join("internal", options.app) : options.app;
|
|
198
|
+
return join(baseDir(kind, options), appSegment);
|
|
199
|
+
}
|
|
200
|
+
function configDir(options) {
|
|
201
|
+
return resolvePath("config", options);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/lib/app-home.ts
|
|
205
|
+
var HASNA_CONFIGS_HOME_ENV = "HASNA_CONFIGS_HOME";
|
|
206
|
+
function homeDir(env = process.env) {
|
|
207
|
+
return env["HOME"] || env["USERPROFILE"] || homedir2();
|
|
208
|
+
}
|
|
209
|
+
function legacyStoreHome(env = process.env) {
|
|
210
|
+
return resolve(join2(homeDir(env), ".hasna", "instructions"));
|
|
211
|
+
}
|
|
212
|
+
function resolverStoreHome(env = process.env) {
|
|
213
|
+
return configDir({ app: "configs", env, home: env.HOME || env.USERPROFILE || homedir2() });
|
|
214
|
+
}
|
|
215
|
+
function adoptResolverStoreHome(resolved, env = process.env) {
|
|
216
|
+
const override = env.HASNA_CONFIG_HOME;
|
|
217
|
+
if (typeof override === "string" && override.trim().length > 0)
|
|
218
|
+
return true;
|
|
219
|
+
return existsSync(join2(resolved, "instructions.db"));
|
|
220
|
+
}
|
|
221
|
+
function exactStoreHome(env = process.env) {
|
|
222
|
+
const v = env[HASNA_CONFIGS_HOME_ENV];
|
|
223
|
+
return v && v.trim() ? v.trim() : undefined;
|
|
224
|
+
}
|
|
225
|
+
function getConfigsStoreHome(env = process.env) {
|
|
226
|
+
const exact = exactStoreHome(env);
|
|
227
|
+
if (exact)
|
|
228
|
+
return resolve(exact);
|
|
229
|
+
const resolved = resolverStoreHome(env);
|
|
230
|
+
return adoptResolverStoreHome(resolved, env) ? resolve(resolved) : legacyStoreHome(env);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/lib/raw-store-root.ts
|
|
134
234
|
var RAW_STORE_ROOT_ENV = "HASNA_CONFIGS_HOME";
|
|
135
|
-
function getRawStoreRoot() {
|
|
136
|
-
return
|
|
235
|
+
function getRawStoreRoot(env = process.env) {
|
|
236
|
+
return resolve2(getConfigsStoreHome(env));
|
|
137
237
|
}
|
|
138
238
|
|
|
139
239
|
// src/db/database.ts
|
|
@@ -143,7 +243,7 @@ function getDbPath() {
|
|
|
143
243
|
}
|
|
144
244
|
const dir = getRawStoreRoot();
|
|
145
245
|
mkdirSync(dir, { recursive: true });
|
|
146
|
-
return
|
|
246
|
+
return join3(dir, "instructions.db");
|
|
147
247
|
}
|
|
148
248
|
function uuid() {
|
|
149
249
|
return randomUUID();
|
|
@@ -267,7 +367,7 @@ function resetLocalDatabase() {
|
|
|
267
367
|
if (dbPath === ":memory:")
|
|
268
368
|
return;
|
|
269
369
|
for (const p of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
270
|
-
if (
|
|
370
|
+
if (existsSync2(p))
|
|
271
371
|
rmSync(p);
|
|
272
372
|
}
|
|
273
373
|
}
|
|
@@ -539,9 +639,9 @@ function getConfigStats(db) {
|
|
|
539
639
|
}
|
|
540
640
|
|
|
541
641
|
// src/lib/machine.ts
|
|
542
|
-
import { arch as currentArch, homedir as
|
|
543
|
-
import { existsSync as
|
|
544
|
-
import { join as
|
|
642
|
+
import { arch as currentArch, homedir as homedir3, hostname as currentHostname, type as currentOsType } from "os";
|
|
643
|
+
import { existsSync as existsSync3 } from "fs";
|
|
644
|
+
import { join as join4 } from "path";
|
|
545
645
|
|
|
546
646
|
// src/lib/template.ts
|
|
547
647
|
var VAR_PATTERN = /\{\{([A-Z0-9_]+)(?::([^}]*))?\}\}/g;
|
|
@@ -614,11 +714,11 @@ function normalizeOsFamily(os) {
|
|
|
614
714
|
return value || "unknown";
|
|
615
715
|
}
|
|
616
716
|
function detectMachineContext(overrides = {}) {
|
|
617
|
-
const
|
|
717
|
+
const homeDir2 = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir3();
|
|
618
718
|
const os = overrides.os ?? currentOsType();
|
|
619
719
|
const osFamily = normalizeOsFamily(os);
|
|
620
|
-
const bunBinDir = overrides.bun_bin_dir ??
|
|
621
|
-
const defaultBunPath = osFamily === "macos" &&
|
|
720
|
+
const bunBinDir = overrides.bun_bin_dir ?? join4(homeDir2, ".bun", "bin");
|
|
721
|
+
const defaultBunPath = osFamily === "macos" && existsSync3(BREW_BUN_PATH) ? BREW_BUN_PATH : join4(bunBinDir, "bun");
|
|
622
722
|
return {
|
|
623
723
|
id: "current-machine",
|
|
624
724
|
hostname: overrides.hostname ?? currentHostname(),
|
|
@@ -627,11 +727,11 @@ function detectMachineContext(overrides = {}) {
|
|
|
627
727
|
last_applied_at: null,
|
|
628
728
|
created_at: "",
|
|
629
729
|
os_family: osFamily,
|
|
630
|
-
home_dir:
|
|
631
|
-
workspace_root: overrides.workspace_root ??
|
|
730
|
+
home_dir: homeDir2,
|
|
731
|
+
workspace_root: overrides.workspace_root ?? join4(homeDir2, osFamily === "macos" ? "Workspace" : "workspace"),
|
|
632
732
|
bun_bin_dir: bunBinDir,
|
|
633
733
|
bun_path: overrides.bun_path ?? defaultBunPath,
|
|
634
|
-
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${
|
|
734
|
+
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join4("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
|
|
635
735
|
};
|
|
636
736
|
}
|
|
637
737
|
function machineContextToVariables(machine) {
|
|
@@ -749,9 +849,9 @@ import { createHash as createHash7 } from "crypto";
|
|
|
749
849
|
|
|
750
850
|
// src/lib/session-render.ts
|
|
751
851
|
import { createHash as createHash6 } from "crypto";
|
|
752
|
-
import { existsSync as
|
|
753
|
-
import { homedir as
|
|
754
|
-
import { basename as basename4, dirname as dirname3, extname as extname2, isAbsolute as isAbsolute3, join as
|
|
852
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
853
|
+
import { homedir as homedir6 } from "os";
|
|
854
|
+
import { basename as basename4, dirname as dirname3, extname as extname2, isAbsolute as isAbsolute3, join as join8, parse as parse2, posix as posix2, relative as relative2, resolve as resolve7 } from "path";
|
|
755
855
|
|
|
756
856
|
// src/lib/global-agent-rules-standard.ts
|
|
757
857
|
import { createHash } from "crypto";
|
|
@@ -1064,7 +1164,7 @@ import { dlopen, FFIType } from "bun:ffi";
|
|
|
1064
1164
|
import {
|
|
1065
1165
|
closeSync,
|
|
1066
1166
|
constants,
|
|
1067
|
-
existsSync as
|
|
1167
|
+
existsSync as existsSync4,
|
|
1068
1168
|
fstatSync,
|
|
1069
1169
|
fsyncSync,
|
|
1070
1170
|
lstatSync,
|
|
@@ -1077,7 +1177,7 @@ import {
|
|
|
1077
1177
|
statSync,
|
|
1078
1178
|
writeFileSync
|
|
1079
1179
|
} from "fs";
|
|
1080
|
-
import { basename, dirname, isAbsolute, join as
|
|
1180
|
+
import { basename, dirname, isAbsolute, join as join5, parse, relative, resolve as resolve3 } from "path";
|
|
1081
1181
|
|
|
1082
1182
|
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
|
|
1083
1183
|
var exports_external = {};
|
|
@@ -5573,7 +5673,7 @@ function planProjectContext(input) {
|
|
|
5573
5673
|
const inlineMarkerOverhead = nativeImports ? 0 : Buffer.byteLength(buildManagedBlock(bundle, "", `
|
|
5574
5674
|
`), "utf8");
|
|
5575
5675
|
const generated = buildCanonicalFragment(bundle, status, ageSeconds, PROJECT_CONTEXT_MAX_RENDERED_BYTES - Math.max(320, inlineMarkerOverhead), PROJECT_CONTEXT_MAX_APPROX_TOKENS - Math.max(80, Math.ceil(inlineMarkerOverhead / 4)));
|
|
5576
|
-
const previousTargetContent =
|
|
5676
|
+
const previousTargetContent = existsSync4(paths.target) ? readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target))) : null;
|
|
5577
5677
|
const markerParse = parseManagedBlock(previousTargetContent ?? "", input.force === true);
|
|
5578
5678
|
if (markerParse.block && markerParse.block.id !== bundle.project.id) {
|
|
5579
5679
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "managed block belongs to a different project");
|
|
@@ -5624,7 +5724,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
5624
5724
|
return null;
|
|
5625
5725
|
const { runtime, workspace_root: workspaceRoot, observed_hashes: observedHashes } = guard;
|
|
5626
5726
|
const paths = runtimePaths(workspaceRoot, runtime);
|
|
5627
|
-
if (!
|
|
5727
|
+
if (!existsSync4(paths.manifest))
|
|
5628
5728
|
return null;
|
|
5629
5729
|
assertCodewithTargetIsConsumed(workspaceRoot, runtime);
|
|
5630
5730
|
const manifest = readProjectContextManifest(paths.manifest, workspaceRoot);
|
|
@@ -5653,7 +5753,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
5653
5753
|
}
|
|
5654
5754
|
const fragment = readUtf8RegularFile(paths.fragment, workspaceRoot, PROJECT_CONTEXT_MAX_RENDERED_BYTES);
|
|
5655
5755
|
scanGeneratedContent(fragment);
|
|
5656
|
-
if (!
|
|
5756
|
+
if (!existsSync4(paths.target)) {
|
|
5657
5757
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider target is missing while durable context is active");
|
|
5658
5758
|
}
|
|
5659
5759
|
const currentTarget = readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target)));
|
|
@@ -5664,7 +5764,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
5664
5764
|
if (currentMarkers.block.id !== cache.project_id || currentMarkers.block.revision !== cache.revision || currentMarkers.block.hash !== cache.hash) {
|
|
5665
5765
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider markers differ from the durable cache");
|
|
5666
5766
|
}
|
|
5667
|
-
const plannedIndexes = input.files.filter((file) => file.role === "index" &&
|
|
5767
|
+
const plannedIndexes = input.files.filter((file) => file.role === "index" && resolve3(file.path) === paths.target);
|
|
5668
5768
|
if (plannedIndexes.length !== 1) {
|
|
5669
5769
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "session renderer does not own the selected project-context provider target");
|
|
5670
5770
|
}
|
|
@@ -5722,7 +5822,7 @@ function withProjectContextSessionGuard(guard, action, options = {}) {
|
|
|
5722
5822
|
verify();
|
|
5723
5823
|
return action(null);
|
|
5724
5824
|
}
|
|
5725
|
-
const lockPath =
|
|
5825
|
+
const lockPath = resolve3(validated.workspace_root, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
5726
5826
|
const lock = acquireWorkspaceLock(validated.workspace_root, lockPath);
|
|
5727
5827
|
try {
|
|
5728
5828
|
verify();
|
|
@@ -5748,7 +5848,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
5748
5848
|
if (!isRecord(observed) || typeof observed.path !== "string") {
|
|
5749
5849
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains malformed hash metadata");
|
|
5750
5850
|
}
|
|
5751
|
-
const path =
|
|
5851
|
+
const path = resolve3(observed.path);
|
|
5752
5852
|
if (!allowedPaths.has(path) || observedPaths.has(path)) {
|
|
5753
5853
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains an unexpected or duplicate path");
|
|
5754
5854
|
}
|
|
@@ -5770,7 +5870,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
5770
5870
|
function applyProjectContext(options) {
|
|
5771
5871
|
const workspaceRoot = assertSafeWorkspaceRoot(options.workspace_root);
|
|
5772
5872
|
const now2 = options.now ?? new Date;
|
|
5773
|
-
const lockPath =
|
|
5873
|
+
const lockPath = resolve3(workspaceRoot, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
5774
5874
|
const lock = options.dry_run ? null : acquireWorkspaceLock(workspaceRoot, lockPath, options.test_hooks?.after_lock_open, options.test_hooks?.before_stale_lock_remove, options.test_hooks?.process_start_identity);
|
|
5775
5875
|
try {
|
|
5776
5876
|
const resolved = resolveBundleForApply(options, workspaceRoot, now2);
|
|
@@ -5917,7 +6017,7 @@ function resolveBundleForApply(options, workspaceRoot, now2) {
|
|
|
5917
6017
|
if (!options.expected_project_id) {
|
|
5918
6018
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_ID_REQUIRED", "expected_project_id is required for stale-cache fallback");
|
|
5919
6019
|
}
|
|
5920
|
-
const cachePath =
|
|
6020
|
+
const cachePath = resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
5921
6021
|
const cache = readProjectContextCache(cachePath, workspaceRoot);
|
|
5922
6022
|
if (!cache)
|
|
5923
6023
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_MISSING", "no last-known-good project context cache exists");
|
|
@@ -6119,7 +6219,7 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
6119
6219
|
if (runtime !== "codewith" || !content)
|
|
6120
6220
|
return null;
|
|
6121
6221
|
const sessionManifestPath = runtimePaths(workspaceRoot, runtime).sessionManifest;
|
|
6122
|
-
if (!
|
|
6222
|
+
if (!existsSync4(sessionManifestPath))
|
|
6123
6223
|
return null;
|
|
6124
6224
|
const manifest = readSessionManifestRecord(sessionManifestPath, workspaceRoot);
|
|
6125
6225
|
if (!manifest || manifest["schema"] !== SESSION_RENDER_SCHEMA) {
|
|
@@ -6170,7 +6270,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
6170
6270
|
const manifest = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
6171
6271
|
if (manifest) {
|
|
6172
6272
|
const manifestHashHasRecoveryProof = manifest.projectContext.hash === plan.bundle.hash || metadataSnapshotMatchesManifest(plan, manifest);
|
|
6173
|
-
const canonicalStateAlreadyInstalled = cache !== null && canonicalCacheHash === plan.bundle.hash && cache.project_id === plan.bundle.project.id && cache.revision === plan.bundle.revision && plan.marker !== null && plan.marker.id === plan.bundle.project.id && plan.marker.revision === plan.bundle.revision && plan.marker.hash === plan.bundle.hash &&
|
|
6273
|
+
const canonicalStateAlreadyInstalled = cache !== null && canonicalCacheHash === plan.bundle.hash && cache.project_id === plan.bundle.project.id && cache.revision === plan.bundle.revision && plan.marker !== null && plan.marker.id === plan.bundle.project.id && plan.marker.revision === plan.bundle.revision && plan.marker.hash === plan.bundle.hash && existsSync4(plan.fragment_path) && fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && manifestHashHasRecoveryProof;
|
|
6174
6274
|
observations.push({
|
|
6175
6275
|
source: "manifest",
|
|
6176
6276
|
id: manifest.projectContext.projectId,
|
|
@@ -6178,7 +6278,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
6178
6278
|
hash: canonicalStateAlreadyInstalled && manifest.projectContext.projectId === plan.bundle.project.id && manifest.projectContext.revision === plan.bundle.revision ? plan.bundle.hash : normalizePersistedHash(manifest.projectContext.revision, manifest.projectContext.hash)
|
|
6179
6279
|
});
|
|
6180
6280
|
const fragmentEntry = manifest.files.find((file) => file.relativePath === PROJECT_CONTEXT_FRAGMENT_PATH);
|
|
6181
|
-
if (fragmentEntry &&
|
|
6281
|
+
if (fragmentEntry && existsSync4(plan.fragment_path)) {
|
|
6182
6282
|
const actual = currentFileHash(plan.fragment_path, plan.workspace_root);
|
|
6183
6283
|
if (actual !== fragmentEntry.sha256 && !fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && !force) {
|
|
6184
6284
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "canonical project-context fragment changed outside Instructions");
|
|
@@ -6273,9 +6373,9 @@ function buildManifest(plan, now2) {
|
|
|
6273
6373
|
function buildSessionCompatibilityManifest(plan, now2) {
|
|
6274
6374
|
const paths = runtimePaths(plan.workspace_root, plan.runtime);
|
|
6275
6375
|
const tool = manifestTool(plan.runtime);
|
|
6276
|
-
const targetHome = plan.runtime === "codewith" ?
|
|
6376
|
+
const targetHome = plan.runtime === "codewith" ? resolve3(plan.workspace_root, ".codewith") : plan.workspace_root;
|
|
6277
6377
|
const targetRelativePath = sessionTargetRelativePath(plan.runtime);
|
|
6278
|
-
const existing =
|
|
6378
|
+
const existing = existsSync4(paths.sessionManifest) ? readSessionManifestRecord(paths.sessionManifest, plan.workspace_root) : {
|
|
6279
6379
|
schema: SESSION_RENDER_SCHEMA,
|
|
6280
6380
|
tool,
|
|
6281
6381
|
adapterMode: plan.native_imports ? "native-imports" : "flattened-markdown",
|
|
@@ -6295,7 +6395,7 @@ function buildSessionCompatibilityManifest(plan, now2) {
|
|
|
6295
6395
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest is malformed or incompatible");
|
|
6296
6396
|
}
|
|
6297
6397
|
const existingTargetHome = safeLegacyMetadataString(existing["targetHome"], null);
|
|
6298
|
-
if (existingTargetHome !== null &&
|
|
6398
|
+
if (existingTargetHome !== null && resolve3(existingTargetHome) !== targetHome) {
|
|
6299
6399
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest targets a different workspace");
|
|
6300
6400
|
}
|
|
6301
6401
|
const sources = sanitizeLegacySources(existing["sources"]).filter((source) => source["id"] !== "project-context-bundle");
|
|
@@ -6581,9 +6681,9 @@ function writeMetadataSnapshot(plan, now2) {
|
|
|
6581
6681
|
const previous = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
6582
6682
|
if (!previous || previous.projectContext.revision === plan.bundle.revision && previous.projectContext.hash === plan.bundle.hash)
|
|
6583
6683
|
return null;
|
|
6584
|
-
const snapshotDir =
|
|
6684
|
+
const snapshotDir = resolve3(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
6585
6685
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
6586
|
-
const snapshotPath =
|
|
6686
|
+
const snapshotPath = resolve3(snapshotDir, `${safeFilename(previous.projectContext.revision)}-${previous.projectContext.hash.slice(-12)}.json`);
|
|
6587
6687
|
const snapshot = {
|
|
6588
6688
|
schema: "hasna.configs.session-render-snapshot/v1",
|
|
6589
6689
|
kind: "project-context-metadata",
|
|
@@ -6599,9 +6699,9 @@ function writeMetadataSnapshot(plan, now2) {
|
|
|
6599
6699
|
return snapshotPath;
|
|
6600
6700
|
}
|
|
6601
6701
|
function metadataSnapshotMatchesManifest(plan, manifest) {
|
|
6602
|
-
const snapshotDir =
|
|
6603
|
-
const snapshotPath =
|
|
6604
|
-
if (!
|
|
6702
|
+
const snapshotDir = resolve3(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
6703
|
+
const snapshotPath = resolve3(snapshotDir, `${safeFilename(manifest.projectContext.revision)}-${manifest.projectContext.hash.slice(-12)}.json`);
|
|
6704
|
+
if (!existsSync4(snapshotPath))
|
|
6605
6705
|
return false;
|
|
6606
6706
|
const record = readJsonRecord(snapshotPath, plan.workspace_root);
|
|
6607
6707
|
const result = projectContextMetadataSnapshotSchema.safeParse(record);
|
|
@@ -6649,10 +6749,10 @@ function writeProjectContextRollbackSnapshot(plan, now2, outputs) {
|
|
|
6649
6749
|
sha256: nextHash
|
|
6650
6750
|
};
|
|
6651
6751
|
});
|
|
6652
|
-
const snapshotDir =
|
|
6752
|
+
const snapshotDir = resolve3(plan.workspace_root, ...SESSION_RENDER_SNAPSHOT_RELATIVE_DIR.split("/"));
|
|
6653
6753
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
6654
6754
|
const timestamp = now2.toISOString().replace(/[:.]/g, "-");
|
|
6655
|
-
const snapshotPath =
|
|
6755
|
+
const snapshotPath = resolve3(snapshotDir, `${timestamp}-${randomUUID2()}.json`);
|
|
6656
6756
|
const snapshot = {
|
|
6657
6757
|
schema: "hasna.configs.session-render-snapshot/v2",
|
|
6658
6758
|
createdAt: now2.toISOString(),
|
|
@@ -6670,7 +6770,7 @@ function writeProjectContextRollbackSnapshot(plan, now2, outputs) {
|
|
|
6670
6770
|
return snapshotPath;
|
|
6671
6771
|
}
|
|
6672
6772
|
function readProjectContextManifest(path, workspaceRoot) {
|
|
6673
|
-
if (!
|
|
6773
|
+
if (!existsSync4(path))
|
|
6674
6774
|
return null;
|
|
6675
6775
|
const record = readJsonRecord(path, workspaceRoot);
|
|
6676
6776
|
const result = storedManifestObservationSchema.safeParse(record);
|
|
@@ -6685,7 +6785,7 @@ function readProjectContextManifest(path, workspaceRoot) {
|
|
|
6685
6785
|
};
|
|
6686
6786
|
}
|
|
6687
6787
|
function readProjectContextCache(path, workspaceRoot) {
|
|
6688
|
-
if (!
|
|
6788
|
+
if (!existsSync4(path))
|
|
6689
6789
|
return null;
|
|
6690
6790
|
const record = readJsonRecord(path, workspaceRoot);
|
|
6691
6791
|
const result = projectContextCacheSchema.safeParse(record);
|
|
@@ -6717,7 +6817,7 @@ function readSessionManifestRecord(path, workspaceRoot) {
|
|
|
6717
6817
|
}
|
|
6718
6818
|
}
|
|
6719
6819
|
function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash, afterExchange, atomicExchangeUnavailable = false, beforeInstall, portableCreateOnly = false, maxObservedBytes, allowPortableReplacement = false) {
|
|
6720
|
-
const dir =
|
|
6820
|
+
const dir = resolve3(path, "..");
|
|
6721
6821
|
ensureSafeDirectory(dir, workspaceRoot, 448);
|
|
6722
6822
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
6723
6823
|
const anchoredOps = portableCreateOnly ? null : resolveAnchoredFsOps();
|
|
@@ -6734,7 +6834,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
6734
6834
|
const previous = anchoredFileObservation(directory, targetName);
|
|
6735
6835
|
const previousMode = previous?.mode ?? defaultMode;
|
|
6736
6836
|
const tempName = `.project-context-${randomUUID2()}.tmp`;
|
|
6737
|
-
const tempPath =
|
|
6837
|
+
const tempPath = join5(dir, tempName);
|
|
6738
6838
|
let fd = null;
|
|
6739
6839
|
let preserveTemp = false;
|
|
6740
6840
|
let directoryChanged = false;
|
|
@@ -6865,7 +6965,7 @@ function atomicWritePortable(path, content, workspaceRoot, defaultMode, expected
|
|
|
6865
6965
|
}
|
|
6866
6966
|
const dir = dirname(path);
|
|
6867
6967
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
6868
|
-
const tempPath =
|
|
6968
|
+
const tempPath = join5(dir, `.project-context-${randomUUID2()}.tmp`);
|
|
6869
6969
|
let fd = null;
|
|
6870
6970
|
let tempIdentity = null;
|
|
6871
6971
|
try {
|
|
@@ -6922,7 +7022,7 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
6922
7022
|
}
|
|
6923
7023
|
const dir = dirname(path);
|
|
6924
7024
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
6925
|
-
const tempPath =
|
|
7025
|
+
const tempPath = join5(dir, `.project-context-${randomUUID2()}.tmp`);
|
|
6926
7026
|
const desiredHash = sha2562(content);
|
|
6927
7027
|
let fd = null;
|
|
6928
7028
|
let tempIdentity = null;
|
|
@@ -6979,7 +7079,7 @@ function portablePreparedHash(tempPath, path, workspaceRoot, maxObservedBytes, s
|
|
|
6979
7079
|
function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
6980
7080
|
if (maxObservedBytes === undefined)
|
|
6981
7081
|
return currentFileHash(path, workspaceRoot);
|
|
6982
|
-
if (!
|
|
7082
|
+
if (!existsSync4(path))
|
|
6983
7083
|
return null;
|
|
6984
7084
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
6985
7085
|
const stat = lstatSync(path);
|
|
@@ -6991,11 +7091,11 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
6991
7091
|
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
6992
7092
|
}
|
|
6993
7093
|
function writeProjectContextCoordinatedFile(input) {
|
|
6994
|
-
atomicWriteFile(
|
|
7094
|
+
atomicWriteFile(resolve3(input.path), input.content, assertSafeWorkspaceRoot(input.workspace_root), input.default_mode ?? 420, input.expected_hash, undefined, false, input.test_hooks?.before_install, input.force_portable_file_ops ?? false, input.max_observed_bytes, input.allow_portable_replacement ?? false);
|
|
6995
7095
|
}
|
|
6996
7096
|
function removeProjectContextCoordinatedFile(input) {
|
|
6997
7097
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
6998
|
-
const path =
|
|
7098
|
+
const path = resolve3(input.path);
|
|
6999
7099
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
7000
7100
|
const dir = dirname(path);
|
|
7001
7101
|
const anchoredOps = input.force_portable_file_ops ? null : resolveAnchoredFsOps();
|
|
@@ -7021,7 +7121,7 @@ function removeProjectContextCoordinatedFile(input) {
|
|
|
7021
7121
|
throw new ProjectContextHashRace(`managed path changed during deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
7022
7122
|
}
|
|
7023
7123
|
displaced = true;
|
|
7024
|
-
input.test_hooks?.after_displace?.(
|
|
7124
|
+
input.test_hooks?.after_displace?.(join5(dir, displacedName));
|
|
7025
7125
|
const moved = anchoredFileObservation(directory, displacedName);
|
|
7026
7126
|
if (!moved || moved.dev !== observed.dev || moved.ino !== observed.ino || moved.hash !== input.expected_hash || anchoredFileObservation(directory, targetName) !== null) {
|
|
7027
7127
|
throw new ProjectContextHashRace(`managed path changed during deletion validation: ${relativePosix(workspaceRoot, path)}`);
|
|
@@ -7067,7 +7167,7 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
7067
7167
|
}
|
|
7068
7168
|
const dir = dirname(path);
|
|
7069
7169
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
7070
|
-
const displacedPath =
|
|
7170
|
+
const displacedPath = join5(dir, `.project-context-delete-${randomUUID2()}.tmp`);
|
|
7071
7171
|
let displaced = false;
|
|
7072
7172
|
try {
|
|
7073
7173
|
assertManagedDirectoryStable(dir, workspaceRoot, directoryIdentity);
|
|
@@ -7078,7 +7178,7 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
7078
7178
|
displaced = true;
|
|
7079
7179
|
afterDisplace?.(displacedPath);
|
|
7080
7180
|
const moved = lstatSync(displacedPath);
|
|
7081
|
-
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash ||
|
|
7181
|
+
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash || existsSync4(path)) {
|
|
7082
7182
|
throw new ProjectContextHashRace(`managed path changed during portable deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
7083
7183
|
}
|
|
7084
7184
|
rmSync2(displacedPath);
|
|
@@ -7139,7 +7239,7 @@ function anchoredOpenExclusive(directory, name, mode) {
|
|
|
7139
7239
|
const requestedMode = mode & 4095;
|
|
7140
7240
|
let fd;
|
|
7141
7241
|
try {
|
|
7142
|
-
fd = openSync(
|
|
7242
|
+
fd = openSync(join5(directory.path, name), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, requestedMode);
|
|
7143
7243
|
} catch {
|
|
7144
7244
|
throw new ProjectContextHashRace(`could not create prepared managed file in ${relativePosix(directory.workspaceRoot, directory.path)}`);
|
|
7145
7245
|
}
|
|
@@ -7182,7 +7282,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
7182
7282
|
const stat = fstatSync(fd);
|
|
7183
7283
|
if (!stat.isFile())
|
|
7184
7284
|
throw new ProjectContextHashRace("managed output is not a regular file");
|
|
7185
|
-
const relativePath = relativePosix(directory.workspaceRoot,
|
|
7285
|
+
const relativePath = relativePosix(directory.workspaceRoot, join5(directory.path, name));
|
|
7186
7286
|
const maxBytes = directory.maxObservedBytes === undefined ? managedObservationMaxBytes(relativePath) : directory.maxObservedBytes;
|
|
7187
7287
|
if (maxBytes !== null && stat.size > maxBytes) {
|
|
7188
7288
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePath}`);
|
|
@@ -7208,7 +7308,7 @@ function anchoredPreparedObservation(directory, name, path, stage) {
|
|
|
7208
7308
|
return observed;
|
|
7209
7309
|
}
|
|
7210
7310
|
function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
7211
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
7311
|
+
assertNoSymlinkSegments(workspaceRoot, join5(path, ".project-context-directory-guard"));
|
|
7212
7312
|
let stat;
|
|
7213
7313
|
try {
|
|
7214
7314
|
stat = lstatSync(path);
|
|
@@ -7221,7 +7321,7 @@ function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
|
7221
7321
|
return { dev: stat.dev, ino: stat.ino };
|
|
7222
7322
|
}
|
|
7223
7323
|
function assertManagedDirectoryStable(path, workspaceRoot, expected) {
|
|
7224
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
7324
|
+
assertNoSymlinkSegments(workspaceRoot, join5(path, ".project-context-directory-guard"));
|
|
7225
7325
|
let current;
|
|
7226
7326
|
try {
|
|
7227
7327
|
current = lstatSync(path);
|
|
@@ -7356,10 +7456,10 @@ function resolveAnchoredFsOps() {
|
|
|
7356
7456
|
return null;
|
|
7357
7457
|
}
|
|
7358
7458
|
function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRemove, processStartIdentityLookup = processStartIdentity) {
|
|
7359
|
-
const lockDirectory =
|
|
7459
|
+
const lockDirectory = resolve3(lockPath, "..");
|
|
7360
7460
|
ensureSafeDirectory(lockDirectory, workspaceRoot, 448);
|
|
7361
7461
|
assertNoSymlinkSegments(workspaceRoot, lockPath);
|
|
7362
|
-
const tempPath =
|
|
7462
|
+
const tempPath = join5(lockDirectory, `.project-context-lock-${randomUUID2()}.tmp`);
|
|
7363
7463
|
let fd = null;
|
|
7364
7464
|
let openedIdentity = null;
|
|
7365
7465
|
let openedContentHash = null;
|
|
@@ -7393,7 +7493,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
7393
7493
|
linked = true;
|
|
7394
7494
|
}
|
|
7395
7495
|
fsyncDirectory(lockDirectory);
|
|
7396
|
-
if (
|
|
7496
|
+
if (existsSync4(tempPath)) {
|
|
7397
7497
|
rmSync2(tempPath);
|
|
7398
7498
|
fsyncDirectory(lockDirectory);
|
|
7399
7499
|
}
|
|
@@ -7408,7 +7508,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
7408
7508
|
if (linked && openedIdentity && openedContentHash) {
|
|
7409
7509
|
removeOwnedLockByInode(lockPath, openedIdentity, openedContentHash);
|
|
7410
7510
|
}
|
|
7411
|
-
if (!preserveTemp &&
|
|
7511
|
+
if (!preserveTemp && existsSync4(tempPath)) {
|
|
7412
7512
|
try {
|
|
7413
7513
|
rmSync2(tempPath);
|
|
7414
7514
|
} catch {}
|
|
@@ -7423,7 +7523,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
7423
7523
|
}
|
|
7424
7524
|
function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
7425
7525
|
try {
|
|
7426
|
-
if (!
|
|
7526
|
+
if (!existsSync4(lockPath))
|
|
7427
7527
|
return;
|
|
7428
7528
|
const current = lstatSync(lockPath);
|
|
7429
7529
|
if (current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino)
|
|
@@ -7431,7 +7531,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
7431
7531
|
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
7432
7532
|
return;
|
|
7433
7533
|
rmSync2(lockPath);
|
|
7434
|
-
fsyncDirectory(
|
|
7534
|
+
fsyncDirectory(resolve3(lockPath, ".."));
|
|
7435
7535
|
} catch {}
|
|
7436
7536
|
}
|
|
7437
7537
|
function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentityLookup = processStartIdentity) {
|
|
@@ -7502,7 +7602,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
7502
7602
|
const candidateInstalled = !current.isSymbolicLink() && current.dev === candidateIdentity.dev && current.ino === candidateIdentity.ino && currentFileHash(lockPath, workspaceRoot) === candidateHash;
|
|
7503
7603
|
const staleDisplaced = !displaced.isSymbolicLink() && displaced.dev === stale.identity.dev && displaced.ino === stale.identity.ino && currentFileHash(candidatePath, workspaceRoot) === stale.contentHash;
|
|
7504
7604
|
if (!candidateInstalled || !staleDisplaced) {
|
|
7505
|
-
if (candidateInstalled &&
|
|
7605
|
+
if (candidateInstalled && existsSync4(candidatePath)) {
|
|
7506
7606
|
atomicExchangePaths(candidatePath, lockPath);
|
|
7507
7607
|
exchanged = false;
|
|
7508
7608
|
return false;
|
|
@@ -7510,13 +7610,13 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
7510
7610
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace lock changed during stale-lock takeover and could not be restored safely");
|
|
7511
7611
|
}
|
|
7512
7612
|
rmSync2(candidatePath);
|
|
7513
|
-
fsyncDirectory(
|
|
7613
|
+
fsyncDirectory(resolve3(lockPath, ".."));
|
|
7514
7614
|
exchanged = false;
|
|
7515
7615
|
return true;
|
|
7516
7616
|
} catch (error) {
|
|
7517
7617
|
if (exchanged) {
|
|
7518
7618
|
try {
|
|
7519
|
-
if (currentFileHash(lockPath, workspaceRoot) === candidateHash &&
|
|
7619
|
+
if (currentFileHash(lockPath, workspaceRoot) === candidateHash && existsSync4(candidatePath)) {
|
|
7520
7620
|
atomicExchangePaths(candidatePath, lockPath);
|
|
7521
7621
|
exchanged = false;
|
|
7522
7622
|
}
|
|
@@ -7529,7 +7629,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
7529
7629
|
}
|
|
7530
7630
|
}
|
|
7531
7631
|
function assertWorkspaceLockHeld(lockPath, lock, workspaceRoot) {
|
|
7532
|
-
if (!
|
|
7632
|
+
if (!existsSync4(lockPath)) {
|
|
7533
7633
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace project-context lock changed during render");
|
|
7534
7634
|
}
|
|
7535
7635
|
const current = lstatSync(lockPath);
|
|
@@ -7589,8 +7689,8 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
7589
7689
|
}
|
|
7590
7690
|
return;
|
|
7591
7691
|
}
|
|
7592
|
-
const lockDirectory =
|
|
7593
|
-
const releasePath =
|
|
7692
|
+
const lockDirectory = resolve3(lockPath, "..");
|
|
7693
|
+
const releasePath = join5(lockDirectory, `.project-context-release-${randomUUID2()}.tmp`);
|
|
7594
7694
|
let releaseFd = null;
|
|
7595
7695
|
let releaseIdentity = null;
|
|
7596
7696
|
let releaseHash = null;
|
|
@@ -7619,7 +7719,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
7619
7719
|
const releaseInstalled = !installed.isSymbolicLink() && installed.dev === releaseIdentity.dev && installed.ino === releaseIdentity.ino && currentFileHash(lockPath, workspaceRoot) === releaseHash;
|
|
7620
7720
|
const ownedDisplaced = !displaced.isSymbolicLink() && displaced.dev === lock.identity.dev && displaced.ino === lock.identity.ino && currentFileHash(releasePath, workspaceRoot) === lock.contentHash;
|
|
7621
7721
|
if (!releaseInstalled || !ownedDisplaced) {
|
|
7622
|
-
if (releaseInstalled &&
|
|
7722
|
+
if (releaseInstalled && existsSync4(releasePath)) {
|
|
7623
7723
|
atomicExchangePaths(releasePath, lockPath);
|
|
7624
7724
|
exchanged = false;
|
|
7625
7725
|
}
|
|
@@ -7632,7 +7732,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
7632
7732
|
} catch {
|
|
7633
7733
|
if (exchanged) {
|
|
7634
7734
|
try {
|
|
7635
|
-
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash &&
|
|
7735
|
+
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash && existsSync4(releasePath)) {
|
|
7636
7736
|
atomicExchangePaths(releasePath, lockPath);
|
|
7637
7737
|
exchanged = false;
|
|
7638
7738
|
}
|
|
@@ -7644,7 +7744,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
7644
7744
|
closeSync(releaseFd);
|
|
7645
7745
|
} catch {}
|
|
7646
7746
|
}
|
|
7647
|
-
if (!exchanged &&
|
|
7747
|
+
if (!exchanged && existsSync4(releasePath)) {
|
|
7648
7748
|
try {
|
|
7649
7749
|
rmSync2(releasePath);
|
|
7650
7750
|
} catch {}
|
|
@@ -7670,15 +7770,15 @@ function ensureSafeDirectory(path, workspaceRoot, mode) {
|
|
|
7670
7770
|
const segments = rel.split(/[\\/]+/).filter(Boolean);
|
|
7671
7771
|
let current = workspaceRoot;
|
|
7672
7772
|
for (const segment of segments) {
|
|
7673
|
-
current =
|
|
7674
|
-
if (
|
|
7773
|
+
current = join5(current, segment);
|
|
7774
|
+
if (existsSync4(current)) {
|
|
7675
7775
|
if (lstatSync(current).isSymbolicLink())
|
|
7676
7776
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
7677
7777
|
if (!statSync(current).isDirectory())
|
|
7678
7778
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", `managed path is not a directory: ${current}`);
|
|
7679
7779
|
} else {
|
|
7680
7780
|
mkdirSync2(current, { mode });
|
|
7681
|
-
fsyncDirectory(
|
|
7781
|
+
fsyncDirectory(resolve3(current, ".."));
|
|
7682
7782
|
}
|
|
7683
7783
|
}
|
|
7684
7784
|
}
|
|
@@ -7734,11 +7834,11 @@ function scanGeneratedContent(content) {
|
|
|
7734
7834
|
function runtimePaths(workspaceRoot, runtime) {
|
|
7735
7835
|
const relativeTarget = runtime === "claude" ? "CLAUDE.md" : runtime === "codewith" ? ".codewith/CODEWITH.md" : "AGENTS.md";
|
|
7736
7836
|
return {
|
|
7737
|
-
target:
|
|
7738
|
-
fragment:
|
|
7739
|
-
manifest:
|
|
7740
|
-
cache:
|
|
7741
|
-
sessionManifest: runtime === "codewith" ?
|
|
7837
|
+
target: resolve3(workspaceRoot, ...relativeTarget.split("/")),
|
|
7838
|
+
fragment: resolve3(workspaceRoot, ...PROJECT_CONTEXT_FRAGMENT_PATH.split("/")),
|
|
7839
|
+
manifest: resolve3(workspaceRoot, ...PROJECT_CONTEXT_MANIFEST_PATH.split("/")),
|
|
7840
|
+
cache: resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/")),
|
|
7841
|
+
sessionManifest: runtime === "codewith" ? resolve3(workspaceRoot, ".codewith", ".hasna", "session-render-manifest.json") : resolve3(workspaceRoot, ".hasna", "session-render-manifest.json")
|
|
7742
7842
|
};
|
|
7743
7843
|
}
|
|
7744
7844
|
function projectContextSessionGuardPaths(paths, runtime) {
|
|
@@ -7748,7 +7848,7 @@ function projectContextSessionGuardPaths(paths, runtime) {
|
|
|
7748
7848
|
paths.fragment,
|
|
7749
7849
|
paths.target,
|
|
7750
7850
|
paths.sessionManifest,
|
|
7751
|
-
...runtime === "codewith" ? [
|
|
7851
|
+
...runtime === "codewith" ? [resolve3(paths.target, "..", "CODEWITH.override.md")] : []
|
|
7752
7852
|
];
|
|
7753
7853
|
}
|
|
7754
7854
|
function sessionTargetRelativePath(runtime) {
|
|
@@ -7768,27 +7868,27 @@ function projectContextRuntimeForSessionTool(tool) {
|
|
|
7768
7868
|
return null;
|
|
7769
7869
|
}
|
|
7770
7870
|
function projectContextWorkspaceForSession(input, runtime) {
|
|
7771
|
-
const targetHome =
|
|
7871
|
+
const targetHome = resolve3(input.target_home);
|
|
7772
7872
|
if (runtime === "codewith") {
|
|
7773
7873
|
const workspaceRoot = basename(targetHome) === ".codewith" ? dirname(targetHome) : null;
|
|
7774
7874
|
if (!workspaceRoot)
|
|
7775
7875
|
return null;
|
|
7776
|
-
if (input.project_root &&
|
|
7876
|
+
if (input.project_root && resolve3(input.project_root) !== workspaceRoot) {
|
|
7777
7877
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "Codewith project_root must be the parent workspace of target_home");
|
|
7778
7878
|
}
|
|
7779
|
-
if (!
|
|
7879
|
+
if (!existsSync4(workspaceRoot) || !lstatSync(workspaceRoot).isDirectory())
|
|
7780
7880
|
return null;
|
|
7781
7881
|
return assertSafeWorkspaceRoot(workspaceRoot);
|
|
7782
7882
|
}
|
|
7783
|
-
if (!
|
|
7883
|
+
if (!existsSync4(targetHome) || !lstatSync(targetHome).isDirectory())
|
|
7784
7884
|
return null;
|
|
7785
7885
|
return assertSafeWorkspaceRoot(targetHome);
|
|
7786
7886
|
}
|
|
7787
7887
|
function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
7788
7888
|
if (runtime !== "codewith")
|
|
7789
7889
|
return;
|
|
7790
|
-
const override =
|
|
7791
|
-
if (!
|
|
7890
|
+
const override = resolve3(workspaceRoot, ".codewith", "CODEWITH.override.md");
|
|
7891
|
+
if (!existsSync4(override))
|
|
7792
7892
|
return;
|
|
7793
7893
|
assertNoSymlinkSegments(workspaceRoot, override);
|
|
7794
7894
|
if (!lstatSync(override).isFile())
|
|
@@ -7798,10 +7898,10 @@ function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
|
7798
7898
|
function assertSafeWorkspaceRoot(path) {
|
|
7799
7899
|
if (!isAbsolute(path))
|
|
7800
7900
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be absolute");
|
|
7801
|
-
const normalized =
|
|
7901
|
+
const normalized = resolve3(path);
|
|
7802
7902
|
if (normalized === parse(normalized).root)
|
|
7803
7903
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root cannot be the filesystem root");
|
|
7804
|
-
if (!
|
|
7904
|
+
if (!existsSync4(normalized) || !lstatSync(normalized).isDirectory())
|
|
7805
7905
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be an existing directory");
|
|
7806
7906
|
assertNoSymlinkAncestors(normalized);
|
|
7807
7907
|
if (lstatSync(normalized).isSymbolicLink())
|
|
@@ -7815,18 +7915,18 @@ function assertNoSymlinkSegments(root, target) {
|
|
|
7815
7915
|
}
|
|
7816
7916
|
let current = root;
|
|
7817
7917
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
7818
|
-
current =
|
|
7819
|
-
if (
|
|
7918
|
+
current = join5(current, segment);
|
|
7919
|
+
if (existsSync4(current) && lstatSync(current).isSymbolicLink()) {
|
|
7820
7920
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
7821
7921
|
}
|
|
7822
7922
|
}
|
|
7823
7923
|
}
|
|
7824
7924
|
function assertNoSymlinkAncestors(path) {
|
|
7825
|
-
const normalized =
|
|
7925
|
+
const normalized = resolve3(path);
|
|
7826
7926
|
let current = parse(normalized).root;
|
|
7827
7927
|
for (const segment of relative(current, normalized).split(/[\\/]+/).filter(Boolean)) {
|
|
7828
|
-
current =
|
|
7829
|
-
if (!
|
|
7928
|
+
current = join5(current, segment);
|
|
7929
|
+
if (!existsSync4(current))
|
|
7830
7930
|
return;
|
|
7831
7931
|
if (lstatSync(current).isSymbolicLink())
|
|
7832
7932
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `workspace ancestor is a symlink: ${current}`);
|
|
@@ -7842,7 +7942,7 @@ function readUtf8RegularFile(path, workspaceRoot, maxBytes = FOREIGN_INPUT_MAX_B
|
|
|
7842
7942
|
return readFileSync(path, "utf8");
|
|
7843
7943
|
}
|
|
7844
7944
|
function currentFileHash(path, workspaceRoot) {
|
|
7845
|
-
if (!
|
|
7945
|
+
if (!existsSync4(path))
|
|
7846
7946
|
return null;
|
|
7847
7947
|
const relativePath = relativePosix(workspaceRoot, path);
|
|
7848
7948
|
return sha2562(readUtf8RegularFile(path, workspaceRoot, managedObservationMaxBytes(relativePath)));
|
|
@@ -7861,10 +7961,10 @@ function fragmentMatchesBundle(path, bundle, workspaceRoot) {
|
|
|
7861
7961
|
}
|
|
7862
7962
|
function durableSourcePath(path, workspaceRoot) {
|
|
7863
7963
|
if (!path || path.startsWith("/dev/fd/"))
|
|
7864
|
-
return
|
|
7865
|
-
const normalized = isAbsolute(path) ?
|
|
7964
|
+
return resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
7965
|
+
const normalized = isAbsolute(path) ? resolve3(path) : resolve3(workspaceRoot, path);
|
|
7866
7966
|
if (normalized.startsWith("/dev/fd/"))
|
|
7867
|
-
return
|
|
7967
|
+
return resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
7868
7968
|
return normalized;
|
|
7869
7969
|
}
|
|
7870
7970
|
function compareRevisions(incoming, previous) {
|
|
@@ -8205,7 +8305,7 @@ function compareProviderVersions(left, right) {
|
|
|
8205
8305
|
|
|
8206
8306
|
// src/lib/asset-plan.ts
|
|
8207
8307
|
import { createHash as createHash3 } from "crypto";
|
|
8208
|
-
import { isAbsolute as isAbsolute2, posix, resolve as
|
|
8308
|
+
import { isAbsolute as isAbsolute2, posix, resolve as resolve4 } from "path";
|
|
8209
8309
|
var ASSET_PLAN_SCHEMA = "hasna.instructions.asset-plan/v1";
|
|
8210
8310
|
var ASSET_CAPABILITY_SCHEMA = "hasna.instructions.asset-capability/v1";
|
|
8211
8311
|
var ASSET_BUNDLE_SCHEMA = "hasna.instructions.asset-bundle/v1";
|
|
@@ -8470,8 +8570,8 @@ function resolveAssetDestination(item, roots) {
|
|
|
8470
8570
|
if (!isAbsolute2(root))
|
|
8471
8571
|
throw new Error(`Asset ${item.assetKey} destination root must be absolute.`);
|
|
8472
8572
|
const relativePath = safeRelativePath(item.destination.relativePath);
|
|
8473
|
-
const target =
|
|
8474
|
-
const normalizedRoot =
|
|
8573
|
+
const target = resolve4(root, ...relativePath.split("/"));
|
|
8574
|
+
const normalizedRoot = resolve4(root);
|
|
8475
8575
|
if (target === normalizedRoot)
|
|
8476
8576
|
throw new Error(`Asset ${item.assetKey} destination cannot replace its root.`);
|
|
8477
8577
|
if (!target.startsWith(`${normalizedRoot}/`))
|
|
@@ -8593,8 +8693,8 @@ function deepFreeze(value) {
|
|
|
8593
8693
|
// src/lib/cursor-authority.ts
|
|
8594
8694
|
import { createHash as createHash4 } from "crypto";
|
|
8595
8695
|
import { lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
|
|
8596
|
-
import { homedir as
|
|
8597
|
-
import { join as
|
|
8696
|
+
import { homedir as homedir4 } from "os";
|
|
8697
|
+
import { join as join6, resolve as resolve5 } from "path";
|
|
8598
8698
|
var CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH = ".cursor/rules/hasna-global.mdc";
|
|
8599
8699
|
var CURSOR_GLOBAL_AUTHORITY_MAX_BYTES = 256 * 1024;
|
|
8600
8700
|
var CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER = "Managed by @hasna/configs cursor global authority";
|
|
@@ -8603,8 +8703,8 @@ var CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN = /^---\n[\s\S]*?\n---(?:\n|$)/;
|
|
|
8603
8703
|
function sha2564(content) {
|
|
8604
8704
|
return createHash4("sha256").update(content).digest("hex");
|
|
8605
8705
|
}
|
|
8606
|
-
function
|
|
8607
|
-
return process.env["HOME"] ||
|
|
8706
|
+
function homeDir2() {
|
|
8707
|
+
return process.env["HOME"] || homedir4();
|
|
8608
8708
|
}
|
|
8609
8709
|
function markerPayload(content, markerLine, markerIndex) {
|
|
8610
8710
|
const index = markerIndex ?? content.indexOf(markerLine);
|
|
@@ -8620,12 +8720,12 @@ function baseObservation(path) {
|
|
|
8620
8720
|
};
|
|
8621
8721
|
}
|
|
8622
8722
|
function observeCursorGlobalAuthority(options = {}) {
|
|
8623
|
-
const authorityPath =
|
|
8723
|
+
const authorityPath = resolve5(join6(options.home ?? homeDir2(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
8624
8724
|
const readFile = options.readFile ?? ((path) => readFileSync2(path, "utf8"));
|
|
8625
8725
|
return observeCursorGlobalAuthorityPath(authorityPath, readFile);
|
|
8626
8726
|
}
|
|
8627
8727
|
function observeCursorGlobalAuthorityAtPath(authorityPath) {
|
|
8628
|
-
return observeCursorGlobalAuthorityPath(
|
|
8728
|
+
return observeCursorGlobalAuthorityPath(resolve5(authorityPath), (path) => readFileSync2(path, "utf8"));
|
|
8629
8729
|
}
|
|
8630
8730
|
function observeCursorGlobalAuthorityPath(authorityPath, readFile) {
|
|
8631
8731
|
const base = baseObservation(authorityPath);
|
|
@@ -8770,11 +8870,21 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile) {
|
|
|
8770
8870
|
};
|
|
8771
8871
|
}
|
|
8772
8872
|
function isCursorGlobalAuthorityPath(path) {
|
|
8773
|
-
return
|
|
8873
|
+
return resolve5(path) === resolve5(join6(homeDir2(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
8774
8874
|
}
|
|
8775
8875
|
function stampCursorGlobalAuthorityMarker(content) {
|
|
8776
|
-
|
|
8777
|
-
|
|
8876
|
+
const existing = content.match(CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN);
|
|
8877
|
+
if (existing) {
|
|
8878
|
+
const markerLine2 = existing[0];
|
|
8879
|
+
const index = existing.index ?? content.indexOf(markerLine2);
|
|
8880
|
+
const payload = markerPayload(content, markerLine2, index);
|
|
8881
|
+
if (sha2564(payload) === existing[1].slice("sha256:".length)) {
|
|
8882
|
+
return content;
|
|
8883
|
+
}
|
|
8884
|
+
const digest2 = sha2564(payload);
|
|
8885
|
+
const freshMarkerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest2} -->`;
|
|
8886
|
+
return content.slice(0, index) + freshMarkerLine + content.slice(index + markerLine2.length);
|
|
8887
|
+
}
|
|
8778
8888
|
const digest = sha2564(content);
|
|
8779
8889
|
const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
|
|
8780
8890
|
const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
|
|
@@ -8810,8 +8920,8 @@ function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthori
|
|
|
8810
8920
|
// src/lib/session-authority.ts
|
|
8811
8921
|
import { createHash as createHash5 } from "crypto";
|
|
8812
8922
|
import { lstatSync as lstatSync3, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "fs";
|
|
8813
|
-
import { homedir as
|
|
8814
|
-
import { join as
|
|
8923
|
+
import { homedir as homedir5 } from "os";
|
|
8924
|
+
import { join as join7, resolve as resolve6 } from "path";
|
|
8815
8925
|
var CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH = "AGENTS.md";
|
|
8816
8926
|
var CLAUDE_LEGACY_AUTHORITY_MAX_BYTES = 256 * 1024;
|
|
8817
8927
|
var CLAUDE_LEGACY_MARKERS = [
|
|
@@ -8823,10 +8933,10 @@ function sha2565(content) {
|
|
|
8823
8933
|
return createHash5("sha256").update(content).digest("hex");
|
|
8824
8934
|
}
|
|
8825
8935
|
function configHomeDir() {
|
|
8826
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
8936
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir5();
|
|
8827
8937
|
}
|
|
8828
8938
|
function normalizeOwnedTargetPath(p) {
|
|
8829
|
-
const expanded = p.startsWith("~/") ?
|
|
8939
|
+
const expanded = p.startsWith("~/") ? resolve6(configHomeDir(), p.slice(2)) : resolve6(p);
|
|
8830
8940
|
try {
|
|
8831
8941
|
return realpathSync(expanded);
|
|
8832
8942
|
} catch {
|
|
@@ -8834,7 +8944,7 @@ function normalizeOwnedTargetPath(p) {
|
|
|
8834
8944
|
}
|
|
8835
8945
|
}
|
|
8836
8946
|
function detectClaudeAuthorityConflicts(targetHome, ownedAuthorities = []) {
|
|
8837
|
-
const authorityPath =
|
|
8947
|
+
const authorityPath = resolve6(join7(targetHome, CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH));
|
|
8838
8948
|
let stat;
|
|
8839
8949
|
try {
|
|
8840
8950
|
stat = lstatSync3(authorityPath);
|
|
@@ -9193,13 +9303,13 @@ function yamlQuote2(value) {
|
|
|
9193
9303
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
9194
9304
|
}
|
|
9195
9305
|
function defaultTargetHome(tool, profile, sessionId) {
|
|
9196
|
-
const home = process.env["HOME"] ||
|
|
9197
|
-
return
|
|
9306
|
+
const home = process.env["HOME"] || homedir6();
|
|
9307
|
+
return join8(home, ".hasna", "accounts", "profiles", tool, slug(profile));
|
|
9198
9308
|
}
|
|
9199
9309
|
function joinTarget(targetHome, relativePath) {
|
|
9200
9310
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
9201
9311
|
const safeRelativePath2 = assertSafeRelativePath(relativePath);
|
|
9202
|
-
return
|
|
9312
|
+
return join8(safeTargetHome, ...safeRelativePath2.split("/"));
|
|
9203
9313
|
}
|
|
9204
9314
|
function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
9205
9315
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
@@ -9769,7 +9879,7 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
|
|
|
9769
9879
|
...sources.flatMap((source) => source.resolvedRules.map((rule) => rule.id))
|
|
9770
9880
|
]);
|
|
9771
9881
|
const existingConfigPath = joinTarget(targetHome, adapter.configFile);
|
|
9772
|
-
const selectedConfig =
|
|
9882
|
+
const selectedConfig = existsSync6(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
|
|
9773
9883
|
const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
|
|
9774
9884
|
const config = {
|
|
9775
9885
|
...selectedConfig,
|
|
@@ -9958,7 +10068,7 @@ function adapterFor(input) {
|
|
|
9958
10068
|
return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
|
|
9959
10069
|
}
|
|
9960
10070
|
function getHomeDir() {
|
|
9961
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
10071
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir6();
|
|
9962
10072
|
}
|
|
9963
10073
|
function cleanSessionPathInput(path) {
|
|
9964
10074
|
const trimmed = path.trim();
|
|
@@ -9973,16 +10083,16 @@ function resolveSessionPath(path) {
|
|
|
9973
10083
|
throw new Error("Session render path cannot be empty.");
|
|
9974
10084
|
const home = getHomeDir();
|
|
9975
10085
|
if (cleaned === "~")
|
|
9976
|
-
return
|
|
10086
|
+
return resolve7(home);
|
|
9977
10087
|
if (cleaned.startsWith("~/"))
|
|
9978
|
-
return
|
|
10088
|
+
return resolve7(home, cleaned.slice(2));
|
|
9979
10089
|
if (cleaned === "{{HOME}}" || cleaned === "${HOME}")
|
|
9980
|
-
return
|
|
10090
|
+
return resolve7(home);
|
|
9981
10091
|
if (cleaned.startsWith("{{HOME}}/"))
|
|
9982
|
-
return
|
|
10092
|
+
return resolve7(home, cleaned.slice("{{HOME}}/".length));
|
|
9983
10093
|
if (cleaned.startsWith("${HOME}/"))
|
|
9984
|
-
return
|
|
9985
|
-
return
|
|
10094
|
+
return resolve7(home, cleaned.slice("${HOME}/".length));
|
|
10095
|
+
return resolve7(cleaned);
|
|
9986
10096
|
}
|
|
9987
10097
|
function assertSafeRelativePath(relativePath) {
|
|
9988
10098
|
if (!relativePath.trim())
|
|
@@ -9998,7 +10108,7 @@ function assertSafeRelativePath(relativePath) {
|
|
|
9998
10108
|
function assertSafeTargetRoot(targetHome) {
|
|
9999
10109
|
if (!isAbsolute3(targetHome))
|
|
10000
10110
|
throw new Error(`Session render target must be an absolute path: ${targetHome}`);
|
|
10001
|
-
const normalized =
|
|
10111
|
+
const normalized = resolve7(targetHome);
|
|
10002
10112
|
if (normalized === parse2(normalized).root) {
|
|
10003
10113
|
throw new Error(`Session render target cannot be the filesystem root: ${targetHome}`);
|
|
10004
10114
|
}
|
|
@@ -10234,7 +10344,7 @@ function planSessionRender(input) {
|
|
|
10234
10344
|
sourceId: input.providerConfig.sourceId,
|
|
10235
10345
|
selectedPayloadSha256: sha2566(input.providerConfig.content),
|
|
10236
10346
|
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2566(input.providerConfig.content),
|
|
10237
|
-
selected: !
|
|
10347
|
+
selected: !existsSync6(joinTarget(targetHome, adapter.configFile))
|
|
10238
10348
|
}
|
|
10239
10349
|
} : {},
|
|
10240
10350
|
...projectContext ? {
|
|
@@ -10596,10 +10706,10 @@ function layerFromIdentityKind(kind, exportShape) {
|
|
|
10596
10706
|
function contentFromIdentitySourcePaths(sourcePaths, exportPath, sourceId) {
|
|
10597
10707
|
if (sourcePaths.length === 0 || !exportPath)
|
|
10598
10708
|
return;
|
|
10599
|
-
const
|
|
10709
|
+
const baseDir2 = dirname3(resolveSessionPath(exportPath));
|
|
10600
10710
|
const contents = [];
|
|
10601
10711
|
for (const sourcePath of sourcePaths) {
|
|
10602
|
-
const content = readIdentitySourcePath(sourcePath,
|
|
10712
|
+
const content = readIdentitySourcePath(sourcePath, baseDir2, sourceId);
|
|
10603
10713
|
if (content !== undefined)
|
|
10604
10714
|
contents.push({ path: sourcePath.path, content });
|
|
10605
10715
|
}
|
|
@@ -10612,9 +10722,9 @@ ${item.content.trimEnd()}`).join(`
|
|
|
10612
10722
|
|
|
10613
10723
|
`));
|
|
10614
10724
|
}
|
|
10615
|
-
function readIdentitySourcePath(sourcePath,
|
|
10616
|
-
const resolvedPath = resolveIdentitySourcePath(sourcePath.path,
|
|
10617
|
-
if (!
|
|
10725
|
+
function readIdentitySourcePath(sourcePath, baseDir2, sourceId) {
|
|
10726
|
+
const resolvedPath = resolveIdentitySourcePath(sourcePath.path, baseDir2, sourceId);
|
|
10727
|
+
if (!existsSync6(resolvedPath)) {
|
|
10618
10728
|
if (sourcePath.required) {
|
|
10619
10729
|
throw new Error(`Required identity instruction source path not found for ${sourceId}: ${sourcePath.path}`);
|
|
10620
10730
|
}
|
|
@@ -10624,27 +10734,27 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
|
|
|
10624
10734
|
if (!stat.isFile()) {
|
|
10625
10735
|
throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
|
|
10626
10736
|
}
|
|
10627
|
-
const realBase = realpathSync2(
|
|
10737
|
+
const realBase = realpathSync2(baseDir2);
|
|
10628
10738
|
const realPath = realpathSync2(resolvedPath);
|
|
10629
10739
|
if (!pathIsInside(realPath, realBase)) {
|
|
10630
10740
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${sourcePath.path}`);
|
|
10631
10741
|
}
|
|
10632
10742
|
return readFileSync4(realPath, "utf-8");
|
|
10633
10743
|
}
|
|
10634
|
-
function resolveIdentitySourcePath(path,
|
|
10744
|
+
function resolveIdentitySourcePath(path, baseDir2, sourceId) {
|
|
10635
10745
|
const cleaned = cleanSessionPathInput(path);
|
|
10636
10746
|
if (!cleaned)
|
|
10637
10747
|
throw new Error(`Identity instruction source path cannot be empty for ${sourceId}.`);
|
|
10638
10748
|
if (cleaned.includes("\\"))
|
|
10639
10749
|
throw new Error(`Identity instruction source path must use POSIX separators for ${sourceId}: ${path}`);
|
|
10640
|
-
const resolvedPath = isAbsolute3(cleaned) ?
|
|
10641
|
-
if (!pathIsInside(resolvedPath,
|
|
10750
|
+
const resolvedPath = isAbsolute3(cleaned) ? resolve7(cleaned) : resolve7(baseDir2, cleaned);
|
|
10751
|
+
if (!pathIsInside(resolvedPath, resolve7(baseDir2))) {
|
|
10642
10752
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${path}`);
|
|
10643
10753
|
}
|
|
10644
10754
|
return resolvedPath;
|
|
10645
10755
|
}
|
|
10646
|
-
function pathIsInside(path,
|
|
10647
|
-
const rel = relative2(
|
|
10756
|
+
function pathIsInside(path, baseDir2) {
|
|
10757
|
+
const rel = relative2(baseDir2, path);
|
|
10648
10758
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
10649
10759
|
}
|
|
10650
10760
|
function providerTargetsTool(targets, tool) {
|
|
@@ -12045,16 +12155,16 @@ function resolveConfigStore(env = process.env) {
|
|
|
12045
12155
|
return cloud ? new CloudConfigStore(cloud) : new LocalConfigStore;
|
|
12046
12156
|
}
|
|
12047
12157
|
// src/status.ts
|
|
12048
|
-
import { existsSync as
|
|
12158
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
12049
12159
|
|
|
12050
12160
|
// src/lib/apply.ts
|
|
12051
|
-
import { existsSync as
|
|
12052
|
-
import { basename as basename5, dirname as dirname5, join as
|
|
12053
|
-
import { homedir as
|
|
12161
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, realpathSync as realpathSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
12162
|
+
import { basename as basename5, dirname as dirname5, join as join10, resolve as resolve8 } from "path";
|
|
12163
|
+
import { homedir as homedir7 } from "os";
|
|
12054
12164
|
|
|
12055
12165
|
// src/lib/session-render-ownership.ts
|
|
12056
|
-
import { existsSync as
|
|
12057
|
-
import { dirname as dirname4, join as
|
|
12166
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
12167
|
+
import { dirname as dirname4, join as join9, parse as parse3, relative as relative3, sep } from "path";
|
|
12058
12168
|
var MANIFEST_ANCESTOR_LIMIT = 24;
|
|
12059
12169
|
var MANAGED_PATH_SEGMENTS = SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS.map((managedPath) => managedPath.split("/").filter(Boolean));
|
|
12060
12170
|
var manifestCache = new Map;
|
|
@@ -12076,7 +12186,7 @@ function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
|
12076
12186
|
function readManifestRelativePaths(manifestPath) {
|
|
12077
12187
|
let stats;
|
|
12078
12188
|
try {
|
|
12079
|
-
if (!
|
|
12189
|
+
if (!existsSync7(manifestPath))
|
|
12080
12190
|
return null;
|
|
12081
12191
|
stats = statSync4(manifestPath);
|
|
12082
12192
|
} catch {
|
|
@@ -12105,7 +12215,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
|
12105
12215
|
const root = parse3(absolutePath2).root;
|
|
12106
12216
|
let home = dirname4(absolutePath2);
|
|
12107
12217
|
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
12108
|
-
const manifestPath =
|
|
12218
|
+
const manifestPath = join9(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
12109
12219
|
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
12110
12220
|
if (relativePaths) {
|
|
12111
12221
|
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
@@ -12125,13 +12235,13 @@ function sessionRenderOwnsPath(absolutePath2) {
|
|
|
12125
12235
|
|
|
12126
12236
|
// src/lib/apply.ts
|
|
12127
12237
|
function getConfigHome() {
|
|
12128
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
12238
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir7();
|
|
12129
12239
|
}
|
|
12130
12240
|
function expandPath(p) {
|
|
12131
12241
|
if (p.startsWith("~/")) {
|
|
12132
|
-
return
|
|
12242
|
+
return resolve8(getConfigHome(), p.slice(2));
|
|
12133
12243
|
}
|
|
12134
|
-
return
|
|
12244
|
+
return resolve8(p);
|
|
12135
12245
|
}
|
|
12136
12246
|
function normalizeTargetPath(p) {
|
|
12137
12247
|
const expanded = expandPath(p);
|
|
@@ -12141,9 +12251,9 @@ function normalizeTargetPath(p) {
|
|
|
12141
12251
|
let current = expanded;
|
|
12142
12252
|
const missingSegments = [];
|
|
12143
12253
|
while (true) {
|
|
12144
|
-
if (
|
|
12254
|
+
if (existsSync8(current)) {
|
|
12145
12255
|
try {
|
|
12146
|
-
return
|
|
12256
|
+
return resolve8(realpathSync3(current), ...missingSegments);
|
|
12147
12257
|
} catch {
|
|
12148
12258
|
return expanded;
|
|
12149
12259
|
}
|
|
@@ -12172,11 +12282,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
12172
12282
|
}
|
|
12173
12283
|
const path = expandPath(renderedTargetPath);
|
|
12174
12284
|
const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
|
|
12175
|
-
const previousContent =
|
|
12285
|
+
const previousContent = existsSync8(path) ? readFileSync6(path, "utf-8") : null;
|
|
12176
12286
|
const changed = previousContent !== renderedForTarget;
|
|
12177
12287
|
if (!opts.dryRun) {
|
|
12178
12288
|
const dir = dirname5(path);
|
|
12179
|
-
if (!
|
|
12289
|
+
if (!existsSync8(dir)) {
|
|
12180
12290
|
mkdirSync3(dir, { recursive: true });
|
|
12181
12291
|
}
|
|
12182
12292
|
if (previousContent !== null && changed) {
|
|
@@ -12210,7 +12320,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
|
12210
12320
|
let current;
|
|
12211
12321
|
try {
|
|
12212
12322
|
const path = expandPath(targetPath);
|
|
12213
|
-
if (!
|
|
12323
|
+
if (!existsSync8(path))
|
|
12214
12324
|
return [];
|
|
12215
12325
|
current = readFileSync6(path, "utf-8");
|
|
12216
12326
|
} catch {
|
|
@@ -12536,14 +12646,14 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
|
12536
12646
|
getConfigHome(),
|
|
12537
12647
|
opts.vars?.["HOME_DIR"]
|
|
12538
12648
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
12539
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
12649
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join10(home, ...relativePath.split("/"))))))
|
|
12540
12650
|
return true;
|
|
12541
12651
|
return sessionRenderOwnsPath(normalized);
|
|
12542
12652
|
}
|
|
12543
12653
|
|
|
12544
12654
|
// src/lib/package-version.ts
|
|
12545
|
-
import { existsSync as
|
|
12546
|
-
import { dirname as dirname6, join as
|
|
12655
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
12656
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
12547
12657
|
import { fileURLToPath } from "url";
|
|
12548
12658
|
var cached = null;
|
|
12549
12659
|
function getPackageVersion() {
|
|
@@ -12552,8 +12662,8 @@ function getPackageVersion() {
|
|
|
12552
12662
|
try {
|
|
12553
12663
|
let dir = dirname6(fileURLToPath(import.meta.url));
|
|
12554
12664
|
for (let i = 0;i < 8; i++) {
|
|
12555
|
-
const pkgPath =
|
|
12556
|
-
if (
|
|
12665
|
+
const pkgPath = join11(dir, "package.json");
|
|
12666
|
+
if (existsSync9(pkgPath)) {
|
|
12557
12667
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
12558
12668
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
12559
12669
|
cached = pkg.version;
|
|
@@ -12574,7 +12684,7 @@ function getPackageVersion() {
|
|
|
12574
12684
|
import { createHash as createHash8 } from "crypto";
|
|
12575
12685
|
import { spawnSync } from "child_process";
|
|
12576
12686
|
import {
|
|
12577
|
-
existsSync as
|
|
12687
|
+
existsSync as existsSync10,
|
|
12578
12688
|
lstatSync as lstatSync4,
|
|
12579
12689
|
mkdirSync as mkdirSync4,
|
|
12580
12690
|
readFileSync as readFileSync8,
|
|
@@ -12582,8 +12692,8 @@ import {
|
|
|
12582
12692
|
rmSync as rmSync3,
|
|
12583
12693
|
writeFileSync as writeFileSync3
|
|
12584
12694
|
} from "fs";
|
|
12585
|
-
import { homedir as
|
|
12586
|
-
import { dirname as dirname7, join as
|
|
12695
|
+
import { homedir as homedir8 } from "os";
|
|
12696
|
+
import { dirname as dirname7, join as join12, parse as parse4, relative as relative4, resolve as resolve9 } from "path";
|
|
12587
12697
|
var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
|
|
12588
12698
|
var INBOX_SKILL_MARKERS = [
|
|
12589
12699
|
[".claude", "skills", "inbox", "SKILL.md"],
|
|
@@ -12604,13 +12714,13 @@ function lstatOrNull(path) {
|
|
|
12604
12714
|
}
|
|
12605
12715
|
}
|
|
12606
12716
|
function findSymlinkedAncestor(path) {
|
|
12607
|
-
const normalized =
|
|
12717
|
+
const normalized = resolve9(path);
|
|
12608
12718
|
const parsed = parse4(normalized);
|
|
12609
12719
|
let current = parsed.root;
|
|
12610
12720
|
const rel = relative4(parsed.root, normalized);
|
|
12611
12721
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
12612
|
-
current =
|
|
12613
|
-
if (!
|
|
12722
|
+
current = join12(current, segment);
|
|
12723
|
+
if (!existsSync10(current))
|
|
12614
12724
|
return null;
|
|
12615
12725
|
if (lstatSync4(current).isSymbolicLink())
|
|
12616
12726
|
return current;
|
|
@@ -12627,11 +12737,11 @@ function packagedInboxSkillPath(explicitPath) {
|
|
|
12627
12737
|
if (explicitPath)
|
|
12628
12738
|
return explicitPath;
|
|
12629
12739
|
const candidates = [
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12740
|
+
join12(import.meta.dir, "..", "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
12741
|
+
join12(import.meta.dir, "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
12742
|
+
join12(process.cwd(), "assets", "skills", "inbox", "SKILL.md")
|
|
12633
12743
|
];
|
|
12634
|
-
const found = candidates.find((candidate) =>
|
|
12744
|
+
const found = candidates.find((candidate) => existsSync10(candidate));
|
|
12635
12745
|
if (!found) {
|
|
12636
12746
|
throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
|
|
12637
12747
|
}
|
|
@@ -12680,8 +12790,8 @@ function compareVersions(left, right) {
|
|
|
12680
12790
|
}
|
|
12681
12791
|
return 0;
|
|
12682
12792
|
}
|
|
12683
|
-
function inspectSkillMarkers(
|
|
12684
|
-
return INBOX_SKILL_MARKERS.map((parts) =>
|
|
12793
|
+
function inspectSkillMarkers(homeDir3) {
|
|
12794
|
+
return INBOX_SKILL_MARKERS.map((parts) => join12(homeDir3, ...parts)).map((path) => {
|
|
12685
12795
|
const stat = lstatOrNull(path);
|
|
12686
12796
|
if (!stat)
|
|
12687
12797
|
return null;
|
|
@@ -12697,9 +12807,9 @@ function inspectSkillMarkers(homeDir2) {
|
|
|
12697
12807
|
}).filter((snapshot) => snapshot !== null);
|
|
12698
12808
|
}
|
|
12699
12809
|
function inspectInbox(options) {
|
|
12700
|
-
const
|
|
12810
|
+
const homeDir3 = options.homeDir ?? homedir8();
|
|
12701
12811
|
const runtimeCommand = options.conversationsCommand ?? "conversations";
|
|
12702
|
-
const snapshots = inspectSkillMarkers(
|
|
12812
|
+
const snapshots = inspectSkillMarkers(homeDir3);
|
|
12703
12813
|
const skillPresent = snapshots.length > 0;
|
|
12704
12814
|
let canonicalContent = null;
|
|
12705
12815
|
let canonicalSha256 = null;
|
|
@@ -13013,7 +13123,7 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
|
13013
13123
|
continue;
|
|
13014
13124
|
knownTargets += 1;
|
|
13015
13125
|
const targetPath = expandPath(config.target_path);
|
|
13016
|
-
if (!
|
|
13126
|
+
if (!existsSync11(targetPath)) {
|
|
13017
13127
|
missingTargets += 1;
|
|
13018
13128
|
continue;
|
|
13019
13129
|
}
|
|
@@ -13109,8 +13219,8 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
|
13109
13219
|
}
|
|
13110
13220
|
// src/lib/provider-context.ts
|
|
13111
13221
|
import { createHash as createHash9 } from "crypto";
|
|
13112
|
-
import { existsSync as
|
|
13113
|
-
import { join as
|
|
13222
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
|
|
13223
|
+
import { join as join13 } from "path";
|
|
13114
13224
|
var PROVIDER_CONTEXT_DIR = ".hasna/provider-context";
|
|
13115
13225
|
var PROVIDER_CONTEXT_MANIFEST = "manifest.json";
|
|
13116
13226
|
var PROVIDER_CONTEXT_SCHEMA = "hasna.instructions.provider-context/v1";
|
|
@@ -13255,17 +13365,17 @@ function resolveAndRenderProviderContext(opts) {
|
|
|
13255
13365
|
const recordedEndpoint = originAccepted ? `${opts.origin.host}${opts.origin.pathPrefix || ""}` : null;
|
|
13256
13366
|
const reason = entry === null && opts.rawEndpoint ? originAccepted ? `endpoint "${recordedEndpoint}" is not in the provider-context registry; using the invariant fragment` : "endpoint rejected (embedded credentials or unparseable); using the invariant fragment" : null;
|
|
13257
13367
|
const content = renderProviderFragment(entry);
|
|
13258
|
-
const dir =
|
|
13259
|
-
if (!
|
|
13368
|
+
const dir = join13(opts.homeDir, PROVIDER_CONTEXT_DIR);
|
|
13369
|
+
if (!existsSync12(dir))
|
|
13260
13370
|
mkdirSync5(dir, { recursive: true });
|
|
13261
13371
|
const filename = `${entry ? entry.key : "invariant"}.md`;
|
|
13262
|
-
const fragmentPath2 =
|
|
13372
|
+
const fragmentPath2 = join13(dir, filename);
|
|
13263
13373
|
const fragmentSha256 = sha2569(content);
|
|
13264
13374
|
writeFileSync4(fragmentPath2, content, "utf8");
|
|
13265
|
-
const manifestPath =
|
|
13375
|
+
const manifestPath = join13(dir, PROVIDER_CONTEXT_MANIFEST);
|
|
13266
13376
|
let manifest = { schema: PROVIDER_CONTEXT_SCHEMA, fragments: {} };
|
|
13267
13377
|
try {
|
|
13268
|
-
if (
|
|
13378
|
+
if (existsSync12(manifestPath)) {
|
|
13269
13379
|
const parsed = JSON.parse(readFileSync10(manifestPath, "utf8"));
|
|
13270
13380
|
if (parsed && typeof parsed === "object")
|
|
13271
13381
|
manifest = parsed;
|
|
@@ -13377,9 +13487,9 @@ var PG_MIGRATIONS = [
|
|
|
13377
13487
|
];
|
|
13378
13488
|
// src/lib/station-profile.ts
|
|
13379
13489
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
13380
|
-
import { existsSync as
|
|
13381
|
-
import { arch as osArch, homedir as
|
|
13382
|
-
import { dirname as dirname8, join as
|
|
13490
|
+
import { existsSync as existsSync13, lstatSync as lstatSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync11, readdirSync, writeFileSync as writeFileSync5 } from "fs";
|
|
13491
|
+
import { arch as osArch, homedir as homedir9, hostname as osHostname, platform as osPlatform, userInfo as osUserInfo } from "os";
|
|
13492
|
+
import { dirname as dirname8, join as join14 } from "path";
|
|
13383
13493
|
var STATION_PROFILE_CACHE_FILENAME = "station-profile.md";
|
|
13384
13494
|
var STATION_PROFILE_SOURCE_ID = "station-profile";
|
|
13385
13495
|
var STATION_PROFILE_LAYER = "machine";
|
|
@@ -13389,22 +13499,21 @@ var STATION_PROFILE_FULL_NAMES_MAX = 6;
|
|
|
13389
13499
|
var STATION_PROFILE_PRIMARY_SCOPE = "@hasna";
|
|
13390
13500
|
var MACHINES_MANIFEST_PATH_ENV = "HASNA_MACHINES_MANIFEST_PATH";
|
|
13391
13501
|
var BUN_INSTALL_ENV = "BUN_INSTALL";
|
|
13392
|
-
function
|
|
13393
|
-
return env["HOME"] || env["USERPROFILE"] ||
|
|
13502
|
+
function homeDir3(env = process.env) {
|
|
13503
|
+
return env["HOME"] || env["USERPROFILE"] || homedir9();
|
|
13394
13504
|
}
|
|
13395
13505
|
function getStationProfileCachePath(env = process.env) {
|
|
13396
|
-
|
|
13397
|
-
return join13(resolve9(root), STATION_PROFILE_CACHE_FILENAME);
|
|
13506
|
+
return join14(getRawStoreRoot(env), STATION_PROFILE_CACHE_FILENAME);
|
|
13398
13507
|
}
|
|
13399
13508
|
function getMachinesManifestPath(env = process.env) {
|
|
13400
|
-
return env[MACHINES_MANIFEST_PATH_ENV] ||
|
|
13509
|
+
return env[MACHINES_MANIFEST_PATH_ENV] || join14(homeDir3(env), ".hasna", "machines", "machines.json");
|
|
13401
13510
|
}
|
|
13402
13511
|
function getBunGlobalModulesDir(env = process.env) {
|
|
13403
|
-
return
|
|
13512
|
+
return join14(env[BUN_INSTALL_ENV] || join14(homeDir3(env), ".bun"), "install", "global", "node_modules");
|
|
13404
13513
|
}
|
|
13405
13514
|
function readMachinesManifest(path) {
|
|
13406
13515
|
try {
|
|
13407
|
-
if (!
|
|
13516
|
+
if (!existsSync13(path))
|
|
13408
13517
|
return null;
|
|
13409
13518
|
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
13410
13519
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -13460,9 +13569,9 @@ function probeMachineStatus(machineId) {
|
|
|
13460
13569
|
function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
13461
13570
|
const hostname2 = osHostname();
|
|
13462
13571
|
const record = findLocalManifestMachine(readMachinesManifest(getMachinesManifestPath(env)), hostname2);
|
|
13463
|
-
const home =
|
|
13572
|
+
const home = homeDir3(env);
|
|
13464
13573
|
const platform = stringField(record, "platform") ?? osPlatform();
|
|
13465
|
-
const workspacePath = stringField(record, "workspacePath") ??
|
|
13574
|
+
const workspacePath = stringField(record, "workspacePath") ?? join14(home, platform === "darwin" ? "Workspace" : "workspace");
|
|
13466
13575
|
const machine = {
|
|
13467
13576
|
id: stringField(record, "id") ?? hostname2,
|
|
13468
13577
|
hostname: stringField(record, "hostname") ?? hostname2,
|
|
@@ -13479,9 +13588,9 @@ function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
|
13479
13588
|
return machine;
|
|
13480
13589
|
}
|
|
13481
13590
|
function scopedPackageNames(modulesDir, scope) {
|
|
13482
|
-
const scopeDir =
|
|
13591
|
+
const scopeDir = join14(modulesDir, scope);
|
|
13483
13592
|
try {
|
|
13484
|
-
if (!
|
|
13593
|
+
if (!existsSync13(scopeDir))
|
|
13485
13594
|
return null;
|
|
13486
13595
|
return readdirNames(scopeDir).sort();
|
|
13487
13596
|
} catch {
|
|
@@ -13491,7 +13600,7 @@ function scopedPackageNames(modulesDir, scope) {
|
|
|
13491
13600
|
function readdirNames(dir) {
|
|
13492
13601
|
return readdirSync(dir).filter((name) => {
|
|
13493
13602
|
try {
|
|
13494
|
-
return lstatSync5(
|
|
13603
|
+
return lstatSync5(join14(dir, name)).isDirectory();
|
|
13495
13604
|
} catch {
|
|
13496
13605
|
return false;
|
|
13497
13606
|
}
|
|
@@ -13501,7 +13610,7 @@ function resolveStationProfilePackages(env = process.env) {
|
|
|
13501
13610
|
const modulesDir = getBunGlobalModulesDir(env);
|
|
13502
13611
|
let scopeDirs;
|
|
13503
13612
|
try {
|
|
13504
|
-
if (!
|
|
13613
|
+
if (!existsSync13(modulesDir))
|
|
13505
13614
|
return null;
|
|
13506
13615
|
scopeDirs = readdirNames(modulesDir).filter((name) => name.startsWith("@") && name.toLowerCase().includes("hasna"));
|
|
13507
13616
|
} catch {
|
|
@@ -13576,7 +13685,7 @@ function refreshStationProfile(options = {}) {
|
|
|
13576
13685
|
const path = getStationProfileCachePath(env);
|
|
13577
13686
|
const generatedAt = new Date().toISOString();
|
|
13578
13687
|
if (!options.dryRun) {
|
|
13579
|
-
const existing =
|
|
13688
|
+
const existing = existsSync13(path) ? readFileSync11(path, "utf8") : null;
|
|
13580
13689
|
if (existing !== content) {
|
|
13581
13690
|
mkdirSync6(dirname8(path), { recursive: true });
|
|
13582
13691
|
writeFileSync5(path, content, "utf8");
|
|
@@ -13595,7 +13704,7 @@ function refreshStationProfile(options = {}) {
|
|
|
13595
13704
|
function readStationProfile(env = process.env) {
|
|
13596
13705
|
const path = getStationProfileCachePath(env);
|
|
13597
13706
|
try {
|
|
13598
|
-
if (!
|
|
13707
|
+
if (!existsSync13(path))
|
|
13599
13708
|
return null;
|
|
13600
13709
|
return readFileSync11(path, "utf8");
|
|
13601
13710
|
} catch {
|
|
@@ -13619,14 +13728,14 @@ function stationProfileSource(env = process.env) {
|
|
|
13619
13728
|
// src/lib/session-apply.ts
|
|
13620
13729
|
import { createHash as createHash10, randomUUID as randomUUID4 } from "crypto";
|
|
13621
13730
|
import {
|
|
13622
|
-
existsSync as
|
|
13731
|
+
existsSync as existsSync14,
|
|
13623
13732
|
lstatSync as lstatSync6,
|
|
13624
13733
|
mkdirSync as mkdirSync7,
|
|
13625
13734
|
readFileSync as readFileSync12,
|
|
13626
13735
|
readdirSync as readdirSync2,
|
|
13627
13736
|
statSync as statSync5
|
|
13628
13737
|
} from "fs";
|
|
13629
|
-
import { dirname as dirname9, isAbsolute as isAbsolute4, join as
|
|
13738
|
+
import { dirname as dirname9, isAbsolute as isAbsolute4, join as join15, parse as parse5, relative as relative5, resolve as resolve10 } from "path";
|
|
13630
13739
|
class SessionApplyError extends Error {
|
|
13631
13740
|
constructor(message) {
|
|
13632
13741
|
super(message);
|
|
@@ -13757,7 +13866,7 @@ function assertClaudeAuthorityStillClear(plan, targetHome, ownedClaudeAuthoritie
|
|
|
13757
13866
|
throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
|
|
13758
13867
|
}
|
|
13759
13868
|
function ensureSessionTargetHome(targetHome) {
|
|
13760
|
-
if (!
|
|
13869
|
+
if (!existsSync14(targetHome))
|
|
13761
13870
|
mkdirSync7(targetHome, { recursive: true, mode: 448 });
|
|
13762
13871
|
assertSafeTargetHome(targetHome);
|
|
13763
13872
|
}
|
|
@@ -13780,7 +13889,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
13780
13889
|
const drifted = [];
|
|
13781
13890
|
for (const file of previousManifest.files) {
|
|
13782
13891
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
13783
|
-
if (!
|
|
13892
|
+
if (!existsSync14(target)) {
|
|
13784
13893
|
missing.push({
|
|
13785
13894
|
path: target,
|
|
13786
13895
|
relativePath: file.relativePath,
|
|
@@ -13932,7 +14041,7 @@ function requiredRestoreHash(file) {
|
|
|
13932
14041
|
}
|
|
13933
14042
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
13934
14043
|
const resolved = resolve10(snapshotPath);
|
|
13935
|
-
if (!
|
|
14044
|
+
if (!existsSync14(resolved))
|
|
13936
14045
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
13937
14046
|
const stat = lstatSync6(resolved);
|
|
13938
14047
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -14199,7 +14308,7 @@ function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
|
14199
14308
|
}
|
|
14200
14309
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
14201
14310
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
14202
|
-
const previousContent =
|
|
14311
|
+
const previousContent = existsSync14(target) ? readFileSync12(target, "utf-8") : null;
|
|
14203
14312
|
const previousSha256 = previousContent === null ? null : sha25610(previousContent);
|
|
14204
14313
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
14205
14314
|
const changed = previousContent !== file.content;
|
|
@@ -14298,7 +14407,7 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
14298
14407
|
}
|
|
14299
14408
|
function planStaleFileResult(file, targetHome, options) {
|
|
14300
14409
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
14301
|
-
if (!
|
|
14410
|
+
if (!existsSync14(target))
|
|
14302
14411
|
return null;
|
|
14303
14412
|
const previousContent = readFileSync12(target, "utf-8");
|
|
14304
14413
|
const previousSha256 = sha25610(previousContent);
|
|
@@ -14366,7 +14475,7 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
14366
14475
|
return target;
|
|
14367
14476
|
}
|
|
14368
14477
|
function readPreviousManifest(path) {
|
|
14369
|
-
if (!
|
|
14478
|
+
if (!existsSync14(path))
|
|
14370
14479
|
return null;
|
|
14371
14480
|
try {
|
|
14372
14481
|
const parsed = JSON.parse(readFileSync12(path, "utf-8"));
|
|
@@ -14408,7 +14517,7 @@ function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
|
14408
14517
|
}
|
|
14409
14518
|
function currentSessionFileHash(path, targetHome) {
|
|
14410
14519
|
assertNoSymlinkSegments2(targetHome, path);
|
|
14411
|
-
if (!
|
|
14520
|
+
if (!existsSync14(path))
|
|
14412
14521
|
return null;
|
|
14413
14522
|
const stat = lstatSync6(path);
|
|
14414
14523
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -14423,7 +14532,7 @@ function requiredPreviousHash(result) {
|
|
|
14423
14532
|
return result.previousSha256;
|
|
14424
14533
|
}
|
|
14425
14534
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
14426
|
-
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) =>
|
|
14535
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync14(result.path)).map((result) => {
|
|
14427
14536
|
const content = readFileSync12(result.path, "utf-8");
|
|
14428
14537
|
return {
|
|
14429
14538
|
path: result.path,
|
|
@@ -14495,7 +14604,7 @@ function assertSafeTargetHome(targetHome) {
|
|
|
14495
14604
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
14496
14605
|
}
|
|
14497
14606
|
assertNoSymlinkAncestors3(normalized);
|
|
14498
|
-
if (
|
|
14607
|
+
if (existsSync14(normalized) && lstatSync6(normalized).isSymbolicLink()) {
|
|
14499
14608
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
14500
14609
|
}
|
|
14501
14610
|
return normalized;
|
|
@@ -14505,8 +14614,8 @@ function assertNoSymlinkSegments2(root, target) {
|
|
|
14505
14614
|
const rel = relative5(root, target);
|
|
14506
14615
|
let current = root;
|
|
14507
14616
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14508
|
-
current =
|
|
14509
|
-
if (
|
|
14617
|
+
current = join15(current, segment);
|
|
14618
|
+
if (existsSync14(current) && lstatSync6(current).isSymbolicLink()) {
|
|
14510
14619
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
14511
14620
|
}
|
|
14512
14621
|
}
|
|
@@ -14517,8 +14626,8 @@ function assertNoSymlinkAncestors3(path) {
|
|
|
14517
14626
|
let current = parsed.root;
|
|
14518
14627
|
const rel = relative5(parsed.root, normalized);
|
|
14519
14628
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14520
|
-
current =
|
|
14521
|
-
if (!
|
|
14629
|
+
current = join15(current, segment);
|
|
14630
|
+
if (!existsSync14(current))
|
|
14522
14631
|
return;
|
|
14523
14632
|
if (lstatSync6(current).isSymbolicLink()) {
|
|
14524
14633
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
@@ -14829,13 +14938,13 @@ async function ensureDangerousOperationGuardStandardConfig(store = resolveConfig
|
|
|
14829
14938
|
}
|
|
14830
14939
|
}
|
|
14831
14940
|
// src/lib/sync.ts
|
|
14832
|
-
import { existsSync as
|
|
14833
|
-
import { basename as basename6, extname as extname3, join as
|
|
14941
|
+
import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync14 } from "fs";
|
|
14942
|
+
import { basename as basename6, extname as extname3, join as join17 } from "path";
|
|
14834
14943
|
|
|
14835
14944
|
// src/lib/sync-dir.ts
|
|
14836
|
-
import { existsSync as
|
|
14837
|
-
import { join as
|
|
14838
|
-
import { homedir as
|
|
14945
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync6 } from "fs";
|
|
14946
|
+
import { join as join16, relative as relative6 } from "path";
|
|
14947
|
+
import { homedir as homedir10 } from "os";
|
|
14839
14948
|
var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
14840
14949
|
function shouldSkip(p) {
|
|
14841
14950
|
return SKIP.some((s) => p.includes(s));
|
|
@@ -14843,11 +14952,11 @@ function shouldSkip(p) {
|
|
|
14843
14952
|
async function syncFromDir(dir, opts = {}) {
|
|
14844
14953
|
const store = opts.store ?? resolveConfigStore();
|
|
14845
14954
|
const absDir = expandPath(dir);
|
|
14846
|
-
if (!
|
|
14955
|
+
if (!existsSync15(absDir))
|
|
14847
14956
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
14848
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync3(absDir).map((f) =>
|
|
14957
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync3(absDir).map((f) => join16(absDir, f)).filter((f) => statSync6(f).isFile());
|
|
14849
14958
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
14850
|
-
const home =
|
|
14959
|
+
const home = homedir10();
|
|
14851
14960
|
const allConfigs = await store.listConfigs();
|
|
14852
14961
|
for (const file of files) {
|
|
14853
14962
|
if (shouldSkip(file)) {
|
|
@@ -14882,7 +14991,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14882
14991
|
}
|
|
14883
14992
|
async function syncToDir(dir, opts = {}) {
|
|
14884
14993
|
const store = opts.store ?? resolveConfigStore();
|
|
14885
|
-
const home =
|
|
14994
|
+
const home = homedir10();
|
|
14886
14995
|
const absDir = expandPath(dir);
|
|
14887
14996
|
const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
|
|
14888
14997
|
const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
|
|
@@ -14906,7 +15015,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
14906
15015
|
}
|
|
14907
15016
|
function walkDir(dir, files = []) {
|
|
14908
15017
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
14909
|
-
const full =
|
|
15018
|
+
const full = join16(dir, entry.name);
|
|
14910
15019
|
if (shouldSkip(full))
|
|
14911
15020
|
continue;
|
|
14912
15021
|
if (entry.isDirectory())
|
|
@@ -14967,7 +15076,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
14967
15076
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
14968
15077
|
}
|
|
14969
15078
|
function hasClaudePromptSource() {
|
|
14970
|
-
return
|
|
15079
|
+
return existsSync16(expandPath("~/.claude/CLAUDE.md"));
|
|
14971
15080
|
}
|
|
14972
15081
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
14973
15082
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -14975,7 +15084,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
14975
15084
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
14976
15085
|
return false;
|
|
14977
15086
|
const stem = basename6(absoluteTargetPath, ".mdc");
|
|
14978
|
-
return
|
|
15087
|
+
return existsSync16(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync16(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
14979
15088
|
}
|
|
14980
15089
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
14981
15090
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -15040,8 +15149,8 @@ async function syncProject(opts) {
|
|
|
15040
15149
|
const allConfigs = await store.listConfigs();
|
|
15041
15150
|
const machine = detectMachineContext();
|
|
15042
15151
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
15043
|
-
const abs =
|
|
15044
|
-
if (!
|
|
15152
|
+
const abs = join17(absDir, pf.file);
|
|
15153
|
+
if (!existsSync16(abs))
|
|
15045
15154
|
continue;
|
|
15046
15155
|
try {
|
|
15047
15156
|
const rawContent = readFileSync14(abs, "utf-8");
|
|
@@ -15073,19 +15182,19 @@ async function syncProject(opts) {
|
|
|
15073
15182
|
}
|
|
15074
15183
|
}
|
|
15075
15184
|
for (const ruleDir of [
|
|
15076
|
-
{ dir:
|
|
15077
|
-
{ dir:
|
|
15078
|
-
{ dir:
|
|
15079
|
-
{ dir:
|
|
15080
|
-
{ dir:
|
|
15081
|
-
{ dir:
|
|
15082
|
-
{ dir:
|
|
15185
|
+
{ dir: join17(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
15186
|
+
{ dir: join17(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
|
|
15187
|
+
{ dir: join17(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
|
|
15188
|
+
{ dir: join17(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
|
|
15189
|
+
{ dir: join17(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
|
|
15190
|
+
{ dir: join17(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
|
|
15191
|
+
{ dir: join17(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
|
|
15083
15192
|
]) {
|
|
15084
|
-
if (!
|
|
15193
|
+
if (!existsSync16(ruleDir.dir))
|
|
15085
15194
|
continue;
|
|
15086
15195
|
const mdFiles = readdirSync4(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
15087
15196
|
for (const f of mdFiles) {
|
|
15088
|
-
const abs =
|
|
15197
|
+
const abs = join17(ruleDir.dir, f);
|
|
15089
15198
|
const raw = readFileSync14(abs, "utf-8");
|
|
15090
15199
|
const redacted = redactContent(raw, "markdown");
|
|
15091
15200
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
@@ -15125,14 +15234,14 @@ async function syncKnown(opts = {}) {
|
|
|
15125
15234
|
for (const known of targets) {
|
|
15126
15235
|
if (known.rulesDir) {
|
|
15127
15236
|
const absDir = expandPath(known.rulesDir);
|
|
15128
|
-
if (!
|
|
15237
|
+
if (!existsSync16(absDir)) {
|
|
15129
15238
|
result.skipped.push(known.rulesDir);
|
|
15130
15239
|
continue;
|
|
15131
15240
|
}
|
|
15132
15241
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
15133
15242
|
const ruleFiles = readdirSync4(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
15134
15243
|
for (const f of ruleFiles) {
|
|
15135
|
-
const abs2 =
|
|
15244
|
+
const abs2 = join17(absDir, f);
|
|
15136
15245
|
const targetPath = abs2.replace(home, "~");
|
|
15137
15246
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
15138
15247
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
@@ -15166,7 +15275,7 @@ async function syncKnown(opts = {}) {
|
|
|
15166
15275
|
continue;
|
|
15167
15276
|
}
|
|
15168
15277
|
const abs = expandPath(known.path);
|
|
15169
|
-
if (!
|
|
15278
|
+
if (!existsSync16(abs)) {
|
|
15170
15279
|
result.skipped.push(known.path);
|
|
15171
15280
|
continue;
|
|
15172
15281
|
}
|
|
@@ -15274,7 +15383,7 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
|
15274
15383
|
}
|
|
15275
15384
|
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
15276
15385
|
const path = expandPath(targetPath);
|
|
15277
|
-
if (!
|
|
15386
|
+
if (!existsSync16(path))
|
|
15278
15387
|
return `(file not found on disk: ${path})`;
|
|
15279
15388
|
const diskContent = readFileSync14(path, "utf-8");
|
|
15280
15389
|
if (diskContent === expectedContent)
|
|
@@ -15434,15 +15543,15 @@ function detectFormat(filePath) {
|
|
|
15434
15543
|
return "text";
|
|
15435
15544
|
}
|
|
15436
15545
|
// src/lib/export.ts
|
|
15437
|
-
import { existsSync as
|
|
15438
|
-
import { join as
|
|
15546
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
15547
|
+
import { join as join18, resolve as resolve11 } from "path";
|
|
15439
15548
|
import { tmpdir } from "os";
|
|
15440
15549
|
async function exportConfigs(outputPath, opts = {}) {
|
|
15441
15550
|
const store = opts.store ?? resolveConfigStore();
|
|
15442
15551
|
const configs = await store.listConfigs(opts.filter);
|
|
15443
15552
|
const absOutput = resolve11(outputPath);
|
|
15444
|
-
const tmpDir =
|
|
15445
|
-
const contentsDir =
|
|
15553
|
+
const tmpDir = join18(tmpdir(), `configs-export-${Date.now()}`);
|
|
15554
|
+
const contentsDir = join18(tmpDir, "contents");
|
|
15446
15555
|
try {
|
|
15447
15556
|
mkdirSync8(contentsDir, { recursive: true });
|
|
15448
15557
|
const manifest = {
|
|
@@ -15450,10 +15559,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
15450
15559
|
exported_at: new Date().toISOString(),
|
|
15451
15560
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
15452
15561
|
};
|
|
15453
|
-
writeFileSync6(
|
|
15562
|
+
writeFileSync6(join18(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
15454
15563
|
for (const config of configs) {
|
|
15455
15564
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
15456
|
-
writeFileSync6(
|
|
15565
|
+
writeFileSync6(join18(contentsDir, fileName), config.content, "utf-8");
|
|
15457
15566
|
}
|
|
15458
15567
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
15459
15568
|
stdout: "pipe",
|
|
@@ -15466,20 +15575,20 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
15466
15575
|
}
|
|
15467
15576
|
return { path: absOutput, count: configs.length };
|
|
15468
15577
|
} finally {
|
|
15469
|
-
if (
|
|
15578
|
+
if (existsSync17(tmpDir)) {
|
|
15470
15579
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
15471
15580
|
}
|
|
15472
15581
|
}
|
|
15473
15582
|
}
|
|
15474
15583
|
// src/lib/import.ts
|
|
15475
|
-
import { existsSync as
|
|
15476
|
-
import { join as
|
|
15584
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync15, rmSync as rmSync5 } from "fs";
|
|
15585
|
+
import { join as join19, resolve as resolve12 } from "path";
|
|
15477
15586
|
import { tmpdir as tmpdir2 } from "os";
|
|
15478
15587
|
async function importConfigs(bundlePath, opts = {}) {
|
|
15479
15588
|
const store = opts.store ?? resolveConfigStore();
|
|
15480
15589
|
const conflict = opts.conflict ?? "skip";
|
|
15481
15590
|
const absPath = resolve12(bundlePath);
|
|
15482
|
-
const tmpDir =
|
|
15591
|
+
const tmpDir = join19(tmpdir2(), `configs-import-${Date.now()}`);
|
|
15483
15592
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
15484
15593
|
try {
|
|
15485
15594
|
mkdirSync9(tmpDir, { recursive: true });
|
|
@@ -15492,15 +15601,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
15492
15601
|
const stderr = await new Response(proc.stderr).text();
|
|
15493
15602
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
15494
15603
|
}
|
|
15495
|
-
const manifestPath =
|
|
15496
|
-
if (!
|
|
15604
|
+
const manifestPath = join19(tmpDir, "manifest.json");
|
|
15605
|
+
if (!existsSync18(manifestPath))
|
|
15497
15606
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
15498
15607
|
const manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
|
|
15499
15608
|
for (const meta of manifest.configs) {
|
|
15500
15609
|
try {
|
|
15501
15610
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
15502
|
-
const contentFile =
|
|
15503
|
-
const content =
|
|
15611
|
+
const contentFile = join19(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
15612
|
+
const content = existsSync18(contentFile) ? readFileSync15(contentFile, "utf-8") : "";
|
|
15504
15613
|
let existing = null;
|
|
15505
15614
|
try {
|
|
15506
15615
|
existing = await store.getConfig(meta.slug);
|
|
@@ -15534,16 +15643,16 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
15534
15643
|
}
|
|
15535
15644
|
return result;
|
|
15536
15645
|
} finally {
|
|
15537
|
-
if (
|
|
15646
|
+
if (existsSync18(tmpDir)) {
|
|
15538
15647
|
rmSync5(tmpDir, { recursive: true, force: true });
|
|
15539
15648
|
}
|
|
15540
15649
|
}
|
|
15541
15650
|
}
|
|
15542
15651
|
// src/lib/package-manager-guard.ts
|
|
15543
15652
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15544
|
-
import { existsSync as
|
|
15545
|
-
import { homedir as
|
|
15546
|
-
import { basename as basename7, dirname as dirname10, isAbsolute as isAbsolute5, join as
|
|
15653
|
+
import { existsSync as existsSync19, lstatSync as lstatSync7, readdirSync as readdirSync5, readFileSync as readFileSync16 } from "fs";
|
|
15654
|
+
import { homedir as homedir11 } from "os";
|
|
15655
|
+
import { basename as basename7, dirname as dirname10, isAbsolute as isAbsolute5, join as join20, relative as relative7, resolve as resolve13 } from "path";
|
|
15547
15656
|
var SKIP_DIRS = new Set([
|
|
15548
15657
|
".git",
|
|
15549
15658
|
"node_modules",
|
|
@@ -15585,7 +15694,7 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15585
15694
|
const findings = [];
|
|
15586
15695
|
let scannedFiles = 0;
|
|
15587
15696
|
for (const root of roots) {
|
|
15588
|
-
if (!
|
|
15697
|
+
if (!existsSync19(root))
|
|
15589
15698
|
continue;
|
|
15590
15699
|
const stat = lstatSync7(root);
|
|
15591
15700
|
if (stat.isFile()) {
|
|
@@ -15612,10 +15721,10 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15612
15721
|
}
|
|
15613
15722
|
}
|
|
15614
15723
|
if (options.includeHome) {
|
|
15615
|
-
const home =
|
|
15724
|
+
const home = homedir11();
|
|
15616
15725
|
for (const name of HOME_FILES) {
|
|
15617
|
-
const file =
|
|
15618
|
-
if (!
|
|
15726
|
+
const file = join20(home, name);
|
|
15727
|
+
if (!existsSync19(file))
|
|
15619
15728
|
continue;
|
|
15620
15729
|
const text = readTextFile(file);
|
|
15621
15730
|
if (text === null)
|
|
@@ -15639,12 +15748,12 @@ function collectRepoFiles(root) {
|
|
|
15639
15748
|
if (entry.isDirectory()) {
|
|
15640
15749
|
if (SKIP_DIRS.has(entry.name))
|
|
15641
15750
|
continue;
|
|
15642
|
-
visit(
|
|
15751
|
+
visit(join20(dir, entry.name));
|
|
15643
15752
|
continue;
|
|
15644
15753
|
}
|
|
15645
15754
|
if (!entry.isFile())
|
|
15646
15755
|
continue;
|
|
15647
|
-
const file =
|
|
15756
|
+
const file = join20(dir, entry.name);
|
|
15648
15757
|
if (shouldScanRepoFile(file))
|
|
15649
15758
|
out.push(file);
|
|
15650
15759
|
}
|
|
@@ -15912,7 +16021,7 @@ function stripInlineComment(value) {
|
|
|
15912
16021
|
return value.replace(/\s[#;].*$/, "").trim();
|
|
15913
16022
|
}
|
|
15914
16023
|
function displayPath(file, root) {
|
|
15915
|
-
const home =
|
|
16024
|
+
const home = homedir11();
|
|
15916
16025
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
15917
16026
|
return "~/" + toPosix(relative7(home, file));
|
|
15918
16027
|
if (isAbsolute5(root) && file.startsWith(root + "/"))
|