@hasna/instructions 0.5.4 → 0.5.6
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/dist/cli/index.js +564 -407
- package/dist/index.js +349 -294
- package/dist/lib/app-home.d.ts +9 -0
- package/dist/lib/app-home.d.ts.map +1 -1
- package/dist/lib/project-context.d.ts.map +1 -1
- package/dist/lib/session-apply.d.ts.map +1 -1
- package/dist/lib/session-render-state.d.ts +48 -0
- package/dist/lib/session-render-state.d.ts.map +1 -0
- package/dist/lib/session-render-state.test.d.ts +2 -0
- package/dist/lib/session-render-state.test.d.ts.map +1 -0
- package/dist/mcp/index.js +19 -3
- package/dist/mcp/server.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -1290,8 +1290,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1290
1290
|
args = args.slice();
|
|
1291
1291
|
let launchWithNode = false;
|
|
1292
1292
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1293
|
-
function findFile(
|
|
1294
|
-
const localBin = path.resolve(
|
|
1293
|
+
function findFile(baseDir2, baseName) {
|
|
1294
|
+
const localBin = path.resolve(baseDir2, baseName);
|
|
1295
1295
|
if (fs.existsSync(localBin))
|
|
1296
1296
|
return localBin;
|
|
1297
1297
|
if (sourceExt.includes(path.extname(baseName)))
|
|
@@ -2161,91 +2161,94 @@ var init_retired_storage_mode = __esm(() => {
|
|
|
2161
2161
|
});
|
|
2162
2162
|
|
|
2163
2163
|
// ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
|
|
2164
|
-
import { homedir as
|
|
2165
|
-
import { join as
|
|
2166
|
-
function
|
|
2164
|
+
import { homedir as homedir3 } from "os";
|
|
2165
|
+
import { join as join4 } from "path";
|
|
2166
|
+
function assertApp2(app) {
|
|
2167
2167
|
if (typeof app !== "string" || app.length === 0) {
|
|
2168
2168
|
throw new TypeError("paths: app must be a non-empty string");
|
|
2169
2169
|
}
|
|
2170
|
-
if (!
|
|
2170
|
+
if (!APP_SLUG_RE2.test(app)) {
|
|
2171
2171
|
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
2172
2172
|
}
|
|
2173
2173
|
}
|
|
2174
|
-
function
|
|
2174
|
+
function envOf2(options) {
|
|
2175
2175
|
return options.env ?? process.env;
|
|
2176
2176
|
}
|
|
2177
|
-
function
|
|
2178
|
-
const value =
|
|
2177
|
+
function envValue2(options, kind) {
|
|
2178
|
+
const value = envOf2(options)[KIND_ENV2[kind]];
|
|
2179
2179
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
2180
2180
|
}
|
|
2181
|
-
function
|
|
2181
|
+
function isMacOS2(platform) {
|
|
2182
2182
|
return platform === "darwin";
|
|
2183
2183
|
}
|
|
2184
|
-
function
|
|
2185
|
-
const override =
|
|
2184
|
+
function baseDir2(kind, options) {
|
|
2185
|
+
const override = envValue2(options, kind);
|
|
2186
2186
|
if (override)
|
|
2187
2187
|
return override;
|
|
2188
|
-
const home = options.home ??
|
|
2188
|
+
const home = options.home ?? homedir3();
|
|
2189
2189
|
const platform = options.platform ?? process.platform;
|
|
2190
|
-
if (
|
|
2190
|
+
if (isMacOS2(platform)) {
|
|
2191
2191
|
switch (kind) {
|
|
2192
2192
|
case "config":
|
|
2193
2193
|
case "data":
|
|
2194
|
-
return
|
|
2194
|
+
return join4(home, "Library", "Application Support", "Hasna");
|
|
2195
2195
|
case "cache":
|
|
2196
|
-
return
|
|
2196
|
+
return join4(home, "Library", "Caches", "Hasna");
|
|
2197
2197
|
case "state":
|
|
2198
|
-
return
|
|
2198
|
+
return join4(home, "Library", "Logs", "Hasna");
|
|
2199
2199
|
}
|
|
2200
2200
|
}
|
|
2201
2201
|
switch (kind) {
|
|
2202
2202
|
case "config":
|
|
2203
|
-
return
|
|
2203
|
+
return join4(home, ".config", "hasna");
|
|
2204
2204
|
case "data":
|
|
2205
|
-
return
|
|
2205
|
+
return join4(home, ".local", "share", "hasna");
|
|
2206
2206
|
case "state":
|
|
2207
|
-
return
|
|
2207
|
+
return join4(home, ".local", "state", "hasna");
|
|
2208
2208
|
case "cache":
|
|
2209
|
-
return
|
|
2209
|
+
return join4(home, ".cache", "hasna");
|
|
2210
2210
|
}
|
|
2211
2211
|
}
|
|
2212
|
-
function
|
|
2213
|
-
|
|
2214
|
-
const appSegment = options.internal === true ?
|
|
2215
|
-
return
|
|
2212
|
+
function resolvePath2(kind, options) {
|
|
2213
|
+
assertApp2(options.app);
|
|
2214
|
+
const appSegment = options.internal === true ? join4("internal", options.app) : options.app;
|
|
2215
|
+
return join4(baseDir2(kind, options), appSegment);
|
|
2216
2216
|
}
|
|
2217
2217
|
function configDir(options) {
|
|
2218
|
-
return
|
|
2218
|
+
return resolvePath2("config", options);
|
|
2219
2219
|
}
|
|
2220
|
-
|
|
2220
|
+
function stateDir(options) {
|
|
2221
|
+
return resolvePath2("state", options);
|
|
2222
|
+
}
|
|
2223
|
+
var KIND_ENV2, APP_SLUG_RE2;
|
|
2221
2224
|
var init_dist = __esm(() => {
|
|
2222
|
-
|
|
2225
|
+
KIND_ENV2 = {
|
|
2223
2226
|
config: "HASNA_CONFIG_HOME",
|
|
2224
2227
|
data: "HASNA_DATA_HOME",
|
|
2225
2228
|
state: "HASNA_STATE_HOME",
|
|
2226
2229
|
cache: "HASNA_CACHE_HOME"
|
|
2227
2230
|
};
|
|
2228
|
-
|
|
2231
|
+
APP_SLUG_RE2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2229
2232
|
});
|
|
2230
2233
|
|
|
2231
2234
|
// src/lib/app-home.ts
|
|
2232
|
-
import { existsSync as
|
|
2233
|
-
import { homedir as
|
|
2234
|
-
import { join as
|
|
2235
|
+
import { existsSync as existsSync3 } from "fs";
|
|
2236
|
+
import { homedir as homedir5 } from "os";
|
|
2237
|
+
import { join as join6, resolve as resolve2 } from "path";
|
|
2235
2238
|
function homeDir(env = process.env) {
|
|
2236
|
-
return env["HOME"] || env["USERPROFILE"] ||
|
|
2239
|
+
return env["HOME"] || env["USERPROFILE"] || homedir5();
|
|
2237
2240
|
}
|
|
2238
2241
|
function legacyStoreHome(env = process.env) {
|
|
2239
|
-
return
|
|
2242
|
+
return resolve2(join6(homeDir(env), ".hasna", "instructions"));
|
|
2240
2243
|
}
|
|
2241
2244
|
function resolverStoreHome(env = process.env) {
|
|
2242
|
-
return configDir({ app: "configs", env, home: env.HOME || env.USERPROFILE ||
|
|
2245
|
+
return configDir({ app: "configs", env, home: env.HOME || env.USERPROFILE || homedir5() });
|
|
2243
2246
|
}
|
|
2244
2247
|
function adoptResolverStoreHome(resolved, env = process.env) {
|
|
2245
2248
|
const override = env.HASNA_CONFIG_HOME;
|
|
2246
2249
|
if (typeof override === "string" && override.trim().length > 0)
|
|
2247
2250
|
return true;
|
|
2248
|
-
return
|
|
2251
|
+
return existsSync3(join6(resolved, "instructions.db"));
|
|
2249
2252
|
}
|
|
2250
2253
|
function exactStoreHome(env = process.env) {
|
|
2251
2254
|
const v = env[HASNA_CONFIGS_HOME_ENV];
|
|
@@ -2254,9 +2257,9 @@ function exactStoreHome(env = process.env) {
|
|
|
2254
2257
|
function getConfigsStoreHome(env = process.env) {
|
|
2255
2258
|
const exact = exactStoreHome(env);
|
|
2256
2259
|
if (exact)
|
|
2257
|
-
return
|
|
2260
|
+
return resolve2(exact);
|
|
2258
2261
|
const resolved = resolverStoreHome(env);
|
|
2259
|
-
return adoptResolverStoreHome(resolved, env) ?
|
|
2262
|
+
return adoptResolverStoreHome(resolved, env) ? resolve2(resolved) : legacyStoreHome(env);
|
|
2260
2263
|
}
|
|
2261
2264
|
var HASNA_CONFIGS_HOME_ENV = "HASNA_CONFIGS_HOME";
|
|
2262
2265
|
var init_app_home = __esm(() => {
|
|
@@ -2264,9 +2267,9 @@ var init_app_home = __esm(() => {
|
|
|
2264
2267
|
});
|
|
2265
2268
|
|
|
2266
2269
|
// src/lib/raw-store-root.ts
|
|
2267
|
-
import { resolve as
|
|
2270
|
+
import { resolve as resolve3 } from "path";
|
|
2268
2271
|
function getRawStoreRoot(env = process.env) {
|
|
2269
|
-
return
|
|
2272
|
+
return resolve3(getConfigsStoreHome(env));
|
|
2270
2273
|
}
|
|
2271
2274
|
var init_raw_store_root = __esm(() => {
|
|
2272
2275
|
init_app_home();
|
|
@@ -2274,8 +2277,8 @@ var init_raw_store_root = __esm(() => {
|
|
|
2274
2277
|
|
|
2275
2278
|
// src/db/database.ts
|
|
2276
2279
|
import { Database } from "bun:sqlite";
|
|
2277
|
-
import { existsSync as
|
|
2278
|
-
import { join as
|
|
2280
|
+
import { existsSync as existsSync5, mkdirSync, rmSync } from "fs";
|
|
2281
|
+
import { join as join7 } from "path";
|
|
2279
2282
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2280
2283
|
function getDbPath() {
|
|
2281
2284
|
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
|
|
@@ -2283,7 +2286,7 @@ function getDbPath() {
|
|
|
2283
2286
|
}
|
|
2284
2287
|
const dir = getRawStoreRoot();
|
|
2285
2288
|
mkdirSync(dir, { recursive: true });
|
|
2286
|
-
return
|
|
2289
|
+
return join7(dir, "instructions.db");
|
|
2287
2290
|
}
|
|
2288
2291
|
function uuid() {
|
|
2289
2292
|
return randomUUID3();
|
|
@@ -2324,7 +2327,7 @@ function resetLocalDatabase() {
|
|
|
2324
2327
|
if (dbPath === ":memory:")
|
|
2325
2328
|
return;
|
|
2326
2329
|
for (const p of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
2327
|
-
if (
|
|
2330
|
+
if (existsSync5(p))
|
|
2328
2331
|
rmSync(p);
|
|
2329
2332
|
}
|
|
2330
2333
|
}
|
|
@@ -2761,9 +2764,9 @@ var init_template = __esm(() => {
|
|
|
2761
2764
|
});
|
|
2762
2765
|
|
|
2763
2766
|
// src/lib/machine.ts
|
|
2764
|
-
import { arch as currentArch, homedir as
|
|
2765
|
-
import { existsSync as
|
|
2766
|
-
import { join as
|
|
2767
|
+
import { arch as currentArch, homedir as homedir6, hostname as currentHostname, type as currentOsType } from "os";
|
|
2768
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2769
|
+
import { join as join8 } from "path";
|
|
2767
2770
|
function normalizeOsFamily(os) {
|
|
2768
2771
|
const value = (os ?? "").trim().toLowerCase();
|
|
2769
2772
|
if (value === "darwin" || value === "macos" || value === "mac" || value === "osx")
|
|
@@ -2775,11 +2778,11 @@ function normalizeOsFamily(os) {
|
|
|
2775
2778
|
return value || "unknown";
|
|
2776
2779
|
}
|
|
2777
2780
|
function detectMachineContext(overrides = {}) {
|
|
2778
|
-
const homeDir2 = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ??
|
|
2781
|
+
const homeDir2 = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir6();
|
|
2779
2782
|
const os = overrides.os ?? currentOsType();
|
|
2780
2783
|
const osFamily = normalizeOsFamily(os);
|
|
2781
|
-
const bunBinDir = overrides.bun_bin_dir ??
|
|
2782
|
-
const defaultBunPath = osFamily === "macos" &&
|
|
2784
|
+
const bunBinDir = overrides.bun_bin_dir ?? join8(homeDir2, ".bun", "bin");
|
|
2785
|
+
const defaultBunPath = osFamily === "macos" && existsSync6(BREW_BUN_PATH) ? BREW_BUN_PATH : join8(bunBinDir, "bun");
|
|
2783
2786
|
return {
|
|
2784
2787
|
id: "current-machine",
|
|
2785
2788
|
hostname: overrides.hostname ?? currentHostname(),
|
|
@@ -2789,10 +2792,10 @@ function detectMachineContext(overrides = {}) {
|
|
|
2789
2792
|
created_at: "",
|
|
2790
2793
|
os_family: osFamily,
|
|
2791
2794
|
home_dir: homeDir2,
|
|
2792
|
-
workspace_root: overrides.workspace_root ??
|
|
2795
|
+
workspace_root: overrides.workspace_root ?? join8(homeDir2, osFamily === "macos" ? "Workspace" : "workspace"),
|
|
2793
2796
|
bun_bin_dir: bunBinDir,
|
|
2794
2797
|
bun_path: overrides.bun_path ?? defaultBunPath,
|
|
2795
|
-
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${
|
|
2798
|
+
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join8("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
|
|
2796
2799
|
};
|
|
2797
2800
|
}
|
|
2798
2801
|
function machineContextToVariables(machine) {
|
|
@@ -7420,6 +7423,55 @@ var init_session_render_contract = __esm(() => {
|
|
|
7420
7423
|
];
|
|
7421
7424
|
});
|
|
7422
7425
|
|
|
7426
|
+
// src/lib/session-render-state.ts
|
|
7427
|
+
import { existsSync as existsSync7, readdirSync } from "fs";
|
|
7428
|
+
import { homedir as homedir7 } from "os";
|
|
7429
|
+
import { dirname, join as join9, resolve as resolve4 } from "path";
|
|
7430
|
+
function homeDir2(env = process.env) {
|
|
7431
|
+
return env["HOME"] || env["USERPROFILE"] || homedir7();
|
|
7432
|
+
}
|
|
7433
|
+
function legacySnapshotDir(targetHome) {
|
|
7434
|
+
return resolve4(join9(targetHome, ".hasna", "session-render-snapshots"));
|
|
7435
|
+
}
|
|
7436
|
+
function resolverSnapshotDir(env = process.env) {
|
|
7437
|
+
const override = env.HASNA_STATE_HOME;
|
|
7438
|
+
if (typeof override === "string" && override.trim().length > 0 && !existsSync7(override)) {
|
|
7439
|
+
return stateDir({
|
|
7440
|
+
app: SESSION_RENDER_STATE_APP,
|
|
7441
|
+
env: { ...env, HASNA_STATE_HOME: undefined },
|
|
7442
|
+
home: homeDir2(env)
|
|
7443
|
+
});
|
|
7444
|
+
}
|
|
7445
|
+
return stateDir({ app: SESSION_RENDER_STATE_APP, env, home: homeDir2(env) });
|
|
7446
|
+
}
|
|
7447
|
+
function adoptResolverSnapshotDir(resolved, env = process.env) {
|
|
7448
|
+
const override = env.HASNA_STATE_HOME;
|
|
7449
|
+
if (typeof override === "string" && override.trim().length > 0 && existsSync7(override)) {
|
|
7450
|
+
return true;
|
|
7451
|
+
}
|
|
7452
|
+
if (!existsSync7(resolved))
|
|
7453
|
+
return false;
|
|
7454
|
+
return readdirSync(resolved).some((name) => name.endsWith(".json"));
|
|
7455
|
+
}
|
|
7456
|
+
function resolveSessionRenderSnapshotLocation(targetHome, env = process.env) {
|
|
7457
|
+
const resolved = resolverSnapshotDir(env);
|
|
7458
|
+
if (!adoptResolverSnapshotDir(resolved, env)) {
|
|
7459
|
+
return { dir: legacySnapshotDir(targetHome), workspaceRoot: targetHome, adopted: false };
|
|
7460
|
+
}
|
|
7461
|
+
const resolvedPath = resolve4(resolved);
|
|
7462
|
+
return { dir: resolvedPath, workspaceRoot: dirname(resolvedPath), adopted: true };
|
|
7463
|
+
}
|
|
7464
|
+
function getSessionRenderSnapshotDir(targetHome, env = process.env) {
|
|
7465
|
+
return resolveSessionRenderSnapshotLocation(targetHome, env).dir;
|
|
7466
|
+
}
|
|
7467
|
+
function sessionRenderSnapshotWorkspaceRoot(targetHome, env = process.env) {
|
|
7468
|
+
return resolveSessionRenderSnapshotLocation(targetHome, env).workspaceRoot;
|
|
7469
|
+
}
|
|
7470
|
+
var SESSION_RENDER_STATE_APP = "instructions";
|
|
7471
|
+
var init_session_render_state = __esm(() => {
|
|
7472
|
+
init_dist();
|
|
7473
|
+
});
|
|
7474
|
+
|
|
7423
7475
|
// src/lib/project-context.ts
|
|
7424
7476
|
import { createHash as createHash2, randomUUID as randomUUID5 } from "crypto";
|
|
7425
7477
|
import { execFileSync } from "child_process";
|
|
@@ -7427,7 +7479,7 @@ import { dlopen, FFIType } from "bun:ffi";
|
|
|
7427
7479
|
import {
|
|
7428
7480
|
closeSync,
|
|
7429
7481
|
constants,
|
|
7430
|
-
existsSync as
|
|
7482
|
+
existsSync as existsSync8,
|
|
7431
7483
|
fstatSync,
|
|
7432
7484
|
fsyncSync,
|
|
7433
7485
|
lstatSync,
|
|
@@ -7440,7 +7492,7 @@ import {
|
|
|
7440
7492
|
statSync,
|
|
7441
7493
|
writeFileSync
|
|
7442
7494
|
} from "fs";
|
|
7443
|
-
import { basename, dirname, isAbsolute, join as
|
|
7495
|
+
import { basename, dirname as dirname2, isAbsolute, join as join10, parse, relative, resolve as resolve5 } from "path";
|
|
7444
7496
|
function managedObservationMaxBytes(relativePath) {
|
|
7445
7497
|
return SESSION_MANAGED_OUTPUT_PATHS.includes(relativePath) ? SESSION_MANAGED_OUTPUT_MAX_BYTES : FOREIGN_INPUT_MAX_BYTES;
|
|
7446
7498
|
}
|
|
@@ -7520,7 +7572,7 @@ function planProjectContext(input) {
|
|
|
7520
7572
|
const inlineMarkerOverhead = nativeImports ? 0 : Buffer.byteLength(buildManagedBlock(bundle, "", `
|
|
7521
7573
|
`), "utf8");
|
|
7522
7574
|
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)));
|
|
7523
|
-
const previousTargetContent =
|
|
7575
|
+
const previousTargetContent = existsSync8(paths.target) ? readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target))) : null;
|
|
7524
7576
|
const markerParse = parseManagedBlock(previousTargetContent ?? "", input.force === true);
|
|
7525
7577
|
if (markerParse.block && markerParse.block.id !== bundle.project.id) {
|
|
7526
7578
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "managed block belongs to a different project");
|
|
@@ -7571,7 +7623,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7571
7623
|
return null;
|
|
7572
7624
|
const { runtime, workspace_root: workspaceRoot, observed_hashes: observedHashes } = guard;
|
|
7573
7625
|
const paths = runtimePaths(workspaceRoot, runtime);
|
|
7574
|
-
if (!
|
|
7626
|
+
if (!existsSync8(paths.manifest))
|
|
7575
7627
|
return null;
|
|
7576
7628
|
assertCodewithTargetIsConsumed(workspaceRoot, runtime);
|
|
7577
7629
|
const manifest = readProjectContextManifest(paths.manifest, workspaceRoot);
|
|
@@ -7600,7 +7652,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7600
7652
|
}
|
|
7601
7653
|
const fragment = readUtf8RegularFile(paths.fragment, workspaceRoot, PROJECT_CONTEXT_MAX_RENDERED_BYTES);
|
|
7602
7654
|
scanGeneratedContent(fragment);
|
|
7603
|
-
if (!
|
|
7655
|
+
if (!existsSync8(paths.target)) {
|
|
7604
7656
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider target is missing while durable context is active");
|
|
7605
7657
|
}
|
|
7606
7658
|
const currentTarget = readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target)));
|
|
@@ -7611,7 +7663,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7611
7663
|
if (currentMarkers.block.id !== cache.project_id || currentMarkers.block.revision !== cache.revision || currentMarkers.block.hash !== cache.hash) {
|
|
7612
7664
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider markers differ from the durable cache");
|
|
7613
7665
|
}
|
|
7614
|
-
const plannedIndexes = input.files.filter((file) => file.role === "index" &&
|
|
7666
|
+
const plannedIndexes = input.files.filter((file) => file.role === "index" && resolve5(file.path) === paths.target);
|
|
7615
7667
|
if (plannedIndexes.length !== 1) {
|
|
7616
7668
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "session renderer does not own the selected project-context provider target");
|
|
7617
7669
|
}
|
|
@@ -7669,7 +7721,7 @@ function withProjectContextSessionGuard(guard, action, options = {}) {
|
|
|
7669
7721
|
verify();
|
|
7670
7722
|
return action(null);
|
|
7671
7723
|
}
|
|
7672
|
-
const lockPath =
|
|
7724
|
+
const lockPath = resolve5(validated.workspace_root, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7673
7725
|
const lock = acquireWorkspaceLock(validated.workspace_root, lockPath);
|
|
7674
7726
|
try {
|
|
7675
7727
|
verify();
|
|
@@ -7695,7 +7747,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7695
7747
|
if (!isRecord(observed) || typeof observed.path !== "string") {
|
|
7696
7748
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains malformed hash metadata");
|
|
7697
7749
|
}
|
|
7698
|
-
const path =
|
|
7750
|
+
const path = resolve5(observed.path);
|
|
7699
7751
|
if (!allowedPaths.has(path) || observedPaths.has(path)) {
|
|
7700
7752
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains an unexpected or duplicate path");
|
|
7701
7753
|
}
|
|
@@ -7717,7 +7769,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7717
7769
|
function applyProjectContext(options) {
|
|
7718
7770
|
const workspaceRoot = assertSafeWorkspaceRoot(options.workspace_root);
|
|
7719
7771
|
const now3 = options.now ?? new Date;
|
|
7720
|
-
const lockPath =
|
|
7772
|
+
const lockPath = resolve5(workspaceRoot, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7721
7773
|
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);
|
|
7722
7774
|
try {
|
|
7723
7775
|
const resolved = resolveBundleForApply(options, workspaceRoot, now3);
|
|
@@ -7864,7 +7916,7 @@ function resolveBundleForApply(options, workspaceRoot, now3) {
|
|
|
7864
7916
|
if (!options.expected_project_id) {
|
|
7865
7917
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_ID_REQUIRED", "expected_project_id is required for stale-cache fallback");
|
|
7866
7918
|
}
|
|
7867
|
-
const cachePath =
|
|
7919
|
+
const cachePath = resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
7868
7920
|
const cache = readProjectContextCache(cachePath, workspaceRoot);
|
|
7869
7921
|
if (!cache)
|
|
7870
7922
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_MISSING", "no last-known-good project context cache exists");
|
|
@@ -8066,7 +8118,7 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
8066
8118
|
if (runtime !== "codewith" || !content)
|
|
8067
8119
|
return null;
|
|
8068
8120
|
const sessionManifestPath = runtimePaths(workspaceRoot, runtime).sessionManifest;
|
|
8069
|
-
if (!
|
|
8121
|
+
if (!existsSync8(sessionManifestPath))
|
|
8070
8122
|
return null;
|
|
8071
8123
|
const manifest = readSessionManifestRecord(sessionManifestPath, workspaceRoot);
|
|
8072
8124
|
if (!manifest || manifest["schema"] !== SESSION_RENDER_SCHEMA) {
|
|
@@ -8117,7 +8169,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
8117
8169
|
const manifest = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8118
8170
|
if (manifest) {
|
|
8119
8171
|
const manifestHashHasRecoveryProof = manifest.projectContext.hash === plan.bundle.hash || metadataSnapshotMatchesManifest(plan, manifest);
|
|
8120
|
-
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 &&
|
|
8172
|
+
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 && existsSync8(plan.fragment_path) && fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && manifestHashHasRecoveryProof;
|
|
8121
8173
|
observations.push({
|
|
8122
8174
|
source: "manifest",
|
|
8123
8175
|
id: manifest.projectContext.projectId,
|
|
@@ -8125,7 +8177,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
8125
8177
|
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)
|
|
8126
8178
|
});
|
|
8127
8179
|
const fragmentEntry = manifest.files.find((file) => file.relativePath === PROJECT_CONTEXT_FRAGMENT_PATH);
|
|
8128
|
-
if (fragmentEntry &&
|
|
8180
|
+
if (fragmentEntry && existsSync8(plan.fragment_path)) {
|
|
8129
8181
|
const actual = currentFileHash(plan.fragment_path, plan.workspace_root);
|
|
8130
8182
|
if (actual !== fragmentEntry.sha256 && !fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && !force) {
|
|
8131
8183
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "canonical project-context fragment changed outside Instructions");
|
|
@@ -8220,9 +8272,9 @@ function buildManifest(plan, now3) {
|
|
|
8220
8272
|
function buildSessionCompatibilityManifest(plan, now3) {
|
|
8221
8273
|
const paths = runtimePaths(plan.workspace_root, plan.runtime);
|
|
8222
8274
|
const tool = manifestTool(plan.runtime);
|
|
8223
|
-
const targetHome = plan.runtime === "codewith" ?
|
|
8275
|
+
const targetHome = plan.runtime === "codewith" ? resolve5(plan.workspace_root, ".codewith") : plan.workspace_root;
|
|
8224
8276
|
const targetRelativePath = sessionTargetRelativePath(plan.runtime);
|
|
8225
|
-
const existing =
|
|
8277
|
+
const existing = existsSync8(paths.sessionManifest) ? readSessionManifestRecord(paths.sessionManifest, plan.workspace_root) : {
|
|
8226
8278
|
schema: SESSION_RENDER_SCHEMA,
|
|
8227
8279
|
tool,
|
|
8228
8280
|
adapterMode: plan.native_imports ? "native-imports" : "flattened-markdown",
|
|
@@ -8242,7 +8294,7 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8242
8294
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest is malformed or incompatible");
|
|
8243
8295
|
}
|
|
8244
8296
|
const existingTargetHome = safeLegacyMetadataString(existing["targetHome"], null);
|
|
8245
|
-
if (existingTargetHome !== null &&
|
|
8297
|
+
if (existingTargetHome !== null && resolve5(existingTargetHome) !== targetHome) {
|
|
8246
8298
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest targets a different workspace");
|
|
8247
8299
|
}
|
|
8248
8300
|
const sources = sanitizeLegacySources(existing["sources"]).filter((source) => source["id"] !== "project-context-bundle");
|
|
@@ -8528,9 +8580,9 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8528
8580
|
const previous = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8529
8581
|
if (!previous || previous.projectContext.revision === plan.bundle.revision && previous.projectContext.hash === plan.bundle.hash)
|
|
8530
8582
|
return null;
|
|
8531
|
-
const snapshotDir =
|
|
8583
|
+
const snapshotDir = resolve5(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8532
8584
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
8533
|
-
const snapshotPath =
|
|
8585
|
+
const snapshotPath = resolve5(snapshotDir, `${safeFilename(previous.projectContext.revision)}-${previous.projectContext.hash.slice(-12)}.json`);
|
|
8534
8586
|
const snapshot = {
|
|
8535
8587
|
schema: "hasna.configs.session-render-snapshot/v1",
|
|
8536
8588
|
kind: "project-context-metadata",
|
|
@@ -8546,9 +8598,9 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8546
8598
|
return snapshotPath;
|
|
8547
8599
|
}
|
|
8548
8600
|
function metadataSnapshotMatchesManifest(plan, manifest) {
|
|
8549
|
-
const snapshotDir =
|
|
8550
|
-
const snapshotPath =
|
|
8551
|
-
if (!
|
|
8601
|
+
const snapshotDir = resolve5(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8602
|
+
const snapshotPath = resolve5(snapshotDir, `${safeFilename(manifest.projectContext.revision)}-${manifest.projectContext.hash.slice(-12)}.json`);
|
|
8603
|
+
if (!existsSync8(snapshotPath))
|
|
8552
8604
|
return false;
|
|
8553
8605
|
const record = readJsonRecord(snapshotPath, plan.workspace_root);
|
|
8554
8606
|
const result = projectContextMetadataSnapshotSchema.safeParse(record);
|
|
@@ -8596,10 +8648,11 @@ function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
|
8596
8648
|
sha256: nextHash
|
|
8597
8649
|
};
|
|
8598
8650
|
});
|
|
8599
|
-
const
|
|
8600
|
-
|
|
8651
|
+
const snapshotWorkspaceRoot = sessionRenderSnapshotWorkspaceRoot(plan.workspace_root);
|
|
8652
|
+
const snapshotDir = getSessionRenderSnapshotDir(plan.workspace_root);
|
|
8653
|
+
ensureSafeDirectory(snapshotDir, snapshotWorkspaceRoot, 448);
|
|
8601
8654
|
const timestamp = now3.toISOString().replace(/[:.]/g, "-");
|
|
8602
|
-
const snapshotPath =
|
|
8655
|
+
const snapshotPath = resolve5(snapshotDir, `${timestamp}-${randomUUID5()}.json`);
|
|
8603
8656
|
const snapshot = {
|
|
8604
8657
|
schema: "hasna.configs.session-render-snapshot/v2",
|
|
8605
8658
|
createdAt: now3.toISOString(),
|
|
@@ -8613,11 +8666,11 @@ function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
|
8613
8666
|
afterFiles
|
|
8614
8667
|
};
|
|
8615
8668
|
atomicWriteFile(snapshotPath, `${JSON.stringify(snapshot, null, 2)}
|
|
8616
|
-
`,
|
|
8669
|
+
`, snapshotWorkspaceRoot, 384, null);
|
|
8617
8670
|
return snapshotPath;
|
|
8618
8671
|
}
|
|
8619
8672
|
function readProjectContextManifest(path, workspaceRoot) {
|
|
8620
|
-
if (!
|
|
8673
|
+
if (!existsSync8(path))
|
|
8621
8674
|
return null;
|
|
8622
8675
|
const record = readJsonRecord(path, workspaceRoot);
|
|
8623
8676
|
const result = storedManifestObservationSchema.safeParse(record);
|
|
@@ -8632,7 +8685,7 @@ function readProjectContextManifest(path, workspaceRoot) {
|
|
|
8632
8685
|
};
|
|
8633
8686
|
}
|
|
8634
8687
|
function readProjectContextCache(path, workspaceRoot) {
|
|
8635
|
-
if (!
|
|
8688
|
+
if (!existsSync8(path))
|
|
8636
8689
|
return null;
|
|
8637
8690
|
const record = readJsonRecord(path, workspaceRoot);
|
|
8638
8691
|
const result = projectContextCacheSchema.safeParse(record);
|
|
@@ -8664,7 +8717,7 @@ function readSessionManifestRecord(path, workspaceRoot) {
|
|
|
8664
8717
|
}
|
|
8665
8718
|
}
|
|
8666
8719
|
function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash, afterExchange, atomicExchangeUnavailable = false, beforeInstall, portableCreateOnly = false, maxObservedBytes, allowPortableReplacement = false) {
|
|
8667
|
-
const dir =
|
|
8720
|
+
const dir = resolve5(path, "..");
|
|
8668
8721
|
ensureSafeDirectory(dir, workspaceRoot, 448);
|
|
8669
8722
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8670
8723
|
const anchoredOps = portableCreateOnly ? null : resolveAnchoredFsOps();
|
|
@@ -8681,7 +8734,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8681
8734
|
const previous = anchoredFileObservation(directory, targetName);
|
|
8682
8735
|
const previousMode = previous?.mode ?? defaultMode;
|
|
8683
8736
|
const tempName = `.project-context-${randomUUID5()}.tmp`;
|
|
8684
|
-
const tempPath =
|
|
8737
|
+
const tempPath = join10(dir, tempName);
|
|
8685
8738
|
let fd = null;
|
|
8686
8739
|
let preserveTemp = false;
|
|
8687
8740
|
let directoryChanged = false;
|
|
@@ -8810,9 +8863,9 @@ function atomicWritePortable(path, content, workspaceRoot, defaultMode, expected
|
|
|
8810
8863
|
if (currentHash !== null) {
|
|
8811
8864
|
throw new ProjectContextHashRace(`managed path appeared before portable creation: ${relativePosix(workspaceRoot, path)}`);
|
|
8812
8865
|
}
|
|
8813
|
-
const dir =
|
|
8866
|
+
const dir = dirname2(path);
|
|
8814
8867
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8815
|
-
const tempPath =
|
|
8868
|
+
const tempPath = join10(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8816
8869
|
let fd = null;
|
|
8817
8870
|
let tempIdentity = null;
|
|
8818
8871
|
try {
|
|
@@ -8867,9 +8920,9 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8867
8920
|
if (current.isSymbolicLink() || !current.isFile()) {
|
|
8868
8921
|
throw new ProjectContextHashRace(`managed path is not a regular file: ${relativePosix(workspaceRoot, path)}`);
|
|
8869
8922
|
}
|
|
8870
|
-
const dir =
|
|
8923
|
+
const dir = dirname2(path);
|
|
8871
8924
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8872
|
-
const tempPath =
|
|
8925
|
+
const tempPath = join10(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8873
8926
|
const desiredHash = sha2562(content);
|
|
8874
8927
|
let fd = null;
|
|
8875
8928
|
let tempIdentity = null;
|
|
@@ -8926,7 +8979,7 @@ function portablePreparedHash(tempPath, path, workspaceRoot, maxObservedBytes, s
|
|
|
8926
8979
|
function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
8927
8980
|
if (maxObservedBytes === undefined)
|
|
8928
8981
|
return currentFileHash(path, workspaceRoot);
|
|
8929
|
-
if (!
|
|
8982
|
+
if (!existsSync8(path))
|
|
8930
8983
|
return null;
|
|
8931
8984
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8932
8985
|
const stat = lstatSync(path);
|
|
@@ -8938,13 +8991,13 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
8938
8991
|
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
8939
8992
|
}
|
|
8940
8993
|
function writeProjectContextCoordinatedFile(input) {
|
|
8941
|
-
atomicWriteFile(
|
|
8994
|
+
atomicWriteFile(resolve5(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);
|
|
8942
8995
|
}
|
|
8943
8996
|
function removeProjectContextCoordinatedFile(input) {
|
|
8944
8997
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
8945
|
-
const path =
|
|
8998
|
+
const path = resolve5(input.path);
|
|
8946
8999
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8947
|
-
const dir =
|
|
9000
|
+
const dir = dirname2(path);
|
|
8948
9001
|
const anchoredOps = input.force_portable_file_ops ? null : resolveAnchoredFsOps();
|
|
8949
9002
|
if (!anchoredOps) {
|
|
8950
9003
|
if (!input.allow_portable_removal) {
|
|
@@ -8968,7 +9021,7 @@ function removeProjectContextCoordinatedFile(input) {
|
|
|
8968
9021
|
throw new ProjectContextHashRace(`managed path changed during deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
8969
9022
|
}
|
|
8970
9023
|
displaced = true;
|
|
8971
|
-
input.test_hooks?.after_displace?.(
|
|
9024
|
+
input.test_hooks?.after_displace?.(join10(dir, displacedName));
|
|
8972
9025
|
const moved = anchoredFileObservation(directory, displacedName);
|
|
8973
9026
|
if (!moved || moved.dev !== observed.dev || moved.ino !== observed.ino || moved.hash !== input.expected_hash || anchoredFileObservation(directory, targetName) !== null) {
|
|
8974
9027
|
throw new ProjectContextHashRace(`managed path changed during deletion validation: ${relativePosix(workspaceRoot, path)}`);
|
|
@@ -9012,9 +9065,9 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
9012
9065
|
if (observed.isSymbolicLink() || !observed.isFile()) {
|
|
9013
9066
|
throw new ProjectContextHashRace(`managed path is not a regular file: ${relativePosix(workspaceRoot, path)}`);
|
|
9014
9067
|
}
|
|
9015
|
-
const dir =
|
|
9068
|
+
const dir = dirname2(path);
|
|
9016
9069
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
9017
|
-
const displacedPath =
|
|
9070
|
+
const displacedPath = join10(dir, `.project-context-delete-${randomUUID5()}.tmp`);
|
|
9018
9071
|
let displaced = false;
|
|
9019
9072
|
try {
|
|
9020
9073
|
assertManagedDirectoryStable(dir, workspaceRoot, directoryIdentity);
|
|
@@ -9025,7 +9078,7 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
9025
9078
|
displaced = true;
|
|
9026
9079
|
afterDisplace?.(displacedPath);
|
|
9027
9080
|
const moved = lstatSync(displacedPath);
|
|
9028
|
-
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash ||
|
|
9081
|
+
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash || existsSync8(path)) {
|
|
9029
9082
|
throw new ProjectContextHashRace(`managed path changed during portable deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
9030
9083
|
}
|
|
9031
9084
|
rmSync2(displacedPath);
|
|
@@ -9051,7 +9104,7 @@ function restorePortableDisplacedFile(displacedPath, path, workspaceRoot, expect
|
|
|
9051
9104
|
if (displaced.isSymbolicLink() || installed.isSymbolicLink() || !displaced.isFile() || !installed.isFile() || displaced.dev !== expected.dev || displaced.ino !== expected.ino || displaced.dev !== installed.dev || displaced.ino !== installed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash || portableFileHash(path, workspaceRoot, maxObservedBytes) !== expectedHash)
|
|
9052
9105
|
return false;
|
|
9053
9106
|
rmSync2(displacedPath);
|
|
9054
|
-
fsyncDirectory(
|
|
9107
|
+
fsyncDirectory(dirname2(path));
|
|
9055
9108
|
return true;
|
|
9056
9109
|
} catch {
|
|
9057
9110
|
return false;
|
|
@@ -9085,7 +9138,7 @@ function anchoredOpenExclusive(directory, name, mode) {
|
|
|
9085
9138
|
const requestedMode = mode & 4095;
|
|
9086
9139
|
let fd;
|
|
9087
9140
|
try {
|
|
9088
|
-
fd = openSync(
|
|
9141
|
+
fd = openSync(join10(directory.path, name), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, requestedMode);
|
|
9089
9142
|
} catch {
|
|
9090
9143
|
throw new ProjectContextHashRace(`could not create prepared managed file in ${relativePosix(directory.workspaceRoot, directory.path)}`);
|
|
9091
9144
|
}
|
|
@@ -9128,7 +9181,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
9128
9181
|
const stat = fstatSync(fd);
|
|
9129
9182
|
if (!stat.isFile())
|
|
9130
9183
|
throw new ProjectContextHashRace("managed output is not a regular file");
|
|
9131
|
-
const relativePath = relativePosix(directory.workspaceRoot,
|
|
9184
|
+
const relativePath = relativePosix(directory.workspaceRoot, join10(directory.path, name));
|
|
9132
9185
|
const maxBytes = directory.maxObservedBytes === undefined ? managedObservationMaxBytes(relativePath) : directory.maxObservedBytes;
|
|
9133
9186
|
if (maxBytes !== null && stat.size > maxBytes) {
|
|
9134
9187
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePath}`);
|
|
@@ -9154,7 +9207,7 @@ function anchoredPreparedObservation(directory, name, path, stage) {
|
|
|
9154
9207
|
return observed;
|
|
9155
9208
|
}
|
|
9156
9209
|
function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
9157
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9210
|
+
assertNoSymlinkSegments(workspaceRoot, join10(path, ".project-context-directory-guard"));
|
|
9158
9211
|
let stat;
|
|
9159
9212
|
try {
|
|
9160
9213
|
stat = lstatSync(path);
|
|
@@ -9167,7 +9220,7 @@ function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
|
9167
9220
|
return { dev: stat.dev, ino: stat.ino };
|
|
9168
9221
|
}
|
|
9169
9222
|
function assertManagedDirectoryStable(path, workspaceRoot, expected) {
|
|
9170
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9223
|
+
assertNoSymlinkSegments(workspaceRoot, join10(path, ".project-context-directory-guard"));
|
|
9171
9224
|
let current;
|
|
9172
9225
|
try {
|
|
9173
9226
|
current = lstatSync(path);
|
|
@@ -9300,10 +9353,10 @@ function resolveAnchoredFsOps() {
|
|
|
9300
9353
|
return null;
|
|
9301
9354
|
}
|
|
9302
9355
|
function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRemove, processStartIdentityLookup = processStartIdentity) {
|
|
9303
|
-
const lockDirectory =
|
|
9356
|
+
const lockDirectory = resolve5(lockPath, "..");
|
|
9304
9357
|
ensureSafeDirectory(lockDirectory, workspaceRoot, 448);
|
|
9305
9358
|
assertNoSymlinkSegments(workspaceRoot, lockPath);
|
|
9306
|
-
const tempPath =
|
|
9359
|
+
const tempPath = join10(lockDirectory, `.project-context-lock-${randomUUID5()}.tmp`);
|
|
9307
9360
|
let fd = null;
|
|
9308
9361
|
let openedIdentity = null;
|
|
9309
9362
|
let openedContentHash = null;
|
|
@@ -9337,7 +9390,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9337
9390
|
linked = true;
|
|
9338
9391
|
}
|
|
9339
9392
|
fsyncDirectory(lockDirectory);
|
|
9340
|
-
if (
|
|
9393
|
+
if (existsSync8(tempPath)) {
|
|
9341
9394
|
rmSync2(tempPath);
|
|
9342
9395
|
fsyncDirectory(lockDirectory);
|
|
9343
9396
|
}
|
|
@@ -9352,7 +9405,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9352
9405
|
if (linked && openedIdentity && openedContentHash) {
|
|
9353
9406
|
removeOwnedLockByInode(lockPath, openedIdentity, openedContentHash);
|
|
9354
9407
|
}
|
|
9355
|
-
if (!preserveTemp &&
|
|
9408
|
+
if (!preserveTemp && existsSync8(tempPath)) {
|
|
9356
9409
|
try {
|
|
9357
9410
|
rmSync2(tempPath);
|
|
9358
9411
|
} catch {}
|
|
@@ -9367,7 +9420,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9367
9420
|
}
|
|
9368
9421
|
function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
9369
9422
|
try {
|
|
9370
|
-
if (!
|
|
9423
|
+
if (!existsSync8(lockPath))
|
|
9371
9424
|
return;
|
|
9372
9425
|
const current = lstatSync(lockPath);
|
|
9373
9426
|
if (current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino)
|
|
@@ -9375,7 +9428,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
9375
9428
|
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
9376
9429
|
return;
|
|
9377
9430
|
rmSync2(lockPath);
|
|
9378
|
-
fsyncDirectory(
|
|
9431
|
+
fsyncDirectory(resolve5(lockPath, ".."));
|
|
9379
9432
|
} catch {}
|
|
9380
9433
|
}
|
|
9381
9434
|
function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentityLookup = processStartIdentity) {
|
|
@@ -9446,7 +9499,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9446
9499
|
const candidateInstalled = !current.isSymbolicLink() && current.dev === candidateIdentity.dev && current.ino === candidateIdentity.ino && currentFileHash(lockPath, workspaceRoot) === candidateHash;
|
|
9447
9500
|
const staleDisplaced = !displaced.isSymbolicLink() && displaced.dev === stale.identity.dev && displaced.ino === stale.identity.ino && currentFileHash(candidatePath, workspaceRoot) === stale.contentHash;
|
|
9448
9501
|
if (!candidateInstalled || !staleDisplaced) {
|
|
9449
|
-
if (candidateInstalled &&
|
|
9502
|
+
if (candidateInstalled && existsSync8(candidatePath)) {
|
|
9450
9503
|
atomicExchangePaths(candidatePath, lockPath);
|
|
9451
9504
|
exchanged = false;
|
|
9452
9505
|
return false;
|
|
@@ -9454,13 +9507,13 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9454
9507
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace lock changed during stale-lock takeover and could not be restored safely");
|
|
9455
9508
|
}
|
|
9456
9509
|
rmSync2(candidatePath);
|
|
9457
|
-
fsyncDirectory(
|
|
9510
|
+
fsyncDirectory(resolve5(lockPath, ".."));
|
|
9458
9511
|
exchanged = false;
|
|
9459
9512
|
return true;
|
|
9460
9513
|
} catch (error) {
|
|
9461
9514
|
if (exchanged) {
|
|
9462
9515
|
try {
|
|
9463
|
-
if (currentFileHash(lockPath, workspaceRoot) === candidateHash &&
|
|
9516
|
+
if (currentFileHash(lockPath, workspaceRoot) === candidateHash && existsSync8(candidatePath)) {
|
|
9464
9517
|
atomicExchangePaths(candidatePath, lockPath);
|
|
9465
9518
|
exchanged = false;
|
|
9466
9519
|
}
|
|
@@ -9473,7 +9526,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9473
9526
|
}
|
|
9474
9527
|
}
|
|
9475
9528
|
function assertWorkspaceLockHeld(lockPath, lock, workspaceRoot) {
|
|
9476
|
-
if (!
|
|
9529
|
+
if (!existsSync8(lockPath)) {
|
|
9477
9530
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace project-context lock changed during render");
|
|
9478
9531
|
}
|
|
9479
9532
|
const current = lstatSync(lockPath);
|
|
@@ -9533,8 +9586,8 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9533
9586
|
}
|
|
9534
9587
|
return;
|
|
9535
9588
|
}
|
|
9536
|
-
const lockDirectory =
|
|
9537
|
-
const releasePath =
|
|
9589
|
+
const lockDirectory = resolve5(lockPath, "..");
|
|
9590
|
+
const releasePath = join10(lockDirectory, `.project-context-release-${randomUUID5()}.tmp`);
|
|
9538
9591
|
let releaseFd = null;
|
|
9539
9592
|
let releaseIdentity = null;
|
|
9540
9593
|
let releaseHash = null;
|
|
@@ -9563,7 +9616,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9563
9616
|
const releaseInstalled = !installed.isSymbolicLink() && installed.dev === releaseIdentity.dev && installed.ino === releaseIdentity.ino && currentFileHash(lockPath, workspaceRoot) === releaseHash;
|
|
9564
9617
|
const ownedDisplaced = !displaced.isSymbolicLink() && displaced.dev === lock.identity.dev && displaced.ino === lock.identity.ino && currentFileHash(releasePath, workspaceRoot) === lock.contentHash;
|
|
9565
9618
|
if (!releaseInstalled || !ownedDisplaced) {
|
|
9566
|
-
if (releaseInstalled &&
|
|
9619
|
+
if (releaseInstalled && existsSync8(releasePath)) {
|
|
9567
9620
|
atomicExchangePaths(releasePath, lockPath);
|
|
9568
9621
|
exchanged = false;
|
|
9569
9622
|
}
|
|
@@ -9576,7 +9629,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9576
9629
|
} catch {
|
|
9577
9630
|
if (exchanged) {
|
|
9578
9631
|
try {
|
|
9579
|
-
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash &&
|
|
9632
|
+
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash && existsSync8(releasePath)) {
|
|
9580
9633
|
atomicExchangePaths(releasePath, lockPath);
|
|
9581
9634
|
exchanged = false;
|
|
9582
9635
|
}
|
|
@@ -9588,7 +9641,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9588
9641
|
closeSync(releaseFd);
|
|
9589
9642
|
} catch {}
|
|
9590
9643
|
}
|
|
9591
|
-
if (!exchanged &&
|
|
9644
|
+
if (!exchanged && existsSync8(releasePath)) {
|
|
9592
9645
|
try {
|
|
9593
9646
|
rmSync2(releasePath);
|
|
9594
9647
|
} catch {}
|
|
@@ -9614,15 +9667,15 @@ function ensureSafeDirectory(path, workspaceRoot, mode) {
|
|
|
9614
9667
|
const segments = rel.split(/[\\/]+/).filter(Boolean);
|
|
9615
9668
|
let current = workspaceRoot;
|
|
9616
9669
|
for (const segment of segments) {
|
|
9617
|
-
current =
|
|
9618
|
-
if (
|
|
9670
|
+
current = join10(current, segment);
|
|
9671
|
+
if (existsSync8(current)) {
|
|
9619
9672
|
if (lstatSync(current).isSymbolicLink())
|
|
9620
9673
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
9621
9674
|
if (!statSync(current).isDirectory())
|
|
9622
9675
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", `managed path is not a directory: ${current}`);
|
|
9623
9676
|
} else {
|
|
9624
9677
|
mkdirSync2(current, { mode });
|
|
9625
|
-
fsyncDirectory(
|
|
9678
|
+
fsyncDirectory(resolve5(current, ".."));
|
|
9626
9679
|
}
|
|
9627
9680
|
}
|
|
9628
9681
|
}
|
|
@@ -9678,11 +9731,11 @@ function scanGeneratedContent(content) {
|
|
|
9678
9731
|
function runtimePaths(workspaceRoot, runtime) {
|
|
9679
9732
|
const relativeTarget = runtime === "claude" ? "CLAUDE.md" : runtime === "codewith" ? ".codewith/CODEWITH.md" : "AGENTS.md";
|
|
9680
9733
|
return {
|
|
9681
|
-
target:
|
|
9682
|
-
fragment:
|
|
9683
|
-
manifest:
|
|
9684
|
-
cache:
|
|
9685
|
-
sessionManifest: runtime === "codewith" ?
|
|
9734
|
+
target: resolve5(workspaceRoot, ...relativeTarget.split("/")),
|
|
9735
|
+
fragment: resolve5(workspaceRoot, ...PROJECT_CONTEXT_FRAGMENT_PATH.split("/")),
|
|
9736
|
+
manifest: resolve5(workspaceRoot, ...PROJECT_CONTEXT_MANIFEST_PATH.split("/")),
|
|
9737
|
+
cache: resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/")),
|
|
9738
|
+
sessionManifest: runtime === "codewith" ? resolve5(workspaceRoot, ".codewith", ".hasna", "session-render-manifest.json") : resolve5(workspaceRoot, ".hasna", "session-render-manifest.json")
|
|
9686
9739
|
};
|
|
9687
9740
|
}
|
|
9688
9741
|
function projectContextSessionGuardPaths(paths, runtime) {
|
|
@@ -9692,7 +9745,7 @@ function projectContextSessionGuardPaths(paths, runtime) {
|
|
|
9692
9745
|
paths.fragment,
|
|
9693
9746
|
paths.target,
|
|
9694
9747
|
paths.sessionManifest,
|
|
9695
|
-
...runtime === "codewith" ? [
|
|
9748
|
+
...runtime === "codewith" ? [resolve5(paths.target, "..", "CODEWITH.override.md")] : []
|
|
9696
9749
|
];
|
|
9697
9750
|
}
|
|
9698
9751
|
function sessionTargetRelativePath(runtime) {
|
|
@@ -9712,27 +9765,27 @@ function projectContextRuntimeForSessionTool(tool) {
|
|
|
9712
9765
|
return null;
|
|
9713
9766
|
}
|
|
9714
9767
|
function projectContextWorkspaceForSession(input, runtime) {
|
|
9715
|
-
const targetHome =
|
|
9768
|
+
const targetHome = resolve5(input.target_home);
|
|
9716
9769
|
if (runtime === "codewith") {
|
|
9717
|
-
const workspaceRoot = basename(targetHome) === ".codewith" ?
|
|
9770
|
+
const workspaceRoot = basename(targetHome) === ".codewith" ? dirname2(targetHome) : null;
|
|
9718
9771
|
if (!workspaceRoot)
|
|
9719
9772
|
return null;
|
|
9720
|
-
if (input.project_root &&
|
|
9773
|
+
if (input.project_root && resolve5(input.project_root) !== workspaceRoot) {
|
|
9721
9774
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "Codewith project_root must be the parent workspace of target_home");
|
|
9722
9775
|
}
|
|
9723
|
-
if (!
|
|
9776
|
+
if (!existsSync8(workspaceRoot) || !lstatSync(workspaceRoot).isDirectory())
|
|
9724
9777
|
return null;
|
|
9725
9778
|
return assertSafeWorkspaceRoot(workspaceRoot);
|
|
9726
9779
|
}
|
|
9727
|
-
if (!
|
|
9780
|
+
if (!existsSync8(targetHome) || !lstatSync(targetHome).isDirectory())
|
|
9728
9781
|
return null;
|
|
9729
9782
|
return assertSafeWorkspaceRoot(targetHome);
|
|
9730
9783
|
}
|
|
9731
9784
|
function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
9732
9785
|
if (runtime !== "codewith")
|
|
9733
9786
|
return;
|
|
9734
|
-
const override =
|
|
9735
|
-
if (!
|
|
9787
|
+
const override = resolve5(workspaceRoot, ".codewith", "CODEWITH.override.md");
|
|
9788
|
+
if (!existsSync8(override))
|
|
9736
9789
|
return;
|
|
9737
9790
|
assertNoSymlinkSegments(workspaceRoot, override);
|
|
9738
9791
|
if (!lstatSync(override).isFile())
|
|
@@ -9742,10 +9795,10 @@ function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
|
9742
9795
|
function assertSafeWorkspaceRoot(path) {
|
|
9743
9796
|
if (!isAbsolute(path))
|
|
9744
9797
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be absolute");
|
|
9745
|
-
const normalized =
|
|
9798
|
+
const normalized = resolve5(path);
|
|
9746
9799
|
if (normalized === parse(normalized).root)
|
|
9747
9800
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root cannot be the filesystem root");
|
|
9748
|
-
if (!
|
|
9801
|
+
if (!existsSync8(normalized) || !lstatSync(normalized).isDirectory())
|
|
9749
9802
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be an existing directory");
|
|
9750
9803
|
assertNoSymlinkAncestors(normalized);
|
|
9751
9804
|
if (lstatSync(normalized).isSymbolicLink())
|
|
@@ -9759,18 +9812,18 @@ function assertNoSymlinkSegments(root, target) {
|
|
|
9759
9812
|
}
|
|
9760
9813
|
let current = root;
|
|
9761
9814
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
9762
|
-
current =
|
|
9763
|
-
if (
|
|
9815
|
+
current = join10(current, segment);
|
|
9816
|
+
if (existsSync8(current) && lstatSync(current).isSymbolicLink()) {
|
|
9764
9817
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
9765
9818
|
}
|
|
9766
9819
|
}
|
|
9767
9820
|
}
|
|
9768
9821
|
function assertNoSymlinkAncestors(path) {
|
|
9769
|
-
const normalized =
|
|
9822
|
+
const normalized = resolve5(path);
|
|
9770
9823
|
let current = parse(normalized).root;
|
|
9771
9824
|
for (const segment of relative(current, normalized).split(/[\\/]+/).filter(Boolean)) {
|
|
9772
|
-
current =
|
|
9773
|
-
if (!
|
|
9825
|
+
current = join10(current, segment);
|
|
9826
|
+
if (!existsSync8(current))
|
|
9774
9827
|
return;
|
|
9775
9828
|
if (lstatSync(current).isSymbolicLink())
|
|
9776
9829
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `workspace ancestor is a symlink: ${current}`);
|
|
@@ -9786,7 +9839,7 @@ function readUtf8RegularFile(path, workspaceRoot, maxBytes = FOREIGN_INPUT_MAX_B
|
|
|
9786
9839
|
return readFileSync(path, "utf8");
|
|
9787
9840
|
}
|
|
9788
9841
|
function currentFileHash(path, workspaceRoot) {
|
|
9789
|
-
if (!
|
|
9842
|
+
if (!existsSync8(path))
|
|
9790
9843
|
return null;
|
|
9791
9844
|
const relativePath = relativePosix(workspaceRoot, path);
|
|
9792
9845
|
return sha2562(readUtf8RegularFile(path, workspaceRoot, managedObservationMaxBytes(relativePath)));
|
|
@@ -9805,10 +9858,10 @@ function fragmentMatchesBundle(path, bundle, workspaceRoot) {
|
|
|
9805
9858
|
}
|
|
9806
9859
|
function durableSourcePath(path, workspaceRoot) {
|
|
9807
9860
|
if (!path || path.startsWith("/dev/fd/"))
|
|
9808
|
-
return
|
|
9809
|
-
const normalized = isAbsolute(path) ?
|
|
9861
|
+
return resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9862
|
+
const normalized = isAbsolute(path) ? resolve5(path) : resolve5(workspaceRoot, path);
|
|
9810
9863
|
if (normalized.startsWith("/dev/fd/"))
|
|
9811
|
-
return
|
|
9864
|
+
return resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9812
9865
|
return normalized;
|
|
9813
9866
|
}
|
|
9814
9867
|
function compareRevisions(incoming, previous) {
|
|
@@ -9980,6 +10033,7 @@ var init_project_context = __esm(() => {
|
|
|
9980
10033
|
init_zod();
|
|
9981
10034
|
init_redact();
|
|
9982
10035
|
init_session_render_contract();
|
|
10036
|
+
init_session_render_state();
|
|
9983
10037
|
PROJECT_CONTEXT_SUPPORTED_SCHEMAS = [PROJECT_CONTEXT_SCHEMA, PROJECT_CONTEXT_SCHEMA_V2];
|
|
9984
10038
|
projectContextSchema = exports_external.enum(PROJECT_CONTEXT_SUPPORTED_SCHEMAS);
|
|
9985
10039
|
PROJECT_CONTEXT_MAX_INPUT_BYTES = 8 * 1024;
|
|
@@ -10368,7 +10422,7 @@ function compareProviderVersions(left, right) {
|
|
|
10368
10422
|
|
|
10369
10423
|
// src/lib/asset-plan.ts
|
|
10370
10424
|
import { createHash as createHash3 } from "crypto";
|
|
10371
|
-
import { isAbsolute as isAbsolute2, posix, resolve as
|
|
10425
|
+
import { isAbsolute as isAbsolute2, posix, resolve as resolve6 } from "path";
|
|
10372
10426
|
function assetCapability(provider, surface, kind, support, strategies, note, providerVersionRange = "*") {
|
|
10373
10427
|
return Object.freeze({
|
|
10374
10428
|
schema: ASSET_CAPABILITY_SCHEMA,
|
|
@@ -10575,8 +10629,8 @@ function resolveAssetDestination(item, roots) {
|
|
|
10575
10629
|
if (!isAbsolute2(root))
|
|
10576
10630
|
throw new Error(`Asset ${item.assetKey} destination root must be absolute.`);
|
|
10577
10631
|
const relativePath = safeRelativePath(item.destination.relativePath);
|
|
10578
|
-
const target =
|
|
10579
|
-
const normalizedRoot =
|
|
10632
|
+
const target = resolve6(root, ...relativePath.split("/"));
|
|
10633
|
+
const normalizedRoot = resolve6(root);
|
|
10580
10634
|
if (target === normalizedRoot)
|
|
10581
10635
|
throw new Error(`Asset ${item.assetKey} destination cannot replace its root.`);
|
|
10582
10636
|
if (!target.startsWith(`${normalizedRoot}/`))
|
|
@@ -10756,13 +10810,13 @@ var init_asset_plan = __esm(() => {
|
|
|
10756
10810
|
// src/lib/cursor-authority.ts
|
|
10757
10811
|
import { createHash as createHash4 } from "crypto";
|
|
10758
10812
|
import { lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
|
|
10759
|
-
import { homedir as
|
|
10760
|
-
import { join as
|
|
10813
|
+
import { homedir as homedir8 } from "os";
|
|
10814
|
+
import { join as join11, resolve as resolve7 } from "path";
|
|
10761
10815
|
function sha2564(content) {
|
|
10762
10816
|
return createHash4("sha256").update(content).digest("hex");
|
|
10763
10817
|
}
|
|
10764
|
-
function
|
|
10765
|
-
return process.env["HOME"] ||
|
|
10818
|
+
function homeDir3() {
|
|
10819
|
+
return process.env["HOME"] || homedir8();
|
|
10766
10820
|
}
|
|
10767
10821
|
function markerPayload(content, markerLine, markerIndex) {
|
|
10768
10822
|
const index = markerIndex ?? content.indexOf(markerLine);
|
|
@@ -10778,12 +10832,12 @@ function baseObservation(path) {
|
|
|
10778
10832
|
};
|
|
10779
10833
|
}
|
|
10780
10834
|
function observeCursorGlobalAuthority(options = {}) {
|
|
10781
|
-
const authorityPath =
|
|
10835
|
+
const authorityPath = resolve7(join11(options.home ?? homeDir3(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10782
10836
|
const readFile2 = options.readFile ?? ((path) => readFileSync2(path, "utf8"));
|
|
10783
10837
|
return observeCursorGlobalAuthorityPath(authorityPath, readFile2);
|
|
10784
10838
|
}
|
|
10785
10839
|
function observeCursorGlobalAuthorityAtPath(authorityPath) {
|
|
10786
|
-
return observeCursorGlobalAuthorityPath(
|
|
10840
|
+
return observeCursorGlobalAuthorityPath(resolve7(authorityPath), (path) => readFileSync2(path, "utf8"));
|
|
10787
10841
|
}
|
|
10788
10842
|
function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
10789
10843
|
const base = baseObservation(authorityPath);
|
|
@@ -10928,7 +10982,7 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
|
10928
10982
|
};
|
|
10929
10983
|
}
|
|
10930
10984
|
function isCursorGlobalAuthorityPath(path) {
|
|
10931
|
-
return
|
|
10985
|
+
return resolve7(path) === resolve7(join11(homeDir3(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10932
10986
|
}
|
|
10933
10987
|
function stampCursorGlobalAuthorityMarker(content) {
|
|
10934
10988
|
const existing = content.match(CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN);
|
|
@@ -10984,16 +11038,16 @@ var init_cursor_authority = __esm(() => {
|
|
|
10984
11038
|
// src/lib/session-authority.ts
|
|
10985
11039
|
import { createHash as createHash5 } from "crypto";
|
|
10986
11040
|
import { lstatSync as lstatSync3, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "fs";
|
|
10987
|
-
import { homedir as
|
|
10988
|
-
import { join as
|
|
11041
|
+
import { homedir as homedir9 } from "os";
|
|
11042
|
+
import { join as join12, resolve as resolve8 } from "path";
|
|
10989
11043
|
function sha2565(content) {
|
|
10990
11044
|
return createHash5("sha256").update(content).digest("hex");
|
|
10991
11045
|
}
|
|
10992
11046
|
function configHomeDir() {
|
|
10993
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
11047
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir9();
|
|
10994
11048
|
}
|
|
10995
11049
|
function normalizeOwnedTargetPath(p) {
|
|
10996
|
-
const expanded = p.startsWith("~/") ?
|
|
11050
|
+
const expanded = p.startsWith("~/") ? resolve8(configHomeDir(), p.slice(2)) : resolve8(p);
|
|
10997
11051
|
try {
|
|
10998
11052
|
return realpathSync(expanded);
|
|
10999
11053
|
} catch {
|
|
@@ -11001,7 +11055,7 @@ function normalizeOwnedTargetPath(p) {
|
|
|
11001
11055
|
}
|
|
11002
11056
|
}
|
|
11003
11057
|
function detectClaudeAuthorityConflicts(targetHome, ownedAuthorities = []) {
|
|
11004
|
-
const authorityPath =
|
|
11058
|
+
const authorityPath = resolve8(join12(targetHome, CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH));
|
|
11005
11059
|
let stat;
|
|
11006
11060
|
try {
|
|
11007
11061
|
stat = lstatSync3(authorityPath);
|
|
@@ -11090,9 +11144,9 @@ var init_session_authority = __esm(() => {
|
|
|
11090
11144
|
|
|
11091
11145
|
// src/lib/session-render.ts
|
|
11092
11146
|
import { createHash as createHash6 } from "crypto";
|
|
11093
|
-
import { existsSync as
|
|
11094
|
-
import { homedir as
|
|
11095
|
-
import { basename as basename4, dirname as
|
|
11147
|
+
import { existsSync as existsSync10, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
11148
|
+
import { homedir as homedir10 } from "os";
|
|
11149
|
+
import { basename as basename4, dirname as dirname4, extname as extname2, isAbsolute as isAbsolute3, join as join13, parse as parse2, posix as posix2, relative as relative2, resolve as resolve9 } from "path";
|
|
11096
11150
|
function normalizeSessionInstructionLayer(value) {
|
|
11097
11151
|
if (value === "provider")
|
|
11098
11152
|
return "tool";
|
|
@@ -11178,13 +11232,13 @@ function yamlQuote2(value) {
|
|
|
11178
11232
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
11179
11233
|
}
|
|
11180
11234
|
function defaultTargetHome(tool, profile, sessionId) {
|
|
11181
|
-
const home = process.env["HOME"] ||
|
|
11182
|
-
return
|
|
11235
|
+
const home = process.env["HOME"] || homedir10();
|
|
11236
|
+
return join13(home, ".hasna", "accounts", "profiles", tool, slug(profile));
|
|
11183
11237
|
}
|
|
11184
11238
|
function joinTarget(targetHome, relativePath) {
|
|
11185
11239
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
11186
11240
|
const safeRelativePath2 = assertSafeRelativePath(relativePath);
|
|
11187
|
-
return
|
|
11241
|
+
return join13(safeTargetHome, ...safeRelativePath2.split("/"));
|
|
11188
11242
|
}
|
|
11189
11243
|
function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
11190
11244
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
@@ -11753,7 +11807,7 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
|
|
|
11753
11807
|
...sources.flatMap((source) => source.resolvedRules.map((rule) => rule.id))
|
|
11754
11808
|
]);
|
|
11755
11809
|
const existingConfigPath = joinTarget(targetHome, adapter.configFile);
|
|
11756
|
-
const selectedConfig =
|
|
11810
|
+
const selectedConfig = existsSync10(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
|
|
11757
11811
|
const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
|
|
11758
11812
|
const config = {
|
|
11759
11813
|
...selectedConfig,
|
|
@@ -11942,7 +11996,7 @@ function adapterFor(input) {
|
|
|
11942
11996
|
return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
|
|
11943
11997
|
}
|
|
11944
11998
|
function getHomeDir() {
|
|
11945
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
11999
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir10();
|
|
11946
12000
|
}
|
|
11947
12001
|
function cleanSessionPathInput(path) {
|
|
11948
12002
|
const trimmed = path.trim();
|
|
@@ -11957,16 +12011,16 @@ function resolveSessionPath(path) {
|
|
|
11957
12011
|
throw new Error("Session render path cannot be empty.");
|
|
11958
12012
|
const home = getHomeDir();
|
|
11959
12013
|
if (cleaned === "~")
|
|
11960
|
-
return
|
|
12014
|
+
return resolve9(home);
|
|
11961
12015
|
if (cleaned.startsWith("~/"))
|
|
11962
|
-
return
|
|
12016
|
+
return resolve9(home, cleaned.slice(2));
|
|
11963
12017
|
if (cleaned === "{{HOME}}" || cleaned === "${HOME}")
|
|
11964
|
-
return
|
|
12018
|
+
return resolve9(home);
|
|
11965
12019
|
if (cleaned.startsWith("{{HOME}}/"))
|
|
11966
|
-
return
|
|
12020
|
+
return resolve9(home, cleaned.slice("{{HOME}}/".length));
|
|
11967
12021
|
if (cleaned.startsWith("${HOME}/"))
|
|
11968
|
-
return
|
|
11969
|
-
return
|
|
12022
|
+
return resolve9(home, cleaned.slice("${HOME}/".length));
|
|
12023
|
+
return resolve9(cleaned);
|
|
11970
12024
|
}
|
|
11971
12025
|
function assertSafeRelativePath(relativePath) {
|
|
11972
12026
|
if (!relativePath.trim())
|
|
@@ -11982,7 +12036,7 @@ function assertSafeRelativePath(relativePath) {
|
|
|
11982
12036
|
function assertSafeTargetRoot(targetHome) {
|
|
11983
12037
|
if (!isAbsolute3(targetHome))
|
|
11984
12038
|
throw new Error(`Session render target must be an absolute path: ${targetHome}`);
|
|
11985
|
-
const normalized =
|
|
12039
|
+
const normalized = resolve9(targetHome);
|
|
11986
12040
|
if (normalized === parse2(normalized).root) {
|
|
11987
12041
|
throw new Error(`Session render target cannot be the filesystem root: ${targetHome}`);
|
|
11988
12042
|
}
|
|
@@ -12218,7 +12272,7 @@ function planSessionRender(input) {
|
|
|
12218
12272
|
sourceId: input.providerConfig.sourceId,
|
|
12219
12273
|
selectedPayloadSha256: sha2566(input.providerConfig.content),
|
|
12220
12274
|
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2566(input.providerConfig.content),
|
|
12221
|
-
selected: !
|
|
12275
|
+
selected: !existsSync10(joinTarget(targetHome, adapter.configFile))
|
|
12222
12276
|
}
|
|
12223
12277
|
} : {},
|
|
12224
12278
|
...projectContext ? {
|
|
@@ -12479,10 +12533,10 @@ function layerFromIdentityKind(kind, exportShape) {
|
|
|
12479
12533
|
function contentFromIdentitySourcePaths(sourcePaths, exportPath, sourceId) {
|
|
12480
12534
|
if (sourcePaths.length === 0 || !exportPath)
|
|
12481
12535
|
return;
|
|
12482
|
-
const
|
|
12536
|
+
const baseDir3 = dirname4(resolveSessionPath(exportPath));
|
|
12483
12537
|
const contents = [];
|
|
12484
12538
|
for (const sourcePath of sourcePaths) {
|
|
12485
|
-
const content = readIdentitySourcePath(sourcePath,
|
|
12539
|
+
const content = readIdentitySourcePath(sourcePath, baseDir3, sourceId);
|
|
12486
12540
|
if (content !== undefined)
|
|
12487
12541
|
contents.push({ path: sourcePath.path, content });
|
|
12488
12542
|
}
|
|
@@ -12495,9 +12549,9 @@ ${item.content.trimEnd()}`).join(`
|
|
|
12495
12549
|
|
|
12496
12550
|
`));
|
|
12497
12551
|
}
|
|
12498
|
-
function readIdentitySourcePath(sourcePath,
|
|
12499
|
-
const resolvedPath = resolveIdentitySourcePath(sourcePath.path,
|
|
12500
|
-
if (!
|
|
12552
|
+
function readIdentitySourcePath(sourcePath, baseDir3, sourceId) {
|
|
12553
|
+
const resolvedPath = resolveIdentitySourcePath(sourcePath.path, baseDir3, sourceId);
|
|
12554
|
+
if (!existsSync10(resolvedPath)) {
|
|
12501
12555
|
if (sourcePath.required) {
|
|
12502
12556
|
throw new Error(`Required identity instruction source path not found for ${sourceId}: ${sourcePath.path}`);
|
|
12503
12557
|
}
|
|
@@ -12507,27 +12561,27 @@ function readIdentitySourcePath(sourcePath, baseDir2, sourceId) {
|
|
|
12507
12561
|
if (!stat.isFile()) {
|
|
12508
12562
|
throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
|
|
12509
12563
|
}
|
|
12510
|
-
const realBase = realpathSync2(
|
|
12564
|
+
const realBase = realpathSync2(baseDir3);
|
|
12511
12565
|
const realPath = realpathSync2(resolvedPath);
|
|
12512
12566
|
if (!pathIsInside(realPath, realBase)) {
|
|
12513
12567
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${sourcePath.path}`);
|
|
12514
12568
|
}
|
|
12515
12569
|
return readFileSync4(realPath, "utf-8");
|
|
12516
12570
|
}
|
|
12517
|
-
function resolveIdentitySourcePath(path,
|
|
12571
|
+
function resolveIdentitySourcePath(path, baseDir3, sourceId) {
|
|
12518
12572
|
const cleaned = cleanSessionPathInput(path);
|
|
12519
12573
|
if (!cleaned)
|
|
12520
12574
|
throw new Error(`Identity instruction source path cannot be empty for ${sourceId}.`);
|
|
12521
12575
|
if (cleaned.includes("\\"))
|
|
12522
12576
|
throw new Error(`Identity instruction source path must use POSIX separators for ${sourceId}: ${path}`);
|
|
12523
|
-
const resolvedPath = isAbsolute3(cleaned) ?
|
|
12524
|
-
if (!pathIsInside(resolvedPath,
|
|
12577
|
+
const resolvedPath = isAbsolute3(cleaned) ? resolve9(cleaned) : resolve9(baseDir3, cleaned);
|
|
12578
|
+
if (!pathIsInside(resolvedPath, resolve9(baseDir3))) {
|
|
12525
12579
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${path}`);
|
|
12526
12580
|
}
|
|
12527
12581
|
return resolvedPath;
|
|
12528
12582
|
}
|
|
12529
|
-
function pathIsInside(path,
|
|
12530
|
-
const rel = relative2(
|
|
12583
|
+
function pathIsInside(path, baseDir3) {
|
|
12584
|
+
const rel = relative2(baseDir3, path);
|
|
12531
12585
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
12532
12586
|
}
|
|
12533
12587
|
function providerTargetsTool(targets, tool) {
|
|
@@ -14187,8 +14241,8 @@ var init_config_store = __esm(() => {
|
|
|
14187
14241
|
});
|
|
14188
14242
|
|
|
14189
14243
|
// src/lib/session-render-ownership.ts
|
|
14190
|
-
import { existsSync as
|
|
14191
|
-
import { dirname as
|
|
14244
|
+
import { existsSync as existsSync11, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
14245
|
+
import { dirname as dirname5, join as join14, parse as parse3, relative as relative3, sep } from "path";
|
|
14192
14246
|
function toSegments(absolutePath2) {
|
|
14193
14247
|
return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
14194
14248
|
}
|
|
@@ -14207,7 +14261,7 @@ function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
|
14207
14261
|
function readManifestRelativePaths(manifestPath) {
|
|
14208
14262
|
let stats;
|
|
14209
14263
|
try {
|
|
14210
|
-
if (!
|
|
14264
|
+
if (!existsSync11(manifestPath))
|
|
14211
14265
|
return null;
|
|
14212
14266
|
stats = statSync4(manifestPath);
|
|
14213
14267
|
} catch {
|
|
@@ -14234,16 +14288,16 @@ function readManifestRelativePaths(manifestPath) {
|
|
|
14234
14288
|
}
|
|
14235
14289
|
function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
14236
14290
|
const root = parse3(absolutePath2).root;
|
|
14237
|
-
let home =
|
|
14291
|
+
let home = dirname5(absolutePath2);
|
|
14238
14292
|
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
14239
|
-
const manifestPath =
|
|
14293
|
+
const manifestPath = join14(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
14240
14294
|
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
14241
14295
|
if (relativePaths) {
|
|
14242
14296
|
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
14243
14297
|
if (relativePaths.has(claimed))
|
|
14244
14298
|
return true;
|
|
14245
14299
|
}
|
|
14246
|
-
const parent =
|
|
14300
|
+
const parent = dirname5(home);
|
|
14247
14301
|
if (parent === home || home === root)
|
|
14248
14302
|
break;
|
|
14249
14303
|
home = parent;
|
|
@@ -14271,17 +14325,17 @@ __export(exports_apply, {
|
|
|
14271
14325
|
applyConfigs: () => applyConfigs,
|
|
14272
14326
|
applyConfig: () => applyConfig
|
|
14273
14327
|
});
|
|
14274
|
-
import { existsSync as
|
|
14275
|
-
import { basename as basename5, dirname as
|
|
14276
|
-
import { homedir as
|
|
14328
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync6, realpathSync as realpathSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
14329
|
+
import { basename as basename5, dirname as dirname6, join as join15, resolve as resolve10 } from "path";
|
|
14330
|
+
import { homedir as homedir11 } from "os";
|
|
14277
14331
|
function getConfigHome() {
|
|
14278
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
14332
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir11();
|
|
14279
14333
|
}
|
|
14280
14334
|
function expandPath(p) {
|
|
14281
14335
|
if (p.startsWith("~/")) {
|
|
14282
|
-
return
|
|
14336
|
+
return resolve10(getConfigHome(), p.slice(2));
|
|
14283
14337
|
}
|
|
14284
|
-
return
|
|
14338
|
+
return resolve10(p);
|
|
14285
14339
|
}
|
|
14286
14340
|
function normalizeTargetPath(p) {
|
|
14287
14341
|
const expanded = expandPath(p);
|
|
@@ -14291,14 +14345,14 @@ function normalizeTargetPath(p) {
|
|
|
14291
14345
|
let current = expanded;
|
|
14292
14346
|
const missingSegments = [];
|
|
14293
14347
|
while (true) {
|
|
14294
|
-
if (
|
|
14348
|
+
if (existsSync12(current)) {
|
|
14295
14349
|
try {
|
|
14296
|
-
return
|
|
14350
|
+
return resolve10(realpathSync3(current), ...missingSegments);
|
|
14297
14351
|
} catch {
|
|
14298
14352
|
return expanded;
|
|
14299
14353
|
}
|
|
14300
14354
|
}
|
|
14301
|
-
const parent =
|
|
14355
|
+
const parent = dirname6(current);
|
|
14302
14356
|
const name = basename5(current);
|
|
14303
14357
|
if (parent === current)
|
|
14304
14358
|
return expanded;
|
|
@@ -14322,11 +14376,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
14322
14376
|
}
|
|
14323
14377
|
const path = expandPath(renderedTargetPath);
|
|
14324
14378
|
const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
|
|
14325
|
-
const previousContent =
|
|
14379
|
+
const previousContent = existsSync12(path) ? readFileSync6(path, "utf-8") : null;
|
|
14326
14380
|
const changed = previousContent !== renderedForTarget;
|
|
14327
14381
|
if (!opts.dryRun) {
|
|
14328
|
-
const dir =
|
|
14329
|
-
if (!
|
|
14382
|
+
const dir = dirname6(path);
|
|
14383
|
+
if (!existsSync12(dir)) {
|
|
14330
14384
|
mkdirSync3(dir, { recursive: true });
|
|
14331
14385
|
}
|
|
14332
14386
|
if (previousContent !== null && changed) {
|
|
@@ -14360,7 +14414,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
|
14360
14414
|
let current;
|
|
14361
14415
|
try {
|
|
14362
14416
|
const path = expandPath(targetPath);
|
|
14363
|
-
if (!
|
|
14417
|
+
if (!existsSync12(path))
|
|
14364
14418
|
return [];
|
|
14365
14419
|
current = readFileSync6(path, "utf-8");
|
|
14366
14420
|
} catch {
|
|
@@ -14686,7 +14740,7 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
|
14686
14740
|
getConfigHome(),
|
|
14687
14741
|
opts.vars?.["HOME_DIR"]
|
|
14688
14742
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
14689
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
14743
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join15(home, ...relativePath.split("/"))))))
|
|
14690
14744
|
return true;
|
|
14691
14745
|
return sessionRenderOwnsPath(normalized);
|
|
14692
14746
|
}
|
|
@@ -14704,20 +14758,20 @@ var init_apply = __esm(() => {
|
|
|
14704
14758
|
});
|
|
14705
14759
|
|
|
14706
14760
|
// src/lib/sync-dir.ts
|
|
14707
|
-
import { existsSync as
|
|
14708
|
-
import { join as
|
|
14709
|
-
import { homedir as
|
|
14761
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
14762
|
+
import { join as join16, relative as relative4 } from "path";
|
|
14763
|
+
import { homedir as homedir12 } from "os";
|
|
14710
14764
|
function shouldSkip(p) {
|
|
14711
14765
|
return SKIP.some((s) => p.includes(s));
|
|
14712
14766
|
}
|
|
14713
14767
|
async function syncFromDir(dir, opts = {}) {
|
|
14714
14768
|
const store = opts.store ?? resolveConfigStore();
|
|
14715
14769
|
const absDir = expandPath(dir);
|
|
14716
|
-
if (!
|
|
14770
|
+
if (!existsSync13(absDir))
|
|
14717
14771
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
14718
|
-
const files = opts.recursive !== false ? walkDir(absDir) :
|
|
14772
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join16(absDir, f)).filter((f) => statSync5(f).isFile());
|
|
14719
14773
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
14720
|
-
const home =
|
|
14774
|
+
const home = homedir12();
|
|
14721
14775
|
const allConfigs = await store.listConfigs();
|
|
14722
14776
|
for (const file of files) {
|
|
14723
14777
|
if (shouldSkip(file)) {
|
|
@@ -14752,7 +14806,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14752
14806
|
}
|
|
14753
14807
|
async function syncToDir(dir, opts = {}) {
|
|
14754
14808
|
const store = opts.store ?? resolveConfigStore();
|
|
14755
|
-
const home =
|
|
14809
|
+
const home = homedir12();
|
|
14756
14810
|
const absDir = expandPath(dir);
|
|
14757
14811
|
const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
|
|
14758
14812
|
const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
|
|
@@ -14775,8 +14829,8 @@ async function syncToDir(dir, opts = {}) {
|
|
|
14775
14829
|
return result;
|
|
14776
14830
|
}
|
|
14777
14831
|
function walkDir(dir, files = []) {
|
|
14778
|
-
for (const entry of
|
|
14779
|
-
const full =
|
|
14832
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
14833
|
+
const full = join16(dir, entry.name);
|
|
14780
14834
|
if (shouldSkip(full))
|
|
14781
14835
|
continue;
|
|
14782
14836
|
if (entry.isDirectory())
|
|
@@ -14811,8 +14865,8 @@ __export(exports_sync, {
|
|
|
14811
14865
|
KNOWN_CONFIGS: () => KNOWN_CONFIGS,
|
|
14812
14866
|
CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
|
|
14813
14867
|
});
|
|
14814
|
-
import { existsSync as
|
|
14815
|
-
import { basename as basename6, extname as extname3, join as
|
|
14868
|
+
import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "fs";
|
|
14869
|
+
import { basename as basename6, extname as extname3, join as join17 } from "path";
|
|
14816
14870
|
function claudeRuleOutputs(fileName) {
|
|
14817
14871
|
const stem = basename6(fileName, extname3(fileName));
|
|
14818
14872
|
return [
|
|
@@ -14851,7 +14905,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
14851
14905
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
14852
14906
|
}
|
|
14853
14907
|
function hasClaudePromptSource() {
|
|
14854
|
-
return
|
|
14908
|
+
return existsSync14(expandPath("~/.claude/CLAUDE.md"));
|
|
14855
14909
|
}
|
|
14856
14910
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
14857
14911
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -14859,7 +14913,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
14859
14913
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
14860
14914
|
return false;
|
|
14861
14915
|
const stem = basename6(absoluteTargetPath, ".mdc");
|
|
14862
|
-
return
|
|
14916
|
+
return existsSync14(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync14(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
14863
14917
|
}
|
|
14864
14918
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
14865
14919
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -14876,8 +14930,8 @@ async function syncProject(opts) {
|
|
|
14876
14930
|
const allConfigs = await store.listConfigs();
|
|
14877
14931
|
const machine = detectMachineContext();
|
|
14878
14932
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
14879
|
-
const abs =
|
|
14880
|
-
if (!
|
|
14933
|
+
const abs = join17(absDir, pf.file);
|
|
14934
|
+
if (!existsSync14(abs))
|
|
14881
14935
|
continue;
|
|
14882
14936
|
try {
|
|
14883
14937
|
const rawContent = readFileSync8(abs, "utf-8");
|
|
@@ -14909,19 +14963,19 @@ async function syncProject(opts) {
|
|
|
14909
14963
|
}
|
|
14910
14964
|
}
|
|
14911
14965
|
for (const ruleDir of [
|
|
14912
|
-
{ dir:
|
|
14913
|
-
{ dir:
|
|
14914
|
-
{ dir:
|
|
14915
|
-
{ dir:
|
|
14916
|
-
{ dir:
|
|
14917
|
-
{ dir:
|
|
14918
|
-
{ dir:
|
|
14966
|
+
{ dir: join17(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
14967
|
+
{ dir: join17(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
|
|
14968
|
+
{ dir: join17(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
|
|
14969
|
+
{ dir: join17(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
|
|
14970
|
+
{ dir: join17(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
|
|
14971
|
+
{ dir: join17(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
|
|
14972
|
+
{ dir: join17(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
|
|
14919
14973
|
]) {
|
|
14920
|
-
if (!
|
|
14974
|
+
if (!existsSync14(ruleDir.dir))
|
|
14921
14975
|
continue;
|
|
14922
|
-
const mdFiles =
|
|
14976
|
+
const mdFiles = readdirSync3(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
14923
14977
|
for (const f of mdFiles) {
|
|
14924
|
-
const abs =
|
|
14978
|
+
const abs = join17(ruleDir.dir, f);
|
|
14925
14979
|
const raw = readFileSync8(abs, "utf-8");
|
|
14926
14980
|
const redacted = redactContent(raw, "markdown");
|
|
14927
14981
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
@@ -14961,14 +15015,14 @@ async function syncKnown(opts = {}) {
|
|
|
14961
15015
|
for (const known of targets) {
|
|
14962
15016
|
if (known.rulesDir) {
|
|
14963
15017
|
const absDir = expandPath(known.rulesDir);
|
|
14964
|
-
if (!
|
|
15018
|
+
if (!existsSync14(absDir)) {
|
|
14965
15019
|
result.skipped.push(known.rulesDir);
|
|
14966
15020
|
continue;
|
|
14967
15021
|
}
|
|
14968
15022
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
14969
|
-
const ruleFiles =
|
|
15023
|
+
const ruleFiles = readdirSync3(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
14970
15024
|
for (const f of ruleFiles) {
|
|
14971
|
-
const abs2 =
|
|
15025
|
+
const abs2 = join17(absDir, f);
|
|
14972
15026
|
const targetPath = abs2.replace(home, "~");
|
|
14973
15027
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
14974
15028
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
@@ -15002,7 +15056,7 @@ async function syncKnown(opts = {}) {
|
|
|
15002
15056
|
continue;
|
|
15003
15057
|
}
|
|
15004
15058
|
const abs = expandPath(known.path);
|
|
15005
|
-
if (!
|
|
15059
|
+
if (!existsSync14(abs)) {
|
|
15006
15060
|
result.skipped.push(known.path);
|
|
15007
15061
|
continue;
|
|
15008
15062
|
}
|
|
@@ -15108,7 +15162,7 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
|
15108
15162
|
}
|
|
15109
15163
|
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
15110
15164
|
const path = expandPath(targetPath);
|
|
15111
|
-
if (!
|
|
15165
|
+
if (!existsSync14(path))
|
|
15112
15166
|
return `(file not found on disk: ${path})`;
|
|
15113
15167
|
const diskContent = readFileSync8(path, "utf-8");
|
|
15114
15168
|
if (diskContent === expectedContent)
|
|
@@ -15343,16 +15397,16 @@ __export(exports_package_manager_guard, {
|
|
|
15343
15397
|
scanPackageManagerSecrets: () => scanPackageManagerSecrets
|
|
15344
15398
|
});
|
|
15345
15399
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15346
|
-
import { existsSync as
|
|
15347
|
-
import { homedir as
|
|
15348
|
-
import { basename as basename7, dirname as
|
|
15400
|
+
import { existsSync as existsSync23, lstatSync as lstatSync7, readdirSync as readdirSync6, readFileSync as readFileSync16 } from "fs";
|
|
15401
|
+
import { homedir as homedir15 } from "os";
|
|
15402
|
+
import { basename as basename7, dirname as dirname11, isAbsolute as isAbsolute5, join as join25, relative as relative7, resolve as resolve15 } from "path";
|
|
15349
15403
|
function scanPackageManagerSecrets(options = {}) {
|
|
15350
|
-
const cwd = options.cwd ?
|
|
15351
|
-
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) =>
|
|
15404
|
+
const cwd = options.cwd ? resolve15(options.cwd) : process.cwd();
|
|
15405
|
+
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve15(cwd, root));
|
|
15352
15406
|
const findings = [];
|
|
15353
15407
|
let scannedFiles = 0;
|
|
15354
15408
|
for (const root of roots) {
|
|
15355
|
-
if (!
|
|
15409
|
+
if (!existsSync23(root))
|
|
15356
15410
|
continue;
|
|
15357
15411
|
const stat = lstatSync7(root);
|
|
15358
15412
|
if (stat.isFile()) {
|
|
@@ -15362,7 +15416,7 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15362
15416
|
if (text === null)
|
|
15363
15417
|
continue;
|
|
15364
15418
|
scannedFiles++;
|
|
15365
|
-
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root),
|
|
15419
|
+
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname11(root)));
|
|
15366
15420
|
continue;
|
|
15367
15421
|
}
|
|
15368
15422
|
if (!stat.isDirectory())
|
|
@@ -15379,10 +15433,10 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15379
15433
|
}
|
|
15380
15434
|
}
|
|
15381
15435
|
if (options.includeHome) {
|
|
15382
|
-
const home =
|
|
15436
|
+
const home = homedir15();
|
|
15383
15437
|
for (const name of HOME_FILES) {
|
|
15384
|
-
const file =
|
|
15385
|
-
if (!
|
|
15438
|
+
const file = join25(home, name);
|
|
15439
|
+
if (!existsSync23(file))
|
|
15386
15440
|
continue;
|
|
15387
15441
|
const text = readTextFile(file);
|
|
15388
15442
|
if (text === null)
|
|
@@ -15402,16 +15456,16 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15402
15456
|
function collectRepoFiles(root) {
|
|
15403
15457
|
const out = [];
|
|
15404
15458
|
const visit = (dir) => {
|
|
15405
|
-
for (const entry of
|
|
15459
|
+
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
15406
15460
|
if (entry.isDirectory()) {
|
|
15407
15461
|
if (SKIP_DIRS.has(entry.name))
|
|
15408
15462
|
continue;
|
|
15409
|
-
visit(
|
|
15463
|
+
visit(join25(dir, entry.name));
|
|
15410
15464
|
continue;
|
|
15411
15465
|
}
|
|
15412
15466
|
if (!entry.isFile())
|
|
15413
15467
|
continue;
|
|
15414
|
-
const file =
|
|
15468
|
+
const file = join25(dir, entry.name);
|
|
15415
15469
|
if (shouldScanRepoFile(file))
|
|
15416
15470
|
out.push(file);
|
|
15417
15471
|
}
|
|
@@ -15649,7 +15703,7 @@ function trackedFiles(root) {
|
|
|
15649
15703
|
}
|
|
15650
15704
|
function isTrackedFile(file) {
|
|
15651
15705
|
try {
|
|
15652
|
-
const repoRoot = execFileSync2("git", ["-C",
|
|
15706
|
+
const repoRoot = execFileSync2("git", ["-C", dirname11(file), "rev-parse", "--show-toplevel"], {
|
|
15653
15707
|
encoding: "utf-8",
|
|
15654
15708
|
stdio: ["ignore", "pipe", "ignore"]
|
|
15655
15709
|
}).trim();
|
|
@@ -15679,7 +15733,7 @@ function stripInlineComment(value) {
|
|
|
15679
15733
|
return value.replace(/\s[#;].*$/, "").trim();
|
|
15680
15734
|
}
|
|
15681
15735
|
function displayPath(file, root) {
|
|
15682
|
-
const home =
|
|
15736
|
+
const home = homedir15();
|
|
15683
15737
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
15684
15738
|
return "~/" + toPosix(relative7(home, file));
|
|
15685
15739
|
if (isAbsolute5(root) && file.startsWith(root + "/"))
|
|
@@ -15733,7 +15787,11 @@ var init_package_manager_guard = __esm(() => {
|
|
|
15733
15787
|
// ../events/dist/commander.js
|
|
15734
15788
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
15735
15789
|
import { Buffer as Buffer2 } from "buffer";
|
|
15790
|
+
import { existsSync as existsSync2 } from "fs";
|
|
15791
|
+
import { join as join3 } from "path";
|
|
15736
15792
|
import { existsSync } from "fs";
|
|
15793
|
+
import { homedir as homedir2 } from "os";
|
|
15794
|
+
import { join as join2, resolve } from "path";
|
|
15737
15795
|
import { homedir } from "os";
|
|
15738
15796
|
import { join } from "path";
|
|
15739
15797
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -15839,13 +15897,106 @@ function channelMatchesEvent(channel, event) {
|
|
|
15839
15897
|
return true;
|
|
15840
15898
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
15841
15899
|
}
|
|
15900
|
+
var KIND_ENV = {
|
|
15901
|
+
config: "HASNA_CONFIG_HOME",
|
|
15902
|
+
data: "HASNA_DATA_HOME",
|
|
15903
|
+
state: "HASNA_STATE_HOME",
|
|
15904
|
+
cache: "HASNA_CACHE_HOME"
|
|
15905
|
+
};
|
|
15906
|
+
var APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
15907
|
+
function assertApp(app) {
|
|
15908
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
15909
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
15910
|
+
}
|
|
15911
|
+
if (!APP_SLUG_RE.test(app)) {
|
|
15912
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
15913
|
+
}
|
|
15914
|
+
}
|
|
15915
|
+
function envOf(options) {
|
|
15916
|
+
return options.env ?? process.env;
|
|
15917
|
+
}
|
|
15918
|
+
function envValue(options, kind) {
|
|
15919
|
+
const value = envOf(options)[KIND_ENV[kind]];
|
|
15920
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
15921
|
+
}
|
|
15922
|
+
function isMacOS(platform) {
|
|
15923
|
+
return platform === "darwin";
|
|
15924
|
+
}
|
|
15925
|
+
function baseDir(kind, options) {
|
|
15926
|
+
const override = envValue(options, kind);
|
|
15927
|
+
if (override)
|
|
15928
|
+
return override;
|
|
15929
|
+
const home = options.home ?? homedir();
|
|
15930
|
+
const platform = options.platform ?? process.platform;
|
|
15931
|
+
if (isMacOS(platform)) {
|
|
15932
|
+
switch (kind) {
|
|
15933
|
+
case "config":
|
|
15934
|
+
case "data":
|
|
15935
|
+
return join(home, "Library", "Application Support", "Hasna");
|
|
15936
|
+
case "cache":
|
|
15937
|
+
return join(home, "Library", "Caches", "Hasna");
|
|
15938
|
+
case "state":
|
|
15939
|
+
return join(home, "Library", "Logs", "Hasna");
|
|
15940
|
+
}
|
|
15941
|
+
}
|
|
15942
|
+
switch (kind) {
|
|
15943
|
+
case "config":
|
|
15944
|
+
return join(home, ".config", "hasna");
|
|
15945
|
+
case "data":
|
|
15946
|
+
return join(home, ".local", "share", "hasna");
|
|
15947
|
+
case "state":
|
|
15948
|
+
return join(home, ".local", "state", "hasna");
|
|
15949
|
+
case "cache":
|
|
15950
|
+
return join(home, ".cache", "hasna");
|
|
15951
|
+
}
|
|
15952
|
+
}
|
|
15953
|
+
function resolvePath(kind, options) {
|
|
15954
|
+
assertApp(options.app);
|
|
15955
|
+
const appSegment = options.internal === true ? join("internal", options.app) : options.app;
|
|
15956
|
+
return join(baseDir(kind, options), appSegment);
|
|
15957
|
+
}
|
|
15958
|
+
function dataDir(options) {
|
|
15959
|
+
return resolvePath("data", options);
|
|
15960
|
+
}
|
|
15842
15961
|
var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR";
|
|
15843
15962
|
var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
|
|
15963
|
+
var EVENTS_STORE_SENTINEL_FILE = "events.json";
|
|
15964
|
+
function effectiveHome() {
|
|
15965
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir2();
|
|
15966
|
+
}
|
|
15967
|
+
function legacyHomeDir() {
|
|
15968
|
+
return join2(effectiveHome(), ".hasna", "events");
|
|
15969
|
+
}
|
|
15970
|
+
function resolverHome() {
|
|
15971
|
+
return dataDir({ app: "events", home: effectiveHome() || undefined });
|
|
15972
|
+
}
|
|
15973
|
+
function adoptResolverHome(resolved, env = process.env) {
|
|
15974
|
+
const dataOverride = env.HASNA_DATA_HOME;
|
|
15975
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
15976
|
+
return true;
|
|
15977
|
+
return existsSync(join2(resolved, EVENTS_STORE_SENTINEL_FILE));
|
|
15978
|
+
}
|
|
15979
|
+
function exactEventsHome() {
|
|
15980
|
+
const dir = process.env[HASNA_EVENTS_DIR_ENV];
|
|
15981
|
+
if (dir && dir.trim())
|
|
15982
|
+
return dir.trim();
|
|
15983
|
+
const home = process.env[HASNA_EVENTS_HOME_ENV];
|
|
15984
|
+
if (home && home.trim())
|
|
15985
|
+
return home.trim();
|
|
15986
|
+
return;
|
|
15987
|
+
}
|
|
15988
|
+
function getEventsHome() {
|
|
15989
|
+
const exact = exactEventsHome();
|
|
15990
|
+
if (exact)
|
|
15991
|
+
return resolve(exact);
|
|
15992
|
+
const resolved = resolverHome();
|
|
15993
|
+
return adoptResolverHome(resolved) ? resolve(resolved) : resolve(legacyHomeDir());
|
|
15994
|
+
}
|
|
15844
15995
|
var LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:";
|
|
15845
15996
|
var DEFAULT_EVENT_PAGE_LIMIT = 100;
|
|
15846
15997
|
var MAX_EVENT_PAGE_LIMIT = 1000;
|
|
15847
15998
|
function getEventsDataDir(override) {
|
|
15848
|
-
return override ||
|
|
15999
|
+
return override || getEventsHome();
|
|
15849
16000
|
}
|
|
15850
16001
|
function getActiveEventsDirEnv() {
|
|
15851
16002
|
if (process.env[HASNA_EVENTS_DIR_ENV])
|
|
@@ -15861,12 +16012,12 @@ class JsonEventsStore {
|
|
|
15861
16012
|
channelsPath;
|
|
15862
16013
|
eventsPath;
|
|
15863
16014
|
deliveriesPath;
|
|
15864
|
-
constructor(
|
|
15865
|
-
this.dataDir =
|
|
15866
|
-
this.runtime = localJsonRuntime(
|
|
15867
|
-
this.channelsPath =
|
|
15868
|
-
this.eventsPath =
|
|
15869
|
-
this.deliveriesPath =
|
|
16015
|
+
constructor(dataDir2 = getEventsDataDir()) {
|
|
16016
|
+
this.dataDir = dataDir2;
|
|
16017
|
+
this.runtime = localJsonRuntime(dataDir2);
|
|
16018
|
+
this.channelsPath = join3(dataDir2, "channels.json");
|
|
16019
|
+
this.eventsPath = join3(dataDir2, "events.json");
|
|
16020
|
+
this.deliveriesPath = join3(dataDir2, "deliveries.json");
|
|
15870
16021
|
}
|
|
15871
16022
|
async init() {
|
|
15872
16023
|
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
@@ -15983,7 +16134,7 @@ class JsonEventsStore {
|
|
|
15983
16134
|
};
|
|
15984
16135
|
}
|
|
15985
16136
|
async ensureArrayFile(path) {
|
|
15986
|
-
if (!
|
|
16137
|
+
if (!existsSync2(path)) {
|
|
15987
16138
|
await writeFile(path, `[]
|
|
15988
16139
|
`, { encoding: "utf-8", mode: 384 });
|
|
15989
16140
|
}
|
|
@@ -16013,7 +16164,7 @@ class JsonEventsStore {
|
|
|
16013
16164
|
});
|
|
16014
16165
|
}
|
|
16015
16166
|
}
|
|
16016
|
-
function localJsonRuntime(
|
|
16167
|
+
function localJsonRuntime(dataDir2 = getEventsDataDir()) {
|
|
16017
16168
|
return {
|
|
16018
16169
|
mode: "local-files",
|
|
16019
16170
|
name: "json-events-store",
|
|
@@ -16026,7 +16177,7 @@ function localJsonRuntime(dataDir = getEventsDataDir()) {
|
|
|
16026
16177
|
durable: true,
|
|
16027
16178
|
idempotency: "best-effort-local",
|
|
16028
16179
|
replayCursors: true,
|
|
16029
|
-
description: `Local JSON files in ${
|
|
16180
|
+
description: `Local JSON files in ${dataDir2}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
|
|
16030
16181
|
};
|
|
16031
16182
|
}
|
|
16032
16183
|
function encodeLocalJsonEventCursor(offset, options = {}) {
|
|
@@ -16090,8 +16241,8 @@ function assertCursorFilter(name, cursorValue, optionValue) {
|
|
|
16090
16241
|
function findEventByIdentity(events, identity) {
|
|
16091
16242
|
return events.find((event) => identity.id !== undefined && event.id === identity.id || identity.dedupeKey !== undefined && event.dedupeKey === identity.dedupeKey);
|
|
16092
16243
|
}
|
|
16093
|
-
async function getEventsStatus(
|
|
16094
|
-
const store = new JsonEventsStore(
|
|
16244
|
+
async function getEventsStatus(dataDir2) {
|
|
16245
|
+
const store = new JsonEventsStore(dataDir2);
|
|
16095
16246
|
await store.init();
|
|
16096
16247
|
const [channels, events, deliveries] = await Promise.all([
|
|
16097
16248
|
store.listChannels(),
|
|
@@ -16133,9 +16284,9 @@ async function getEventsStatus(dataDir) {
|
|
|
16133
16284
|
}
|
|
16134
16285
|
};
|
|
16135
16286
|
}
|
|
16136
|
-
function statusFile(
|
|
16137
|
-
const path =
|
|
16138
|
-
return { path, exists:
|
|
16287
|
+
function statusFile(dataDir2, fileName, records) {
|
|
16288
|
+
const path = join3(dataDir2, fileName);
|
|
16289
|
+
return { path, exists: existsSync2(path), records };
|
|
16139
16290
|
}
|
|
16140
16291
|
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
16141
16292
|
function buildSignatureBase(timestamp, body) {
|
|
@@ -16474,7 +16625,7 @@ async function pinnedNativeRequest(target, addresses, method, headers, body, sig
|
|
|
16474
16625
|
callback(null, entries);
|
|
16475
16626
|
}
|
|
16476
16627
|
};
|
|
16477
|
-
return new Promise((
|
|
16628
|
+
return new Promise((resolve2, reject) => {
|
|
16478
16629
|
const request = isHttps ? nodeHttpsRequest(requestOptions, onResponse) : nodeHttpRequest(requestOptions, onResponse);
|
|
16479
16630
|
const onAbort = () => {
|
|
16480
16631
|
const error = new Error("The operation was aborted.");
|
|
@@ -16501,7 +16652,7 @@ async function pinnedNativeRequest(target, addresses, method, headers, body, sig
|
|
|
16501
16652
|
else if (Array.isArray(value))
|
|
16502
16653
|
headersRecord[name] = value.join(", ");
|
|
16503
16654
|
}
|
|
16504
|
-
|
|
16655
|
+
resolve2(new Response(Buffer.concat(chunks), { status: response.statusCode ?? 200, headers: headersRecord }));
|
|
16505
16656
|
});
|
|
16506
16657
|
}
|
|
16507
16658
|
});
|
|
@@ -16598,7 +16749,7 @@ async function dispatchCommand(event, channel) {
|
|
|
16598
16749
|
HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
|
|
16599
16750
|
HASNA_EVENT_JSON: eventJson
|
|
16600
16751
|
};
|
|
16601
|
-
return new Promise((
|
|
16752
|
+
return new Promise((resolve2) => {
|
|
16602
16753
|
const child = spawn(channel.command.command, channel.command.args ?? [], {
|
|
16603
16754
|
cwd: channel.command.cwd,
|
|
16604
16755
|
env,
|
|
@@ -16616,7 +16767,7 @@ async function dispatchCommand(event, channel) {
|
|
|
16616
16767
|
});
|
|
16617
16768
|
child.on("error", (error) => {
|
|
16618
16769
|
clearTimeout(timeout);
|
|
16619
|
-
|
|
16770
|
+
resolve2({
|
|
16620
16771
|
attempt: 1,
|
|
16621
16772
|
status: "failed",
|
|
16622
16773
|
startedAt,
|
|
@@ -16629,7 +16780,7 @@ async function dispatchCommand(event, channel) {
|
|
|
16629
16780
|
child.on("close", (code, signal) => {
|
|
16630
16781
|
clearTimeout(timeout);
|
|
16631
16782
|
const success = code === 0;
|
|
16632
|
-
|
|
16783
|
+
resolve2({
|
|
16633
16784
|
attempt: 1,
|
|
16634
16785
|
status: success ? "success" : "failed",
|
|
16635
16786
|
startedAt,
|
|
@@ -17282,9 +17433,9 @@ var {
|
|
|
17282
17433
|
// src/cli/index.tsx
|
|
17283
17434
|
init_apply();
|
|
17284
17435
|
import chalk from "chalk";
|
|
17285
|
-
import { existsSync as
|
|
17286
|
-
import { homedir as
|
|
17287
|
-
import { basename as basename8, join as
|
|
17436
|
+
import { existsSync as existsSync24, lstatSync as lstatSync8, readFileSync as readFileSync17, readSync, writeSync } from "fs";
|
|
17437
|
+
import { homedir as homedir16 } from "os";
|
|
17438
|
+
import { basename as basename8, join as join26, resolve as resolve16 } from "path";
|
|
17288
17439
|
|
|
17289
17440
|
// src/lib/config-target-identity.ts
|
|
17290
17441
|
init_apply();
|
|
@@ -17330,15 +17481,15 @@ init_redact();
|
|
|
17330
17481
|
|
|
17331
17482
|
// src/lib/export.ts
|
|
17332
17483
|
init_config_store();
|
|
17333
|
-
import { existsSync as
|
|
17334
|
-
import { join as
|
|
17484
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
17485
|
+
import { join as join18, resolve as resolve11 } from "path";
|
|
17335
17486
|
import { tmpdir } from "os";
|
|
17336
17487
|
async function exportConfigs(outputPath, opts = {}) {
|
|
17337
17488
|
const store = opts.store ?? resolveConfigStore();
|
|
17338
17489
|
const configs = await store.listConfigs(opts.filter);
|
|
17339
|
-
const absOutput =
|
|
17340
|
-
const tmpDir =
|
|
17341
|
-
const contentsDir =
|
|
17490
|
+
const absOutput = resolve11(outputPath);
|
|
17491
|
+
const tmpDir = join18(tmpdir(), `configs-export-${Date.now()}`);
|
|
17492
|
+
const contentsDir = join18(tmpDir, "contents");
|
|
17342
17493
|
try {
|
|
17343
17494
|
mkdirSync4(contentsDir, { recursive: true });
|
|
17344
17495
|
const manifest = {
|
|
@@ -17346,10 +17497,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17346
17497
|
exported_at: new Date().toISOString(),
|
|
17347
17498
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
17348
17499
|
};
|
|
17349
|
-
writeFileSync3(
|
|
17500
|
+
writeFileSync3(join18(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
17350
17501
|
for (const config of configs) {
|
|
17351
17502
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
17352
|
-
writeFileSync3(
|
|
17503
|
+
writeFileSync3(join18(contentsDir, fileName), config.content, "utf-8");
|
|
17353
17504
|
}
|
|
17354
17505
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
17355
17506
|
stdout: "pipe",
|
|
@@ -17362,7 +17513,7 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17362
17513
|
}
|
|
17363
17514
|
return { path: absOutput, count: configs.length };
|
|
17364
17515
|
} finally {
|
|
17365
|
-
if (
|
|
17516
|
+
if (existsSync15(tmpDir)) {
|
|
17366
17517
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
17367
17518
|
}
|
|
17368
17519
|
}
|
|
@@ -17370,14 +17521,14 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17370
17521
|
|
|
17371
17522
|
// src/lib/import.ts
|
|
17372
17523
|
init_config_store();
|
|
17373
|
-
import { existsSync as
|
|
17374
|
-
import { join as
|
|
17524
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync9, rmSync as rmSync4 } from "fs";
|
|
17525
|
+
import { join as join19, resolve as resolve12 } from "path";
|
|
17375
17526
|
import { tmpdir as tmpdir2 } from "os";
|
|
17376
17527
|
async function importConfigs(bundlePath, opts = {}) {
|
|
17377
17528
|
const store = opts.store ?? resolveConfigStore();
|
|
17378
17529
|
const conflict = opts.conflict ?? "skip";
|
|
17379
|
-
const absPath =
|
|
17380
|
-
const tmpDir =
|
|
17530
|
+
const absPath = resolve12(bundlePath);
|
|
17531
|
+
const tmpDir = join19(tmpdir2(), `configs-import-${Date.now()}`);
|
|
17381
17532
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
17382
17533
|
try {
|
|
17383
17534
|
mkdirSync5(tmpDir, { recursive: true });
|
|
@@ -17390,15 +17541,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
17390
17541
|
const stderr = await new Response(proc.stderr).text();
|
|
17391
17542
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
17392
17543
|
}
|
|
17393
|
-
const manifestPath =
|
|
17394
|
-
if (!
|
|
17544
|
+
const manifestPath = join19(tmpDir, "manifest.json");
|
|
17545
|
+
if (!existsSync16(manifestPath))
|
|
17395
17546
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
17396
17547
|
const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
|
|
17397
17548
|
for (const meta of manifest.configs) {
|
|
17398
17549
|
try {
|
|
17399
17550
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
17400
|
-
const contentFile =
|
|
17401
|
-
const content =
|
|
17551
|
+
const contentFile = join19(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
17552
|
+
const content = existsSync16(contentFile) ? readFileSync9(contentFile, "utf-8") : "";
|
|
17402
17553
|
let existing = null;
|
|
17403
17554
|
try {
|
|
17404
17555
|
existing = await store.getConfig(meta.slug);
|
|
@@ -17432,7 +17583,7 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
17432
17583
|
}
|
|
17433
17584
|
return result;
|
|
17434
17585
|
} finally {
|
|
17435
|
-
if (
|
|
17586
|
+
if (existsSync16(tmpDir)) {
|
|
17436
17587
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
17437
17588
|
}
|
|
17438
17589
|
}
|
|
@@ -17444,19 +17595,20 @@ init_machine();
|
|
|
17444
17595
|
|
|
17445
17596
|
// src/lib/session-apply.ts
|
|
17446
17597
|
init_project_context();
|
|
17598
|
+
init_session_render_state();
|
|
17447
17599
|
init_session_render();
|
|
17448
17600
|
init_cursor_authority();
|
|
17449
17601
|
init_session_authority();
|
|
17450
17602
|
import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
|
|
17451
17603
|
import {
|
|
17452
|
-
existsSync as
|
|
17604
|
+
existsSync as existsSync17,
|
|
17453
17605
|
lstatSync as lstatSync4,
|
|
17454
17606
|
mkdirSync as mkdirSync6,
|
|
17455
17607
|
readFileSync as readFileSync10,
|
|
17456
|
-
readdirSync as
|
|
17608
|
+
readdirSync as readdirSync4,
|
|
17457
17609
|
statSync as statSync6
|
|
17458
17610
|
} from "fs";
|
|
17459
|
-
import { dirname as
|
|
17611
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join20, parse as parse4, relative as relative5, resolve as resolve13 } from "path";
|
|
17460
17612
|
|
|
17461
17613
|
class SessionApplyError extends Error {
|
|
17462
17614
|
constructor(message) {
|
|
@@ -17588,13 +17740,13 @@ function assertClaudeAuthorityStillClear(plan, targetHome, ownedClaudeAuthoritie
|
|
|
17588
17740
|
throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
|
|
17589
17741
|
}
|
|
17590
17742
|
function ensureSessionTargetHome(targetHome) {
|
|
17591
|
-
if (!
|
|
17743
|
+
if (!existsSync17(targetHome))
|
|
17592
17744
|
mkdirSync6(targetHome, { recursive: true, mode: 448 });
|
|
17593
17745
|
assertSafeTargetHome(targetHome);
|
|
17594
17746
|
}
|
|
17595
17747
|
function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
17596
17748
|
const safeTargetHome = assertSafeTargetHome(targetHome);
|
|
17597
|
-
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome,
|
|
17749
|
+
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve13(manifestPath)), safeTargetHome) : resolve13(safeTargetHome, ".hasna", "session-render-manifest.json");
|
|
17598
17750
|
const checkedAt = new Date().toISOString();
|
|
17599
17751
|
const previousManifest = readPreviousManifest(resolvedManifestPath);
|
|
17600
17752
|
if (!previousManifest) {
|
|
@@ -17611,7 +17763,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
17611
17763
|
const drifted = [];
|
|
17612
17764
|
for (const file of previousManifest.files) {
|
|
17613
17765
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
17614
|
-
if (!
|
|
17766
|
+
if (!existsSync17(target)) {
|
|
17615
17767
|
missing.push({
|
|
17616
17768
|
path: target,
|
|
17617
17769
|
relativePath: file.relativePath,
|
|
@@ -17644,12 +17796,17 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
17644
17796
|
function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
|
|
17645
17797
|
const snapshot = readSessionRenderSnapshot(snapshotPath);
|
|
17646
17798
|
const targetHome = assertSafeTargetHome(snapshot.targetHome);
|
|
17647
|
-
const resolvedSnapshotPath =
|
|
17648
|
-
const
|
|
17649
|
-
|
|
17650
|
-
|
|
17799
|
+
const resolvedSnapshotPath = resolve13(snapshotPath);
|
|
17800
|
+
const snapshotDir = getSessionRenderSnapshotDir(targetHome);
|
|
17801
|
+
const snapshotDirRelative = relative5(snapshotDir, resolvedSnapshotPath);
|
|
17802
|
+
const insideSnapshotDir = snapshotDirRelative !== ".." && !snapshotDirRelative.startsWith("../") && !isAbsolute4(snapshotDirRelative);
|
|
17803
|
+
if (!insideSnapshotDir) {
|
|
17804
|
+
const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
|
|
17805
|
+
if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute4(snapshotRelativePath)) {
|
|
17806
|
+
throw new SessionApplyError("Session snapshot must be stored inside its session-render snapshot location.");
|
|
17807
|
+
}
|
|
17651
17808
|
}
|
|
17652
|
-
assertNoSymlinkSegments2(targetHome, resolvedSnapshotPath);
|
|
17809
|
+
assertNoSymlinkSegments2(sessionRenderSnapshotWorkspaceRoot(targetHome), resolvedSnapshotPath);
|
|
17653
17810
|
const guard = observeProjectContextSessionGuard({
|
|
17654
17811
|
tool: snapshot.tool,
|
|
17655
17812
|
target_home: targetHome,
|
|
@@ -17762,8 +17919,8 @@ function requiredRestoreHash(file) {
|
|
|
17762
17919
|
return file.previousSha256;
|
|
17763
17920
|
}
|
|
17764
17921
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
17765
|
-
const resolved =
|
|
17766
|
-
if (!
|
|
17922
|
+
const resolved = resolve13(snapshotPath);
|
|
17923
|
+
if (!existsSync17(resolved))
|
|
17767
17924
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
17768
17925
|
const stat = lstatSync4(resolved);
|
|
17769
17926
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -17843,7 +18000,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
17843
18000
|
}
|
|
17844
18001
|
function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
|
|
17845
18002
|
assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
|
|
17846
|
-
const manifestPath =
|
|
18003
|
+
const manifestPath = resolve13(snapshot.manifestPath);
|
|
17847
18004
|
const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
|
|
17848
18005
|
resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
|
|
17849
18006
|
const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
|
|
@@ -17860,7 +18017,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
17860
18017
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is invalid: ${snapshotPath}`);
|
|
17861
18018
|
}
|
|
17862
18019
|
const appliedManifest = parsedManifest;
|
|
17863
|
-
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" ||
|
|
18020
|
+
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve13(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
|
|
17864
18021
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest does not match its snapshot: ${snapshotPath}`);
|
|
17865
18022
|
}
|
|
17866
18023
|
const afterFiles = [];
|
|
@@ -17944,9 +18101,9 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
17944
18101
|
if (!Number.isFinite(createdAtMs)) {
|
|
17945
18102
|
throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
|
|
17946
18103
|
}
|
|
17947
|
-
for (const entry of
|
|
17948
|
-
const candidatePath =
|
|
17949
|
-
if (candidatePath ===
|
|
18104
|
+
for (const entry of readdirSync4(dirname7(snapshotPath))) {
|
|
18105
|
+
const candidatePath = resolve13(dirname7(snapshotPath), entry);
|
|
18106
|
+
if (candidatePath === resolve13(snapshotPath) || !entry.endsWith(".json"))
|
|
17950
18107
|
continue;
|
|
17951
18108
|
const candidateStat = lstatSync4(candidatePath);
|
|
17952
18109
|
if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
|
|
@@ -17954,7 +18111,7 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
17954
18111
|
try {
|
|
17955
18112
|
const candidate = JSON.parse(readFileSync10(candidatePath, "utf8"));
|
|
17956
18113
|
const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
|
|
17957
|
-
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" &&
|
|
18114
|
+
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve13(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
|
|
17958
18115
|
throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
|
|
17959
18116
|
}
|
|
17960
18117
|
} catch (error) {
|
|
@@ -18023,14 +18180,14 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
18023
18180
|
}
|
|
18024
18181
|
function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
18025
18182
|
const path = resolveManifestRelativePath(relativePath, targetHome);
|
|
18026
|
-
if (
|
|
18183
|
+
if (resolve13(recordedPath) !== path) {
|
|
18027
18184
|
throw new SessionApplyError(`Session snapshot file path mismatch for ${relativePath}`);
|
|
18028
18185
|
}
|
|
18029
18186
|
return path;
|
|
18030
18187
|
}
|
|
18031
18188
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
18032
18189
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
18033
|
-
const previousContent =
|
|
18190
|
+
const previousContent = existsSync17(target) ? readFileSync10(target, "utf-8") : null;
|
|
18034
18191
|
const previousSha256 = previousContent === null ? null : sha2568(previousContent);
|
|
18035
18192
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
18036
18193
|
const changed = previousContent !== file.content;
|
|
@@ -18129,7 +18286,7 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
18129
18286
|
}
|
|
18130
18287
|
function planStaleFileResult(file, targetHome, options) {
|
|
18131
18288
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
18132
|
-
if (!
|
|
18289
|
+
if (!existsSync17(target))
|
|
18133
18290
|
return null;
|
|
18134
18291
|
const previousContent = readFileSync10(target, "utf-8");
|
|
18135
18292
|
const previousSha256 = sha2568(previousContent);
|
|
@@ -18176,19 +18333,19 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
|
|
|
18176
18333
|
return previousHashes.get(file.relativePath) === previousSha256;
|
|
18177
18334
|
}
|
|
18178
18335
|
function resolvePlannedFilePath(plan, file, targetHome) {
|
|
18179
|
-
const target =
|
|
18336
|
+
const target = resolve13(targetHome, ...file.relativePath.split("/"));
|
|
18180
18337
|
const rel = relative5(targetHome, target);
|
|
18181
18338
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
18182
18339
|
throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
|
|
18183
18340
|
}
|
|
18184
|
-
if (
|
|
18341
|
+
if (resolve13(file.path) !== target) {
|
|
18185
18342
|
throw new SessionApplyError(`Session file path mismatch for ${file.relativePath}: ${file.path}`);
|
|
18186
18343
|
}
|
|
18187
18344
|
assertNoSymlinkSegments2(targetHome, target);
|
|
18188
18345
|
return target;
|
|
18189
18346
|
}
|
|
18190
18347
|
function resolveManifestRelativePath(relativePath, targetHome) {
|
|
18191
|
-
const target =
|
|
18348
|
+
const target = resolve13(targetHome, ...relativePath.split(/[\\/]+/));
|
|
18192
18349
|
const rel = relative5(targetHome, target);
|
|
18193
18350
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
18194
18351
|
throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
|
|
@@ -18197,7 +18354,7 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
18197
18354
|
return target;
|
|
18198
18355
|
}
|
|
18199
18356
|
function readPreviousManifest(path) {
|
|
18200
|
-
if (!
|
|
18357
|
+
if (!existsSync17(path))
|
|
18201
18358
|
return null;
|
|
18202
18359
|
try {
|
|
18203
18360
|
const parsed = JSON.parse(readFileSync10(path, "utf-8"));
|
|
@@ -18239,7 +18396,7 @@ function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
|
18239
18396
|
}
|
|
18240
18397
|
function currentSessionFileHash(path, targetHome) {
|
|
18241
18398
|
assertNoSymlinkSegments2(targetHome, path);
|
|
18242
|
-
if (!
|
|
18399
|
+
if (!existsSync17(path))
|
|
18243
18400
|
return null;
|
|
18244
18401
|
const stat = lstatSync4(path);
|
|
18245
18402
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -18254,7 +18411,7 @@ function requiredPreviousHash(result) {
|
|
|
18254
18411
|
return result.previousSha256;
|
|
18255
18412
|
}
|
|
18256
18413
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
18257
|
-
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) =>
|
|
18414
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync17(result.path)).map((result) => {
|
|
18258
18415
|
const content = readFileSync10(result.path, "utf-8");
|
|
18259
18416
|
return {
|
|
18260
18417
|
path: result.path,
|
|
@@ -18273,7 +18430,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18273
18430
|
};
|
|
18274
18431
|
}
|
|
18275
18432
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
18276
|
-
const snapshotPath =
|
|
18433
|
+
const snapshotPath = join20(getSessionRenderSnapshotDir(targetHome), `${timestamp}-${randomUUID7()}.json`);
|
|
18277
18434
|
const afterFiles = results.map((result) => {
|
|
18278
18435
|
if (result.action === "conflict") {
|
|
18279
18436
|
throw new SessionApplyError(`Cannot snapshot unresolved conflict: ${result.relativePath}`);
|
|
@@ -18303,7 +18460,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18303
18460
|
path: snapshotPath,
|
|
18304
18461
|
content: `${JSON.stringify(snapshot, null, 2)}
|
|
18305
18462
|
`,
|
|
18306
|
-
workspace_root: targetHome,
|
|
18463
|
+
workspace_root: sessionRenderSnapshotWorkspaceRoot(targetHome),
|
|
18307
18464
|
default_mode: 384,
|
|
18308
18465
|
expected_hash: null,
|
|
18309
18466
|
max_observed_bytes: null,
|
|
@@ -18321,12 +18478,12 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18321
18478
|
function assertSafeTargetHome(targetHome) {
|
|
18322
18479
|
if (!isAbsolute4(targetHome))
|
|
18323
18480
|
throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
|
|
18324
|
-
const normalized =
|
|
18481
|
+
const normalized = resolve13(targetHome);
|
|
18325
18482
|
if (normalized === parse4(normalized).root) {
|
|
18326
18483
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
18327
18484
|
}
|
|
18328
18485
|
assertNoSymlinkAncestors2(normalized);
|
|
18329
|
-
if (
|
|
18486
|
+
if (existsSync17(normalized) && lstatSync4(normalized).isSymbolicLink()) {
|
|
18330
18487
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
18331
18488
|
}
|
|
18332
18489
|
return normalized;
|
|
@@ -18336,20 +18493,20 @@ function assertNoSymlinkSegments2(root, target) {
|
|
|
18336
18493
|
const rel = relative5(root, target);
|
|
18337
18494
|
let current = root;
|
|
18338
18495
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18339
|
-
current =
|
|
18340
|
-
if (
|
|
18496
|
+
current = join20(current, segment);
|
|
18497
|
+
if (existsSync17(current) && lstatSync4(current).isSymbolicLink()) {
|
|
18341
18498
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
18342
18499
|
}
|
|
18343
18500
|
}
|
|
18344
18501
|
}
|
|
18345
18502
|
function assertNoSymlinkAncestors2(path) {
|
|
18346
|
-
const normalized =
|
|
18503
|
+
const normalized = resolve13(path);
|
|
18347
18504
|
const parsed = parse4(normalized);
|
|
18348
18505
|
let current = parsed.root;
|
|
18349
18506
|
const rel = relative5(parsed.root, normalized);
|
|
18350
18507
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18351
|
-
current =
|
|
18352
|
-
if (!
|
|
18508
|
+
current = join20(current, segment);
|
|
18509
|
+
if (!existsSync17(current))
|
|
18353
18510
|
return;
|
|
18354
18511
|
if (lstatSync4(current).isSymbolicLink()) {
|
|
18355
18512
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
@@ -18400,9 +18557,9 @@ function formatGlobalSourceCoverageWarnings(result) {
|
|
|
18400
18557
|
// src/lib/station-profile.ts
|
|
18401
18558
|
init_raw_store_root();
|
|
18402
18559
|
import { spawnSync } from "child_process";
|
|
18403
|
-
import { existsSync as
|
|
18404
|
-
import { arch as osArch, homedir as
|
|
18405
|
-
import { dirname as
|
|
18560
|
+
import { existsSync as existsSync18, lstatSync as lstatSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
18561
|
+
import { arch as osArch, homedir as homedir13, hostname as osHostname, platform as osPlatform, userInfo as osUserInfo } from "os";
|
|
18562
|
+
import { dirname as dirname8, join as join21 } from "path";
|
|
18406
18563
|
var STATION_PROFILE_CACHE_FILENAME = "station-profile.md";
|
|
18407
18564
|
var STATION_PROFILE_SOURCE_ID = "station-profile";
|
|
18408
18565
|
var STATION_PROFILE_LAYER = "machine";
|
|
@@ -18412,21 +18569,21 @@ var STATION_PROFILE_FULL_NAMES_MAX = 6;
|
|
|
18412
18569
|
var STATION_PROFILE_PRIMARY_SCOPE = "@hasna";
|
|
18413
18570
|
var MACHINES_MANIFEST_PATH_ENV = "HASNA_MACHINES_MANIFEST_PATH";
|
|
18414
18571
|
var BUN_INSTALL_ENV = "BUN_INSTALL";
|
|
18415
|
-
function
|
|
18416
|
-
return env["HOME"] || env["USERPROFILE"] ||
|
|
18572
|
+
function homeDir4(env = process.env) {
|
|
18573
|
+
return env["HOME"] || env["USERPROFILE"] || homedir13();
|
|
18417
18574
|
}
|
|
18418
18575
|
function getStationProfileCachePath(env = process.env) {
|
|
18419
|
-
return
|
|
18576
|
+
return join21(getRawStoreRoot(env), STATION_PROFILE_CACHE_FILENAME);
|
|
18420
18577
|
}
|
|
18421
18578
|
function getMachinesManifestPath(env = process.env) {
|
|
18422
|
-
return env[MACHINES_MANIFEST_PATH_ENV] ||
|
|
18579
|
+
return env[MACHINES_MANIFEST_PATH_ENV] || join21(homeDir4(env), ".hasna", "machines", "machines.json");
|
|
18423
18580
|
}
|
|
18424
18581
|
function getBunGlobalModulesDir(env = process.env) {
|
|
18425
|
-
return
|
|
18582
|
+
return join21(env[BUN_INSTALL_ENV] || join21(homeDir4(env), ".bun"), "install", "global", "node_modules");
|
|
18426
18583
|
}
|
|
18427
18584
|
function readMachinesManifest(path) {
|
|
18428
18585
|
try {
|
|
18429
|
-
if (!
|
|
18586
|
+
if (!existsSync18(path))
|
|
18430
18587
|
return null;
|
|
18431
18588
|
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
18432
18589
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -18482,9 +18639,9 @@ function probeMachineStatus(machineId) {
|
|
|
18482
18639
|
function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
18483
18640
|
const hostname2 = osHostname();
|
|
18484
18641
|
const record = findLocalManifestMachine(readMachinesManifest(getMachinesManifestPath(env)), hostname2);
|
|
18485
|
-
const home =
|
|
18642
|
+
const home = homeDir4(env);
|
|
18486
18643
|
const platform = stringField(record, "platform") ?? osPlatform();
|
|
18487
|
-
const workspacePath = stringField(record, "workspacePath") ??
|
|
18644
|
+
const workspacePath = stringField(record, "workspacePath") ?? join21(home, platform === "darwin" ? "Workspace" : "workspace");
|
|
18488
18645
|
const machine = {
|
|
18489
18646
|
id: stringField(record, "id") ?? hostname2,
|
|
18490
18647
|
hostname: stringField(record, "hostname") ?? hostname2,
|
|
@@ -18501,9 +18658,9 @@ function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
|
18501
18658
|
return machine;
|
|
18502
18659
|
}
|
|
18503
18660
|
function scopedPackageNames(modulesDir, scope) {
|
|
18504
|
-
const scopeDir =
|
|
18661
|
+
const scopeDir = join21(modulesDir, scope);
|
|
18505
18662
|
try {
|
|
18506
|
-
if (!
|
|
18663
|
+
if (!existsSync18(scopeDir))
|
|
18507
18664
|
return null;
|
|
18508
18665
|
return readdirNames(scopeDir).sort();
|
|
18509
18666
|
} catch {
|
|
@@ -18511,9 +18668,9 @@ function scopedPackageNames(modulesDir, scope) {
|
|
|
18511
18668
|
}
|
|
18512
18669
|
}
|
|
18513
18670
|
function readdirNames(dir) {
|
|
18514
|
-
return
|
|
18671
|
+
return readdirSync5(dir).filter((name) => {
|
|
18515
18672
|
try {
|
|
18516
|
-
return lstatSync5(
|
|
18673
|
+
return lstatSync5(join21(dir, name)).isDirectory();
|
|
18517
18674
|
} catch {
|
|
18518
18675
|
return false;
|
|
18519
18676
|
}
|
|
@@ -18523,7 +18680,7 @@ function resolveStationProfilePackages(env = process.env) {
|
|
|
18523
18680
|
const modulesDir = getBunGlobalModulesDir(env);
|
|
18524
18681
|
let scopeDirs;
|
|
18525
18682
|
try {
|
|
18526
|
-
if (!
|
|
18683
|
+
if (!existsSync18(modulesDir))
|
|
18527
18684
|
return null;
|
|
18528
18685
|
scopeDirs = readdirNames(modulesDir).filter((name) => name.startsWith("@") && name.toLowerCase().includes("hasna"));
|
|
18529
18686
|
} catch {
|
|
@@ -18598,9 +18755,9 @@ function refreshStationProfile(options = {}) {
|
|
|
18598
18755
|
const path = getStationProfileCachePath(env);
|
|
18599
18756
|
const generatedAt = new Date().toISOString();
|
|
18600
18757
|
if (!options.dryRun) {
|
|
18601
|
-
const existing =
|
|
18758
|
+
const existing = existsSync18(path) ? readFileSync11(path, "utf8") : null;
|
|
18602
18759
|
if (existing !== content) {
|
|
18603
|
-
mkdirSync7(
|
|
18760
|
+
mkdirSync7(dirname8(path), { recursive: true });
|
|
18604
18761
|
writeFileSync4(path, content, "utf8");
|
|
18605
18762
|
}
|
|
18606
18763
|
}
|
|
@@ -18617,7 +18774,7 @@ function refreshStationProfile(options = {}) {
|
|
|
18617
18774
|
function readStationProfile(env = process.env) {
|
|
18618
18775
|
const path = getStationProfileCachePath(env);
|
|
18619
18776
|
try {
|
|
18620
|
-
if (!
|
|
18777
|
+
if (!existsSync18(path))
|
|
18621
18778
|
return null;
|
|
18622
18779
|
return readFileSync11(path, "utf8");
|
|
18623
18780
|
} catch {
|
|
@@ -18956,7 +19113,7 @@ init_codewith_shared_todos_storage_standard();
|
|
|
18956
19113
|
import { createHash as createHash9 } from "crypto";
|
|
18957
19114
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
18958
19115
|
import {
|
|
18959
|
-
existsSync as
|
|
19116
|
+
existsSync as existsSync19,
|
|
18960
19117
|
lstatSync as lstatSync6,
|
|
18961
19118
|
mkdirSync as mkdirSync8,
|
|
18962
19119
|
readFileSync as readFileSync12,
|
|
@@ -18964,8 +19121,8 @@ import {
|
|
|
18964
19121
|
rmSync as rmSync5,
|
|
18965
19122
|
writeFileSync as writeFileSync5
|
|
18966
19123
|
} from "fs";
|
|
18967
|
-
import { homedir as
|
|
18968
|
-
import { dirname as
|
|
19124
|
+
import { homedir as homedir14 } from "os";
|
|
19125
|
+
import { dirname as dirname9, join as join22, parse as parse5, relative as relative6, resolve as resolve14 } from "path";
|
|
18969
19126
|
var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
|
|
18970
19127
|
var INBOX_SKILL_MARKERS = [
|
|
18971
19128
|
[".claude", "skills", "inbox", "SKILL.md"],
|
|
@@ -18986,13 +19143,13 @@ function lstatOrNull(path) {
|
|
|
18986
19143
|
}
|
|
18987
19144
|
}
|
|
18988
19145
|
function findSymlinkedAncestor(path) {
|
|
18989
|
-
const normalized =
|
|
19146
|
+
const normalized = resolve14(path);
|
|
18990
19147
|
const parsed = parse5(normalized);
|
|
18991
19148
|
let current = parsed.root;
|
|
18992
19149
|
const rel = relative6(parsed.root, normalized);
|
|
18993
19150
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18994
|
-
current =
|
|
18995
|
-
if (!
|
|
19151
|
+
current = join22(current, segment);
|
|
19152
|
+
if (!existsSync19(current))
|
|
18996
19153
|
return null;
|
|
18997
19154
|
if (lstatSync6(current).isSymbolicLink())
|
|
18998
19155
|
return current;
|
|
@@ -19009,11 +19166,11 @@ function packagedInboxSkillPath(explicitPath) {
|
|
|
19009
19166
|
if (explicitPath)
|
|
19010
19167
|
return explicitPath;
|
|
19011
19168
|
const candidates = [
|
|
19012
|
-
|
|
19013
|
-
|
|
19014
|
-
|
|
19169
|
+
join22(import.meta.dir, "..", "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
19170
|
+
join22(import.meta.dir, "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
19171
|
+
join22(process.cwd(), "assets", "skills", "inbox", "SKILL.md")
|
|
19015
19172
|
];
|
|
19016
|
-
const found = candidates.find((candidate) =>
|
|
19173
|
+
const found = candidates.find((candidate) => existsSync19(candidate));
|
|
19017
19174
|
if (!found) {
|
|
19018
19175
|
throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
|
|
19019
19176
|
}
|
|
@@ -19062,8 +19219,8 @@ function compareVersions(left, right) {
|
|
|
19062
19219
|
}
|
|
19063
19220
|
return 0;
|
|
19064
19221
|
}
|
|
19065
|
-
function inspectSkillMarkers(
|
|
19066
|
-
return INBOX_SKILL_MARKERS.map((parts) =>
|
|
19222
|
+
function inspectSkillMarkers(homeDir5) {
|
|
19223
|
+
return INBOX_SKILL_MARKERS.map((parts) => join22(homeDir5, ...parts)).map((path) => {
|
|
19067
19224
|
const stat = lstatOrNull(path);
|
|
19068
19225
|
if (!stat)
|
|
19069
19226
|
return null;
|
|
@@ -19079,9 +19236,9 @@ function inspectSkillMarkers(homeDir4) {
|
|
|
19079
19236
|
}).filter((snapshot) => snapshot !== null);
|
|
19080
19237
|
}
|
|
19081
19238
|
function inspectInbox(options) {
|
|
19082
|
-
const
|
|
19239
|
+
const homeDir5 = options.homeDir ?? homedir14();
|
|
19083
19240
|
const runtimeCommand = options.conversationsCommand ?? "conversations";
|
|
19084
|
-
const snapshots = inspectSkillMarkers(
|
|
19241
|
+
const snapshots = inspectSkillMarkers(homeDir5);
|
|
19085
19242
|
const skillPresent = snapshots.length > 0;
|
|
19086
19243
|
let canonicalContent = null;
|
|
19087
19244
|
let canonicalSha256 = null;
|
|
@@ -19107,7 +19264,7 @@ function inspectInbox(options) {
|
|
|
19107
19264
|
let reason = "skill not installed";
|
|
19108
19265
|
if (skillPresent) {
|
|
19109
19266
|
const nonRegular = snapshots.some((snapshot) => !snapshot.regular);
|
|
19110
|
-
const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(
|
|
19267
|
+
const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname9(path))).find((found) => found !== null);
|
|
19111
19268
|
if (nonRegular)
|
|
19112
19269
|
reason = "managed skill target is not a regular file";
|
|
19113
19270
|
else if (symlinkAncestor)
|
|
@@ -19194,10 +19351,10 @@ function cleanup(path) {
|
|
|
19194
19351
|
rmSync5(path, { force: true });
|
|
19195
19352
|
}
|
|
19196
19353
|
function writeAtomic(path, content, mode) {
|
|
19197
|
-
assertNoSymlinkAncestors3(
|
|
19354
|
+
assertNoSymlinkAncestors3(dirname9(path));
|
|
19198
19355
|
const tempPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
19199
19356
|
try {
|
|
19200
|
-
mkdirSync8(
|
|
19357
|
+
mkdirSync8(dirname9(path), { recursive: true, mode: 493 });
|
|
19201
19358
|
writeFileSync5(tempPath, content, { mode, flag: "wx" });
|
|
19202
19359
|
renameSync2(tempPath, path);
|
|
19203
19360
|
} finally {
|
|
@@ -19273,7 +19430,7 @@ async function reconcileManagedSkillRuntimes(options = {}) {
|
|
|
19273
19430
|
dry_run: dryRun
|
|
19274
19431
|
};
|
|
19275
19432
|
}
|
|
19276
|
-
const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(
|
|
19433
|
+
const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname9(path))).find((found) => found !== null);
|
|
19277
19434
|
if (symlinkedAncestor) {
|
|
19278
19435
|
return {
|
|
19279
19436
|
runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
|
|
@@ -19356,28 +19513,28 @@ init_project_context();
|
|
|
19356
19513
|
init_config_store();
|
|
19357
19514
|
init_apply();
|
|
19358
19515
|
init_config_agents();
|
|
19359
|
-
import { existsSync as
|
|
19516
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14 } from "fs";
|
|
19360
19517
|
|
|
19361
19518
|
// src/lib/package-version.ts
|
|
19362
|
-
import { existsSync as
|
|
19363
|
-
import { dirname as
|
|
19519
|
+
import { existsSync as existsSync20, readFileSync as readFileSync13 } from "fs";
|
|
19520
|
+
import { dirname as dirname10, join as join23 } from "path";
|
|
19364
19521
|
import { fileURLToPath } from "url";
|
|
19365
19522
|
var cached = null;
|
|
19366
19523
|
function getPackageVersion() {
|
|
19367
19524
|
if (cached)
|
|
19368
19525
|
return cached;
|
|
19369
19526
|
try {
|
|
19370
|
-
let dir =
|
|
19527
|
+
let dir = dirname10(fileURLToPath(import.meta.url));
|
|
19371
19528
|
for (let i = 0;i < 8; i++) {
|
|
19372
|
-
const pkgPath =
|
|
19373
|
-
if (
|
|
19529
|
+
const pkgPath = join23(dir, "package.json");
|
|
19530
|
+
if (existsSync20(pkgPath)) {
|
|
19374
19531
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
19375
19532
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
19376
19533
|
cached = pkg.version;
|
|
19377
19534
|
return cached;
|
|
19378
19535
|
}
|
|
19379
19536
|
}
|
|
19380
|
-
const parent =
|
|
19537
|
+
const parent = dirname10(dir);
|
|
19381
19538
|
if (parent === dir)
|
|
19382
19539
|
break;
|
|
19383
19540
|
dir = parent;
|
|
@@ -19434,7 +19591,7 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
|
19434
19591
|
continue;
|
|
19435
19592
|
knownTargets += 1;
|
|
19436
19593
|
const targetPath = expandPath(config.target_path);
|
|
19437
|
-
if (!
|
|
19594
|
+
if (!existsSync21(targetPath)) {
|
|
19438
19595
|
missingTargets += 1;
|
|
19439
19596
|
continue;
|
|
19440
19597
|
}
|
|
@@ -19534,8 +19691,8 @@ init_config_store();
|
|
|
19534
19691
|
|
|
19535
19692
|
// src/lib/provider-context.ts
|
|
19536
19693
|
import { createHash as createHash10 } from "crypto";
|
|
19537
|
-
import { existsSync as
|
|
19538
|
-
import { join as
|
|
19694
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "fs";
|
|
19695
|
+
import { join as join24 } from "path";
|
|
19539
19696
|
var PROVIDER_CONTEXT_DIR = ".hasna/provider-context";
|
|
19540
19697
|
var PROVIDER_CONTEXT_MANIFEST = "manifest.json";
|
|
19541
19698
|
var PROVIDER_CONTEXT_SCHEMA = "hasna.instructions.provider-context/v1";
|
|
@@ -19680,17 +19837,17 @@ function resolveAndRenderProviderContext(opts) {
|
|
|
19680
19837
|
const recordedEndpoint = originAccepted ? `${opts.origin.host}${opts.origin.pathPrefix || ""}` : null;
|
|
19681
19838
|
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;
|
|
19682
19839
|
const content = renderProviderFragment(entry);
|
|
19683
|
-
const dir =
|
|
19684
|
-
if (!
|
|
19840
|
+
const dir = join24(opts.homeDir, PROVIDER_CONTEXT_DIR);
|
|
19841
|
+
if (!existsSync22(dir))
|
|
19685
19842
|
mkdirSync9(dir, { recursive: true });
|
|
19686
19843
|
const filename = `${entry ? entry.key : "invariant"}.md`;
|
|
19687
|
-
const fragmentPath2 =
|
|
19844
|
+
const fragmentPath2 = join24(dir, filename);
|
|
19688
19845
|
const fragmentSha256 = sha25610(content);
|
|
19689
19846
|
writeFileSync6(fragmentPath2, content, "utf8");
|
|
19690
|
-
const manifestPath =
|
|
19847
|
+
const manifestPath = join24(dir, PROVIDER_CONTEXT_MANIFEST);
|
|
19691
19848
|
let manifest = { schema: PROVIDER_CONTEXT_SCHEMA, fragments: {} };
|
|
19692
19849
|
try {
|
|
19693
|
-
if (
|
|
19850
|
+
if (existsSync22(manifestPath)) {
|
|
19694
19851
|
const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
19695
19852
|
if (parsed && typeof parsed === "object")
|
|
19696
19853
|
manifest = parsed;
|
|
@@ -19834,7 +19991,7 @@ function parseSessionSource(value, order) {
|
|
|
19834
19991
|
if (!path)
|
|
19835
19992
|
throw new Error(`Invalid --source "${value}" (expected path or id=path)`);
|
|
19836
19993
|
const absPath = resolveSessionPath(path);
|
|
19837
|
-
if (!
|
|
19994
|
+
if (!existsSync24(absPath))
|
|
19838
19995
|
throw new Error(`Instruction source file not found: ${absPath}`);
|
|
19839
19996
|
const content = readSessionInstructionSourceFile(absPath);
|
|
19840
19997
|
const source = sourceFromFilePath(absPath, content, order);
|
|
@@ -19915,7 +20072,7 @@ async function collectSessionSources(opts, tool, store) {
|
|
|
19915
20072
|
}
|
|
19916
20073
|
for (const value of opts.identityExport ?? []) {
|
|
19917
20074
|
const path = resolveSessionPath(value);
|
|
19918
|
-
if (!
|
|
20075
|
+
if (!existsSync24(path))
|
|
19919
20076
|
throw new Error(`Identity instruction export not found: ${path}`);
|
|
19920
20077
|
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
19921
20078
|
sources.push(...sourcesFromIdentityExport(parsed, { path, tool, orderOffset: sources.length }));
|
|
@@ -20049,7 +20206,7 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
20049
20206
|
if (value === "-")
|
|
20050
20207
|
return { json: readBoundedProjectContextStdin() };
|
|
20051
20208
|
const path = resolveSessionPath(value);
|
|
20052
|
-
if (!
|
|
20209
|
+
if (!existsSync24(path)) {
|
|
20053
20210
|
if (allowMissing)
|
|
20054
20211
|
return {};
|
|
20055
20212
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_MISSING", `bundle file not found: ${path}`);
|
|
@@ -20220,8 +20377,8 @@ program.command("tag <id>").description("Add or remove tags on a stored config (
|
|
|
20220
20377
|
console.log(chalk.green("\u2713") + ` Tags on ${chalk.bold(updated.name)} ${chalk.dim(`(${updated.slug})`)}: ${nextTags.join(", ") || chalk.dim("(none)")}`);
|
|
20221
20378
|
});
|
|
20222
20379
|
program.command("add <path>").description("Ingest a file into the config DB").option("-n, --name <name>", "config name (defaults to filename)").option("-c, --category <cat>", "category override").option("-a, --agent <agent>", "agent override").option("-k, --kind <kind>", "kind: file|reference", "file").option("--template", "mark as template (has {{VAR}} placeholders)").option("--update", "if a config already owns this path, update that row in place instead of refusing").action(async (filePath, opts) => {
|
|
20223
|
-
const abs =
|
|
20224
|
-
if (!
|
|
20380
|
+
const abs = resolve16(filePath);
|
|
20381
|
+
if (!existsSync24(abs)) {
|
|
20225
20382
|
console.error(chalk.red(`File not found: ${abs}`));
|
|
20226
20383
|
process.exit(1);
|
|
20227
20384
|
}
|
|
@@ -20229,7 +20386,7 @@ program.command("add <path>").description("Ingest a file into the config DB").op
|
|
|
20229
20386
|
const storedFmt = detectFormat(abs);
|
|
20230
20387
|
const fmt = redactFormatForTarget(abs, storedFmt);
|
|
20231
20388
|
const { content, redacted, isTemplate: isTemplate2 } = redactContent(rawContent, fmt);
|
|
20232
|
-
const targetPath = abs.startsWith(
|
|
20389
|
+
const targetPath = abs.startsWith(homedir16()) ? abs.replace(homedir16(), "~") : abs;
|
|
20233
20390
|
const name = opts.name || filePath.split("/").pop();
|
|
20234
20391
|
const store = resolveConfigStore();
|
|
20235
20392
|
const allConfigs = await store.listConfigs();
|
|
@@ -20397,14 +20554,14 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
20397
20554
|
if (opts.project) {
|
|
20398
20555
|
const dir = typeof opts.project === "string" ? opts.project : process.cwd();
|
|
20399
20556
|
if (opts.all) {
|
|
20400
|
-
const { readdirSync:
|
|
20557
|
+
const { readdirSync: readdirSync7 } = await import("fs");
|
|
20401
20558
|
const absDir = expandPath(dir);
|
|
20402
|
-
const entries =
|
|
20559
|
+
const entries = readdirSync7(absDir, { withFileTypes: true });
|
|
20403
20560
|
let totalAdded = 0, totalUpdated = 0, totalUnchanged = 0, projects = 0;
|
|
20404
20561
|
for (const entry of entries) {
|
|
20405
20562
|
if (!entry.isDirectory())
|
|
20406
20563
|
continue;
|
|
20407
|
-
const projDir =
|
|
20564
|
+
const projDir = join26(absDir, entry.name);
|
|
20408
20565
|
const hasAgentConfig = [
|
|
20409
20566
|
"CLAUDE.md",
|
|
20410
20567
|
".mcp.json",
|
|
@@ -20417,7 +20574,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
20417
20574
|
".aicopilot",
|
|
20418
20575
|
".cursor",
|
|
20419
20576
|
".agents"
|
|
20420
|
-
].some((marker) =>
|
|
20577
|
+
].some((marker) => existsSync24(join26(projDir, marker)));
|
|
20421
20578
|
if (!hasAgentConfig)
|
|
20422
20579
|
continue;
|
|
20423
20580
|
const result2 = await syncProject({ projectDir: projDir, dryRun: opts.dryRun, store });
|
|
@@ -20468,7 +20625,7 @@ program.command("import <file>").description("Import configs from a tar.gz bundl
|
|
|
20468
20625
|
});
|
|
20469
20626
|
program.command("whoami").description("Show setup summary").action(async () => {
|
|
20470
20627
|
const store = resolveConfigStore();
|
|
20471
|
-
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
20628
|
+
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join26(getRawStoreRoot(), "instructions.db");
|
|
20472
20629
|
const stats = await store.getConfigStats();
|
|
20473
20630
|
console.log(chalk.bold("@hasna/instructions") + chalk.dim(" v" + pkg.version));
|
|
20474
20631
|
console.log(chalk.cyan(isApiTransport() ? "API:" : "DB:") + " " + dbPath);
|
|
@@ -21347,7 +21504,7 @@ mcpCmd.command("install").alias("add").description("Install configs MCP server i
|
|
|
21347
21504
|
} else if (target === "codex") {
|
|
21348
21505
|
const { appendFileSync, existsSync: ex } = await import("fs");
|
|
21349
21506
|
const { join: j } = await import("path");
|
|
21350
|
-
const configPath = j(
|
|
21507
|
+
const configPath = j(homedir16(), ".codex", "config.toml");
|
|
21351
21508
|
const block = `
|
|
21352
21509
|
[mcp_servers.configs]
|
|
21353
21510
|
command = "${mcpBinary}"
|
|
@@ -21365,7 +21522,7 @@ args = []
|
|
|
21365
21522
|
} else if (target === "antigravity") {
|
|
21366
21523
|
const { mkdirSync: md, readFileSync: rf, writeFileSync: wf, existsSync: ex } = await import("fs");
|
|
21367
21524
|
const { dirname: dn, join: j } = await import("path");
|
|
21368
|
-
const configPath = j(
|
|
21525
|
+
const configPath = j(homedir16(), ".gemini", "config", "mcp_config.json");
|
|
21369
21526
|
let settings = {};
|
|
21370
21527
|
if (ex(configPath)) {
|
|
21371
21528
|
try {
|
|
@@ -21449,7 +21606,7 @@ DB stats:`));
|
|
|
21449
21606
|
if (count > 0)
|
|
21450
21607
|
console.log(` ${key.padEnd(18)} ${count}`);
|
|
21451
21608
|
}
|
|
21452
|
-
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
21609
|
+
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join26(getRawStoreRoot(), "instructions.db");
|
|
21453
21610
|
console.log(chalk.dim(`
|
|
21454
21611
|
${isApiTransport() ? "API" : "DB"}: ${location}`));
|
|
21455
21612
|
});
|
|
@@ -21510,10 +21667,10 @@ managedSkillsCmd.command("apply").option("--dry-run", "preview without writing")
|
|
|
21510
21667
|
});
|
|
21511
21668
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
21512
21669
|
const { mkdirSync: mk } = await import("fs");
|
|
21513
|
-
const backupDir =
|
|
21670
|
+
const backupDir = join26(getRawStoreRoot(), "backups");
|
|
21514
21671
|
mk(backupDir, { recursive: true });
|
|
21515
21672
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
21516
|
-
const outPath =
|
|
21673
|
+
const outPath = join26(backupDir, `configs-${ts}.tar.gz`);
|
|
21517
21674
|
const result = await exportConfigs(outPath, { store: resolveConfigStore() });
|
|
21518
21675
|
const { statSync: st } = await import("fs");
|
|
21519
21676
|
const size = st(outPath).size;
|
|
@@ -21541,9 +21698,9 @@ program.command("doctor").description("Validate configs: syntax, permissions, mi
|
|
|
21541
21698
|
console.log(chalk.cyan("Known files on disk:"));
|
|
21542
21699
|
for (const k of KNOWN_CONFIGS) {
|
|
21543
21700
|
if (k.rulesDir) {
|
|
21544
|
-
|
|
21701
|
+
existsSync24(expandPath(k.rulesDir)) ? pass(`${k.rulesDir}/ exists`) : k.optional ? skip(`${k.rulesDir}/ (optional)`) : fail2(`${k.rulesDir}/ not found`);
|
|
21545
21702
|
} else {
|
|
21546
|
-
|
|
21703
|
+
existsSync24(expandPath(k.path)) ? pass(k.path) : k.optional ? skip(`${k.path} (optional)`) : fail2(`${k.path} not found`);
|
|
21547
21704
|
}
|
|
21548
21705
|
}
|
|
21549
21706
|
const allConfigs = await store.listConfigs();
|
|
@@ -21697,16 +21854,16 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21697
21854
|
for (const k of KNOWN_CONFIGS) {
|
|
21698
21855
|
if (k.rulesDir) {
|
|
21699
21856
|
const absDir = expandPath2(k.rulesDir);
|
|
21700
|
-
if (!
|
|
21857
|
+
if (!existsSync24(absDir))
|
|
21701
21858
|
continue;
|
|
21702
|
-
const { readdirSync:
|
|
21703
|
-
for (const f of
|
|
21704
|
-
const abs =
|
|
21859
|
+
const { readdirSync: readdirSync7 } = await import("fs");
|
|
21860
|
+
for (const f of readdirSync7(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
21861
|
+
const abs = join26(absDir, f);
|
|
21705
21862
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21706
21863
|
}
|
|
21707
21864
|
} else {
|
|
21708
21865
|
const abs = expandPath2(k.path);
|
|
21709
|
-
if (
|
|
21866
|
+
if (existsSync24(abs))
|
|
21710
21867
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21711
21868
|
}
|
|
21712
21869
|
}
|
|
@@ -21714,7 +21871,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21714
21871
|
const tick = async () => {
|
|
21715
21872
|
let changed = 0;
|
|
21716
21873
|
for (const [abs, oldMtime] of mtimes) {
|
|
21717
|
-
if (!
|
|
21874
|
+
if (!existsSync24(abs))
|
|
21718
21875
|
continue;
|
|
21719
21876
|
const newMtime = st(abs).mtimeMs;
|
|
21720
21877
|
if (newMtime !== oldMtime) {
|
|
@@ -21726,10 +21883,10 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21726
21883
|
for (const k of KNOWN_CONFIGS) {
|
|
21727
21884
|
if (k.rulesDir) {
|
|
21728
21885
|
const absDir = expandPath2(k.rulesDir);
|
|
21729
|
-
if (!
|
|
21886
|
+
if (!existsSync24(absDir))
|
|
21730
21887
|
continue;
|
|
21731
21888
|
for (const f of rd(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
21732
|
-
const abs =
|
|
21889
|
+
const abs = join26(absDir, f);
|
|
21733
21890
|
if (!mtimes.has(abs)) {
|
|
21734
21891
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21735
21892
|
changed++;
|
|
@@ -21737,7 +21894,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21737
21894
|
}
|
|
21738
21895
|
} else {
|
|
21739
21896
|
const abs = expandPath2(k.path);
|
|
21740
|
-
if (
|
|
21897
|
+
if (existsSync24(abs) && !mtimes.has(abs)) {
|
|
21741
21898
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21742
21899
|
changed++;
|
|
21743
21900
|
}
|
|
@@ -21765,7 +21922,7 @@ program.command("report").description("Summary of stored configs, drift, and eco
|
|
|
21765
21922
|
if (!c.target_path)
|
|
21766
21923
|
continue;
|
|
21767
21924
|
const abs = expandPath(c.target_path);
|
|
21768
|
-
if (!
|
|
21925
|
+
if (!existsSync24(abs)) {
|
|
21769
21926
|
missing++;
|
|
21770
21927
|
continue;
|
|
21771
21928
|
}
|
|
@@ -21836,7 +21993,7 @@ program.command("clean").description("Remove configs from DB whose target files
|
|
|
21836
21993
|
if (!c.target_path)
|
|
21837
21994
|
continue;
|
|
21838
21995
|
const abs = expandPath(c.target_path);
|
|
21839
|
-
if (!
|
|
21996
|
+
if (!existsSync24(abs)) {
|
|
21840
21997
|
if (printed < maxPrinted) {
|
|
21841
21998
|
if (opts.dryRun) {
|
|
21842
21999
|
console.log(chalk.yellow(" would remove:") + ` ${c.slug} ${chalk.dim(`(${truncateMiddle(c.target_path, 88)})`)}`);
|
|
@@ -21982,13 +22139,13 @@ providerContextCmd.command("resolve").description("Resolve the endpoint to a pro
|
|
|
21982
22139
|
try {
|
|
21983
22140
|
const rawEndpoint = opts.endpoint ?? process.env["ANTHROPIC_BASE_URL"] ?? "";
|
|
21984
22141
|
const rawModel = opts.model ?? process.env["ANTHROPIC_MODEL"] ?? "";
|
|
21985
|
-
const
|
|
22142
|
+
const homeDir5 = opts.home ?? homedir16();
|
|
21986
22143
|
const origin = normalizeEndpointOrigin(rawEndpoint);
|
|
21987
22144
|
const resolution = resolveAndRenderProviderContext({
|
|
21988
22145
|
origin,
|
|
21989
22146
|
rawEndpoint,
|
|
21990
22147
|
rawModel,
|
|
21991
|
-
homeDir:
|
|
22148
|
+
homeDir: homeDir5
|
|
21992
22149
|
});
|
|
21993
22150
|
if (opts.json) {
|
|
21994
22151
|
printJson({
|