@hasna/instructions 0.5.3 → 0.5.5
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 +540 -340
- package/dist/index.js +365 -266
- package/dist/lib/app-home.d.ts +54 -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/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 +187 -73
- package/dist/mcp/server.d.ts.map +1 -1
- 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,7 +8870,7 @@ 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
8876
|
const existing = content.match(CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN);
|
|
@@ -8820,8 +8920,8 @@ function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthori
|
|
|
8820
8920
|
// src/lib/session-authority.ts
|
|
8821
8921
|
import { createHash as createHash5 } from "crypto";
|
|
8822
8922
|
import { lstatSync as lstatSync3, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "fs";
|
|
8823
|
-
import { homedir as
|
|
8824
|
-
import { join as
|
|
8923
|
+
import { homedir as homedir5 } from "os";
|
|
8924
|
+
import { join as join7, resolve as resolve6 } from "path";
|
|
8825
8925
|
var CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH = "AGENTS.md";
|
|
8826
8926
|
var CLAUDE_LEGACY_AUTHORITY_MAX_BYTES = 256 * 1024;
|
|
8827
8927
|
var CLAUDE_LEGACY_MARKERS = [
|
|
@@ -8833,10 +8933,10 @@ function sha2565(content) {
|
|
|
8833
8933
|
return createHash5("sha256").update(content).digest("hex");
|
|
8834
8934
|
}
|
|
8835
8935
|
function configHomeDir() {
|
|
8836
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
8936
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir5();
|
|
8837
8937
|
}
|
|
8838
8938
|
function normalizeOwnedTargetPath(p) {
|
|
8839
|
-
const expanded = p.startsWith("~/") ?
|
|
8939
|
+
const expanded = p.startsWith("~/") ? resolve6(configHomeDir(), p.slice(2)) : resolve6(p);
|
|
8840
8940
|
try {
|
|
8841
8941
|
return realpathSync(expanded);
|
|
8842
8942
|
} catch {
|
|
@@ -8844,7 +8944,7 @@ function normalizeOwnedTargetPath(p) {
|
|
|
8844
8944
|
}
|
|
8845
8945
|
}
|
|
8846
8946
|
function detectClaudeAuthorityConflicts(targetHome, ownedAuthorities = []) {
|
|
8847
|
-
const authorityPath =
|
|
8947
|
+
const authorityPath = resolve6(join7(targetHome, CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH));
|
|
8848
8948
|
let stat;
|
|
8849
8949
|
try {
|
|
8850
8950
|
stat = lstatSync3(authorityPath);
|
|
@@ -9203,13 +9303,13 @@ function yamlQuote2(value) {
|
|
|
9203
9303
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
9204
9304
|
}
|
|
9205
9305
|
function defaultTargetHome(tool, profile, sessionId) {
|
|
9206
|
-
const home = process.env["HOME"] ||
|
|
9207
|
-
return
|
|
9306
|
+
const home = process.env["HOME"] || homedir6();
|
|
9307
|
+
return join8(home, ".hasna", "accounts", "profiles", tool, slug(profile));
|
|
9208
9308
|
}
|
|
9209
9309
|
function joinTarget(targetHome, relativePath) {
|
|
9210
9310
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
9211
9311
|
const safeRelativePath2 = assertSafeRelativePath(relativePath);
|
|
9212
|
-
return
|
|
9312
|
+
return join8(safeTargetHome, ...safeRelativePath2.split("/"));
|
|
9213
9313
|
}
|
|
9214
9314
|
function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
9215
9315
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
@@ -9779,7 +9879,7 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
|
|
|
9779
9879
|
...sources.flatMap((source) => source.resolvedRules.map((rule) => rule.id))
|
|
9780
9880
|
]);
|
|
9781
9881
|
const existingConfigPath = joinTarget(targetHome, adapter.configFile);
|
|
9782
|
-
const selectedConfig =
|
|
9882
|
+
const selectedConfig = existsSync6(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
|
|
9783
9883
|
const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
|
|
9784
9884
|
const config = {
|
|
9785
9885
|
...selectedConfig,
|
|
@@ -9968,7 +10068,7 @@ function adapterFor(input) {
|
|
|
9968
10068
|
return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
|
|
9969
10069
|
}
|
|
9970
10070
|
function getHomeDir() {
|
|
9971
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
10071
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir6();
|
|
9972
10072
|
}
|
|
9973
10073
|
function cleanSessionPathInput(path) {
|
|
9974
10074
|
const trimmed = path.trim();
|
|
@@ -9983,16 +10083,16 @@ function resolveSessionPath(path) {
|
|
|
9983
10083
|
throw new Error("Session render path cannot be empty.");
|
|
9984
10084
|
const home = getHomeDir();
|
|
9985
10085
|
if (cleaned === "~")
|
|
9986
|
-
return
|
|
10086
|
+
return resolve7(home);
|
|
9987
10087
|
if (cleaned.startsWith("~/"))
|
|
9988
|
-
return
|
|
10088
|
+
return resolve7(home, cleaned.slice(2));
|
|
9989
10089
|
if (cleaned === "{{HOME}}" || cleaned === "${HOME}")
|
|
9990
|
-
return
|
|
10090
|
+
return resolve7(home);
|
|
9991
10091
|
if (cleaned.startsWith("{{HOME}}/"))
|
|
9992
|
-
return
|
|
10092
|
+
return resolve7(home, cleaned.slice("{{HOME}}/".length));
|
|
9993
10093
|
if (cleaned.startsWith("${HOME}/"))
|
|
9994
|
-
return
|
|
9995
|
-
return
|
|
10094
|
+
return resolve7(home, cleaned.slice("${HOME}/".length));
|
|
10095
|
+
return resolve7(cleaned);
|
|
9996
10096
|
}
|
|
9997
10097
|
function assertSafeRelativePath(relativePath) {
|
|
9998
10098
|
if (!relativePath.trim())
|
|
@@ -10008,7 +10108,7 @@ function assertSafeRelativePath(relativePath) {
|
|
|
10008
10108
|
function assertSafeTargetRoot(targetHome) {
|
|
10009
10109
|
if (!isAbsolute3(targetHome))
|
|
10010
10110
|
throw new Error(`Session render target must be an absolute path: ${targetHome}`);
|
|
10011
|
-
const normalized =
|
|
10111
|
+
const normalized = resolve7(targetHome);
|
|
10012
10112
|
if (normalized === parse2(normalized).root) {
|
|
10013
10113
|
throw new Error(`Session render target cannot be the filesystem root: ${targetHome}`);
|
|
10014
10114
|
}
|
|
@@ -10244,7 +10344,7 @@ function planSessionRender(input) {
|
|
|
10244
10344
|
sourceId: input.providerConfig.sourceId,
|
|
10245
10345
|
selectedPayloadSha256: sha2566(input.providerConfig.content),
|
|
10246
10346
|
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2566(input.providerConfig.content),
|
|
10247
|
-
selected: !
|
|
10347
|
+
selected: !existsSync6(joinTarget(targetHome, adapter.configFile))
|
|
10248
10348
|
}
|
|
10249
10349
|
} : {},
|
|
10250
10350
|
...projectContext ? {
|
|
@@ -10606,10 +10706,10 @@ function layerFromIdentityKind(kind, exportShape) {
|
|
|
10606
10706
|
function contentFromIdentitySourcePaths(sourcePaths, exportPath, sourceId) {
|
|
10607
10707
|
if (sourcePaths.length === 0 || !exportPath)
|
|
10608
10708
|
return;
|
|
10609
|
-
const
|
|
10709
|
+
const baseDir2 = dirname3(resolveSessionPath(exportPath));
|
|
10610
10710
|
const contents = [];
|
|
10611
10711
|
for (const sourcePath of sourcePaths) {
|
|
10612
|
-
const content = readIdentitySourcePath(sourcePath,
|
|
10712
|
+
const content = readIdentitySourcePath(sourcePath, baseDir2, sourceId);
|
|
10613
10713
|
if (content !== undefined)
|
|
10614
10714
|
contents.push({ path: sourcePath.path, content });
|
|
10615
10715
|
}
|
|
@@ -10622,9 +10722,9 @@ ${item.content.trimEnd()}`).join(`
|
|
|
10622
10722
|
|
|
10623
10723
|
`));
|
|
10624
10724
|
}
|
|
10625
|
-
function readIdentitySourcePath(sourcePath,
|
|
10626
|
-
const resolvedPath = resolveIdentitySourcePath(sourcePath.path,
|
|
10627
|
-
if (!
|
|
10725
|
+
function readIdentitySourcePath(sourcePath, baseDir2, sourceId) {
|
|
10726
|
+
const resolvedPath = resolveIdentitySourcePath(sourcePath.path, baseDir2, sourceId);
|
|
10727
|
+
if (!existsSync6(resolvedPath)) {
|
|
10628
10728
|
if (sourcePath.required) {
|
|
10629
10729
|
throw new Error(`Required identity instruction source path not found for ${sourceId}: ${sourcePath.path}`);
|
|
10630
10730
|
}
|
|
@@ -10634,27 +10734,27 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
|
|
|
10634
10734
|
if (!stat.isFile()) {
|
|
10635
10735
|
throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
|
|
10636
10736
|
}
|
|
10637
|
-
const realBase = realpathSync2(
|
|
10737
|
+
const realBase = realpathSync2(baseDir2);
|
|
10638
10738
|
const realPath = realpathSync2(resolvedPath);
|
|
10639
10739
|
if (!pathIsInside(realPath, realBase)) {
|
|
10640
10740
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${sourcePath.path}`);
|
|
10641
10741
|
}
|
|
10642
10742
|
return readFileSync4(realPath, "utf-8");
|
|
10643
10743
|
}
|
|
10644
|
-
function resolveIdentitySourcePath(path,
|
|
10744
|
+
function resolveIdentitySourcePath(path, baseDir2, sourceId) {
|
|
10645
10745
|
const cleaned = cleanSessionPathInput(path);
|
|
10646
10746
|
if (!cleaned)
|
|
10647
10747
|
throw new Error(`Identity instruction source path cannot be empty for ${sourceId}.`);
|
|
10648
10748
|
if (cleaned.includes("\\"))
|
|
10649
10749
|
throw new Error(`Identity instruction source path must use POSIX separators for ${sourceId}: ${path}`);
|
|
10650
|
-
const resolvedPath = isAbsolute3(cleaned) ?
|
|
10651
|
-
if (!pathIsInside(resolvedPath,
|
|
10750
|
+
const resolvedPath = isAbsolute3(cleaned) ? resolve7(cleaned) : resolve7(baseDir2, cleaned);
|
|
10751
|
+
if (!pathIsInside(resolvedPath, resolve7(baseDir2))) {
|
|
10652
10752
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${path}`);
|
|
10653
10753
|
}
|
|
10654
10754
|
return resolvedPath;
|
|
10655
10755
|
}
|
|
10656
|
-
function pathIsInside(path,
|
|
10657
|
-
const rel = relative2(
|
|
10756
|
+
function pathIsInside(path, baseDir2) {
|
|
10757
|
+
const rel = relative2(baseDir2, path);
|
|
10658
10758
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
10659
10759
|
}
|
|
10660
10760
|
function providerTargetsTool(targets, tool) {
|
|
@@ -12055,16 +12155,16 @@ function resolveConfigStore(env = process.env) {
|
|
|
12055
12155
|
return cloud ? new CloudConfigStore(cloud) : new LocalConfigStore;
|
|
12056
12156
|
}
|
|
12057
12157
|
// src/status.ts
|
|
12058
|
-
import { existsSync as
|
|
12158
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
12059
12159
|
|
|
12060
12160
|
// src/lib/apply.ts
|
|
12061
|
-
import { existsSync as
|
|
12062
|
-
import { basename as basename5, dirname as dirname5, join as
|
|
12063
|
-
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";
|
|
12064
12164
|
|
|
12065
12165
|
// src/lib/session-render-ownership.ts
|
|
12066
|
-
import { existsSync as
|
|
12067
|
-
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";
|
|
12068
12168
|
var MANIFEST_ANCESTOR_LIMIT = 24;
|
|
12069
12169
|
var MANAGED_PATH_SEGMENTS = SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS.map((managedPath) => managedPath.split("/").filter(Boolean));
|
|
12070
12170
|
var manifestCache = new Map;
|
|
@@ -12086,7 +12186,7 @@ function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
|
12086
12186
|
function readManifestRelativePaths(manifestPath) {
|
|
12087
12187
|
let stats;
|
|
12088
12188
|
try {
|
|
12089
|
-
if (!
|
|
12189
|
+
if (!existsSync7(manifestPath))
|
|
12090
12190
|
return null;
|
|
12091
12191
|
stats = statSync4(manifestPath);
|
|
12092
12192
|
} catch {
|
|
@@ -12115,7 +12215,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
|
12115
12215
|
const root = parse3(absolutePath2).root;
|
|
12116
12216
|
let home = dirname4(absolutePath2);
|
|
12117
12217
|
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
12118
|
-
const manifestPath =
|
|
12218
|
+
const manifestPath = join9(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
12119
12219
|
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
12120
12220
|
if (relativePaths) {
|
|
12121
12221
|
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
@@ -12135,13 +12235,13 @@ function sessionRenderOwnsPath(absolutePath2) {
|
|
|
12135
12235
|
|
|
12136
12236
|
// src/lib/apply.ts
|
|
12137
12237
|
function getConfigHome() {
|
|
12138
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
12238
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir7();
|
|
12139
12239
|
}
|
|
12140
12240
|
function expandPath(p) {
|
|
12141
12241
|
if (p.startsWith("~/")) {
|
|
12142
|
-
return
|
|
12242
|
+
return resolve8(getConfigHome(), p.slice(2));
|
|
12143
12243
|
}
|
|
12144
|
-
return
|
|
12244
|
+
return resolve8(p);
|
|
12145
12245
|
}
|
|
12146
12246
|
function normalizeTargetPath(p) {
|
|
12147
12247
|
const expanded = expandPath(p);
|
|
@@ -12151,9 +12251,9 @@ function normalizeTargetPath(p) {
|
|
|
12151
12251
|
let current = expanded;
|
|
12152
12252
|
const missingSegments = [];
|
|
12153
12253
|
while (true) {
|
|
12154
|
-
if (
|
|
12254
|
+
if (existsSync8(current)) {
|
|
12155
12255
|
try {
|
|
12156
|
-
return
|
|
12256
|
+
return resolve8(realpathSync3(current), ...missingSegments);
|
|
12157
12257
|
} catch {
|
|
12158
12258
|
return expanded;
|
|
12159
12259
|
}
|
|
@@ -12182,11 +12282,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
12182
12282
|
}
|
|
12183
12283
|
const path = expandPath(renderedTargetPath);
|
|
12184
12284
|
const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
|
|
12185
|
-
const previousContent =
|
|
12285
|
+
const previousContent = existsSync8(path) ? readFileSync6(path, "utf-8") : null;
|
|
12186
12286
|
const changed = previousContent !== renderedForTarget;
|
|
12187
12287
|
if (!opts.dryRun) {
|
|
12188
12288
|
const dir = dirname5(path);
|
|
12189
|
-
if (!
|
|
12289
|
+
if (!existsSync8(dir)) {
|
|
12190
12290
|
mkdirSync3(dir, { recursive: true });
|
|
12191
12291
|
}
|
|
12192
12292
|
if (previousContent !== null && changed) {
|
|
@@ -12220,7 +12320,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
|
12220
12320
|
let current;
|
|
12221
12321
|
try {
|
|
12222
12322
|
const path = expandPath(targetPath);
|
|
12223
|
-
if (!
|
|
12323
|
+
if (!existsSync8(path))
|
|
12224
12324
|
return [];
|
|
12225
12325
|
current = readFileSync6(path, "utf-8");
|
|
12226
12326
|
} catch {
|
|
@@ -12546,14 +12646,14 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
|
12546
12646
|
getConfigHome(),
|
|
12547
12647
|
opts.vars?.["HOME_DIR"]
|
|
12548
12648
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
12549
|
-
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("/"))))))
|
|
12550
12650
|
return true;
|
|
12551
12651
|
return sessionRenderOwnsPath(normalized);
|
|
12552
12652
|
}
|
|
12553
12653
|
|
|
12554
12654
|
// src/lib/package-version.ts
|
|
12555
|
-
import { existsSync as
|
|
12556
|
-
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";
|
|
12557
12657
|
import { fileURLToPath } from "url";
|
|
12558
12658
|
var cached = null;
|
|
12559
12659
|
function getPackageVersion() {
|
|
@@ -12562,8 +12662,8 @@ function getPackageVersion() {
|
|
|
12562
12662
|
try {
|
|
12563
12663
|
let dir = dirname6(fileURLToPath(import.meta.url));
|
|
12564
12664
|
for (let i = 0;i < 8; i++) {
|
|
12565
|
-
const pkgPath =
|
|
12566
|
-
if (
|
|
12665
|
+
const pkgPath = join11(dir, "package.json");
|
|
12666
|
+
if (existsSync9(pkgPath)) {
|
|
12567
12667
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
12568
12668
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
12569
12669
|
cached = pkg.version;
|
|
@@ -12584,7 +12684,7 @@ function getPackageVersion() {
|
|
|
12584
12684
|
import { createHash as createHash8 } from "crypto";
|
|
12585
12685
|
import { spawnSync } from "child_process";
|
|
12586
12686
|
import {
|
|
12587
|
-
existsSync as
|
|
12687
|
+
existsSync as existsSync10,
|
|
12588
12688
|
lstatSync as lstatSync4,
|
|
12589
12689
|
mkdirSync as mkdirSync4,
|
|
12590
12690
|
readFileSync as readFileSync8,
|
|
@@ -12592,8 +12692,8 @@ import {
|
|
|
12592
12692
|
rmSync as rmSync3,
|
|
12593
12693
|
writeFileSync as writeFileSync3
|
|
12594
12694
|
} from "fs";
|
|
12595
|
-
import { homedir as
|
|
12596
|
-
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";
|
|
12597
12697
|
var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
|
|
12598
12698
|
var INBOX_SKILL_MARKERS = [
|
|
12599
12699
|
[".claude", "skills", "inbox", "SKILL.md"],
|
|
@@ -12614,13 +12714,13 @@ function lstatOrNull(path) {
|
|
|
12614
12714
|
}
|
|
12615
12715
|
}
|
|
12616
12716
|
function findSymlinkedAncestor(path) {
|
|
12617
|
-
const normalized =
|
|
12717
|
+
const normalized = resolve9(path);
|
|
12618
12718
|
const parsed = parse4(normalized);
|
|
12619
12719
|
let current = parsed.root;
|
|
12620
12720
|
const rel = relative4(parsed.root, normalized);
|
|
12621
12721
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
12622
|
-
current =
|
|
12623
|
-
if (!
|
|
12722
|
+
current = join12(current, segment);
|
|
12723
|
+
if (!existsSync10(current))
|
|
12624
12724
|
return null;
|
|
12625
12725
|
if (lstatSync4(current).isSymbolicLink())
|
|
12626
12726
|
return current;
|
|
@@ -12637,11 +12737,11 @@ function packagedInboxSkillPath(explicitPath) {
|
|
|
12637
12737
|
if (explicitPath)
|
|
12638
12738
|
return explicitPath;
|
|
12639
12739
|
const candidates = [
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
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")
|
|
12643
12743
|
];
|
|
12644
|
-
const found = candidates.find((candidate) =>
|
|
12744
|
+
const found = candidates.find((candidate) => existsSync10(candidate));
|
|
12645
12745
|
if (!found) {
|
|
12646
12746
|
throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
|
|
12647
12747
|
}
|
|
@@ -12690,8 +12790,8 @@ function compareVersions(left, right) {
|
|
|
12690
12790
|
}
|
|
12691
12791
|
return 0;
|
|
12692
12792
|
}
|
|
12693
|
-
function inspectSkillMarkers(
|
|
12694
|
-
return INBOX_SKILL_MARKERS.map((parts) =>
|
|
12793
|
+
function inspectSkillMarkers(homeDir3) {
|
|
12794
|
+
return INBOX_SKILL_MARKERS.map((parts) => join12(homeDir3, ...parts)).map((path) => {
|
|
12695
12795
|
const stat = lstatOrNull(path);
|
|
12696
12796
|
if (!stat)
|
|
12697
12797
|
return null;
|
|
@@ -12707,9 +12807,9 @@ function inspectSkillMarkers(homeDir2) {
|
|
|
12707
12807
|
}).filter((snapshot) => snapshot !== null);
|
|
12708
12808
|
}
|
|
12709
12809
|
function inspectInbox(options) {
|
|
12710
|
-
const
|
|
12810
|
+
const homeDir3 = options.homeDir ?? homedir8();
|
|
12711
12811
|
const runtimeCommand = options.conversationsCommand ?? "conversations";
|
|
12712
|
-
const snapshots = inspectSkillMarkers(
|
|
12812
|
+
const snapshots = inspectSkillMarkers(homeDir3);
|
|
12713
12813
|
const skillPresent = snapshots.length > 0;
|
|
12714
12814
|
let canonicalContent = null;
|
|
12715
12815
|
let canonicalSha256 = null;
|
|
@@ -13023,7 +13123,7 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
|
13023
13123
|
continue;
|
|
13024
13124
|
knownTargets += 1;
|
|
13025
13125
|
const targetPath = expandPath(config.target_path);
|
|
13026
|
-
if (!
|
|
13126
|
+
if (!existsSync11(targetPath)) {
|
|
13027
13127
|
missingTargets += 1;
|
|
13028
13128
|
continue;
|
|
13029
13129
|
}
|
|
@@ -13119,8 +13219,8 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
|
13119
13219
|
}
|
|
13120
13220
|
// src/lib/provider-context.ts
|
|
13121
13221
|
import { createHash as createHash9 } from "crypto";
|
|
13122
|
-
import { existsSync as
|
|
13123
|
-
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";
|
|
13124
13224
|
var PROVIDER_CONTEXT_DIR = ".hasna/provider-context";
|
|
13125
13225
|
var PROVIDER_CONTEXT_MANIFEST = "manifest.json";
|
|
13126
13226
|
var PROVIDER_CONTEXT_SCHEMA = "hasna.instructions.provider-context/v1";
|
|
@@ -13265,17 +13365,17 @@ function resolveAndRenderProviderContext(opts) {
|
|
|
13265
13365
|
const recordedEndpoint = originAccepted ? `${opts.origin.host}${opts.origin.pathPrefix || ""}` : null;
|
|
13266
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;
|
|
13267
13367
|
const content = renderProviderFragment(entry);
|
|
13268
|
-
const dir =
|
|
13269
|
-
if (!
|
|
13368
|
+
const dir = join13(opts.homeDir, PROVIDER_CONTEXT_DIR);
|
|
13369
|
+
if (!existsSync12(dir))
|
|
13270
13370
|
mkdirSync5(dir, { recursive: true });
|
|
13271
13371
|
const filename = `${entry ? entry.key : "invariant"}.md`;
|
|
13272
|
-
const fragmentPath2 =
|
|
13372
|
+
const fragmentPath2 = join13(dir, filename);
|
|
13273
13373
|
const fragmentSha256 = sha2569(content);
|
|
13274
13374
|
writeFileSync4(fragmentPath2, content, "utf8");
|
|
13275
|
-
const manifestPath =
|
|
13375
|
+
const manifestPath = join13(dir, PROVIDER_CONTEXT_MANIFEST);
|
|
13276
13376
|
let manifest = { schema: PROVIDER_CONTEXT_SCHEMA, fragments: {} };
|
|
13277
13377
|
try {
|
|
13278
|
-
if (
|
|
13378
|
+
if (existsSync12(manifestPath)) {
|
|
13279
13379
|
const parsed = JSON.parse(readFileSync10(manifestPath, "utf8"));
|
|
13280
13380
|
if (parsed && typeof parsed === "object")
|
|
13281
13381
|
manifest = parsed;
|
|
@@ -13387,9 +13487,9 @@ var PG_MIGRATIONS = [
|
|
|
13387
13487
|
];
|
|
13388
13488
|
// src/lib/station-profile.ts
|
|
13389
13489
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
13390
|
-
import { existsSync as
|
|
13391
|
-
import { arch as osArch, homedir as
|
|
13392
|
-
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";
|
|
13393
13493
|
var STATION_PROFILE_CACHE_FILENAME = "station-profile.md";
|
|
13394
13494
|
var STATION_PROFILE_SOURCE_ID = "station-profile";
|
|
13395
13495
|
var STATION_PROFILE_LAYER = "machine";
|
|
@@ -13399,22 +13499,21 @@ var STATION_PROFILE_FULL_NAMES_MAX = 6;
|
|
|
13399
13499
|
var STATION_PROFILE_PRIMARY_SCOPE = "@hasna";
|
|
13400
13500
|
var MACHINES_MANIFEST_PATH_ENV = "HASNA_MACHINES_MANIFEST_PATH";
|
|
13401
13501
|
var BUN_INSTALL_ENV = "BUN_INSTALL";
|
|
13402
|
-
function
|
|
13403
|
-
return env["HOME"] || env["USERPROFILE"] ||
|
|
13502
|
+
function homeDir3(env = process.env) {
|
|
13503
|
+
return env["HOME"] || env["USERPROFILE"] || homedir9();
|
|
13404
13504
|
}
|
|
13405
13505
|
function getStationProfileCachePath(env = process.env) {
|
|
13406
|
-
|
|
13407
|
-
return join13(resolve9(root), STATION_PROFILE_CACHE_FILENAME);
|
|
13506
|
+
return join14(getRawStoreRoot(env), STATION_PROFILE_CACHE_FILENAME);
|
|
13408
13507
|
}
|
|
13409
13508
|
function getMachinesManifestPath(env = process.env) {
|
|
13410
|
-
return env[MACHINES_MANIFEST_PATH_ENV] ||
|
|
13509
|
+
return env[MACHINES_MANIFEST_PATH_ENV] || join14(homeDir3(env), ".hasna", "machines", "machines.json");
|
|
13411
13510
|
}
|
|
13412
13511
|
function getBunGlobalModulesDir(env = process.env) {
|
|
13413
|
-
return
|
|
13512
|
+
return join14(env[BUN_INSTALL_ENV] || join14(homeDir3(env), ".bun"), "install", "global", "node_modules");
|
|
13414
13513
|
}
|
|
13415
13514
|
function readMachinesManifest(path) {
|
|
13416
13515
|
try {
|
|
13417
|
-
if (!
|
|
13516
|
+
if (!existsSync13(path))
|
|
13418
13517
|
return null;
|
|
13419
13518
|
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
13420
13519
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -13470,9 +13569,9 @@ function probeMachineStatus(machineId) {
|
|
|
13470
13569
|
function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
13471
13570
|
const hostname2 = osHostname();
|
|
13472
13571
|
const record = findLocalManifestMachine(readMachinesManifest(getMachinesManifestPath(env)), hostname2);
|
|
13473
|
-
const home =
|
|
13572
|
+
const home = homeDir3(env);
|
|
13474
13573
|
const platform = stringField(record, "platform") ?? osPlatform();
|
|
13475
|
-
const workspacePath = stringField(record, "workspacePath") ??
|
|
13574
|
+
const workspacePath = stringField(record, "workspacePath") ?? join14(home, platform === "darwin" ? "Workspace" : "workspace");
|
|
13476
13575
|
const machine = {
|
|
13477
13576
|
id: stringField(record, "id") ?? hostname2,
|
|
13478
13577
|
hostname: stringField(record, "hostname") ?? hostname2,
|
|
@@ -13489,9 +13588,9 @@ function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
|
13489
13588
|
return machine;
|
|
13490
13589
|
}
|
|
13491
13590
|
function scopedPackageNames(modulesDir, scope) {
|
|
13492
|
-
const scopeDir =
|
|
13591
|
+
const scopeDir = join14(modulesDir, scope);
|
|
13493
13592
|
try {
|
|
13494
|
-
if (!
|
|
13593
|
+
if (!existsSync13(scopeDir))
|
|
13495
13594
|
return null;
|
|
13496
13595
|
return readdirNames(scopeDir).sort();
|
|
13497
13596
|
} catch {
|
|
@@ -13501,7 +13600,7 @@ function scopedPackageNames(modulesDir, scope) {
|
|
|
13501
13600
|
function readdirNames(dir) {
|
|
13502
13601
|
return readdirSync(dir).filter((name) => {
|
|
13503
13602
|
try {
|
|
13504
|
-
return lstatSync5(
|
|
13603
|
+
return lstatSync5(join14(dir, name)).isDirectory();
|
|
13505
13604
|
} catch {
|
|
13506
13605
|
return false;
|
|
13507
13606
|
}
|
|
@@ -13511,7 +13610,7 @@ function resolveStationProfilePackages(env = process.env) {
|
|
|
13511
13610
|
const modulesDir = getBunGlobalModulesDir(env);
|
|
13512
13611
|
let scopeDirs;
|
|
13513
13612
|
try {
|
|
13514
|
-
if (!
|
|
13613
|
+
if (!existsSync13(modulesDir))
|
|
13515
13614
|
return null;
|
|
13516
13615
|
scopeDirs = readdirNames(modulesDir).filter((name) => name.startsWith("@") && name.toLowerCase().includes("hasna"));
|
|
13517
13616
|
} catch {
|
|
@@ -13586,7 +13685,7 @@ function refreshStationProfile(options = {}) {
|
|
|
13586
13685
|
const path = getStationProfileCachePath(env);
|
|
13587
13686
|
const generatedAt = new Date().toISOString();
|
|
13588
13687
|
if (!options.dryRun) {
|
|
13589
|
-
const existing =
|
|
13688
|
+
const existing = existsSync13(path) ? readFileSync11(path, "utf8") : null;
|
|
13590
13689
|
if (existing !== content) {
|
|
13591
13690
|
mkdirSync6(dirname8(path), { recursive: true });
|
|
13592
13691
|
writeFileSync5(path, content, "utf8");
|
|
@@ -13605,7 +13704,7 @@ function refreshStationProfile(options = {}) {
|
|
|
13605
13704
|
function readStationProfile(env = process.env) {
|
|
13606
13705
|
const path = getStationProfileCachePath(env);
|
|
13607
13706
|
try {
|
|
13608
|
-
if (!
|
|
13707
|
+
if (!existsSync13(path))
|
|
13609
13708
|
return null;
|
|
13610
13709
|
return readFileSync11(path, "utf8");
|
|
13611
13710
|
} catch {
|
|
@@ -13629,14 +13728,14 @@ function stationProfileSource(env = process.env) {
|
|
|
13629
13728
|
// src/lib/session-apply.ts
|
|
13630
13729
|
import { createHash as createHash10, randomUUID as randomUUID4 } from "crypto";
|
|
13631
13730
|
import {
|
|
13632
|
-
existsSync as
|
|
13731
|
+
existsSync as existsSync14,
|
|
13633
13732
|
lstatSync as lstatSync6,
|
|
13634
13733
|
mkdirSync as mkdirSync7,
|
|
13635
13734
|
readFileSync as readFileSync12,
|
|
13636
13735
|
readdirSync as readdirSync2,
|
|
13637
13736
|
statSync as statSync5
|
|
13638
13737
|
} from "fs";
|
|
13639
|
-
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";
|
|
13640
13739
|
class SessionApplyError extends Error {
|
|
13641
13740
|
constructor(message) {
|
|
13642
13741
|
super(message);
|
|
@@ -13767,7 +13866,7 @@ function assertClaudeAuthorityStillClear(plan, targetHome, ownedClaudeAuthoritie
|
|
|
13767
13866
|
throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
|
|
13768
13867
|
}
|
|
13769
13868
|
function ensureSessionTargetHome(targetHome) {
|
|
13770
|
-
if (!
|
|
13869
|
+
if (!existsSync14(targetHome))
|
|
13771
13870
|
mkdirSync7(targetHome, { recursive: true, mode: 448 });
|
|
13772
13871
|
assertSafeTargetHome(targetHome);
|
|
13773
13872
|
}
|
|
@@ -13790,7 +13889,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
13790
13889
|
const drifted = [];
|
|
13791
13890
|
for (const file of previousManifest.files) {
|
|
13792
13891
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
13793
|
-
if (!
|
|
13892
|
+
if (!existsSync14(target)) {
|
|
13794
13893
|
missing.push({
|
|
13795
13894
|
path: target,
|
|
13796
13895
|
relativePath: file.relativePath,
|
|
@@ -13942,7 +14041,7 @@ function requiredRestoreHash(file) {
|
|
|
13942
14041
|
}
|
|
13943
14042
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
13944
14043
|
const resolved = resolve10(snapshotPath);
|
|
13945
|
-
if (!
|
|
14044
|
+
if (!existsSync14(resolved))
|
|
13946
14045
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
13947
14046
|
const stat = lstatSync6(resolved);
|
|
13948
14047
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -14209,7 +14308,7 @@ function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
|
14209
14308
|
}
|
|
14210
14309
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
14211
14310
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
14212
|
-
const previousContent =
|
|
14311
|
+
const previousContent = existsSync14(target) ? readFileSync12(target, "utf-8") : null;
|
|
14213
14312
|
const previousSha256 = previousContent === null ? null : sha25610(previousContent);
|
|
14214
14313
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
14215
14314
|
const changed = previousContent !== file.content;
|
|
@@ -14308,7 +14407,7 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
14308
14407
|
}
|
|
14309
14408
|
function planStaleFileResult(file, targetHome, options) {
|
|
14310
14409
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
14311
|
-
if (!
|
|
14410
|
+
if (!existsSync14(target))
|
|
14312
14411
|
return null;
|
|
14313
14412
|
const previousContent = readFileSync12(target, "utf-8");
|
|
14314
14413
|
const previousSha256 = sha25610(previousContent);
|
|
@@ -14376,7 +14475,7 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
14376
14475
|
return target;
|
|
14377
14476
|
}
|
|
14378
14477
|
function readPreviousManifest(path) {
|
|
14379
|
-
if (!
|
|
14478
|
+
if (!existsSync14(path))
|
|
14380
14479
|
return null;
|
|
14381
14480
|
try {
|
|
14382
14481
|
const parsed = JSON.parse(readFileSync12(path, "utf-8"));
|
|
@@ -14418,7 +14517,7 @@ function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
|
14418
14517
|
}
|
|
14419
14518
|
function currentSessionFileHash(path, targetHome) {
|
|
14420
14519
|
assertNoSymlinkSegments2(targetHome, path);
|
|
14421
|
-
if (!
|
|
14520
|
+
if (!existsSync14(path))
|
|
14422
14521
|
return null;
|
|
14423
14522
|
const stat = lstatSync6(path);
|
|
14424
14523
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -14433,7 +14532,7 @@ function requiredPreviousHash(result) {
|
|
|
14433
14532
|
return result.previousSha256;
|
|
14434
14533
|
}
|
|
14435
14534
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
14436
|
-
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) => {
|
|
14437
14536
|
const content = readFileSync12(result.path, "utf-8");
|
|
14438
14537
|
return {
|
|
14439
14538
|
path: result.path,
|
|
@@ -14505,7 +14604,7 @@ function assertSafeTargetHome(targetHome) {
|
|
|
14505
14604
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
14506
14605
|
}
|
|
14507
14606
|
assertNoSymlinkAncestors3(normalized);
|
|
14508
|
-
if (
|
|
14607
|
+
if (existsSync14(normalized) && lstatSync6(normalized).isSymbolicLink()) {
|
|
14509
14608
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
14510
14609
|
}
|
|
14511
14610
|
return normalized;
|
|
@@ -14515,8 +14614,8 @@ function assertNoSymlinkSegments2(root, target) {
|
|
|
14515
14614
|
const rel = relative5(root, target);
|
|
14516
14615
|
let current = root;
|
|
14517
14616
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14518
|
-
current =
|
|
14519
|
-
if (
|
|
14617
|
+
current = join15(current, segment);
|
|
14618
|
+
if (existsSync14(current) && lstatSync6(current).isSymbolicLink()) {
|
|
14520
14619
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
14521
14620
|
}
|
|
14522
14621
|
}
|
|
@@ -14527,8 +14626,8 @@ function assertNoSymlinkAncestors3(path) {
|
|
|
14527
14626
|
let current = parsed.root;
|
|
14528
14627
|
const rel = relative5(parsed.root, normalized);
|
|
14529
14628
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14530
|
-
current =
|
|
14531
|
-
if (!
|
|
14629
|
+
current = join15(current, segment);
|
|
14630
|
+
if (!existsSync14(current))
|
|
14532
14631
|
return;
|
|
14533
14632
|
if (lstatSync6(current).isSymbolicLink()) {
|
|
14534
14633
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
@@ -14839,13 +14938,13 @@ async function ensureDangerousOperationGuardStandardConfig(store = resolveConfig
|
|
|
14839
14938
|
}
|
|
14840
14939
|
}
|
|
14841
14940
|
// src/lib/sync.ts
|
|
14842
|
-
import { existsSync as
|
|
14843
|
-
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";
|
|
14844
14943
|
|
|
14845
14944
|
// src/lib/sync-dir.ts
|
|
14846
|
-
import { existsSync as
|
|
14847
|
-
import { join as
|
|
14848
|
-
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";
|
|
14849
14948
|
var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
14850
14949
|
function shouldSkip(p) {
|
|
14851
14950
|
return SKIP.some((s) => p.includes(s));
|
|
@@ -14853,11 +14952,11 @@ function shouldSkip(p) {
|
|
|
14853
14952
|
async function syncFromDir(dir, opts = {}) {
|
|
14854
14953
|
const store = opts.store ?? resolveConfigStore();
|
|
14855
14954
|
const absDir = expandPath(dir);
|
|
14856
|
-
if (!
|
|
14955
|
+
if (!existsSync15(absDir))
|
|
14857
14956
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
14858
|
-
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());
|
|
14859
14958
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
14860
|
-
const home =
|
|
14959
|
+
const home = homedir10();
|
|
14861
14960
|
const allConfigs = await store.listConfigs();
|
|
14862
14961
|
for (const file of files) {
|
|
14863
14962
|
if (shouldSkip(file)) {
|
|
@@ -14892,7 +14991,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14892
14991
|
}
|
|
14893
14992
|
async function syncToDir(dir, opts = {}) {
|
|
14894
14993
|
const store = opts.store ?? resolveConfigStore();
|
|
14895
|
-
const home =
|
|
14994
|
+
const home = homedir10();
|
|
14896
14995
|
const absDir = expandPath(dir);
|
|
14897
14996
|
const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
|
|
14898
14997
|
const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
|
|
@@ -14916,7 +15015,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
14916
15015
|
}
|
|
14917
15016
|
function walkDir(dir, files = []) {
|
|
14918
15017
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
14919
|
-
const full =
|
|
15018
|
+
const full = join16(dir, entry.name);
|
|
14920
15019
|
if (shouldSkip(full))
|
|
14921
15020
|
continue;
|
|
14922
15021
|
if (entry.isDirectory())
|
|
@@ -14977,7 +15076,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
14977
15076
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
14978
15077
|
}
|
|
14979
15078
|
function hasClaudePromptSource() {
|
|
14980
|
-
return
|
|
15079
|
+
return existsSync16(expandPath("~/.claude/CLAUDE.md"));
|
|
14981
15080
|
}
|
|
14982
15081
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
14983
15082
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -14985,7 +15084,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
14985
15084
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
14986
15085
|
return false;
|
|
14987
15086
|
const stem = basename6(absoluteTargetPath, ".mdc");
|
|
14988
|
-
return
|
|
15087
|
+
return existsSync16(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync16(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
14989
15088
|
}
|
|
14990
15089
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
14991
15090
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -15050,8 +15149,8 @@ async function syncProject(opts) {
|
|
|
15050
15149
|
const allConfigs = await store.listConfigs();
|
|
15051
15150
|
const machine = detectMachineContext();
|
|
15052
15151
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
15053
|
-
const abs =
|
|
15054
|
-
if (!
|
|
15152
|
+
const abs = join17(absDir, pf.file);
|
|
15153
|
+
if (!existsSync16(abs))
|
|
15055
15154
|
continue;
|
|
15056
15155
|
try {
|
|
15057
15156
|
const rawContent = readFileSync14(abs, "utf-8");
|
|
@@ -15083,19 +15182,19 @@ async function syncProject(opts) {
|
|
|
15083
15182
|
}
|
|
15084
15183
|
}
|
|
15085
15184
|
for (const ruleDir of [
|
|
15086
|
-
{ dir:
|
|
15087
|
-
{ dir:
|
|
15088
|
-
{ dir:
|
|
15089
|
-
{ dir:
|
|
15090
|
-
{ dir:
|
|
15091
|
-
{ dir:
|
|
15092
|
-
{ 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" }
|
|
15093
15192
|
]) {
|
|
15094
|
-
if (!
|
|
15193
|
+
if (!existsSync16(ruleDir.dir))
|
|
15095
15194
|
continue;
|
|
15096
15195
|
const mdFiles = readdirSync4(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
15097
15196
|
for (const f of mdFiles) {
|
|
15098
|
-
const abs =
|
|
15197
|
+
const abs = join17(ruleDir.dir, f);
|
|
15099
15198
|
const raw = readFileSync14(abs, "utf-8");
|
|
15100
15199
|
const redacted = redactContent(raw, "markdown");
|
|
15101
15200
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
@@ -15135,14 +15234,14 @@ async function syncKnown(opts = {}) {
|
|
|
15135
15234
|
for (const known of targets) {
|
|
15136
15235
|
if (known.rulesDir) {
|
|
15137
15236
|
const absDir = expandPath(known.rulesDir);
|
|
15138
|
-
if (!
|
|
15237
|
+
if (!existsSync16(absDir)) {
|
|
15139
15238
|
result.skipped.push(known.rulesDir);
|
|
15140
15239
|
continue;
|
|
15141
15240
|
}
|
|
15142
15241
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
15143
15242
|
const ruleFiles = readdirSync4(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
15144
15243
|
for (const f of ruleFiles) {
|
|
15145
|
-
const abs2 =
|
|
15244
|
+
const abs2 = join17(absDir, f);
|
|
15146
15245
|
const targetPath = abs2.replace(home, "~");
|
|
15147
15246
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
15148
15247
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
@@ -15176,7 +15275,7 @@ async function syncKnown(opts = {}) {
|
|
|
15176
15275
|
continue;
|
|
15177
15276
|
}
|
|
15178
15277
|
const abs = expandPath(known.path);
|
|
15179
|
-
if (!
|
|
15278
|
+
if (!existsSync16(abs)) {
|
|
15180
15279
|
result.skipped.push(known.path);
|
|
15181
15280
|
continue;
|
|
15182
15281
|
}
|
|
@@ -15284,7 +15383,7 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
|
15284
15383
|
}
|
|
15285
15384
|
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
15286
15385
|
const path = expandPath(targetPath);
|
|
15287
|
-
if (!
|
|
15386
|
+
if (!existsSync16(path))
|
|
15288
15387
|
return `(file not found on disk: ${path})`;
|
|
15289
15388
|
const diskContent = readFileSync14(path, "utf-8");
|
|
15290
15389
|
if (diskContent === expectedContent)
|
|
@@ -15444,15 +15543,15 @@ function detectFormat(filePath) {
|
|
|
15444
15543
|
return "text";
|
|
15445
15544
|
}
|
|
15446
15545
|
// src/lib/export.ts
|
|
15447
|
-
import { existsSync as
|
|
15448
|
-
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";
|
|
15449
15548
|
import { tmpdir } from "os";
|
|
15450
15549
|
async function exportConfigs(outputPath, opts = {}) {
|
|
15451
15550
|
const store = opts.store ?? resolveConfigStore();
|
|
15452
15551
|
const configs = await store.listConfigs(opts.filter);
|
|
15453
15552
|
const absOutput = resolve11(outputPath);
|
|
15454
|
-
const tmpDir =
|
|
15455
|
-
const contentsDir =
|
|
15553
|
+
const tmpDir = join18(tmpdir(), `configs-export-${Date.now()}`);
|
|
15554
|
+
const contentsDir = join18(tmpDir, "contents");
|
|
15456
15555
|
try {
|
|
15457
15556
|
mkdirSync8(contentsDir, { recursive: true });
|
|
15458
15557
|
const manifest = {
|
|
@@ -15460,10 +15559,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
15460
15559
|
exported_at: new Date().toISOString(),
|
|
15461
15560
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
15462
15561
|
};
|
|
15463
|
-
writeFileSync6(
|
|
15562
|
+
writeFileSync6(join18(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
15464
15563
|
for (const config of configs) {
|
|
15465
15564
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
15466
|
-
writeFileSync6(
|
|
15565
|
+
writeFileSync6(join18(contentsDir, fileName), config.content, "utf-8");
|
|
15467
15566
|
}
|
|
15468
15567
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
15469
15568
|
stdout: "pipe",
|
|
@@ -15476,20 +15575,20 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
15476
15575
|
}
|
|
15477
15576
|
return { path: absOutput, count: configs.length };
|
|
15478
15577
|
} finally {
|
|
15479
|
-
if (
|
|
15578
|
+
if (existsSync17(tmpDir)) {
|
|
15480
15579
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
15481
15580
|
}
|
|
15482
15581
|
}
|
|
15483
15582
|
}
|
|
15484
15583
|
// src/lib/import.ts
|
|
15485
|
-
import { existsSync as
|
|
15486
|
-
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";
|
|
15487
15586
|
import { tmpdir as tmpdir2 } from "os";
|
|
15488
15587
|
async function importConfigs(bundlePath, opts = {}) {
|
|
15489
15588
|
const store = opts.store ?? resolveConfigStore();
|
|
15490
15589
|
const conflict = opts.conflict ?? "skip";
|
|
15491
15590
|
const absPath = resolve12(bundlePath);
|
|
15492
|
-
const tmpDir =
|
|
15591
|
+
const tmpDir = join19(tmpdir2(), `configs-import-${Date.now()}`);
|
|
15493
15592
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
15494
15593
|
try {
|
|
15495
15594
|
mkdirSync9(tmpDir, { recursive: true });
|
|
@@ -15502,15 +15601,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
15502
15601
|
const stderr = await new Response(proc.stderr).text();
|
|
15503
15602
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
15504
15603
|
}
|
|
15505
|
-
const manifestPath =
|
|
15506
|
-
if (!
|
|
15604
|
+
const manifestPath = join19(tmpDir, "manifest.json");
|
|
15605
|
+
if (!existsSync18(manifestPath))
|
|
15507
15606
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
15508
15607
|
const manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
|
|
15509
15608
|
for (const meta of manifest.configs) {
|
|
15510
15609
|
try {
|
|
15511
15610
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
15512
|
-
const contentFile =
|
|
15513
|
-
const content =
|
|
15611
|
+
const contentFile = join19(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
15612
|
+
const content = existsSync18(contentFile) ? readFileSync15(contentFile, "utf-8") : "";
|
|
15514
15613
|
let existing = null;
|
|
15515
15614
|
try {
|
|
15516
15615
|
existing = await store.getConfig(meta.slug);
|
|
@@ -15544,16 +15643,16 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
15544
15643
|
}
|
|
15545
15644
|
return result;
|
|
15546
15645
|
} finally {
|
|
15547
|
-
if (
|
|
15646
|
+
if (existsSync18(tmpDir)) {
|
|
15548
15647
|
rmSync5(tmpDir, { recursive: true, force: true });
|
|
15549
15648
|
}
|
|
15550
15649
|
}
|
|
15551
15650
|
}
|
|
15552
15651
|
// src/lib/package-manager-guard.ts
|
|
15553
15652
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15554
|
-
import { existsSync as
|
|
15555
|
-
import { homedir as
|
|
15556
|
-
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";
|
|
15557
15656
|
var SKIP_DIRS = new Set([
|
|
15558
15657
|
".git",
|
|
15559
15658
|
"node_modules",
|
|
@@ -15595,7 +15694,7 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15595
15694
|
const findings = [];
|
|
15596
15695
|
let scannedFiles = 0;
|
|
15597
15696
|
for (const root of roots) {
|
|
15598
|
-
if (!
|
|
15697
|
+
if (!existsSync19(root))
|
|
15599
15698
|
continue;
|
|
15600
15699
|
const stat = lstatSync7(root);
|
|
15601
15700
|
if (stat.isFile()) {
|
|
@@ -15622,10 +15721,10 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15622
15721
|
}
|
|
15623
15722
|
}
|
|
15624
15723
|
if (options.includeHome) {
|
|
15625
|
-
const home =
|
|
15724
|
+
const home = homedir11();
|
|
15626
15725
|
for (const name of HOME_FILES) {
|
|
15627
|
-
const file =
|
|
15628
|
-
if (!
|
|
15726
|
+
const file = join20(home, name);
|
|
15727
|
+
if (!existsSync19(file))
|
|
15629
15728
|
continue;
|
|
15630
15729
|
const text = readTextFile(file);
|
|
15631
15730
|
if (text === null)
|
|
@@ -15649,12 +15748,12 @@ function collectRepoFiles(root) {
|
|
|
15649
15748
|
if (entry.isDirectory()) {
|
|
15650
15749
|
if (SKIP_DIRS.has(entry.name))
|
|
15651
15750
|
continue;
|
|
15652
|
-
visit(
|
|
15751
|
+
visit(join20(dir, entry.name));
|
|
15653
15752
|
continue;
|
|
15654
15753
|
}
|
|
15655
15754
|
if (!entry.isFile())
|
|
15656
15755
|
continue;
|
|
15657
|
-
const file =
|
|
15756
|
+
const file = join20(dir, entry.name);
|
|
15658
15757
|
if (shouldScanRepoFile(file))
|
|
15659
15758
|
out.push(file);
|
|
15660
15759
|
}
|
|
@@ -15922,7 +16021,7 @@ function stripInlineComment(value) {
|
|
|
15922
16021
|
return value.replace(/\s[#;].*$/, "").trim();
|
|
15923
16022
|
}
|
|
15924
16023
|
function displayPath(file, root) {
|
|
15925
|
-
const home =
|
|
16024
|
+
const home = homedir11();
|
|
15926
16025
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
15927
16026
|
return "~/" + toPosix(relative7(home, file));
|
|
15928
16027
|
if (isAbsolute5(root) && file.startsWith(root + "/"))
|