@hasna/instructions 0.4.35 → 0.4.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/assets/skills/inbox/SKILL.md +86 -0
- package/dashboard/README.md +34 -70
- package/dist/cli/index.js +1136 -526
- package/dist/cli/raw-store-root.test.d.ts +2 -0
- package/dist/cli/raw-store-root.test.d.ts.map +1 -0
- package/dist/db/configs.d.ts.map +1 -1
- package/dist/generated/storage-kit/index.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1276 -754
- package/dist/lib/apply.d.ts.map +1 -1
- package/dist/lib/cursor-authority.d.ts +36 -3
- package/dist/lib/cursor-authority.d.ts.map +1 -1
- package/dist/lib/managed-skill-runtimes.d.ts +80 -0
- package/dist/lib/managed-skill-runtimes.d.ts.map +1 -0
- package/dist/lib/managed-skill-runtimes.test.d.ts +2 -0
- package/dist/lib/managed-skill-runtimes.test.d.ts.map +1 -0
- package/dist/lib/raw-store-root.d.ts +17 -0
- package/dist/lib/raw-store-root.d.ts.map +1 -0
- package/dist/lib/session-apply.d.ts.map +1 -1
- package/dist/lib/session-authority.d.ts +27 -0
- package/dist/lib/session-authority.d.ts.map +1 -0
- package/dist/lib/session-authority.test.d.ts +2 -0
- package/dist/lib/session-authority.test.d.ts.map +1 -0
- package/dist/lib/session-render.d.ts +5 -3
- package/dist/lib/session-render.d.ts.map +1 -1
- package/dist/mcp/index.js +290 -233
- package/dist/server/index.js +425 -231
- package/dist/status.d.ts +11 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/storage/cloud-store.d.ts.map +1 -1
- package/dist/storage/cloud-store.test.d.ts +2 -0
- package/dist/storage/cloud-store.test.d.ts.map +1 -0
- package/package.json +9 -7
package/dist/cli/index.js
CHANGED
|
@@ -20,12 +20,14 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
20
20
|
}
|
|
21
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
24
|
+
for (let key of __getOwnPropNames(mod))
|
|
25
|
+
if (!__hasOwnProp.call(to, key))
|
|
26
|
+
__defProp(to, key, {
|
|
27
|
+
get: __accessProp.bind(mod, key),
|
|
28
|
+
enumerable: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
29
31
|
if (canCache)
|
|
30
32
|
cache.set(mod, to);
|
|
31
33
|
return to;
|
|
@@ -48,7 +50,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
48
50
|
var __require = import.meta.require;
|
|
49
51
|
|
|
50
52
|
// ../../node_modules/.bun/commander@13.1.0/node_modules/commander/lib/error.js
|
|
51
|
-
var require_error = __commonJS((exports)
|
|
53
|
+
var require_error = __commonJS(function(exports) {
|
|
52
54
|
class CommanderError extends Error {
|
|
53
55
|
constructor(exitCode, code, message) {
|
|
54
56
|
super(message);
|
|
@@ -72,7 +74,7 @@ var require_error = __commonJS((exports) => {
|
|
|
72
74
|
});
|
|
73
75
|
|
|
74
76
|
// ../../node_modules/.bun/commander@13.1.0/node_modules/commander/lib/argument.js
|
|
75
|
-
var require_argument = __commonJS((exports)
|
|
77
|
+
var require_argument = __commonJS(function(exports) {
|
|
76
78
|
var { InvalidArgumentError } = require_error();
|
|
77
79
|
|
|
78
80
|
class Argument {
|
|
@@ -151,7 +153,7 @@ var require_argument = __commonJS((exports) => {
|
|
|
151
153
|
});
|
|
152
154
|
|
|
153
155
|
// ../../node_modules/.bun/commander@13.1.0/node_modules/commander/lib/help.js
|
|
154
|
-
var require_help = __commonJS((exports)
|
|
156
|
+
var require_help = __commonJS(function(exports) {
|
|
155
157
|
var { humanReadableArgName } = require_argument();
|
|
156
158
|
|
|
157
159
|
class Help {
|
|
@@ -501,7 +503,7 @@ ${itemIndentStr}`);
|
|
|
501
503
|
});
|
|
502
504
|
|
|
503
505
|
// ../../node_modules/.bun/commander@13.1.0/node_modules/commander/lib/option.js
|
|
504
|
-
var require_option = __commonJS((exports)
|
|
506
|
+
var require_option = __commonJS(function(exports) {
|
|
505
507
|
var { InvalidArgumentError } = require_error();
|
|
506
508
|
|
|
507
509
|
class Option {
|
|
@@ -679,7 +681,7 @@ var require_option = __commonJS((exports) => {
|
|
|
679
681
|
});
|
|
680
682
|
|
|
681
683
|
// ../../node_modules/.bun/commander@13.1.0/node_modules/commander/lib/suggestSimilar.js
|
|
682
|
-
var require_suggestSimilar = __commonJS((exports)
|
|
684
|
+
var require_suggestSimilar = __commonJS(function(exports) {
|
|
683
685
|
var maxDistance = 3;
|
|
684
686
|
function editDistance(a, b) {
|
|
685
687
|
if (Math.abs(a.length - b.length) > maxDistance)
|
|
@@ -752,7 +754,7 @@ var require_suggestSimilar = __commonJS((exports) => {
|
|
|
752
754
|
});
|
|
753
755
|
|
|
754
756
|
// ../../node_modules/.bun/commander@13.1.0/node_modules/commander/lib/command.js
|
|
755
|
-
var require_command = __commonJS((exports)
|
|
757
|
+
var require_command = __commonJS(function(exports) {
|
|
756
758
|
var EventEmitter = __require("events").EventEmitter;
|
|
757
759
|
var childProcess = __require("child_process");
|
|
758
760
|
var path = __require("path");
|
|
@@ -993,7 +995,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
993
995
|
this._exitCallback = (err) => {
|
|
994
996
|
if (err.code !== "commander.executeSubCommandAsync") {
|
|
995
997
|
throw err;
|
|
996
|
-
}
|
|
998
|
+
}
|
|
997
999
|
};
|
|
998
1000
|
}
|
|
999
1001
|
return this;
|
|
@@ -2062,7 +2064,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2062
2064
|
});
|
|
2063
2065
|
|
|
2064
2066
|
// ../../node_modules/.bun/commander@13.1.0/node_modules/commander/index.js
|
|
2065
|
-
var require_commander = __commonJS((exports)
|
|
2067
|
+
var require_commander = __commonJS(function(exports) {
|
|
2066
2068
|
var { Argument } = require_argument();
|
|
2067
2069
|
var { Command } = require_command();
|
|
2068
2070
|
var { CommanderError, InvalidArgumentError } = require_error();
|
|
@@ -2160,19 +2162,27 @@ var init_retired_storage_mode = __esm(() => {
|
|
|
2160
2162
|
];
|
|
2161
2163
|
});
|
|
2162
2164
|
|
|
2165
|
+
// src/lib/raw-store-root.ts
|
|
2166
|
+
import { homedir as homedir2 } from "os";
|
|
2167
|
+
import { join as join2, resolve } from "path";
|
|
2168
|
+
function getRawStoreRoot() {
|
|
2169
|
+
return resolve(process.env[RAW_STORE_ROOT_ENV] || join2(process.env["HOME"] || homedir2(), ".hasna", "instructions"));
|
|
2170
|
+
}
|
|
2171
|
+
var RAW_STORE_ROOT_ENV = "HASNA_CONFIGS_HOME";
|
|
2172
|
+
var init_raw_store_root = () => {};
|
|
2173
|
+
|
|
2163
2174
|
// src/db/database.ts
|
|
2164
2175
|
import { Database } from "bun:sqlite";
|
|
2165
2176
|
import { existsSync as existsSync2, mkdirSync, rmSync } from "fs";
|
|
2166
|
-
import { join as
|
|
2177
|
+
import { join as join3 } from "path";
|
|
2167
2178
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2168
2179
|
function getDbPath() {
|
|
2169
2180
|
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
|
|
2170
2181
|
return process.env["HASNA_INSTRUCTIONS_DB_PATH"];
|
|
2171
2182
|
}
|
|
2172
|
-
const
|
|
2173
|
-
const dir = join2(home, ".hasna", "instructions");
|
|
2183
|
+
const dir = getRawStoreRoot();
|
|
2174
2184
|
mkdirSync(dir, { recursive: true });
|
|
2175
|
-
return
|
|
2185
|
+
return join3(dir, "instructions.db");
|
|
2176
2186
|
}
|
|
2177
2187
|
function uuid() {
|
|
2178
2188
|
return randomUUID3();
|
|
@@ -2270,6 +2280,7 @@ function insertFeedback(input, db) {
|
|
|
2270
2280
|
var MIGRATIONS, _db = null;
|
|
2271
2281
|
var init_database = __esm(() => {
|
|
2272
2282
|
init_retired_storage_mode();
|
|
2283
|
+
init_raw_store_root();
|
|
2273
2284
|
MIGRATIONS = [
|
|
2274
2285
|
`
|
|
2275
2286
|
CREATE TABLE IF NOT EXISTS configs (
|
|
@@ -2354,6 +2365,37 @@ var init_database = __esm(() => {
|
|
|
2354
2365
|
];
|
|
2355
2366
|
});
|
|
2356
2367
|
|
|
2368
|
+
// src/db/snapshots.ts
|
|
2369
|
+
function createSnapshot(configId, content, version, db) {
|
|
2370
|
+
const d = db || getDatabase();
|
|
2371
|
+
const id = uuid();
|
|
2372
|
+
const ts = now2();
|
|
2373
|
+
d.run("INSERT INTO config_snapshots (id, config_id, content, version, created_at) VALUES (?, ?, ?, ?, ?)", [id, configId, content, version, ts]);
|
|
2374
|
+
return { id, config_id: configId, content, version, created_at: ts };
|
|
2375
|
+
}
|
|
2376
|
+
function listSnapshots(configId, db) {
|
|
2377
|
+
const d = db || getDatabase();
|
|
2378
|
+
return d.query("SELECT * FROM config_snapshots WHERE config_id = ? ORDER BY version DESC").all(configId);
|
|
2379
|
+
}
|
|
2380
|
+
function getSnapshot(id, db) {
|
|
2381
|
+
const d = db || getDatabase();
|
|
2382
|
+
return d.query("SELECT * FROM config_snapshots WHERE id = ?").get(id);
|
|
2383
|
+
}
|
|
2384
|
+
function getSnapshotByVersion(configId, version, db) {
|
|
2385
|
+
const d = db || getDatabase();
|
|
2386
|
+
return d.query("SELECT * FROM config_snapshots WHERE config_id = ? AND version = ?").get(configId, version);
|
|
2387
|
+
}
|
|
2388
|
+
function pruneSnapshots(configId, keep = 10, db) {
|
|
2389
|
+
const d = db || getDatabase();
|
|
2390
|
+
const result = d.run(`DELETE FROM config_snapshots WHERE config_id = ? AND id NOT IN (
|
|
2391
|
+
SELECT id FROM config_snapshots WHERE config_id = ? ORDER BY version DESC LIMIT ?
|
|
2392
|
+
)`, [configId, configId, keep]);
|
|
2393
|
+
return result.changes;
|
|
2394
|
+
}
|
|
2395
|
+
var init_snapshots = __esm(() => {
|
|
2396
|
+
init_database();
|
|
2397
|
+
});
|
|
2398
|
+
|
|
2357
2399
|
// src/db/configs.ts
|
|
2358
2400
|
function rowToConfig(row) {
|
|
2359
2401
|
let outputs = [];
|
|
@@ -2392,25 +2434,28 @@ function createConfig(input, db) {
|
|
|
2392
2434
|
const slug = uniqueSlug(input.name, d);
|
|
2393
2435
|
const tags = JSON.stringify(input.tags || []);
|
|
2394
2436
|
const outputs = JSON.stringify(input.outputs || []);
|
|
2395
|
-
d.
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2437
|
+
return d.transaction(() => {
|
|
2438
|
+
d.run(`INSERT INTO configs (id, name, slug, kind, category, agent, target_path, outputs, format, content, description, tags, is_template, version, created_at, updated_at, synced_at)
|
|
2439
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, NULL)`, [
|
|
2440
|
+
id,
|
|
2441
|
+
input.name,
|
|
2442
|
+
slug,
|
|
2443
|
+
input.kind ?? "file",
|
|
2444
|
+
input.category,
|
|
2445
|
+
input.agent ?? "global",
|
|
2446
|
+
input.target_path ?? null,
|
|
2447
|
+
outputs,
|
|
2448
|
+
input.format ?? "text",
|
|
2449
|
+
input.content,
|
|
2450
|
+
input.description ?? null,
|
|
2451
|
+
tags,
|
|
2452
|
+
input.is_template ? 1 : 0,
|
|
2453
|
+
ts,
|
|
2454
|
+
ts
|
|
2455
|
+
]);
|
|
2456
|
+
createSnapshot(id, input.content, 1, d);
|
|
2457
|
+
return getConfig(id, d);
|
|
2458
|
+
})();
|
|
2414
2459
|
}
|
|
2415
2460
|
function getConfig(idOrSlug, db) {
|
|
2416
2461
|
const d = db || getDatabase();
|
|
@@ -2515,9 +2560,13 @@ function updateConfig(idOrSlug, input, db) {
|
|
|
2515
2560
|
updates.push("synced_at = ?");
|
|
2516
2561
|
params.push(input.synced_at);
|
|
2517
2562
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2563
|
+
return d.transaction(() => {
|
|
2564
|
+
params.push(existing.id);
|
|
2565
|
+
d.run(`UPDATE configs SET ${updates.join(", ")} WHERE id = ?`, params);
|
|
2566
|
+
const updated = getConfigById(existing.id, d);
|
|
2567
|
+
createSnapshot(updated.id, updated.content, updated.version, d);
|
|
2568
|
+
return updated;
|
|
2569
|
+
})();
|
|
2521
2570
|
}
|
|
2522
2571
|
function deleteConfig(idOrSlug, db) {
|
|
2523
2572
|
const d = db || getDatabase();
|
|
@@ -2537,16 +2586,17 @@ function getConfigStats(db) {
|
|
|
2537
2586
|
var init_configs = __esm(() => {
|
|
2538
2587
|
init_types();
|
|
2539
2588
|
init_database();
|
|
2589
|
+
init_snapshots();
|
|
2540
2590
|
});
|
|
2541
2591
|
|
|
2542
2592
|
// src/lib/template.ts
|
|
2543
2593
|
var exports_template = {};
|
|
2544
2594
|
__export(exports_template, {
|
|
2545
|
-
|
|
2546
|
-
renderTemplate: () => renderTemplate,
|
|
2547
|
-
parseTemplateVars: () => parseTemplateVars,
|
|
2595
|
+
extractTemplateVars: () => extractTemplateVars,
|
|
2548
2596
|
isTemplate: () => isTemplate,
|
|
2549
|
-
|
|
2597
|
+
parseTemplateVars: () => parseTemplateVars,
|
|
2598
|
+
renderTemplate: () => renderTemplate,
|
|
2599
|
+
renderTemplatePreview: () => renderTemplatePreview
|
|
2550
2600
|
});
|
|
2551
2601
|
function parseTemplateVars(content) {
|
|
2552
2602
|
const names = new Set;
|
|
@@ -2610,9 +2660,9 @@ var init_template = __esm(() => {
|
|
|
2610
2660
|
});
|
|
2611
2661
|
|
|
2612
2662
|
// src/lib/machine.ts
|
|
2613
|
-
import { arch as currentArch, homedir as
|
|
2663
|
+
import { arch as currentArch, homedir as homedir3, hostname as currentHostname, type as currentOsType } from "os";
|
|
2614
2664
|
import { existsSync as existsSync3 } from "fs";
|
|
2615
|
-
import { join as
|
|
2665
|
+
import { join as join4 } from "path";
|
|
2616
2666
|
function normalizeOsFamily(os) {
|
|
2617
2667
|
const value = (os ?? "").trim().toLowerCase();
|
|
2618
2668
|
if (value === "darwin" || value === "macos" || value === "mac" || value === "osx")
|
|
@@ -2624,11 +2674,11 @@ function normalizeOsFamily(os) {
|
|
|
2624
2674
|
return value || "unknown";
|
|
2625
2675
|
}
|
|
2626
2676
|
function detectMachineContext(overrides = {}) {
|
|
2627
|
-
const homeDir = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ??
|
|
2677
|
+
const homeDir = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir3();
|
|
2628
2678
|
const os = overrides.os ?? currentOsType();
|
|
2629
2679
|
const osFamily = normalizeOsFamily(os);
|
|
2630
|
-
const bunBinDir = overrides.bun_bin_dir ??
|
|
2631
|
-
const defaultBunPath = osFamily === "macos" && existsSync3(BREW_BUN_PATH) ? BREW_BUN_PATH :
|
|
2680
|
+
const bunBinDir = overrides.bun_bin_dir ?? join4(homeDir, ".bun", "bin");
|
|
2681
|
+
const defaultBunPath = osFamily === "macos" && existsSync3(BREW_BUN_PATH) ? BREW_BUN_PATH : join4(bunBinDir, "bun");
|
|
2632
2682
|
return {
|
|
2633
2683
|
id: "current-machine",
|
|
2634
2684
|
hostname: overrides.hostname ?? currentHostname(),
|
|
@@ -2638,10 +2688,10 @@ function detectMachineContext(overrides = {}) {
|
|
|
2638
2688
|
created_at: "",
|
|
2639
2689
|
os_family: osFamily,
|
|
2640
2690
|
home_dir: homeDir,
|
|
2641
|
-
workspace_root: overrides.workspace_root ??
|
|
2691
|
+
workspace_root: overrides.workspace_root ?? join4(homeDir, osFamily === "macos" ? "Workspace" : "workspace"),
|
|
2642
2692
|
bun_bin_dir: bunBinDir,
|
|
2643
2693
|
bun_path: overrides.bun_path ?? defaultBunPath,
|
|
2644
|
-
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${
|
|
2694
|
+
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join4("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
|
|
2645
2695
|
};
|
|
2646
2696
|
}
|
|
2647
2697
|
function machineContextToVariables(machine) {
|
|
@@ -6906,113 +6956,113 @@ var init_types2 = __esm(() => {
|
|
|
6906
6956
|
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
|
|
6907
6957
|
var exports_external = {};
|
|
6908
6958
|
__export(exports_external, {
|
|
6909
|
-
|
|
6910
|
-
util: () => util,
|
|
6911
|
-
unknown: () => unknownType,
|
|
6912
|
-
union: () => unionType,
|
|
6913
|
-
undefined: () => undefinedType,
|
|
6914
|
-
tuple: () => tupleType,
|
|
6915
|
-
transformer: () => effectsType,
|
|
6916
|
-
symbol: () => symbolType,
|
|
6917
|
-
string: () => stringType,
|
|
6918
|
-
strictObject: () => strictObjectType,
|
|
6919
|
-
setErrorMap: () => setErrorMap,
|
|
6920
|
-
set: () => setType,
|
|
6921
|
-
record: () => recordType,
|
|
6922
|
-
quotelessJson: () => quotelessJson,
|
|
6923
|
-
promise: () => promiseType,
|
|
6924
|
-
preprocess: () => preprocessType,
|
|
6925
|
-
pipeline: () => pipelineType,
|
|
6926
|
-
ostring: () => ostring,
|
|
6927
|
-
optional: () => optionalType,
|
|
6928
|
-
onumber: () => onumber,
|
|
6929
|
-
oboolean: () => oboolean,
|
|
6930
|
-
objectUtil: () => objectUtil,
|
|
6931
|
-
object: () => objectType,
|
|
6932
|
-
number: () => numberType,
|
|
6933
|
-
nullable: () => nullableType,
|
|
6934
|
-
null: () => nullType,
|
|
6935
|
-
never: () => neverType,
|
|
6936
|
-
nativeEnum: () => nativeEnumType,
|
|
6937
|
-
nan: () => nanType,
|
|
6938
|
-
map: () => mapType,
|
|
6939
|
-
makeIssue: () => makeIssue,
|
|
6940
|
-
literal: () => literalType,
|
|
6941
|
-
lazy: () => lazyType,
|
|
6942
|
-
late: () => late,
|
|
6943
|
-
isValid: () => isValid,
|
|
6944
|
-
isDirty: () => isDirty,
|
|
6945
|
-
isAsync: () => isAsync,
|
|
6946
|
-
isAborted: () => isAborted,
|
|
6947
|
-
intersection: () => intersectionType,
|
|
6948
|
-
instanceof: () => instanceOfType,
|
|
6949
|
-
getParsedType: () => getParsedType,
|
|
6950
|
-
getErrorMap: () => getErrorMap,
|
|
6951
|
-
function: () => functionType,
|
|
6952
|
-
enum: () => enumType,
|
|
6953
|
-
effect: () => effectsType,
|
|
6954
|
-
discriminatedUnion: () => discriminatedUnionType,
|
|
6955
|
-
defaultErrorMap: () => en_default,
|
|
6956
|
-
datetimeRegex: () => datetimeRegex,
|
|
6957
|
-
date: () => dateType,
|
|
6958
|
-
custom: () => custom,
|
|
6959
|
-
coerce: () => coerce,
|
|
6960
|
-
boolean: () => booleanType,
|
|
6961
|
-
bigint: () => bigIntType,
|
|
6962
|
-
array: () => arrayType,
|
|
6963
|
-
any: () => anyType,
|
|
6964
|
-
addIssueToContext: () => addIssueToContext,
|
|
6965
|
-
ZodVoid: () => ZodVoid,
|
|
6966
|
-
ZodUnknown: () => ZodUnknown,
|
|
6967
|
-
ZodUnion: () => ZodUnion,
|
|
6968
|
-
ZodUndefined: () => ZodUndefined,
|
|
6969
|
-
ZodType: () => ZodType,
|
|
6970
|
-
ZodTuple: () => ZodTuple,
|
|
6971
|
-
ZodTransformer: () => ZodEffects,
|
|
6972
|
-
ZodSymbol: () => ZodSymbol,
|
|
6973
|
-
ZodString: () => ZodString,
|
|
6974
|
-
ZodSet: () => ZodSet,
|
|
6975
|
-
ZodSchema: () => ZodType,
|
|
6976
|
-
ZodRecord: () => ZodRecord,
|
|
6977
|
-
ZodReadonly: () => ZodReadonly,
|
|
6978
|
-
ZodPromise: () => ZodPromise,
|
|
6979
|
-
ZodPipeline: () => ZodPipeline,
|
|
6980
|
-
ZodParsedType: () => ZodParsedType,
|
|
6981
|
-
ZodOptional: () => ZodOptional,
|
|
6982
|
-
ZodObject: () => ZodObject,
|
|
6983
|
-
ZodNumber: () => ZodNumber,
|
|
6984
|
-
ZodNullable: () => ZodNullable,
|
|
6985
|
-
ZodNull: () => ZodNull,
|
|
6986
|
-
ZodNever: () => ZodNever,
|
|
6987
|
-
ZodNativeEnum: () => ZodNativeEnum,
|
|
6988
|
-
ZodNaN: () => ZodNaN,
|
|
6989
|
-
ZodMap: () => ZodMap,
|
|
6990
|
-
ZodLiteral: () => ZodLiteral,
|
|
6991
|
-
ZodLazy: () => ZodLazy,
|
|
6992
|
-
ZodIssueCode: () => ZodIssueCode,
|
|
6993
|
-
ZodIntersection: () => ZodIntersection,
|
|
6994
|
-
ZodFunction: () => ZodFunction,
|
|
6995
|
-
ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
|
|
6996
|
-
ZodError: () => ZodError,
|
|
6997
|
-
ZodEnum: () => ZodEnum,
|
|
6998
|
-
ZodEffects: () => ZodEffects,
|
|
6999
|
-
ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
|
|
7000
|
-
ZodDefault: () => ZodDefault,
|
|
7001
|
-
ZodDate: () => ZodDate,
|
|
7002
|
-
ZodCatch: () => ZodCatch,
|
|
7003
|
-
ZodBranded: () => ZodBranded,
|
|
7004
|
-
ZodBoolean: () => ZodBoolean,
|
|
7005
|
-
ZodBigInt: () => ZodBigInt,
|
|
7006
|
-
ZodArray: () => ZodArray,
|
|
7007
|
-
ZodAny: () => ZodAny,
|
|
7008
|
-
Schema: () => ZodType,
|
|
7009
|
-
ParseStatus: () => ParseStatus,
|
|
7010
|
-
OK: () => OK,
|
|
7011
|
-
NEVER: () => NEVER,
|
|
7012
|
-
INVALID: () => INVALID,
|
|
7013
|
-
EMPTY_PATH: () => EMPTY_PATH,
|
|
6959
|
+
BRAND: () => BRAND,
|
|
7014
6960
|
DIRTY: () => DIRTY,
|
|
7015
|
-
|
|
6961
|
+
EMPTY_PATH: () => EMPTY_PATH,
|
|
6962
|
+
INVALID: () => INVALID,
|
|
6963
|
+
NEVER: () => NEVER,
|
|
6964
|
+
OK: () => OK,
|
|
6965
|
+
ParseStatus: () => ParseStatus,
|
|
6966
|
+
Schema: () => ZodType,
|
|
6967
|
+
ZodAny: () => ZodAny,
|
|
6968
|
+
ZodArray: () => ZodArray,
|
|
6969
|
+
ZodBigInt: () => ZodBigInt,
|
|
6970
|
+
ZodBoolean: () => ZodBoolean,
|
|
6971
|
+
ZodBranded: () => ZodBranded,
|
|
6972
|
+
ZodCatch: () => ZodCatch,
|
|
6973
|
+
ZodDate: () => ZodDate,
|
|
6974
|
+
ZodDefault: () => ZodDefault,
|
|
6975
|
+
ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
|
|
6976
|
+
ZodEffects: () => ZodEffects,
|
|
6977
|
+
ZodEnum: () => ZodEnum,
|
|
6978
|
+
ZodError: () => ZodError,
|
|
6979
|
+
ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
|
|
6980
|
+
ZodFunction: () => ZodFunction,
|
|
6981
|
+
ZodIntersection: () => ZodIntersection,
|
|
6982
|
+
ZodIssueCode: () => ZodIssueCode,
|
|
6983
|
+
ZodLazy: () => ZodLazy,
|
|
6984
|
+
ZodLiteral: () => ZodLiteral,
|
|
6985
|
+
ZodMap: () => ZodMap,
|
|
6986
|
+
ZodNaN: () => ZodNaN,
|
|
6987
|
+
ZodNativeEnum: () => ZodNativeEnum,
|
|
6988
|
+
ZodNever: () => ZodNever,
|
|
6989
|
+
ZodNull: () => ZodNull,
|
|
6990
|
+
ZodNullable: () => ZodNullable,
|
|
6991
|
+
ZodNumber: () => ZodNumber,
|
|
6992
|
+
ZodObject: () => ZodObject,
|
|
6993
|
+
ZodOptional: () => ZodOptional,
|
|
6994
|
+
ZodParsedType: () => ZodParsedType,
|
|
6995
|
+
ZodPipeline: () => ZodPipeline,
|
|
6996
|
+
ZodPromise: () => ZodPromise,
|
|
6997
|
+
ZodReadonly: () => ZodReadonly,
|
|
6998
|
+
ZodRecord: () => ZodRecord,
|
|
6999
|
+
ZodSchema: () => ZodType,
|
|
7000
|
+
ZodSet: () => ZodSet,
|
|
7001
|
+
ZodString: () => ZodString,
|
|
7002
|
+
ZodSymbol: () => ZodSymbol,
|
|
7003
|
+
ZodTransformer: () => ZodEffects,
|
|
7004
|
+
ZodTuple: () => ZodTuple,
|
|
7005
|
+
ZodType: () => ZodType,
|
|
7006
|
+
ZodUndefined: () => ZodUndefined,
|
|
7007
|
+
ZodUnion: () => ZodUnion,
|
|
7008
|
+
ZodUnknown: () => ZodUnknown,
|
|
7009
|
+
ZodVoid: () => ZodVoid,
|
|
7010
|
+
addIssueToContext: () => addIssueToContext,
|
|
7011
|
+
any: () => anyType,
|
|
7012
|
+
array: () => arrayType,
|
|
7013
|
+
bigint: () => bigIntType,
|
|
7014
|
+
boolean: () => booleanType,
|
|
7015
|
+
coerce: () => coerce,
|
|
7016
|
+
custom: () => custom,
|
|
7017
|
+
date: () => dateType,
|
|
7018
|
+
datetimeRegex: () => datetimeRegex,
|
|
7019
|
+
defaultErrorMap: () => en_default,
|
|
7020
|
+
discriminatedUnion: () => discriminatedUnionType,
|
|
7021
|
+
effect: () => effectsType,
|
|
7022
|
+
enum: () => enumType,
|
|
7023
|
+
function: () => functionType,
|
|
7024
|
+
getErrorMap: () => getErrorMap,
|
|
7025
|
+
getParsedType: () => getParsedType,
|
|
7026
|
+
instanceof: () => instanceOfType,
|
|
7027
|
+
intersection: () => intersectionType,
|
|
7028
|
+
isAborted: () => isAborted,
|
|
7029
|
+
isAsync: () => isAsync,
|
|
7030
|
+
isDirty: () => isDirty,
|
|
7031
|
+
isValid: () => isValid,
|
|
7032
|
+
late: () => late,
|
|
7033
|
+
lazy: () => lazyType,
|
|
7034
|
+
literal: () => literalType,
|
|
7035
|
+
makeIssue: () => makeIssue,
|
|
7036
|
+
map: () => mapType,
|
|
7037
|
+
nan: () => nanType,
|
|
7038
|
+
nativeEnum: () => nativeEnumType,
|
|
7039
|
+
never: () => neverType,
|
|
7040
|
+
null: () => nullType,
|
|
7041
|
+
nullable: () => nullableType,
|
|
7042
|
+
number: () => numberType,
|
|
7043
|
+
object: () => objectType,
|
|
7044
|
+
objectUtil: () => objectUtil,
|
|
7045
|
+
oboolean: () => oboolean,
|
|
7046
|
+
onumber: () => onumber,
|
|
7047
|
+
optional: () => optionalType,
|
|
7048
|
+
ostring: () => ostring,
|
|
7049
|
+
pipeline: () => pipelineType,
|
|
7050
|
+
preprocess: () => preprocessType,
|
|
7051
|
+
promise: () => promiseType,
|
|
7052
|
+
quotelessJson: () => quotelessJson,
|
|
7053
|
+
record: () => recordType,
|
|
7054
|
+
set: () => setType,
|
|
7055
|
+
setErrorMap: () => setErrorMap,
|
|
7056
|
+
strictObject: () => strictObjectType,
|
|
7057
|
+
string: () => stringType,
|
|
7058
|
+
symbol: () => symbolType,
|
|
7059
|
+
transformer: () => effectsType,
|
|
7060
|
+
tuple: () => tupleType,
|
|
7061
|
+
undefined: () => undefinedType,
|
|
7062
|
+
union: () => unionType,
|
|
7063
|
+
unknown: () => unknownType,
|
|
7064
|
+
util: () => util,
|
|
7065
|
+
void: () => voidType
|
|
7016
7066
|
});
|
|
7017
7067
|
var init_external = __esm(() => {
|
|
7018
7068
|
init_errors();
|
|
@@ -7265,7 +7315,7 @@ import {
|
|
|
7265
7315
|
statSync,
|
|
7266
7316
|
writeFileSync
|
|
7267
7317
|
} from "fs";
|
|
7268
|
-
import { basename, dirname, isAbsolute, join as
|
|
7318
|
+
import { basename, dirname, isAbsolute, join as join5, parse, relative, resolve as resolve2 } from "path";
|
|
7269
7319
|
function managedObservationMaxBytes(relativePath) {
|
|
7270
7320
|
return SESSION_MANAGED_OUTPUT_PATHS.includes(relativePath) ? SESSION_MANAGED_OUTPUT_MAX_BYTES : FOREIGN_INPUT_MAX_BYTES;
|
|
7271
7321
|
}
|
|
@@ -7436,7 +7486,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7436
7486
|
if (currentMarkers.block.id !== cache.project_id || currentMarkers.block.revision !== cache.revision || currentMarkers.block.hash !== cache.hash) {
|
|
7437
7487
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider markers differ from the durable cache");
|
|
7438
7488
|
}
|
|
7439
|
-
const plannedIndexes = input.files.filter((file) => file.role === "index" &&
|
|
7489
|
+
const plannedIndexes = input.files.filter((file) => file.role === "index" && resolve2(file.path) === paths.target);
|
|
7440
7490
|
if (plannedIndexes.length !== 1) {
|
|
7441
7491
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "session renderer does not own the selected project-context provider target");
|
|
7442
7492
|
}
|
|
@@ -7494,7 +7544,7 @@ function withProjectContextSessionGuard(guard, action, options = {}) {
|
|
|
7494
7544
|
verify();
|
|
7495
7545
|
return action(null);
|
|
7496
7546
|
}
|
|
7497
|
-
const lockPath =
|
|
7547
|
+
const lockPath = resolve2(validated.workspace_root, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7498
7548
|
const lock = acquireWorkspaceLock(validated.workspace_root, lockPath);
|
|
7499
7549
|
try {
|
|
7500
7550
|
verify();
|
|
@@ -7520,7 +7570,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7520
7570
|
if (!isRecord(observed) || typeof observed.path !== "string") {
|
|
7521
7571
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains malformed hash metadata");
|
|
7522
7572
|
}
|
|
7523
|
-
const path =
|
|
7573
|
+
const path = resolve2(observed.path);
|
|
7524
7574
|
if (!allowedPaths.has(path) || observedPaths.has(path)) {
|
|
7525
7575
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains an unexpected or duplicate path");
|
|
7526
7576
|
}
|
|
@@ -7542,7 +7592,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7542
7592
|
function applyProjectContext(options) {
|
|
7543
7593
|
const workspaceRoot = assertSafeWorkspaceRoot(options.workspace_root);
|
|
7544
7594
|
const now3 = options.now ?? new Date;
|
|
7545
|
-
const lockPath =
|
|
7595
|
+
const lockPath = resolve2(workspaceRoot, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7546
7596
|
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);
|
|
7547
7597
|
try {
|
|
7548
7598
|
const resolved = resolveBundleForApply(options, workspaceRoot, now3);
|
|
@@ -7689,7 +7739,7 @@ function resolveBundleForApply(options, workspaceRoot, now3) {
|
|
|
7689
7739
|
if (!options.expected_project_id) {
|
|
7690
7740
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_ID_REQUIRED", "expected_project_id is required for stale-cache fallback");
|
|
7691
7741
|
}
|
|
7692
|
-
const cachePath =
|
|
7742
|
+
const cachePath = resolve2(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
7693
7743
|
const cache = readProjectContextCache(cachePath, workspaceRoot);
|
|
7694
7744
|
if (!cache)
|
|
7695
7745
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_MISSING", "no last-known-good project context cache exists");
|
|
@@ -8045,7 +8095,7 @@ function buildManifest(plan, now3) {
|
|
|
8045
8095
|
function buildSessionCompatibilityManifest(plan, now3) {
|
|
8046
8096
|
const paths = runtimePaths(plan.workspace_root, plan.runtime);
|
|
8047
8097
|
const tool = manifestTool(plan.runtime);
|
|
8048
|
-
const targetHome = plan.runtime === "codewith" ?
|
|
8098
|
+
const targetHome = plan.runtime === "codewith" ? resolve2(plan.workspace_root, ".codewith") : plan.workspace_root;
|
|
8049
8099
|
const targetRelativePath = sessionTargetRelativePath(plan.runtime);
|
|
8050
8100
|
const existing = existsSync4(paths.sessionManifest) ? readSessionManifestRecord(paths.sessionManifest, plan.workspace_root) : {
|
|
8051
8101
|
schema: SESSION_RENDER_SCHEMA,
|
|
@@ -8067,7 +8117,7 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8067
8117
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest is malformed or incompatible");
|
|
8068
8118
|
}
|
|
8069
8119
|
const existingTargetHome = safeLegacyMetadataString(existing["targetHome"], null);
|
|
8070
|
-
if (existingTargetHome !== null &&
|
|
8120
|
+
if (existingTargetHome !== null && resolve2(existingTargetHome) !== targetHome) {
|
|
8071
8121
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest targets a different workspace");
|
|
8072
8122
|
}
|
|
8073
8123
|
const sources = sanitizeLegacySources(existing["sources"]).filter((source) => source["id"] !== "project-context-bundle");
|
|
@@ -8353,9 +8403,9 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8353
8403
|
const previous = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8354
8404
|
if (!previous || previous.projectContext.revision === plan.bundle.revision && previous.projectContext.hash === plan.bundle.hash)
|
|
8355
8405
|
return null;
|
|
8356
|
-
const snapshotDir =
|
|
8406
|
+
const snapshotDir = resolve2(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8357
8407
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
8358
|
-
const snapshotPath =
|
|
8408
|
+
const snapshotPath = resolve2(snapshotDir, `${safeFilename(previous.projectContext.revision)}-${previous.projectContext.hash.slice(-12)}.json`);
|
|
8359
8409
|
const snapshot = {
|
|
8360
8410
|
schema: "hasna.configs.session-render-snapshot/v1",
|
|
8361
8411
|
kind: "project-context-metadata",
|
|
@@ -8371,8 +8421,8 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8371
8421
|
return snapshotPath;
|
|
8372
8422
|
}
|
|
8373
8423
|
function metadataSnapshotMatchesManifest(plan, manifest) {
|
|
8374
|
-
const snapshotDir =
|
|
8375
|
-
const snapshotPath =
|
|
8424
|
+
const snapshotDir = resolve2(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8425
|
+
const snapshotPath = resolve2(snapshotDir, `${safeFilename(manifest.projectContext.revision)}-${manifest.projectContext.hash.slice(-12)}.json`);
|
|
8376
8426
|
if (!existsSync4(snapshotPath))
|
|
8377
8427
|
return false;
|
|
8378
8428
|
const record = readJsonRecord(snapshotPath, plan.workspace_root);
|
|
@@ -8421,10 +8471,10 @@ function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
|
8421
8471
|
sha256: nextHash
|
|
8422
8472
|
};
|
|
8423
8473
|
});
|
|
8424
|
-
const snapshotDir =
|
|
8474
|
+
const snapshotDir = resolve2(plan.workspace_root, ...SESSION_RENDER_SNAPSHOT_RELATIVE_DIR.split("/"));
|
|
8425
8475
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
8426
8476
|
const timestamp = now3.toISOString().replace(/[:.]/g, "-");
|
|
8427
|
-
const snapshotPath =
|
|
8477
|
+
const snapshotPath = resolve2(snapshotDir, `${timestamp}-${randomUUID5()}.json`);
|
|
8428
8478
|
const snapshot = {
|
|
8429
8479
|
schema: "hasna.configs.session-render-snapshot/v2",
|
|
8430
8480
|
createdAt: now3.toISOString(),
|
|
@@ -8489,7 +8539,7 @@ function readSessionManifestRecord(path, workspaceRoot) {
|
|
|
8489
8539
|
}
|
|
8490
8540
|
}
|
|
8491
8541
|
function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash, afterExchange, atomicExchangeUnavailable = false, beforeInstall, portableCreateOnly = false, maxObservedBytes, allowPortableReplacement = false) {
|
|
8492
|
-
const dir =
|
|
8542
|
+
const dir = resolve2(path, "..");
|
|
8493
8543
|
ensureSafeDirectory(dir, workspaceRoot, 448);
|
|
8494
8544
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8495
8545
|
const anchoredOps = portableCreateOnly ? null : resolveAnchoredFsOps();
|
|
@@ -8506,7 +8556,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8506
8556
|
const previous = anchoredFileObservation(directory, targetName);
|
|
8507
8557
|
const previousMode = previous?.mode ?? defaultMode;
|
|
8508
8558
|
const tempName = `.project-context-${randomUUID5()}.tmp`;
|
|
8509
|
-
const tempPath =
|
|
8559
|
+
const tempPath = join5(dir, tempName);
|
|
8510
8560
|
let fd = null;
|
|
8511
8561
|
let preserveTemp = false;
|
|
8512
8562
|
let directoryChanged = false;
|
|
@@ -8637,7 +8687,7 @@ function atomicWritePortable(path, content, workspaceRoot, defaultMode, expected
|
|
|
8637
8687
|
}
|
|
8638
8688
|
const dir = dirname(path);
|
|
8639
8689
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8640
|
-
const tempPath =
|
|
8690
|
+
const tempPath = join5(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8641
8691
|
let fd = null;
|
|
8642
8692
|
let tempIdentity = null;
|
|
8643
8693
|
try {
|
|
@@ -8694,7 +8744,7 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8694
8744
|
}
|
|
8695
8745
|
const dir = dirname(path);
|
|
8696
8746
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8697
|
-
const tempPath =
|
|
8747
|
+
const tempPath = join5(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8698
8748
|
const desiredHash = sha2562(content);
|
|
8699
8749
|
let fd = null;
|
|
8700
8750
|
let tempIdentity = null;
|
|
@@ -8763,11 +8813,11 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
8763
8813
|
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
8764
8814
|
}
|
|
8765
8815
|
function writeProjectContextCoordinatedFile(input) {
|
|
8766
|
-
atomicWriteFile(
|
|
8816
|
+
atomicWriteFile(resolve2(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);
|
|
8767
8817
|
}
|
|
8768
8818
|
function removeProjectContextCoordinatedFile(input) {
|
|
8769
8819
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
8770
|
-
const path =
|
|
8820
|
+
const path = resolve2(input.path);
|
|
8771
8821
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8772
8822
|
const dir = dirname(path);
|
|
8773
8823
|
const anchoredOps = input.force_portable_file_ops ? null : resolveAnchoredFsOps();
|
|
@@ -8793,7 +8843,7 @@ function removeProjectContextCoordinatedFile(input) {
|
|
|
8793
8843
|
throw new ProjectContextHashRace(`managed path changed during deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
8794
8844
|
}
|
|
8795
8845
|
displaced = true;
|
|
8796
|
-
input.test_hooks?.after_displace?.(
|
|
8846
|
+
input.test_hooks?.after_displace?.(join5(dir, displacedName));
|
|
8797
8847
|
const moved = anchoredFileObservation(directory, displacedName);
|
|
8798
8848
|
if (!moved || moved.dev !== observed.dev || moved.ino !== observed.ino || moved.hash !== input.expected_hash || anchoredFileObservation(directory, targetName) !== null) {
|
|
8799
8849
|
throw new ProjectContextHashRace(`managed path changed during deletion validation: ${relativePosix(workspaceRoot, path)}`);
|
|
@@ -8839,7 +8889,7 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
8839
8889
|
}
|
|
8840
8890
|
const dir = dirname(path);
|
|
8841
8891
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8842
|
-
const displacedPath =
|
|
8892
|
+
const displacedPath = join5(dir, `.project-context-delete-${randomUUID5()}.tmp`);
|
|
8843
8893
|
let displaced = false;
|
|
8844
8894
|
try {
|
|
8845
8895
|
assertManagedDirectoryStable(dir, workspaceRoot, directoryIdentity);
|
|
@@ -8910,7 +8960,7 @@ function anchoredOpenExclusive(directory, name, mode) {
|
|
|
8910
8960
|
const requestedMode = mode & 4095;
|
|
8911
8961
|
let fd;
|
|
8912
8962
|
try {
|
|
8913
|
-
fd = openSync(
|
|
8963
|
+
fd = openSync(join5(directory.path, name), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, requestedMode);
|
|
8914
8964
|
} catch {
|
|
8915
8965
|
throw new ProjectContextHashRace(`could not create prepared managed file in ${relativePosix(directory.workspaceRoot, directory.path)}`);
|
|
8916
8966
|
}
|
|
@@ -8953,7 +9003,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
8953
9003
|
const stat = fstatSync(fd);
|
|
8954
9004
|
if (!stat.isFile())
|
|
8955
9005
|
throw new ProjectContextHashRace("managed output is not a regular file");
|
|
8956
|
-
const relativePath = relativePosix(directory.workspaceRoot,
|
|
9006
|
+
const relativePath = relativePosix(directory.workspaceRoot, join5(directory.path, name));
|
|
8957
9007
|
const maxBytes = directory.maxObservedBytes === undefined ? managedObservationMaxBytes(relativePath) : directory.maxObservedBytes;
|
|
8958
9008
|
if (maxBytes !== null && stat.size > maxBytes) {
|
|
8959
9009
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePath}`);
|
|
@@ -8979,7 +9029,7 @@ function anchoredPreparedObservation(directory, name, path, stage) {
|
|
|
8979
9029
|
return observed;
|
|
8980
9030
|
}
|
|
8981
9031
|
function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
8982
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9032
|
+
assertNoSymlinkSegments(workspaceRoot, join5(path, ".project-context-directory-guard"));
|
|
8983
9033
|
let stat;
|
|
8984
9034
|
try {
|
|
8985
9035
|
stat = lstatSync(path);
|
|
@@ -8992,7 +9042,7 @@ function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
|
8992
9042
|
return { dev: stat.dev, ino: stat.ino };
|
|
8993
9043
|
}
|
|
8994
9044
|
function assertManagedDirectoryStable(path, workspaceRoot, expected) {
|
|
8995
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9045
|
+
assertNoSymlinkSegments(workspaceRoot, join5(path, ".project-context-directory-guard"));
|
|
8996
9046
|
let current;
|
|
8997
9047
|
try {
|
|
8998
9048
|
current = lstatSync(path);
|
|
@@ -9125,10 +9175,10 @@ function resolveAnchoredFsOps() {
|
|
|
9125
9175
|
return null;
|
|
9126
9176
|
}
|
|
9127
9177
|
function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRemove, processStartIdentityLookup = processStartIdentity) {
|
|
9128
|
-
const lockDirectory =
|
|
9178
|
+
const lockDirectory = resolve2(lockPath, "..");
|
|
9129
9179
|
ensureSafeDirectory(lockDirectory, workspaceRoot, 448);
|
|
9130
9180
|
assertNoSymlinkSegments(workspaceRoot, lockPath);
|
|
9131
|
-
const tempPath =
|
|
9181
|
+
const tempPath = join5(lockDirectory, `.project-context-lock-${randomUUID5()}.tmp`);
|
|
9132
9182
|
let fd = null;
|
|
9133
9183
|
let openedIdentity = null;
|
|
9134
9184
|
let openedContentHash = null;
|
|
@@ -9200,7 +9250,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
9200
9250
|
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
9201
9251
|
return;
|
|
9202
9252
|
rmSync2(lockPath);
|
|
9203
|
-
fsyncDirectory(
|
|
9253
|
+
fsyncDirectory(resolve2(lockPath, ".."));
|
|
9204
9254
|
} catch {}
|
|
9205
9255
|
}
|
|
9206
9256
|
function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentityLookup = processStartIdentity) {
|
|
@@ -9279,7 +9329,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9279
9329
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace lock changed during stale-lock takeover and could not be restored safely");
|
|
9280
9330
|
}
|
|
9281
9331
|
rmSync2(candidatePath);
|
|
9282
|
-
fsyncDirectory(
|
|
9332
|
+
fsyncDirectory(resolve2(lockPath, ".."));
|
|
9283
9333
|
exchanged = false;
|
|
9284
9334
|
return true;
|
|
9285
9335
|
} catch (error) {
|
|
@@ -9358,8 +9408,8 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9358
9408
|
}
|
|
9359
9409
|
return;
|
|
9360
9410
|
}
|
|
9361
|
-
const lockDirectory =
|
|
9362
|
-
const releasePath =
|
|
9411
|
+
const lockDirectory = resolve2(lockPath, "..");
|
|
9412
|
+
const releasePath = join5(lockDirectory, `.project-context-release-${randomUUID5()}.tmp`);
|
|
9363
9413
|
let releaseFd = null;
|
|
9364
9414
|
let releaseIdentity = null;
|
|
9365
9415
|
let releaseHash = null;
|
|
@@ -9439,7 +9489,7 @@ function ensureSafeDirectory(path, workspaceRoot, mode) {
|
|
|
9439
9489
|
const segments = rel.split(/[\\/]+/).filter(Boolean);
|
|
9440
9490
|
let current = workspaceRoot;
|
|
9441
9491
|
for (const segment of segments) {
|
|
9442
|
-
current =
|
|
9492
|
+
current = join5(current, segment);
|
|
9443
9493
|
if (existsSync4(current)) {
|
|
9444
9494
|
if (lstatSync(current).isSymbolicLink())
|
|
9445
9495
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
@@ -9447,7 +9497,7 @@ function ensureSafeDirectory(path, workspaceRoot, mode) {
|
|
|
9447
9497
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", `managed path is not a directory: ${current}`);
|
|
9448
9498
|
} else {
|
|
9449
9499
|
mkdirSync2(current, { mode });
|
|
9450
|
-
fsyncDirectory(
|
|
9500
|
+
fsyncDirectory(resolve2(current, ".."));
|
|
9451
9501
|
}
|
|
9452
9502
|
}
|
|
9453
9503
|
}
|
|
@@ -9503,11 +9553,11 @@ function scanGeneratedContent(content) {
|
|
|
9503
9553
|
function runtimePaths(workspaceRoot, runtime) {
|
|
9504
9554
|
const relativeTarget = runtime === "claude" ? "CLAUDE.md" : runtime === "codewith" ? ".codewith/CODEWITH.md" : "AGENTS.md";
|
|
9505
9555
|
return {
|
|
9506
|
-
target:
|
|
9507
|
-
fragment:
|
|
9508
|
-
manifest:
|
|
9509
|
-
cache:
|
|
9510
|
-
sessionManifest: runtime === "codewith" ?
|
|
9556
|
+
target: resolve2(workspaceRoot, ...relativeTarget.split("/")),
|
|
9557
|
+
fragment: resolve2(workspaceRoot, ...PROJECT_CONTEXT_FRAGMENT_PATH.split("/")),
|
|
9558
|
+
manifest: resolve2(workspaceRoot, ...PROJECT_CONTEXT_MANIFEST_PATH.split("/")),
|
|
9559
|
+
cache: resolve2(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/")),
|
|
9560
|
+
sessionManifest: runtime === "codewith" ? resolve2(workspaceRoot, ".codewith", ".hasna", "session-render-manifest.json") : resolve2(workspaceRoot, ".hasna", "session-render-manifest.json")
|
|
9511
9561
|
};
|
|
9512
9562
|
}
|
|
9513
9563
|
function projectContextSessionGuardPaths(paths, runtime) {
|
|
@@ -9517,7 +9567,7 @@ function projectContextSessionGuardPaths(paths, runtime) {
|
|
|
9517
9567
|
paths.fragment,
|
|
9518
9568
|
paths.target,
|
|
9519
9569
|
paths.sessionManifest,
|
|
9520
|
-
...runtime === "codewith" ? [
|
|
9570
|
+
...runtime === "codewith" ? [resolve2(paths.target, "..", "CODEWITH.override.md")] : []
|
|
9521
9571
|
];
|
|
9522
9572
|
}
|
|
9523
9573
|
function sessionTargetRelativePath(runtime) {
|
|
@@ -9537,12 +9587,12 @@ function projectContextRuntimeForSessionTool(tool) {
|
|
|
9537
9587
|
return null;
|
|
9538
9588
|
}
|
|
9539
9589
|
function projectContextWorkspaceForSession(input, runtime) {
|
|
9540
|
-
const targetHome =
|
|
9590
|
+
const targetHome = resolve2(input.target_home);
|
|
9541
9591
|
if (runtime === "codewith") {
|
|
9542
9592
|
const workspaceRoot = basename(targetHome) === ".codewith" ? dirname(targetHome) : null;
|
|
9543
9593
|
if (!workspaceRoot)
|
|
9544
9594
|
return null;
|
|
9545
|
-
if (input.project_root &&
|
|
9595
|
+
if (input.project_root && resolve2(input.project_root) !== workspaceRoot) {
|
|
9546
9596
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "Codewith project_root must be the parent workspace of target_home");
|
|
9547
9597
|
}
|
|
9548
9598
|
if (!existsSync4(workspaceRoot) || !lstatSync(workspaceRoot).isDirectory())
|
|
@@ -9556,7 +9606,7 @@ function projectContextWorkspaceForSession(input, runtime) {
|
|
|
9556
9606
|
function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
9557
9607
|
if (runtime !== "codewith")
|
|
9558
9608
|
return;
|
|
9559
|
-
const override =
|
|
9609
|
+
const override = resolve2(workspaceRoot, ".codewith", "CODEWITH.override.md");
|
|
9560
9610
|
if (!existsSync4(override))
|
|
9561
9611
|
return;
|
|
9562
9612
|
assertNoSymlinkSegments(workspaceRoot, override);
|
|
@@ -9567,7 +9617,7 @@ function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
|
9567
9617
|
function assertSafeWorkspaceRoot(path) {
|
|
9568
9618
|
if (!isAbsolute(path))
|
|
9569
9619
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be absolute");
|
|
9570
|
-
const normalized =
|
|
9620
|
+
const normalized = resolve2(path);
|
|
9571
9621
|
if (normalized === parse(normalized).root)
|
|
9572
9622
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root cannot be the filesystem root");
|
|
9573
9623
|
if (!existsSync4(normalized) || !lstatSync(normalized).isDirectory())
|
|
@@ -9584,17 +9634,17 @@ function assertNoSymlinkSegments(root, target) {
|
|
|
9584
9634
|
}
|
|
9585
9635
|
let current = root;
|
|
9586
9636
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
9587
|
-
current =
|
|
9637
|
+
current = join5(current, segment);
|
|
9588
9638
|
if (existsSync4(current) && lstatSync(current).isSymbolicLink()) {
|
|
9589
9639
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
9590
9640
|
}
|
|
9591
9641
|
}
|
|
9592
9642
|
}
|
|
9593
9643
|
function assertNoSymlinkAncestors(path) {
|
|
9594
|
-
const normalized =
|
|
9644
|
+
const normalized = resolve2(path);
|
|
9595
9645
|
let current = parse(normalized).root;
|
|
9596
9646
|
for (const segment of relative(current, normalized).split(/[\\/]+/).filter(Boolean)) {
|
|
9597
|
-
current =
|
|
9647
|
+
current = join5(current, segment);
|
|
9598
9648
|
if (!existsSync4(current))
|
|
9599
9649
|
return;
|
|
9600
9650
|
if (lstatSync(current).isSymbolicLink())
|
|
@@ -9630,10 +9680,10 @@ function fragmentMatchesBundle(path, bundle, workspaceRoot) {
|
|
|
9630
9680
|
}
|
|
9631
9681
|
function durableSourcePath(path, workspaceRoot) {
|
|
9632
9682
|
if (!path || path.startsWith("/dev/fd/"))
|
|
9633
|
-
return
|
|
9634
|
-
const normalized = isAbsolute(path) ?
|
|
9683
|
+
return resolve2(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9684
|
+
const normalized = isAbsolute(path) ? resolve2(path) : resolve2(workspaceRoot, path);
|
|
9635
9685
|
if (normalized.startsWith("/dev/fd/"))
|
|
9636
|
-
return
|
|
9686
|
+
return resolve2(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9637
9687
|
return normalized;
|
|
9638
9688
|
}
|
|
9639
9689
|
function compareRevisions(incoming, previous) {
|
|
@@ -10193,7 +10243,7 @@ function compareProviderVersions(left, right) {
|
|
|
10193
10243
|
|
|
10194
10244
|
// src/lib/asset-plan.ts
|
|
10195
10245
|
import { createHash as createHash3 } from "crypto";
|
|
10196
|
-
import { isAbsolute as isAbsolute2, posix, resolve as
|
|
10246
|
+
import { isAbsolute as isAbsolute2, posix, resolve as resolve3 } from "path";
|
|
10197
10247
|
function assetCapability(provider, surface, kind, support, strategies, note, providerVersionRange = "*") {
|
|
10198
10248
|
return Object.freeze({
|
|
10199
10249
|
schema: ASSET_CAPABILITY_SCHEMA,
|
|
@@ -10400,8 +10450,8 @@ function resolveAssetDestination(item, roots) {
|
|
|
10400
10450
|
if (!isAbsolute2(root))
|
|
10401
10451
|
throw new Error(`Asset ${item.assetKey} destination root must be absolute.`);
|
|
10402
10452
|
const relativePath = safeRelativePath(item.destination.relativePath);
|
|
10403
|
-
const target =
|
|
10404
|
-
const normalizedRoot =
|
|
10453
|
+
const target = resolve3(root, ...relativePath.split("/"));
|
|
10454
|
+
const normalizedRoot = resolve3(root);
|
|
10405
10455
|
if (target === normalizedRoot)
|
|
10406
10456
|
throw new Error(`Asset ${item.assetKey} destination cannot replace its root.`);
|
|
10407
10457
|
if (!target.startsWith(`${normalizedRoot}/`))
|
|
@@ -10581,23 +10631,19 @@ var init_asset_plan = __esm(() => {
|
|
|
10581
10631
|
// src/lib/cursor-authority.ts
|
|
10582
10632
|
import { createHash as createHash4 } from "crypto";
|
|
10583
10633
|
import { lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
|
|
10584
|
-
import { homedir as
|
|
10585
|
-
import { join as
|
|
10634
|
+
import { homedir as homedir4 } from "os";
|
|
10635
|
+
import { join as join6, resolve as resolve4 } from "path";
|
|
10586
10636
|
function sha2564(content) {
|
|
10587
10637
|
return createHash4("sha256").update(content).digest("hex");
|
|
10588
10638
|
}
|
|
10589
10639
|
function homeDir() {
|
|
10590
|
-
return process.env["HOME"] ||
|
|
10640
|
+
return process.env["HOME"] || homedir4();
|
|
10591
10641
|
}
|
|
10592
|
-
function markerPayload(content, markerLine) {
|
|
10593
|
-
const
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
if (content.startsWith(markerLine))
|
|
10598
|
-
return content.slice(markerLine.length).replace(/^\n/, "");
|
|
10599
|
-
return content.replace(`${markerLine}
|
|
10600
|
-
`, "").replace(markerLine, "");
|
|
10642
|
+
function markerPayload(content, markerLine, markerIndex) {
|
|
10643
|
+
const index = markerIndex ?? content.indexOf(markerLine);
|
|
10644
|
+
if (index < 0)
|
|
10645
|
+
return content;
|
|
10646
|
+
return content.slice(0, index) + content.slice(index + markerLine.length).replace(/^\n/, "");
|
|
10601
10647
|
}
|
|
10602
10648
|
function baseObservation(path) {
|
|
10603
10649
|
return {
|
|
@@ -10607,12 +10653,12 @@ function baseObservation(path) {
|
|
|
10607
10653
|
};
|
|
10608
10654
|
}
|
|
10609
10655
|
function observeCursorGlobalAuthority(options = {}) {
|
|
10610
|
-
const authorityPath =
|
|
10656
|
+
const authorityPath = resolve4(join6(options.home ?? homeDir(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10611
10657
|
const readFile2 = options.readFile ?? ((path) => readFileSync2(path, "utf8"));
|
|
10612
10658
|
return observeCursorGlobalAuthorityPath(authorityPath, readFile2);
|
|
10613
10659
|
}
|
|
10614
10660
|
function observeCursorGlobalAuthorityAtPath(authorityPath) {
|
|
10615
|
-
return observeCursorGlobalAuthorityPath(
|
|
10661
|
+
return observeCursorGlobalAuthorityPath(resolve4(authorityPath), (path) => readFileSync2(path, "utf8"));
|
|
10616
10662
|
}
|
|
10617
10663
|
function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
10618
10664
|
const base = baseObservation(authorityPath);
|
|
@@ -10724,7 +10770,7 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
|
10724
10770
|
};
|
|
10725
10771
|
}
|
|
10726
10772
|
const markerLine = markerMatch[0];
|
|
10727
|
-
const payloadSha256 = sha2564(markerPayload(content, markerLine));
|
|
10773
|
+
const payloadSha256 = sha2564(markerPayload(content, markerLine, markerMatch.index ?? -1));
|
|
10728
10774
|
if (payloadSha256 !== markerSha256) {
|
|
10729
10775
|
return {
|
|
10730
10776
|
...base,
|
|
@@ -10741,53 +10787,147 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
|
10741
10787
|
}
|
|
10742
10788
|
};
|
|
10743
10789
|
}
|
|
10744
|
-
return {
|
|
10745
|
-
...base,
|
|
10746
|
-
fileType,
|
|
10747
|
-
status: "
|
|
10748
|
-
sha256: contentSha256,
|
|
10790
|
+
return {
|
|
10791
|
+
...base,
|
|
10792
|
+
fileType,
|
|
10793
|
+
status: "managed",
|
|
10794
|
+
sha256: contentSha256,
|
|
10795
|
+
markers,
|
|
10796
|
+
markerSha256,
|
|
10797
|
+
provenance: {
|
|
10798
|
+
source: "filesystem",
|
|
10799
|
+
authority: "managed",
|
|
10800
|
+
observedPath: authorityPath,
|
|
10801
|
+
detection: "managed-marker"
|
|
10802
|
+
}
|
|
10803
|
+
};
|
|
10804
|
+
}
|
|
10805
|
+
function isCursorGlobalAuthorityPath(path) {
|
|
10806
|
+
return resolve4(path) === resolve4(join6(homeDir(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10807
|
+
}
|
|
10808
|
+
function stampCursorGlobalAuthorityMarker(content) {
|
|
10809
|
+
if (CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN.test(content))
|
|
10810
|
+
return content;
|
|
10811
|
+
const digest = sha2564(content);
|
|
10812
|
+
const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
|
|
10813
|
+
const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
|
|
10814
|
+
if (frontmatter) {
|
|
10815
|
+
return `${frontmatter}${markerLine}
|
|
10816
|
+
${content.slice(frontmatter.length)}`;
|
|
10817
|
+
}
|
|
10818
|
+
return `${markerLine}
|
|
10819
|
+
${content}`;
|
|
10820
|
+
}
|
|
10821
|
+
function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthority()) {
|
|
10822
|
+
if (observation.status === "absent" || observation.status === "managed")
|
|
10823
|
+
return [];
|
|
10824
|
+
const detection = observation.provenance.detection;
|
|
10825
|
+
const invalid = observation.status === "invalid";
|
|
10826
|
+
return [{
|
|
10827
|
+
tool: "cursor",
|
|
10828
|
+
relativePath: CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH,
|
|
10829
|
+
path: observation.path,
|
|
10830
|
+
kind: invalid ? "invalid-unmanaged-authority" : "unknown-unmanaged-authority",
|
|
10831
|
+
sha256: observation.sha256,
|
|
10832
|
+
markers: observation.markers,
|
|
10833
|
+
provenance: {
|
|
10834
|
+
source: "filesystem",
|
|
10835
|
+
authority: "unmanaged",
|
|
10836
|
+
observedPath: observation.path,
|
|
10837
|
+
detection
|
|
10838
|
+
},
|
|
10839
|
+
reason: invalid ? `Cursor fixed global authority ${CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH} is not a verifiable regular managed file; refusing to render project rules.` : `Cursor fixed global authority ${CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH} is unmanaged; refusing to guess whether it conflicts with the managed project render.`
|
|
10840
|
+
}];
|
|
10841
|
+
}
|
|
10842
|
+
var CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH = ".cursor/rules/hasna-global.mdc", CURSOR_GLOBAL_AUTHORITY_MAX_BYTES, CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER = "Managed by @hasna/configs cursor global authority", CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN, CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN;
|
|
10843
|
+
var init_cursor_authority = __esm(() => {
|
|
10844
|
+
CURSOR_GLOBAL_AUTHORITY_MAX_BYTES = 256 * 1024;
|
|
10845
|
+
CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN = /^<!-- Managed by @hasna\/configs cursor global authority hash=(sha256:[a-f0-9]{64}) -->$/m;
|
|
10846
|
+
CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN = /^---\n[\s\S]*?\n---(?:\n|$)/;
|
|
10847
|
+
});
|
|
10848
|
+
|
|
10849
|
+
// src/lib/session-authority.ts
|
|
10850
|
+
import { createHash as createHash5 } from "crypto";
|
|
10851
|
+
import { lstatSync as lstatSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
10852
|
+
import { join as join7, resolve as resolve5 } from "path";
|
|
10853
|
+
function sha2565(content) {
|
|
10854
|
+
return createHash5("sha256").update(content).digest("hex");
|
|
10855
|
+
}
|
|
10856
|
+
function detectClaudeAuthorityConflicts(targetHome) {
|
|
10857
|
+
const authorityPath = resolve5(join7(targetHome, CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH));
|
|
10858
|
+
let stat;
|
|
10859
|
+
try {
|
|
10860
|
+
stat = lstatSync3(authorityPath);
|
|
10861
|
+
} catch {
|
|
10862
|
+
return [];
|
|
10863
|
+
}
|
|
10864
|
+
const provenanceBase = {
|
|
10865
|
+
tool: "claude",
|
|
10866
|
+
relativePath: CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH,
|
|
10867
|
+
path: authorityPath
|
|
10868
|
+
};
|
|
10869
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
10870
|
+
return [{
|
|
10871
|
+
...provenanceBase,
|
|
10872
|
+
kind: "invalid-unmanaged-authority",
|
|
10873
|
+
sha256: null,
|
|
10874
|
+
markers: [],
|
|
10875
|
+
provenance: {
|
|
10876
|
+
source: "filesystem",
|
|
10877
|
+
authority: "unmanaged",
|
|
10878
|
+
observedPath: authorityPath,
|
|
10879
|
+
detection: "non-regular-file"
|
|
10880
|
+
},
|
|
10881
|
+
reason: "Claude target contains unmanaged AGENTS.md that is not a regular file; authority cannot be verified safely."
|
|
10882
|
+
}];
|
|
10883
|
+
}
|
|
10884
|
+
if (statSync2(authorityPath).size > CLAUDE_LEGACY_AUTHORITY_MAX_BYTES) {
|
|
10885
|
+
return [{
|
|
10886
|
+
...provenanceBase,
|
|
10887
|
+
kind: "invalid-unmanaged-authority",
|
|
10888
|
+
sha256: null,
|
|
10889
|
+
markers: [],
|
|
10890
|
+
provenance: {
|
|
10891
|
+
source: "filesystem",
|
|
10892
|
+
authority: "unmanaged",
|
|
10893
|
+
observedPath: authorityPath,
|
|
10894
|
+
detection: "oversized-file"
|
|
10895
|
+
},
|
|
10896
|
+
reason: `Claude target contains unmanaged AGENTS.md larger than ${CLAUDE_LEGACY_AUTHORITY_MAX_BYTES} bytes; authority cannot be classified safely.`
|
|
10897
|
+
}];
|
|
10898
|
+
}
|
|
10899
|
+
const content = readFileSync3(authorityPath, "utf8");
|
|
10900
|
+
const markers = CLAUDE_LEGACY_MARKERS.filter((marker) => marker.pattern.test(content)).map((marker) => marker.id);
|
|
10901
|
+
const knownLegacy = markers.includes("no-worktrees-heading") && markers.includes("no-worktrees-directive");
|
|
10902
|
+
return [{
|
|
10903
|
+
...provenanceBase,
|
|
10904
|
+
kind: knownLegacy ? "known-legacy-no-worktree" : "unknown-unmanaged-authority",
|
|
10905
|
+
sha256: sha2565(content),
|
|
10749
10906
|
markers,
|
|
10750
|
-
markerSha256,
|
|
10751
10907
|
provenance: {
|
|
10752
10908
|
source: "filesystem",
|
|
10753
10909
|
authority: "unmanaged",
|
|
10754
10910
|
observedPath: authorityPath,
|
|
10755
|
-
detection: "unknown-content"
|
|
10756
|
-
}
|
|
10757
|
-
};
|
|
10758
|
-
}
|
|
10759
|
-
function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthority()) {
|
|
10760
|
-
if (observation.status === "absent" || observation.status === "managed")
|
|
10761
|
-
return [];
|
|
10762
|
-
const detection = observation.provenance.detection;
|
|
10763
|
-
const invalid = observation.status === "invalid";
|
|
10764
|
-
return [{
|
|
10765
|
-
tool: "cursor",
|
|
10766
|
-
relativePath: CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH,
|
|
10767
|
-
path: observation.path,
|
|
10768
|
-
kind: invalid ? "invalid-unmanaged-authority" : "unknown-unmanaged-authority",
|
|
10769
|
-
sha256: observation.sha256,
|
|
10770
|
-
markers: observation.markers,
|
|
10771
|
-
provenance: {
|
|
10772
|
-
source: "filesystem",
|
|
10773
|
-
authority: "unmanaged",
|
|
10774
|
-
observedPath: observation.path,
|
|
10775
|
-
detection
|
|
10911
|
+
detection: knownLegacy ? "known-legacy-markers" : "unknown-content"
|
|
10776
10912
|
},
|
|
10777
|
-
reason:
|
|
10913
|
+
reason: knownLegacy ? "Claude target contains unmanaged legacy AGENTS.md with no-worktree directives; migrate or remove it through an owned authority path before applying." : "Claude target contains unmanaged AGENTS.md with unknown authority content; refusing to guess whether it conflicts with the managed Claude render."
|
|
10778
10914
|
}];
|
|
10779
10915
|
}
|
|
10780
|
-
var
|
|
10781
|
-
var
|
|
10782
|
-
|
|
10783
|
-
|
|
10916
|
+
var CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH = "AGENTS.md", CLAUDE_LEGACY_AUTHORITY_MAX_BYTES, CLAUDE_LEGACY_MARKERS;
|
|
10917
|
+
var init_session_authority = __esm(() => {
|
|
10918
|
+
CLAUDE_LEGACY_AUTHORITY_MAX_BYTES = 256 * 1024;
|
|
10919
|
+
CLAUDE_LEGACY_MARKERS = [
|
|
10920
|
+
{ id: "claude-agent-rules-heading", pattern: /^# Agent Rules \(Claude\)/m },
|
|
10921
|
+
{ id: "no-worktrees-heading", pattern: /^## No Worktrees/m },
|
|
10922
|
+
{ id: "no-worktrees-directive", pattern: /\bNever use git worktrees\b/m }
|
|
10923
|
+
];
|
|
10784
10924
|
});
|
|
10785
10925
|
|
|
10786
10926
|
// src/lib/session-render.ts
|
|
10787
|
-
import { createHash as
|
|
10788
|
-
import { existsSync as existsSync5, readFileSync as
|
|
10789
|
-
import { homedir as
|
|
10790
|
-
import { basename as basename3, dirname as dirname2, extname as extname2, isAbsolute as isAbsolute3, join as
|
|
10927
|
+
import { createHash as createHash6 } from "crypto";
|
|
10928
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync, statSync as statSync3 } from "fs";
|
|
10929
|
+
import { homedir as homedir5 } from "os";
|
|
10930
|
+
import { basename as basename3, dirname as dirname2, extname as extname2, isAbsolute as isAbsolute3, join as join8, parse as parse2, posix as posix2, relative as relative2, resolve as resolve6 } from "path";
|
|
10791
10931
|
function normalizeSessionInstructionLayer(value) {
|
|
10792
10932
|
if (value === "provider")
|
|
10793
10933
|
return "tool";
|
|
@@ -10804,11 +10944,11 @@ function ensureTrailingNewline3(content) {
|
|
|
10804
10944
|
`) ? content : `${content}
|
|
10805
10945
|
`;
|
|
10806
10946
|
}
|
|
10807
|
-
function
|
|
10808
|
-
return
|
|
10947
|
+
function sha2566(content) {
|
|
10948
|
+
return createHash6("sha256").update(content).digest("hex");
|
|
10809
10949
|
}
|
|
10810
10950
|
function fingerprint(value) {
|
|
10811
|
-
return
|
|
10951
|
+
return sha2566(JSON.stringify(value));
|
|
10812
10952
|
}
|
|
10813
10953
|
function canonicalFingerprintValue(value) {
|
|
10814
10954
|
if (Array.isArray(value))
|
|
@@ -10826,7 +10966,7 @@ function ruleAttestation(rule) {
|
|
|
10826
10966
|
};
|
|
10827
10967
|
const applied = metadata["payloadFloorApplied"];
|
|
10828
10968
|
return {
|
|
10829
|
-
contentSha256:
|
|
10969
|
+
contentSha256: sha2566(rule.content ?? ""),
|
|
10830
10970
|
payloadFloorApplied: typeof applied === "boolean" ? applied : null,
|
|
10831
10971
|
flooredFromRulesVersion: read("flooredFromRulesVersion"),
|
|
10832
10972
|
flooredFromPayloadSha256: read("flooredFromPayloadSha256"),
|
|
@@ -10872,16 +11012,14 @@ function slug(value) {
|
|
|
10872
11012
|
function yamlQuote2(value) {
|
|
10873
11013
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
10874
11014
|
}
|
|
10875
|
-
function getRawStoreRoot() {
|
|
10876
|
-
return resolve4(process.env[RAW_STORE_ROOT_ENV] || join6(process.env["HOME"] || homedir4(), ".hasna", "configs"));
|
|
10877
|
-
}
|
|
10878
11015
|
function defaultTargetHome(tool, profile, sessionId) {
|
|
10879
|
-
|
|
11016
|
+
const home = process.env["HOME"] || homedir5();
|
|
11017
|
+
return join8(home, ".hasna", "accounts", "profiles", tool, slug(profile));
|
|
10880
11018
|
}
|
|
10881
11019
|
function joinTarget(targetHome, relativePath) {
|
|
10882
11020
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
10883
11021
|
const safeRelativePath2 = assertSafeRelativePath(relativePath);
|
|
10884
|
-
return
|
|
11022
|
+
return join8(safeTargetHome, ...safeRelativePath2.split("/"));
|
|
10885
11023
|
}
|
|
10886
11024
|
function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
10887
11025
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
@@ -10892,7 +11030,7 @@ function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
|
10892
11030
|
relativePath: safeRelativePath2,
|
|
10893
11031
|
role,
|
|
10894
11032
|
content: normalizedContent,
|
|
10895
|
-
sha256:
|
|
11033
|
+
sha256: sha2566(normalizedContent),
|
|
10896
11034
|
sourceIds
|
|
10897
11035
|
};
|
|
10898
11036
|
}
|
|
@@ -10926,7 +11064,7 @@ function applyAgentOperatingRulesFloor(source, content) {
|
|
|
10926
11064
|
const floored = {
|
|
10927
11065
|
payloadFloorApplied: true,
|
|
10928
11066
|
flooredFromRulesVersion: parseAgentOperatingRulesVersion(content),
|
|
10929
|
-
flooredFromPayloadSha256:
|
|
11067
|
+
flooredFromPayloadSha256: sha2566(content)
|
|
10930
11068
|
};
|
|
10931
11069
|
return {
|
|
10932
11070
|
content: payload.content,
|
|
@@ -10948,7 +11086,7 @@ function applyAgentOperatingRulesFloorToRule(source, rule, content) {
|
|
|
10948
11086
|
const floored = {
|
|
10949
11087
|
payloadFloorApplied: true,
|
|
10950
11088
|
flooredFromRulesVersion: parseAgentOperatingRulesVersion(content),
|
|
10951
|
-
flooredFromPayloadSha256:
|
|
11089
|
+
flooredFromPayloadSha256: sha2566(content)
|
|
10952
11090
|
};
|
|
10953
11091
|
return {
|
|
10954
11092
|
content: payload.content,
|
|
@@ -10967,7 +11105,7 @@ function skippedSource(source, reason) {
|
|
|
10967
11105
|
order: source.resolvedOrder,
|
|
10968
11106
|
path: source.path ?? null,
|
|
10969
11107
|
hash: source.hash ?? null,
|
|
10970
|
-
renderedPayloadSha256:
|
|
11108
|
+
renderedPayloadSha256: sha2566(source.content),
|
|
10971
11109
|
nonOverridable: source.nonOverridable === true,
|
|
10972
11110
|
provenance: source.provenance ?? null
|
|
10973
11111
|
}
|
|
@@ -11009,7 +11147,7 @@ function compareSessionInstructionSources(a, b) {
|
|
|
11009
11147
|
return SESSION_LAYER_RANK[a.resolvedLayer] - SESSION_LAYER_RANK[b.resolvedLayer] || a.resolvedOrder - b.resolvedOrder || a.id.localeCompare(b.id);
|
|
11010
11148
|
}
|
|
11011
11149
|
function semanticPolicyIntegrity(body) {
|
|
11012
|
-
return
|
|
11150
|
+
return sha2566(body) === AGENT_OPERATING_RULES_PAYLOAD_SHA256 ? "pinned-digest" : "unverified-self-declared";
|
|
11013
11151
|
}
|
|
11014
11152
|
function semanticPolicyDeclaration(source) {
|
|
11015
11153
|
const normalize = (value) => value.replace(/\r\n/g, `
|
|
@@ -11234,7 +11372,7 @@ function composeSources(sources, tool) {
|
|
|
11234
11372
|
targetSourceId: target.id,
|
|
11235
11373
|
targetNormalizedSourceId: target.normalizedId,
|
|
11236
11374
|
targetHash: target.hash ?? null,
|
|
11237
|
-
targetRenderedPayloadSha256:
|
|
11375
|
+
targetRenderedPayloadSha256: sha2566(target.content),
|
|
11238
11376
|
targetNonOverridable: protectedReplacement,
|
|
11239
11377
|
authority: protectedReplacement ? "canonical-identity-export/codewith-provider/v1" : "overridable-source/v1"
|
|
11240
11378
|
}
|
|
@@ -11416,7 +11554,7 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
|
|
|
11416
11554
|
...sources.flatMap((source) => source.resolvedRules.map((rule) => rule.id))
|
|
11417
11555
|
]);
|
|
11418
11556
|
const existingConfigPath = joinTarget(targetHome, adapter.configFile);
|
|
11419
|
-
const selectedConfig = existsSync5(existingConfigPath) ? readOpenCodeConfig(
|
|
11557
|
+
const selectedConfig = existsSync5(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
|
|
11420
11558
|
const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
|
|
11421
11559
|
const config = {
|
|
11422
11560
|
...selectedConfig,
|
|
@@ -11565,7 +11703,7 @@ function buildAssetFiles(input, targetHome, blocked) {
|
|
|
11565
11703
|
relativePath: assertSafeRelativePath(relativePath),
|
|
11566
11704
|
role: "asset",
|
|
11567
11705
|
content,
|
|
11568
|
-
sha256:
|
|
11706
|
+
sha256: sha2566(content),
|
|
11569
11707
|
sourceIds: [item.sourceConfigId, item.assetId]
|
|
11570
11708
|
};
|
|
11571
11709
|
});
|
|
@@ -11605,7 +11743,7 @@ function adapterFor(input) {
|
|
|
11605
11743
|
return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
|
|
11606
11744
|
}
|
|
11607
11745
|
function getHomeDir() {
|
|
11608
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
11746
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir5();
|
|
11609
11747
|
}
|
|
11610
11748
|
function cleanSessionPathInput(path) {
|
|
11611
11749
|
const trimmed = path.trim();
|
|
@@ -11620,16 +11758,16 @@ function resolveSessionPath(path) {
|
|
|
11620
11758
|
throw new Error("Session render path cannot be empty.");
|
|
11621
11759
|
const home = getHomeDir();
|
|
11622
11760
|
if (cleaned === "~")
|
|
11623
|
-
return
|
|
11761
|
+
return resolve6(home);
|
|
11624
11762
|
if (cleaned.startsWith("~/"))
|
|
11625
|
-
return
|
|
11763
|
+
return resolve6(home, cleaned.slice(2));
|
|
11626
11764
|
if (cleaned === "{{HOME}}" || cleaned === "${HOME}")
|
|
11627
|
-
return
|
|
11765
|
+
return resolve6(home);
|
|
11628
11766
|
if (cleaned.startsWith("{{HOME}}/"))
|
|
11629
|
-
return
|
|
11767
|
+
return resolve6(home, cleaned.slice("{{HOME}}/".length));
|
|
11630
11768
|
if (cleaned.startsWith("${HOME}/"))
|
|
11631
|
-
return
|
|
11632
|
-
return
|
|
11769
|
+
return resolve6(home, cleaned.slice("${HOME}/".length));
|
|
11770
|
+
return resolve6(cleaned);
|
|
11633
11771
|
}
|
|
11634
11772
|
function assertSafeRelativePath(relativePath) {
|
|
11635
11773
|
if (!relativePath.trim())
|
|
@@ -11645,7 +11783,7 @@ function assertSafeRelativePath(relativePath) {
|
|
|
11645
11783
|
function assertSafeTargetRoot(targetHome) {
|
|
11646
11784
|
if (!isAbsolute3(targetHome))
|
|
11647
11785
|
throw new Error(`Session render target must be an absolute path: ${targetHome}`);
|
|
11648
|
-
const normalized =
|
|
11786
|
+
const normalized = resolve6(targetHome);
|
|
11649
11787
|
if (normalized === parse2(normalized).root) {
|
|
11650
11788
|
throw new Error(`Session render target cannot be the filesystem root: ${targetHome}`);
|
|
11651
11789
|
}
|
|
@@ -11743,7 +11881,7 @@ function planSessionRender(input) {
|
|
|
11743
11881
|
blockers: targetBlockers
|
|
11744
11882
|
} = resolveRenderTarget(input);
|
|
11745
11883
|
const authorityObservations = input.tool === "cursor" && targetKind !== "blocked" ? [observeCursorGlobalAuthority({ home: input.cursorAuthorityHome })] : [];
|
|
11746
|
-
const authorityConflicts = input.tool === "cursor" && targetKind !== "blocked" ? detectCursorAuthorityConflicts(authorityObservations[0]) : [];
|
|
11884
|
+
const authorityConflicts = input.tool === "cursor" && targetKind !== "blocked" ? detectCursorAuthorityConflicts(authorityObservations[0]) : input.tool === "claude" && targetKind !== "blocked" ? detectClaudeAuthorityConflicts(targetHome) : [];
|
|
11747
11885
|
const blockers = [
|
|
11748
11886
|
...targetBlockers,
|
|
11749
11887
|
...authorityConflicts.map((conflict) => `${conflict.relativePath}: ${conflict.reason}`)
|
|
@@ -11841,7 +11979,7 @@ function planSessionRender(input) {
|
|
|
11841
11979
|
hash: rule.hash ?? null,
|
|
11842
11980
|
...ruleAttestation(rule)
|
|
11843
11981
|
})),
|
|
11844
|
-
renderedPayloadSha256:
|
|
11982
|
+
renderedPayloadSha256: sha2566(source.content),
|
|
11845
11983
|
provenance: source.provenance ?? null,
|
|
11846
11984
|
metadata: source.metadata ?? null
|
|
11847
11985
|
})),
|
|
@@ -11879,8 +12017,8 @@ function planSessionRender(input) {
|
|
|
11879
12017
|
...input.providerConfig ? {
|
|
11880
12018
|
providerConfig: {
|
|
11881
12019
|
sourceId: input.providerConfig.sourceId,
|
|
11882
|
-
selectedPayloadSha256:
|
|
11883
|
-
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ??
|
|
12020
|
+
selectedPayloadSha256: sha2566(input.providerConfig.content),
|
|
12021
|
+
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2566(input.providerConfig.content),
|
|
11884
12022
|
selected: !existsSync5(joinTarget(targetHome, adapter.configFile))
|
|
11885
12023
|
}
|
|
11886
12024
|
} : {},
|
|
@@ -12166,7 +12304,7 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
|
|
|
12166
12304
|
}
|
|
12167
12305
|
return;
|
|
12168
12306
|
}
|
|
12169
|
-
const stat =
|
|
12307
|
+
const stat = statSync3(resolvedPath);
|
|
12170
12308
|
if (!stat.isFile()) {
|
|
12171
12309
|
throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
|
|
12172
12310
|
}
|
|
@@ -12175,7 +12313,7 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
|
|
|
12175
12313
|
if (!pathIsInside(realPath, realBase)) {
|
|
12176
12314
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${sourcePath.path}`);
|
|
12177
12315
|
}
|
|
12178
|
-
return
|
|
12316
|
+
return readFileSync4(realPath, "utf-8");
|
|
12179
12317
|
}
|
|
12180
12318
|
function resolveIdentitySourcePath(path, baseDir, sourceId) {
|
|
12181
12319
|
const cleaned = cleanSessionPathInput(path);
|
|
@@ -12183,8 +12321,8 @@ function resolveIdentitySourcePath(path, baseDir, sourceId) {
|
|
|
12183
12321
|
throw new Error(`Identity instruction source path cannot be empty for ${sourceId}.`);
|
|
12184
12322
|
if (cleaned.includes("\\"))
|
|
12185
12323
|
throw new Error(`Identity instruction source path must use POSIX separators for ${sourceId}: ${path}`);
|
|
12186
|
-
const resolvedPath = isAbsolute3(cleaned) ?
|
|
12187
|
-
if (!pathIsInside(resolvedPath,
|
|
12324
|
+
const resolvedPath = isAbsolute3(cleaned) ? resolve6(cleaned) : resolve6(baseDir, cleaned);
|
|
12325
|
+
if (!pathIsInside(resolvedPath, resolve6(baseDir))) {
|
|
12188
12326
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${path}`);
|
|
12189
12327
|
}
|
|
12190
12328
|
return resolvedPath;
|
|
@@ -12272,7 +12410,7 @@ function asStringArray(value) {
|
|
|
12272
12410
|
return [];
|
|
12273
12411
|
return value.filter((item) => typeof item === "string");
|
|
12274
12412
|
}
|
|
12275
|
-
var
|
|
12413
|
+
var ANTIGRAVITY_RULE_FILE_CHAR_LIMIT = 12000, SESSION_RENDERER_OWNER_ID = "instructions-session-renderer", SESSION_RENDER_TOOLS, SESSION_RENDER_PROFILE_ENTRYPOINTS, SESSION_RENDER_OWNED_CONFIG_TARGETS, IDENTITY_EXPORT_AUTHORITY, CODEWITH_PROTECTED_REPLACEMENT_TARGETS, CODEWITH_FLATTENED_ADAPTER, CODEWITH_NATIVE_ADAPTER, SESSION_TOOL_ADAPTERS, SESSION_RENDER_MANAGED_DIRS, SESSION_RENDER_SHARED_MANAGED_DIRS, SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS, SESSION_LAYER_RANK, CANONICAL_IDENTITY_EXPORT_PACKAGES;
|
|
12276
12414
|
var init_session_render = __esm(() => {
|
|
12277
12415
|
init_global_agent_rules_standard();
|
|
12278
12416
|
init_codewith_shared_todos_storage_standard();
|
|
@@ -12281,8 +12419,11 @@ var init_session_render = __esm(() => {
|
|
|
12281
12419
|
init_transforms();
|
|
12282
12420
|
init_asset_plan();
|
|
12283
12421
|
init_cursor_authority();
|
|
12422
|
+
init_session_authority();
|
|
12284
12423
|
init_session_render_contract();
|
|
12285
12424
|
init_session_render_contract();
|
|
12425
|
+
init_raw_store_root();
|
|
12426
|
+
init_raw_store_root();
|
|
12286
12427
|
SESSION_RENDER_TOOLS = [
|
|
12287
12428
|
"claude",
|
|
12288
12429
|
"codex",
|
|
@@ -12483,7 +12624,7 @@ var init_session_render = __esm(() => {
|
|
|
12483
12624
|
});
|
|
12484
12625
|
|
|
12485
12626
|
// src/lib/instruction-graph.ts
|
|
12486
|
-
import { createHash as
|
|
12627
|
+
import { createHash as createHash7 } from "crypto";
|
|
12487
12628
|
function capability(provider, providerVersionRange, selectedRepresentation, loadingPath, options = {}) {
|
|
12488
12629
|
return Object.freeze({
|
|
12489
12630
|
schema: PROVIDER_CAPABILITY_SCHEMA,
|
|
@@ -12730,7 +12871,7 @@ function compileInstructionGraph(input) {
|
|
|
12730
12871
|
effective_activation: effective.get(configId),
|
|
12731
12872
|
fallback: row.binding.fallback,
|
|
12732
12873
|
required: row.binding.required,
|
|
12733
|
-
content_sha256:
|
|
12874
|
+
content_sha256: sha2567(config.content),
|
|
12734
12875
|
dependencies: dependencies.get(configId) ?? []
|
|
12735
12876
|
};
|
|
12736
12877
|
});
|
|
@@ -12767,7 +12908,7 @@ function compileInstructionGraph(input) {
|
|
|
12767
12908
|
diagnostics
|
|
12768
12909
|
};
|
|
12769
12910
|
return {
|
|
12770
|
-
plan: deepFreeze2({ ...planWithoutHash, source_hash:
|
|
12911
|
+
plan: deepFreeze2({ ...planWithoutHash, source_hash: sha2567(stableJson2(planWithoutHash)) }),
|
|
12771
12912
|
sources,
|
|
12772
12913
|
capability: capability2
|
|
12773
12914
|
};
|
|
@@ -12941,8 +13082,8 @@ function assertExactOnce(units, artifacts) {
|
|
|
12941
13082
|
function errorDiagnostic(configId, code, message) {
|
|
12942
13083
|
return { severity: "error", code, config_id: configId, message };
|
|
12943
13084
|
}
|
|
12944
|
-
function
|
|
12945
|
-
return
|
|
13085
|
+
function sha2567(value) {
|
|
13086
|
+
return createHash7("sha256").update(value).digest("hex");
|
|
12946
13087
|
}
|
|
12947
13088
|
function stableJson2(value) {
|
|
12948
13089
|
const canonical = (entry) => Array.isArray(entry) ? entry.map(canonical) : entry && typeof entry === "object" ? Object.fromEntries(Object.entries(entry).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => [key, canonical(child)])) : entry;
|
|
@@ -13241,37 +13382,6 @@ var init_profiles = __esm(() => {
|
|
|
13241
13382
|
init_asset_plan();
|
|
13242
13383
|
});
|
|
13243
13384
|
|
|
13244
|
-
// src/db/snapshots.ts
|
|
13245
|
-
function createSnapshot(configId, content, version, db) {
|
|
13246
|
-
const d = db || getDatabase();
|
|
13247
|
-
const id = uuid();
|
|
13248
|
-
const ts = now2();
|
|
13249
|
-
d.run("INSERT INTO config_snapshots (id, config_id, content, version, created_at) VALUES (?, ?, ?, ?, ?)", [id, configId, content, version, ts]);
|
|
13250
|
-
return { id, config_id: configId, content, version, created_at: ts };
|
|
13251
|
-
}
|
|
13252
|
-
function listSnapshots(configId, db) {
|
|
13253
|
-
const d = db || getDatabase();
|
|
13254
|
-
return d.query("SELECT * FROM config_snapshots WHERE config_id = ? ORDER BY version DESC").all(configId);
|
|
13255
|
-
}
|
|
13256
|
-
function getSnapshot(id, db) {
|
|
13257
|
-
const d = db || getDatabase();
|
|
13258
|
-
return d.query("SELECT * FROM config_snapshots WHERE id = ?").get(id);
|
|
13259
|
-
}
|
|
13260
|
-
function getSnapshotByVersion(configId, version, db) {
|
|
13261
|
-
const d = db || getDatabase();
|
|
13262
|
-
return d.query("SELECT * FROM config_snapshots WHERE config_id = ? AND version = ?").get(configId, version);
|
|
13263
|
-
}
|
|
13264
|
-
function pruneSnapshots(configId, keep = 10, db) {
|
|
13265
|
-
const d = db || getDatabase();
|
|
13266
|
-
const result = d.run(`DELETE FROM config_snapshots WHERE config_id = ? AND id NOT IN (
|
|
13267
|
-
SELECT id FROM config_snapshots WHERE config_id = ? ORDER BY version DESC LIMIT ?
|
|
13268
|
-
)`, [configId, configId, keep]);
|
|
13269
|
-
return result.changes;
|
|
13270
|
-
}
|
|
13271
|
-
var init_snapshots = __esm(() => {
|
|
13272
|
-
init_database();
|
|
13273
|
-
});
|
|
13274
|
-
|
|
13275
13385
|
// src/db/machines.ts
|
|
13276
13386
|
import { arch, hostname, type } from "os";
|
|
13277
13387
|
function currentHostname2() {
|
|
@@ -13873,8 +13983,8 @@ var init_config_store = __esm(() => {
|
|
|
13873
13983
|
});
|
|
13874
13984
|
|
|
13875
13985
|
// src/lib/session-render-ownership.ts
|
|
13876
|
-
import { existsSync as existsSync6, readFileSync as
|
|
13877
|
-
import { dirname as dirname3, join as
|
|
13986
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
13987
|
+
import { dirname as dirname3, join as join9, parse as parse3, relative as relative3, sep } from "path";
|
|
13878
13988
|
function toSegments(absolutePath2) {
|
|
13879
13989
|
return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
13880
13990
|
}
|
|
@@ -13895,7 +14005,7 @@ function readManifestRelativePaths(manifestPath) {
|
|
|
13895
14005
|
try {
|
|
13896
14006
|
if (!existsSync6(manifestPath))
|
|
13897
14007
|
return null;
|
|
13898
|
-
stats =
|
|
14008
|
+
stats = statSync4(manifestPath);
|
|
13899
14009
|
} catch {
|
|
13900
14010
|
return null;
|
|
13901
14011
|
}
|
|
@@ -13905,7 +14015,7 @@ function readManifestRelativePaths(manifestPath) {
|
|
|
13905
14015
|
}
|
|
13906
14016
|
let manifest;
|
|
13907
14017
|
try {
|
|
13908
|
-
manifest = JSON.parse(
|
|
14018
|
+
manifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
|
|
13909
14019
|
} catch {
|
|
13910
14020
|
return null;
|
|
13911
14021
|
}
|
|
@@ -13922,7 +14032,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
|
13922
14032
|
const root = parse3(absolutePath2).root;
|
|
13923
14033
|
let home = dirname3(absolutePath2);
|
|
13924
14034
|
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
13925
|
-
const manifestPath =
|
|
14035
|
+
const manifestPath = join9(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
13926
14036
|
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
13927
14037
|
if (relativePaths) {
|
|
13928
14038
|
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
@@ -13949,25 +14059,25 @@ var init_session_render_ownership = __esm(() => {
|
|
|
13949
14059
|
// src/lib/apply.ts
|
|
13950
14060
|
var exports_apply = {};
|
|
13951
14061
|
__export(exports_apply, {
|
|
13952
|
-
|
|
13953
|
-
normalizeTargetPath: () => normalizeTargetPath,
|
|
13954
|
-
getConfigHome: () => getConfigHome,
|
|
13955
|
-
expandPath: () => expandPath,
|
|
13956
|
-
applyConfigsWithReport: () => applyConfigsWithReport,
|
|
14062
|
+
applyConfig: () => applyConfig,
|
|
13957
14063
|
applyConfigs: () => applyConfigs,
|
|
13958
|
-
|
|
14064
|
+
applyConfigsWithReport: () => applyConfigsWithReport,
|
|
14065
|
+
expandPath: () => expandPath,
|
|
14066
|
+
getConfigHome: () => getConfigHome,
|
|
14067
|
+
normalizeTargetPath: () => normalizeTargetPath,
|
|
14068
|
+
previewConfigs: () => previewConfigs
|
|
13959
14069
|
});
|
|
13960
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as
|
|
13961
|
-
import { basename as basename4, dirname as dirname4, join as
|
|
13962
|
-
import { homedir as
|
|
14070
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
14071
|
+
import { basename as basename4, dirname as dirname4, join as join10, resolve as resolve7 } from "path";
|
|
14072
|
+
import { homedir as homedir6 } from "os";
|
|
13963
14073
|
function getConfigHome() {
|
|
13964
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
14074
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir6();
|
|
13965
14075
|
}
|
|
13966
14076
|
function expandPath(p) {
|
|
13967
14077
|
if (p.startsWith("~/")) {
|
|
13968
|
-
return
|
|
14078
|
+
return resolve7(getConfigHome(), p.slice(2));
|
|
13969
14079
|
}
|
|
13970
|
-
return
|
|
14080
|
+
return resolve7(p);
|
|
13971
14081
|
}
|
|
13972
14082
|
function normalizeTargetPath(p) {
|
|
13973
14083
|
const expanded = expandPath(p);
|
|
@@ -13979,7 +14089,7 @@ function normalizeTargetPath(p) {
|
|
|
13979
14089
|
while (true) {
|
|
13980
14090
|
if (existsSync7(current)) {
|
|
13981
14091
|
try {
|
|
13982
|
-
return
|
|
14092
|
+
return resolve7(realpathSync2(current), ...missingSegments);
|
|
13983
14093
|
} catch {
|
|
13984
14094
|
return expanded;
|
|
13985
14095
|
}
|
|
@@ -14007,8 +14117,9 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
14007
14117
|
throw new ConfigApplyError(`Antigravity rule file ${renderedTargetPath} is ${renderedContent.length} characters; split it before applying because Antigravity limits rule files to ${ANTIGRAVITY_RULE_FILE_CHAR_LIMIT} characters.`);
|
|
14008
14118
|
}
|
|
14009
14119
|
const path = expandPath(renderedTargetPath);
|
|
14010
|
-
const
|
|
14011
|
-
const
|
|
14120
|
+
const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
|
|
14121
|
+
const previousContent = existsSync7(path) ? readFileSync6(path, "utf-8") : null;
|
|
14122
|
+
const changed = previousContent !== renderedForTarget;
|
|
14012
14123
|
if (!opts.dryRun) {
|
|
14013
14124
|
const dir = dirname4(path);
|
|
14014
14125
|
if (!existsSync7(dir)) {
|
|
@@ -14018,13 +14129,13 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
14018
14129
|
const store = opts.store ?? resolveConfigStore();
|
|
14019
14130
|
await store.createSnapshot(config.id, previousContent, config.version);
|
|
14020
14131
|
}
|
|
14021
|
-
writeFileSync2(path,
|
|
14132
|
+
writeFileSync2(path, renderedForTarget, "utf-8");
|
|
14022
14133
|
}
|
|
14023
14134
|
return {
|
|
14024
14135
|
config_id: config.id,
|
|
14025
14136
|
path,
|
|
14026
14137
|
previous_content: previousContent,
|
|
14027
|
-
new_content:
|
|
14138
|
+
new_content: renderedForTarget,
|
|
14028
14139
|
dry_run: opts.dryRun ?? false,
|
|
14029
14140
|
changed,
|
|
14030
14141
|
primary_changed: changed,
|
|
@@ -14047,7 +14158,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
|
14047
14158
|
const path = expandPath(targetPath);
|
|
14048
14159
|
if (!existsSync7(path))
|
|
14049
14160
|
return [];
|
|
14050
|
-
current =
|
|
14161
|
+
current = readFileSync6(path, "utf-8");
|
|
14051
14162
|
} catch {
|
|
14052
14163
|
return secretTokens;
|
|
14053
14164
|
}
|
|
@@ -14371,7 +14482,7 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
|
14371
14482
|
getConfigHome(),
|
|
14372
14483
|
opts.vars?.["HOME_DIR"]
|
|
14373
14484
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
14374
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
14485
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join10(home, ...relativePath.split("/"))))))
|
|
14375
14486
|
return true;
|
|
14376
14487
|
return sessionRenderOwnsPath(normalized);
|
|
14377
14488
|
}
|
|
@@ -14385,12 +14496,13 @@ var init_apply = __esm(() => {
|
|
|
14385
14496
|
init_redact();
|
|
14386
14497
|
init_template();
|
|
14387
14498
|
init_transforms();
|
|
14499
|
+
init_cursor_authority();
|
|
14388
14500
|
});
|
|
14389
14501
|
|
|
14390
14502
|
// src/lib/sync-dir.ts
|
|
14391
|
-
import { existsSync as existsSync8, readdirSync, readFileSync as
|
|
14392
|
-
import { join as
|
|
14393
|
-
import { homedir as
|
|
14503
|
+
import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
14504
|
+
import { join as join11, relative as relative4 } from "path";
|
|
14505
|
+
import { homedir as homedir7 } from "os";
|
|
14394
14506
|
function shouldSkip(p) {
|
|
14395
14507
|
return SKIP.some((s) => p.includes(s));
|
|
14396
14508
|
}
|
|
@@ -14399,9 +14511,9 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14399
14511
|
const absDir = expandPath(dir);
|
|
14400
14512
|
if (!existsSync8(absDir))
|
|
14401
14513
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
14402
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) =>
|
|
14514
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join11(absDir, f)).filter((f) => statSync5(f).isFile());
|
|
14403
14515
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
14404
|
-
const home =
|
|
14516
|
+
const home = homedir7();
|
|
14405
14517
|
const allConfigs = await store.listConfigs();
|
|
14406
14518
|
for (const file of files) {
|
|
14407
14519
|
if (shouldSkip(file)) {
|
|
@@ -14409,7 +14521,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14409
14521
|
continue;
|
|
14410
14522
|
}
|
|
14411
14523
|
try {
|
|
14412
|
-
const content =
|
|
14524
|
+
const content = readFileSync7(file, "utf-8");
|
|
14413
14525
|
if (content.length > 500000) {
|
|
14414
14526
|
result.skipped.push(file + " (too large)");
|
|
14415
14527
|
continue;
|
|
@@ -14435,7 +14547,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14435
14547
|
}
|
|
14436
14548
|
async function syncToDir(dir, opts = {}) {
|
|
14437
14549
|
const store = opts.store ?? resolveConfigStore();
|
|
14438
|
-
const home =
|
|
14550
|
+
const home = homedir7();
|
|
14439
14551
|
const absDir = expandPath(dir);
|
|
14440
14552
|
const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
|
|
14441
14553
|
const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
|
|
@@ -14459,7 +14571,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
14459
14571
|
}
|
|
14460
14572
|
function walkDir(dir, files = []) {
|
|
14461
14573
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
14462
|
-
const full =
|
|
14574
|
+
const full = join11(dir, entry.name);
|
|
14463
14575
|
if (shouldSkip(full))
|
|
14464
14576
|
continue;
|
|
14465
14577
|
if (entry.isDirectory())
|
|
@@ -14480,21 +14592,21 @@ var init_sync_dir = __esm(() => {
|
|
|
14480
14592
|
// src/lib/sync.ts
|
|
14481
14593
|
var exports_sync = {};
|
|
14482
14594
|
__export(exports_sync, {
|
|
14483
|
-
|
|
14484
|
-
syncToDir: () => syncToDir,
|
|
14485
|
-
syncProject: () => syncProject,
|
|
14486
|
-
syncKnown: () => syncKnown,
|
|
14487
|
-
syncFromDir: () => syncFromDir,
|
|
14488
|
-
diffConfig: () => diffConfig,
|
|
14489
|
-
detectFormat: () => detectFormat,
|
|
14490
|
-
detectCategory: () => detectCategory,
|
|
14491
|
-
detectAgent: () => detectAgent,
|
|
14492
|
-
PROJECT_CONFIG_FILES: () => PROJECT_CONFIG_FILES,
|
|
14595
|
+
CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS,
|
|
14493
14596
|
KNOWN_CONFIGS: () => KNOWN_CONFIGS,
|
|
14494
|
-
|
|
14597
|
+
PROJECT_CONFIG_FILES: () => PROJECT_CONFIG_FILES,
|
|
14598
|
+
detectAgent: () => detectAgent,
|
|
14599
|
+
detectCategory: () => detectCategory,
|
|
14600
|
+
detectFormat: () => detectFormat,
|
|
14601
|
+
diffConfig: () => diffConfig,
|
|
14602
|
+
syncFromDir: () => syncFromDir,
|
|
14603
|
+
syncKnown: () => syncKnown,
|
|
14604
|
+
syncProject: () => syncProject,
|
|
14605
|
+
syncToDir: () => syncToDir,
|
|
14606
|
+
syncToDisk: () => syncToDisk
|
|
14495
14607
|
});
|
|
14496
|
-
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as
|
|
14497
|
-
import { basename as basename5, extname as extname3, join as
|
|
14608
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
|
|
14609
|
+
import { basename as basename5, extname as extname3, join as join12 } from "path";
|
|
14498
14610
|
function claudeRuleOutputs(fileName) {
|
|
14499
14611
|
const stem = basename5(fileName, extname3(fileName));
|
|
14500
14612
|
return [
|
|
@@ -14558,11 +14670,11 @@ async function syncProject(opts) {
|
|
|
14558
14670
|
const allConfigs = await store.listConfigs();
|
|
14559
14671
|
const machine = detectMachineContext();
|
|
14560
14672
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
14561
|
-
const abs =
|
|
14673
|
+
const abs = join12(absDir, pf.file);
|
|
14562
14674
|
if (!existsSync9(abs))
|
|
14563
14675
|
continue;
|
|
14564
14676
|
try {
|
|
14565
|
-
const rawContent =
|
|
14677
|
+
const rawContent = readFileSync8(abs, "utf-8");
|
|
14566
14678
|
if (rawContent.length > 500000) {
|
|
14567
14679
|
result.skipped.push(pf.file);
|
|
14568
14680
|
continue;
|
|
@@ -14591,20 +14703,20 @@ async function syncProject(opts) {
|
|
|
14591
14703
|
}
|
|
14592
14704
|
}
|
|
14593
14705
|
for (const ruleDir of [
|
|
14594
|
-
{ dir:
|
|
14595
|
-
{ dir:
|
|
14596
|
-
{ dir:
|
|
14597
|
-
{ dir:
|
|
14598
|
-
{ dir:
|
|
14599
|
-
{ dir:
|
|
14600
|
-
{ dir:
|
|
14706
|
+
{ dir: join12(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
14707
|
+
{ dir: join12(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
|
|
14708
|
+
{ dir: join12(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
|
|
14709
|
+
{ dir: join12(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
|
|
14710
|
+
{ dir: join12(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
|
|
14711
|
+
{ dir: join12(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
|
|
14712
|
+
{ dir: join12(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
|
|
14601
14713
|
]) {
|
|
14602
14714
|
if (!existsSync9(ruleDir.dir))
|
|
14603
14715
|
continue;
|
|
14604
14716
|
const mdFiles = readdirSync2(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
14605
14717
|
for (const f of mdFiles) {
|
|
14606
|
-
const abs =
|
|
14607
|
-
const raw =
|
|
14718
|
+
const abs = join12(ruleDir.dir, f);
|
|
14719
|
+
const raw = readFileSync8(abs, "utf-8");
|
|
14608
14720
|
const redacted = redactContent(raw, "markdown");
|
|
14609
14721
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
14610
14722
|
const content = machineAware.content;
|
|
@@ -14650,13 +14762,13 @@ async function syncKnown(opts = {}) {
|
|
|
14650
14762
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
14651
14763
|
const ruleFiles = readdirSync2(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
14652
14764
|
for (const f of ruleFiles) {
|
|
14653
|
-
const abs2 =
|
|
14765
|
+
const abs2 = join12(absDir, f);
|
|
14654
14766
|
const targetPath = abs2.replace(home, "~");
|
|
14655
14767
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
14656
14768
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
14657
14769
|
continue;
|
|
14658
14770
|
}
|
|
14659
|
-
const raw =
|
|
14771
|
+
const raw = readFileSync8(abs2, "utf-8");
|
|
14660
14772
|
const redacted = redactContent(raw, "markdown");
|
|
14661
14773
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
14662
14774
|
const content = machineAware.content;
|
|
@@ -14689,7 +14801,7 @@ async function syncKnown(opts = {}) {
|
|
|
14689
14801
|
continue;
|
|
14690
14802
|
}
|
|
14691
14803
|
try {
|
|
14692
|
-
const rawContent = normalizeKnownConfigSource(known,
|
|
14804
|
+
const rawContent = normalizeKnownConfigSource(known, readFileSync8(abs, "utf-8"));
|
|
14693
14805
|
if (rawContent.length > 500000) {
|
|
14694
14806
|
result.skipped.push(known.path + " (too large)");
|
|
14695
14807
|
continue;
|
|
@@ -14792,7 +14904,7 @@ function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
|
14792
14904
|
const path = expandPath(targetPath);
|
|
14793
14905
|
if (!existsSync9(path))
|
|
14794
14906
|
return `(file not found on disk: ${path})`;
|
|
14795
|
-
const diskContent =
|
|
14907
|
+
const diskContent = readFileSync8(path, "utf-8");
|
|
14796
14908
|
if (diskContent === expectedContent)
|
|
14797
14909
|
return "(no diff \u2014 identical)";
|
|
14798
14910
|
const format = redactFormatForTarget(targetPath, storedFormat);
|
|
@@ -15025,18 +15137,18 @@ __export(exports_package_manager_guard, {
|
|
|
15025
15137
|
scanPackageManagerSecrets: () => scanPackageManagerSecrets
|
|
15026
15138
|
});
|
|
15027
15139
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15028
|
-
import { existsSync as
|
|
15029
|
-
import { homedir as
|
|
15030
|
-
import { basename as basename6, dirname as
|
|
15140
|
+
import { existsSync as existsSync16, lstatSync as lstatSync6, readdirSync as readdirSync4, readFileSync as readFileSync14 } from "fs";
|
|
15141
|
+
import { homedir as homedir9 } from "os";
|
|
15142
|
+
import { basename as basename6, dirname as dirname8, isAbsolute as isAbsolute5, join as join18, relative as relative7, resolve as resolve12 } from "path";
|
|
15031
15143
|
function scanPackageManagerSecrets(options = {}) {
|
|
15032
|
-
const cwd = options.cwd ?
|
|
15033
|
-
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) =>
|
|
15144
|
+
const cwd = options.cwd ? resolve12(options.cwd) : process.cwd();
|
|
15145
|
+
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve12(cwd, root));
|
|
15034
15146
|
const findings = [];
|
|
15035
15147
|
let scannedFiles = 0;
|
|
15036
15148
|
for (const root of roots) {
|
|
15037
|
-
if (!
|
|
15149
|
+
if (!existsSync16(root))
|
|
15038
15150
|
continue;
|
|
15039
|
-
const stat =
|
|
15151
|
+
const stat = lstatSync6(root);
|
|
15040
15152
|
if (stat.isFile()) {
|
|
15041
15153
|
if (!shouldScanRepoFile(root))
|
|
15042
15154
|
continue;
|
|
@@ -15044,14 +15156,14 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15044
15156
|
if (text === null)
|
|
15045
15157
|
continue;
|
|
15046
15158
|
scannedFiles++;
|
|
15047
|
-
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root),
|
|
15159
|
+
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname8(root)));
|
|
15048
15160
|
continue;
|
|
15049
15161
|
}
|
|
15050
15162
|
if (!stat.isDirectory())
|
|
15051
15163
|
continue;
|
|
15052
15164
|
const tracked = trackedFiles(root);
|
|
15053
15165
|
for (const file of collectRepoFiles(root)) {
|
|
15054
|
-
const rel = toPosix(
|
|
15166
|
+
const rel = toPosix(relative7(root, file));
|
|
15055
15167
|
const isTracked = tracked.has(rel);
|
|
15056
15168
|
const text = readTextFile(file);
|
|
15057
15169
|
if (text === null)
|
|
@@ -15061,10 +15173,10 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15061
15173
|
}
|
|
15062
15174
|
}
|
|
15063
15175
|
if (options.includeHome) {
|
|
15064
|
-
const home =
|
|
15176
|
+
const home = homedir9();
|
|
15065
15177
|
for (const name of HOME_FILES) {
|
|
15066
|
-
const file =
|
|
15067
|
-
if (!
|
|
15178
|
+
const file = join18(home, name);
|
|
15179
|
+
if (!existsSync16(file))
|
|
15068
15180
|
continue;
|
|
15069
15181
|
const text = readTextFile(file);
|
|
15070
15182
|
if (text === null)
|
|
@@ -15088,12 +15200,12 @@ function collectRepoFiles(root) {
|
|
|
15088
15200
|
if (entry.isDirectory()) {
|
|
15089
15201
|
if (SKIP_DIRS.has(entry.name))
|
|
15090
15202
|
continue;
|
|
15091
|
-
visit(
|
|
15203
|
+
visit(join18(dir, entry.name));
|
|
15092
15204
|
continue;
|
|
15093
15205
|
}
|
|
15094
15206
|
if (!entry.isFile())
|
|
15095
15207
|
continue;
|
|
15096
|
-
const file =
|
|
15208
|
+
const file = join18(dir, entry.name);
|
|
15097
15209
|
if (shouldScanRepoFile(file))
|
|
15098
15210
|
out.push(file);
|
|
15099
15211
|
}
|
|
@@ -15128,10 +15240,10 @@ function isNpmrcName(name) {
|
|
|
15128
15240
|
}
|
|
15129
15241
|
function readTextFile(file) {
|
|
15130
15242
|
try {
|
|
15131
|
-
const stat =
|
|
15243
|
+
const stat = lstatSync6(file);
|
|
15132
15244
|
if (!stat.isFile() || stat.size > 5000000)
|
|
15133
15245
|
return null;
|
|
15134
|
-
const buf =
|
|
15246
|
+
const buf = readFileSync14(file);
|
|
15135
15247
|
if (buf.includes(0))
|
|
15136
15248
|
return null;
|
|
15137
15249
|
return buf.toString("utf-8");
|
|
@@ -15331,11 +15443,11 @@ function trackedFiles(root) {
|
|
|
15331
15443
|
}
|
|
15332
15444
|
function isTrackedFile(file) {
|
|
15333
15445
|
try {
|
|
15334
|
-
const repoRoot = execFileSync2("git", ["-C",
|
|
15446
|
+
const repoRoot = execFileSync2("git", ["-C", dirname8(file), "rev-parse", "--show-toplevel"], {
|
|
15335
15447
|
encoding: "utf-8",
|
|
15336
15448
|
stdio: ["ignore", "pipe", "ignore"]
|
|
15337
15449
|
}).trim();
|
|
15338
|
-
const rel = toPosix(
|
|
15450
|
+
const rel = toPosix(relative7(repoRoot, file));
|
|
15339
15451
|
execFileSync2("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", rel], {
|
|
15340
15452
|
stdio: ["ignore", "ignore", "ignore"]
|
|
15341
15453
|
});
|
|
@@ -15361,13 +15473,13 @@ function stripInlineComment(value) {
|
|
|
15361
15473
|
return value.replace(/\s[#;].*$/, "").trim();
|
|
15362
15474
|
}
|
|
15363
15475
|
function displayPath(file, root) {
|
|
15364
|
-
const home =
|
|
15476
|
+
const home = homedir9();
|
|
15365
15477
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
15366
|
-
return "~/" + toPosix(
|
|
15478
|
+
return "~/" + toPosix(relative7(home, file));
|
|
15367
15479
|
if (isAbsolute5(root) && file.startsWith(root + "/"))
|
|
15368
|
-
return toPosix(
|
|
15480
|
+
return toPosix(relative7(root, file));
|
|
15369
15481
|
if (file === home || file.startsWith(home + "/"))
|
|
15370
|
-
return "~/" + toPosix(
|
|
15482
|
+
return "~/" + toPosix(relative7(home, file));
|
|
15371
15483
|
return file;
|
|
15372
15484
|
}
|
|
15373
15485
|
function toPosix(path) {
|
|
@@ -15412,7 +15524,7 @@ var init_package_manager_guard = __esm(() => {
|
|
|
15412
15524
|
];
|
|
15413
15525
|
});
|
|
15414
15526
|
|
|
15415
|
-
//
|
|
15527
|
+
// ../events/dist/commander.js
|
|
15416
15528
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
15417
15529
|
import { Buffer as Buffer2 } from "buffer";
|
|
15418
15530
|
import { existsSync } from "fs";
|
|
@@ -16613,9 +16725,9 @@ var {
|
|
|
16613
16725
|
// src/cli/index.tsx
|
|
16614
16726
|
init_apply();
|
|
16615
16727
|
import chalk from "chalk";
|
|
16616
|
-
import { existsSync as
|
|
16617
|
-
import { homedir as
|
|
16618
|
-
import { basename as basename7, join as
|
|
16728
|
+
import { existsSync as existsSync17, lstatSync as lstatSync7, readFileSync as readFileSync15, readSync, writeSync } from "fs";
|
|
16729
|
+
import { homedir as homedir10 } from "os";
|
|
16730
|
+
import { basename as basename7, join as join19, resolve as resolve13 } from "path";
|
|
16619
16731
|
|
|
16620
16732
|
// src/lib/config-target-identity.ts
|
|
16621
16733
|
init_apply();
|
|
@@ -16662,14 +16774,14 @@ init_redact();
|
|
|
16662
16774
|
// src/lib/export.ts
|
|
16663
16775
|
init_config_store();
|
|
16664
16776
|
import { existsSync as existsSync10, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
16665
|
-
import { join as
|
|
16777
|
+
import { join as join13, resolve as resolve8 } from "path";
|
|
16666
16778
|
import { tmpdir } from "os";
|
|
16667
16779
|
async function exportConfigs(outputPath, opts = {}) {
|
|
16668
16780
|
const store = opts.store ?? resolveConfigStore();
|
|
16669
16781
|
const configs = await store.listConfigs(opts.filter);
|
|
16670
|
-
const absOutput =
|
|
16671
|
-
const tmpDir =
|
|
16672
|
-
const contentsDir =
|
|
16782
|
+
const absOutput = resolve8(outputPath);
|
|
16783
|
+
const tmpDir = join13(tmpdir(), `configs-export-${Date.now()}`);
|
|
16784
|
+
const contentsDir = join13(tmpDir, "contents");
|
|
16673
16785
|
try {
|
|
16674
16786
|
mkdirSync4(contentsDir, { recursive: true });
|
|
16675
16787
|
const manifest = {
|
|
@@ -16677,10 +16789,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
16677
16789
|
exported_at: new Date().toISOString(),
|
|
16678
16790
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
16679
16791
|
};
|
|
16680
|
-
writeFileSync3(
|
|
16792
|
+
writeFileSync3(join13(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
16681
16793
|
for (const config of configs) {
|
|
16682
16794
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
16683
|
-
writeFileSync3(
|
|
16795
|
+
writeFileSync3(join13(contentsDir, fileName), config.content, "utf-8");
|
|
16684
16796
|
}
|
|
16685
16797
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
16686
16798
|
stdout: "pipe",
|
|
@@ -16701,14 +16813,14 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
16701
16813
|
|
|
16702
16814
|
// src/lib/import.ts
|
|
16703
16815
|
init_config_store();
|
|
16704
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as
|
|
16705
|
-
import { join as
|
|
16816
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync9, rmSync as rmSync4 } from "fs";
|
|
16817
|
+
import { join as join14, resolve as resolve9 } from "path";
|
|
16706
16818
|
import { tmpdir as tmpdir2 } from "os";
|
|
16707
16819
|
async function importConfigs(bundlePath, opts = {}) {
|
|
16708
16820
|
const store = opts.store ?? resolveConfigStore();
|
|
16709
16821
|
const conflict = opts.conflict ?? "skip";
|
|
16710
|
-
const absPath =
|
|
16711
|
-
const tmpDir =
|
|
16822
|
+
const absPath = resolve9(bundlePath);
|
|
16823
|
+
const tmpDir = join14(tmpdir2(), `configs-import-${Date.now()}`);
|
|
16712
16824
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
16713
16825
|
try {
|
|
16714
16826
|
mkdirSync5(tmpDir, { recursive: true });
|
|
@@ -16721,15 +16833,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
16721
16833
|
const stderr = await new Response(proc.stderr).text();
|
|
16722
16834
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
16723
16835
|
}
|
|
16724
|
-
const manifestPath =
|
|
16836
|
+
const manifestPath = join14(tmpDir, "manifest.json");
|
|
16725
16837
|
if (!existsSync11(manifestPath))
|
|
16726
16838
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
16727
|
-
const manifest = JSON.parse(
|
|
16839
|
+
const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
|
|
16728
16840
|
for (const meta of manifest.configs) {
|
|
16729
16841
|
try {
|
|
16730
16842
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
16731
|
-
const contentFile =
|
|
16732
|
-
const content = existsSync11(contentFile) ?
|
|
16843
|
+
const contentFile = join14(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
16844
|
+
const content = existsSync11(contentFile) ? readFileSync9(contentFile, "utf-8") : "";
|
|
16733
16845
|
let existing = null;
|
|
16734
16846
|
try {
|
|
16735
16847
|
existing = await store.getConfig(meta.slug);
|
|
@@ -16777,16 +16889,17 @@ init_machine();
|
|
|
16777
16889
|
init_project_context();
|
|
16778
16890
|
init_session_render();
|
|
16779
16891
|
init_cursor_authority();
|
|
16780
|
-
|
|
16892
|
+
init_session_authority();
|
|
16893
|
+
import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
|
|
16781
16894
|
import {
|
|
16782
16895
|
existsSync as existsSync12,
|
|
16783
|
-
lstatSync as
|
|
16896
|
+
lstatSync as lstatSync4,
|
|
16784
16897
|
mkdirSync as mkdirSync6,
|
|
16785
|
-
readFileSync as
|
|
16898
|
+
readFileSync as readFileSync10,
|
|
16786
16899
|
readdirSync as readdirSync3,
|
|
16787
|
-
statSync as
|
|
16900
|
+
statSync as statSync6
|
|
16788
16901
|
} from "fs";
|
|
16789
|
-
import { dirname as dirname5, isAbsolute as isAbsolute4, join as
|
|
16902
|
+
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join15, parse as parse4, relative as relative5, resolve as resolve10 } from "path";
|
|
16790
16903
|
|
|
16791
16904
|
class SessionApplyError extends Error {
|
|
16792
16905
|
constructor(message) {
|
|
@@ -16807,6 +16920,7 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
16807
16920
|
}
|
|
16808
16921
|
assertCursorAuthorityUnchanged(plan);
|
|
16809
16922
|
const targetHome = assertSafeTargetHome(plan.targetHome);
|
|
16923
|
+
assertClaudeAuthorityStillClear(plan, targetHome);
|
|
16810
16924
|
const payloadFiles = [...plan.files, ...plan.assetFiles ?? []];
|
|
16811
16925
|
const files = [...payloadFiles, plan.manifestFile];
|
|
16812
16926
|
const manifestPath = resolvePlannedFilePath(plan, plan.manifestFile, targetHome);
|
|
@@ -16907,6 +17021,15 @@ function assertCursorAuthorityUnchanged(plan) {
|
|
|
16907
17021
|
throw new SessionApplyError("Cursor fixed global authority changed after planning; refusing to apply a stale render plan.");
|
|
16908
17022
|
}
|
|
16909
17023
|
}
|
|
17024
|
+
function assertClaudeAuthorityStillClear(plan, targetHome) {
|
|
17025
|
+
if (plan.tool !== "claude" || plan.targetKind === "blocked")
|
|
17026
|
+
return;
|
|
17027
|
+
const conflicts = detectClaudeAuthorityConflicts(targetHome);
|
|
17028
|
+
if (conflicts.length === 0)
|
|
17029
|
+
return;
|
|
17030
|
+
const summary = conflicts.map((conflict) => `${conflict.relativePath}: ${conflict.reason}`).join("; ");
|
|
17031
|
+
throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
|
|
17032
|
+
}
|
|
16910
17033
|
function ensureSessionTargetHome(targetHome) {
|
|
16911
17034
|
if (!existsSync12(targetHome))
|
|
16912
17035
|
mkdirSync6(targetHome, { recursive: true, mode: 448 });
|
|
@@ -16914,7 +17037,7 @@ function ensureSessionTargetHome(targetHome) {
|
|
|
16914
17037
|
}
|
|
16915
17038
|
function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
16916
17039
|
const safeTargetHome = assertSafeTargetHome(targetHome);
|
|
16917
|
-
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome,
|
|
17040
|
+
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve10(manifestPath)), safeTargetHome) : resolve10(safeTargetHome, ".hasna", "session-render-manifest.json");
|
|
16918
17041
|
const checkedAt = new Date().toISOString();
|
|
16919
17042
|
const previousManifest = readPreviousManifest(resolvedManifestPath);
|
|
16920
17043
|
if (!previousManifest) {
|
|
@@ -16941,7 +17064,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
16941
17064
|
});
|
|
16942
17065
|
continue;
|
|
16943
17066
|
}
|
|
16944
|
-
const actualSha256 =
|
|
17067
|
+
const actualSha256 = sha2568(readFileSync10(target, "utf-8"));
|
|
16945
17068
|
if (actualSha256 !== file.sha256) {
|
|
16946
17069
|
drifted.push({
|
|
16947
17070
|
path: target,
|
|
@@ -16964,7 +17087,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
16964
17087
|
function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
|
|
16965
17088
|
const snapshot = readSessionRenderSnapshot(snapshotPath);
|
|
16966
17089
|
const targetHome = assertSafeTargetHome(snapshot.targetHome);
|
|
16967
|
-
const resolvedSnapshotPath =
|
|
17090
|
+
const resolvedSnapshotPath = resolve10(snapshotPath);
|
|
16968
17091
|
const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
|
|
16969
17092
|
if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute4(snapshotRelativePath)) {
|
|
16970
17093
|
throw new SessionApplyError("Session snapshot must be stored inside its target home.");
|
|
@@ -17082,19 +17205,19 @@ function requiredRestoreHash(file) {
|
|
|
17082
17205
|
return file.previousSha256;
|
|
17083
17206
|
}
|
|
17084
17207
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
17085
|
-
const resolved =
|
|
17208
|
+
const resolved = resolve10(snapshotPath);
|
|
17086
17209
|
if (!existsSync12(resolved))
|
|
17087
17210
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
17088
|
-
const stat =
|
|
17211
|
+
const stat = lstatSync4(resolved);
|
|
17089
17212
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
17090
17213
|
throw new SessionApplyError(`Session snapshot is not a regular file: ${snapshotPath}`);
|
|
17091
17214
|
}
|
|
17092
|
-
if (
|
|
17215
|
+
if (statSync6(resolved).size > 32 * 1024 * 1024) {
|
|
17093
17216
|
throw new SessionApplyError(`Session snapshot exceeds the 32 MiB restore limit: ${snapshotPath}`);
|
|
17094
17217
|
}
|
|
17095
17218
|
let parsed;
|
|
17096
17219
|
try {
|
|
17097
|
-
parsed = JSON.parse(
|
|
17220
|
+
parsed = JSON.parse(readFileSync10(resolved, "utf8"));
|
|
17098
17221
|
} catch {
|
|
17099
17222
|
throw new SessionApplyError(`Session snapshot is not valid JSON: ${snapshotPath}`);
|
|
17100
17223
|
}
|
|
@@ -17116,7 +17239,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
17116
17239
|
const previousManifest = snapshot.previousManifest;
|
|
17117
17240
|
const previousFiles = new Map;
|
|
17118
17241
|
for (const file of snapshot.files) {
|
|
17119
|
-
if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" ||
|
|
17242
|
+
if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha2568(file.content) !== file.sha256) {
|
|
17120
17243
|
throw new SessionApplyError(`Session snapshot previous file metadata is invalid: ${snapshotPath}`);
|
|
17121
17244
|
}
|
|
17122
17245
|
resolveSnapshotFilePath(file.relativePath, file.path, targetHome);
|
|
@@ -17163,7 +17286,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
17163
17286
|
}
|
|
17164
17287
|
function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
|
|
17165
17288
|
assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
|
|
17166
|
-
const manifestPath =
|
|
17289
|
+
const manifestPath = resolve10(snapshot.manifestPath);
|
|
17167
17290
|
const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
|
|
17168
17291
|
resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
|
|
17169
17292
|
const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
|
|
@@ -17172,7 +17295,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
17172
17295
|
}
|
|
17173
17296
|
let parsedManifest;
|
|
17174
17297
|
try {
|
|
17175
|
-
parsedManifest = JSON.parse(
|
|
17298
|
+
parsedManifest = JSON.parse(readFileSync10(manifestPath, "utf8"));
|
|
17176
17299
|
} catch {
|
|
17177
17300
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is not valid JSON: ${snapshotPath}`);
|
|
17178
17301
|
}
|
|
@@ -17180,7 +17303,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
17180
17303
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is invalid: ${snapshotPath}`);
|
|
17181
17304
|
}
|
|
17182
17305
|
const appliedManifest = parsedManifest;
|
|
17183
|
-
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" ||
|
|
17306
|
+
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve10(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
|
|
17184
17307
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest does not match its snapshot: ${snapshotPath}`);
|
|
17185
17308
|
}
|
|
17186
17309
|
const afterFiles = [];
|
|
@@ -17265,16 +17388,16 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
17265
17388
|
throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
|
|
17266
17389
|
}
|
|
17267
17390
|
for (const entry of readdirSync3(dirname5(snapshotPath))) {
|
|
17268
|
-
const candidatePath =
|
|
17269
|
-
if (candidatePath ===
|
|
17391
|
+
const candidatePath = resolve10(dirname5(snapshotPath), entry);
|
|
17392
|
+
if (candidatePath === resolve10(snapshotPath) || !entry.endsWith(".json"))
|
|
17270
17393
|
continue;
|
|
17271
|
-
const candidateStat =
|
|
17394
|
+
const candidateStat = lstatSync4(candidatePath);
|
|
17272
17395
|
if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
|
|
17273
17396
|
continue;
|
|
17274
17397
|
try {
|
|
17275
|
-
const candidate = JSON.parse(
|
|
17398
|
+
const candidate = JSON.parse(readFileSync10(candidatePath, "utf8"));
|
|
17276
17399
|
const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
|
|
17277
|
-
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" &&
|
|
17400
|
+
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve10(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
|
|
17278
17401
|
throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
|
|
17279
17402
|
}
|
|
17280
17403
|
} catch (error) {
|
|
@@ -17332,7 +17455,7 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
17332
17455
|
return "create";
|
|
17333
17456
|
}
|
|
17334
17457
|
if (file.role === "manifest" && previousManifest) {
|
|
17335
|
-
const previousManifestSha256 =
|
|
17458
|
+
const previousManifestSha256 = sha2568(`${JSON.stringify(previousManifest, null, 2)}
|
|
17336
17459
|
`);
|
|
17337
17460
|
if (previousManifestSha256 !== file.sha256) {
|
|
17338
17461
|
throw new SessionApplyError(`Cannot infer legacy v1 manifest action without a before-image: ${file.relativePath}`);
|
|
@@ -17343,15 +17466,15 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
17343
17466
|
}
|
|
17344
17467
|
function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
17345
17468
|
const path = resolveManifestRelativePath(relativePath, targetHome);
|
|
17346
|
-
if (
|
|
17469
|
+
if (resolve10(recordedPath) !== path) {
|
|
17347
17470
|
throw new SessionApplyError(`Session snapshot file path mismatch for ${relativePath}`);
|
|
17348
17471
|
}
|
|
17349
17472
|
return path;
|
|
17350
17473
|
}
|
|
17351
17474
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
17352
17475
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
17353
|
-
const previousContent = existsSync12(target) ?
|
|
17354
|
-
const previousSha256 = previousContent === null ? null :
|
|
17476
|
+
const previousContent = existsSync12(target) ? readFileSync10(target, "utf-8") : null;
|
|
17477
|
+
const previousSha256 = previousContent === null ? null : sha2568(previousContent);
|
|
17355
17478
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
17356
17479
|
const changed = previousContent !== file.content;
|
|
17357
17480
|
if (previousContent !== null && !options.force && !previouslyManaged) {
|
|
@@ -17448,8 +17571,8 @@ function planStaleFileResult(file, targetHome, options) {
|
|
|
17448
17571
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
17449
17572
|
if (!existsSync12(target))
|
|
17450
17573
|
return null;
|
|
17451
|
-
const previousContent =
|
|
17452
|
-
const previousSha256 =
|
|
17574
|
+
const previousContent = readFileSync10(target, "utf-8");
|
|
17575
|
+
const previousSha256 = sha2568(previousContent);
|
|
17453
17576
|
if (!options.force && previousSha256 !== file.sha256) {
|
|
17454
17577
|
return {
|
|
17455
17578
|
path: target,
|
|
@@ -17493,19 +17616,19 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
|
|
|
17493
17616
|
return previousHashes.get(file.relativePath) === previousSha256;
|
|
17494
17617
|
}
|
|
17495
17618
|
function resolvePlannedFilePath(plan, file, targetHome) {
|
|
17496
|
-
const target =
|
|
17619
|
+
const target = resolve10(targetHome, ...file.relativePath.split("/"));
|
|
17497
17620
|
const rel = relative5(targetHome, target);
|
|
17498
17621
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
17499
17622
|
throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
|
|
17500
17623
|
}
|
|
17501
|
-
if (
|
|
17624
|
+
if (resolve10(file.path) !== target) {
|
|
17502
17625
|
throw new SessionApplyError(`Session file path mismatch for ${file.relativePath}: ${file.path}`);
|
|
17503
17626
|
}
|
|
17504
17627
|
assertNoSymlinkSegments2(targetHome, target);
|
|
17505
17628
|
return target;
|
|
17506
17629
|
}
|
|
17507
17630
|
function resolveManifestRelativePath(relativePath, targetHome) {
|
|
17508
|
-
const target =
|
|
17631
|
+
const target = resolve10(targetHome, ...relativePath.split(/[\\/]+/));
|
|
17509
17632
|
const rel = relative5(targetHome, target);
|
|
17510
17633
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
17511
17634
|
throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
|
|
@@ -17517,7 +17640,7 @@ function readPreviousManifest(path) {
|
|
|
17517
17640
|
if (!existsSync12(path))
|
|
17518
17641
|
return null;
|
|
17519
17642
|
try {
|
|
17520
|
-
const parsed = JSON.parse(
|
|
17643
|
+
const parsed = JSON.parse(readFileSync10(path, "utf-8"));
|
|
17521
17644
|
if (parsed.schema !== SESSION_RENDER_SCHEMA)
|
|
17522
17645
|
return null;
|
|
17523
17646
|
if (!Array.isArray(parsed.files))
|
|
@@ -17558,11 +17681,11 @@ function currentSessionFileHash(path, targetHome) {
|
|
|
17558
17681
|
assertNoSymlinkSegments2(targetHome, path);
|
|
17559
17682
|
if (!existsSync12(path))
|
|
17560
17683
|
return null;
|
|
17561
|
-
const stat =
|
|
17684
|
+
const stat = lstatSync4(path);
|
|
17562
17685
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
17563
17686
|
throw new SessionApplyError(`Session apply path is not a regular file: ${path}`);
|
|
17564
17687
|
}
|
|
17565
|
-
return
|
|
17688
|
+
return sha2568(readFileSync10(path, "utf-8"));
|
|
17566
17689
|
}
|
|
17567
17690
|
function requiredPreviousHash(result) {
|
|
17568
17691
|
if (result.previousSha256 === null) {
|
|
@@ -17572,12 +17695,12 @@ function requiredPreviousHash(result) {
|
|
|
17572
17695
|
}
|
|
17573
17696
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
17574
17697
|
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync12(result.path)).map((result) => {
|
|
17575
|
-
const content =
|
|
17698
|
+
const content = readFileSync10(result.path, "utf-8");
|
|
17576
17699
|
return {
|
|
17577
17700
|
path: result.path,
|
|
17578
17701
|
relativePath: result.relativePath,
|
|
17579
17702
|
role: result.role,
|
|
17580
|
-
sha256:
|
|
17703
|
+
sha256: sha2568(content),
|
|
17581
17704
|
content
|
|
17582
17705
|
};
|
|
17583
17706
|
});
|
|
@@ -17590,7 +17713,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
17590
17713
|
};
|
|
17591
17714
|
}
|
|
17592
17715
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
17593
|
-
const snapshotPath =
|
|
17716
|
+
const snapshotPath = resolve10(targetHome, ".hasna", "session-render-snapshots", `${timestamp}-${randomUUID7()}.json`);
|
|
17594
17717
|
const afterFiles = results.map((result) => {
|
|
17595
17718
|
if (result.action === "conflict") {
|
|
17596
17719
|
throw new SessionApplyError(`Cannot snapshot unresolved conflict: ${result.relativePath}`);
|
|
@@ -17638,12 +17761,12 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
17638
17761
|
function assertSafeTargetHome(targetHome) {
|
|
17639
17762
|
if (!isAbsolute4(targetHome))
|
|
17640
17763
|
throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
|
|
17641
|
-
const normalized =
|
|
17764
|
+
const normalized = resolve10(targetHome);
|
|
17642
17765
|
if (normalized === parse4(normalized).root) {
|
|
17643
17766
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
17644
17767
|
}
|
|
17645
17768
|
assertNoSymlinkAncestors2(normalized);
|
|
17646
|
-
if (existsSync12(normalized) &&
|
|
17769
|
+
if (existsSync12(normalized) && lstatSync4(normalized).isSymbolicLink()) {
|
|
17647
17770
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
17648
17771
|
}
|
|
17649
17772
|
return normalized;
|
|
@@ -17653,32 +17776,33 @@ function assertNoSymlinkSegments2(root, target) {
|
|
|
17653
17776
|
const rel = relative5(root, target);
|
|
17654
17777
|
let current = root;
|
|
17655
17778
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
17656
|
-
current =
|
|
17657
|
-
if (existsSync12(current) &&
|
|
17779
|
+
current = join15(current, segment);
|
|
17780
|
+
if (existsSync12(current) && lstatSync4(current).isSymbolicLink()) {
|
|
17658
17781
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
17659
17782
|
}
|
|
17660
17783
|
}
|
|
17661
17784
|
}
|
|
17662
17785
|
function assertNoSymlinkAncestors2(path) {
|
|
17663
|
-
const normalized =
|
|
17786
|
+
const normalized = resolve10(path);
|
|
17664
17787
|
const parsed = parse4(normalized);
|
|
17665
17788
|
let current = parsed.root;
|
|
17666
17789
|
const rel = relative5(parsed.root, normalized);
|
|
17667
17790
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
17668
|
-
current =
|
|
17791
|
+
current = join15(current, segment);
|
|
17669
17792
|
if (!existsSync12(current))
|
|
17670
17793
|
return;
|
|
17671
|
-
if (
|
|
17794
|
+
if (lstatSync4(current).isSymbolicLink()) {
|
|
17672
17795
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
17673
17796
|
}
|
|
17674
17797
|
}
|
|
17675
17798
|
}
|
|
17676
|
-
function
|
|
17677
|
-
return
|
|
17799
|
+
function sha2568(content) {
|
|
17800
|
+
return createHash8("sha256").update(content).digest("hex");
|
|
17678
17801
|
}
|
|
17679
17802
|
|
|
17680
17803
|
// src/cli/index.tsx
|
|
17681
17804
|
init_session_render();
|
|
17805
|
+
init_raw_store_root();
|
|
17682
17806
|
init_asset_plan();
|
|
17683
17807
|
init_instruction_graph();
|
|
17684
17808
|
|
|
@@ -18025,34 +18149,433 @@ async function ensureDangerousOperationGuardStandardConfig(store = resolveConfig
|
|
|
18025
18149
|
|
|
18026
18150
|
// src/cli/index.tsx
|
|
18027
18151
|
init_codewith_shared_todos_storage_standard();
|
|
18152
|
+
|
|
18153
|
+
// src/lib/managed-skill-runtimes.ts
|
|
18154
|
+
import { createHash as createHash9 } from "crypto";
|
|
18155
|
+
import { spawnSync } from "child_process";
|
|
18156
|
+
import {
|
|
18157
|
+
existsSync as existsSync13,
|
|
18158
|
+
lstatSync as lstatSync5,
|
|
18159
|
+
mkdirSync as mkdirSync7,
|
|
18160
|
+
readFileSync as readFileSync11,
|
|
18161
|
+
renameSync as renameSync2,
|
|
18162
|
+
rmSync as rmSync5,
|
|
18163
|
+
writeFileSync as writeFileSync4
|
|
18164
|
+
} from "fs";
|
|
18165
|
+
import { homedir as homedir8 } from "os";
|
|
18166
|
+
import { dirname as dirname6, join as join16, parse as parse5, relative as relative6, resolve as resolve11 } from "path";
|
|
18167
|
+
var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
|
|
18168
|
+
var INBOX_SKILL_MARKERS = [
|
|
18169
|
+
[".claude", "skills", "inbox", "SKILL.md"],
|
|
18170
|
+
[".codex", "skills", "inbox", "SKILL.md"],
|
|
18171
|
+
[".codewith", "skills", "inbox", "SKILL.md"],
|
|
18172
|
+
[".config", "opencode", "skills", "inbox", "SKILL.md"],
|
|
18173
|
+
[".cursor", "skills", "inbox", "SKILL.md"]
|
|
18174
|
+
];
|
|
18175
|
+
var REQUIRED_WATCH_FLAGS = ["--from <agent>", "--all", "--full-content"];
|
|
18176
|
+
function sha2569(content) {
|
|
18177
|
+
return createHash9("sha256").update(content).digest("hex");
|
|
18178
|
+
}
|
|
18179
|
+
function lstatOrNull(path) {
|
|
18180
|
+
try {
|
|
18181
|
+
return lstatSync5(path);
|
|
18182
|
+
} catch {
|
|
18183
|
+
return null;
|
|
18184
|
+
}
|
|
18185
|
+
}
|
|
18186
|
+
function findSymlinkedAncestor(path) {
|
|
18187
|
+
const normalized = resolve11(path);
|
|
18188
|
+
const parsed = parse5(normalized);
|
|
18189
|
+
let current = parsed.root;
|
|
18190
|
+
const rel = relative6(parsed.root, normalized);
|
|
18191
|
+
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18192
|
+
current = join16(current, segment);
|
|
18193
|
+
if (!existsSync13(current))
|
|
18194
|
+
return null;
|
|
18195
|
+
if (lstatSync5(current).isSymbolicLink())
|
|
18196
|
+
return current;
|
|
18197
|
+
}
|
|
18198
|
+
return null;
|
|
18199
|
+
}
|
|
18200
|
+
function assertNoSymlinkAncestors3(path) {
|
|
18201
|
+
const found = findSymlinkedAncestor(path);
|
|
18202
|
+
if (found !== null) {
|
|
18203
|
+
throw new Error(`managed skill path uses a symlink ancestor: ${found}`);
|
|
18204
|
+
}
|
|
18205
|
+
}
|
|
18206
|
+
function packagedInboxSkillPath(explicitPath) {
|
|
18207
|
+
if (explicitPath)
|
|
18208
|
+
return explicitPath;
|
|
18209
|
+
const candidates = [
|
|
18210
|
+
join16(import.meta.dir, "..", "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
18211
|
+
join16(import.meta.dir, "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
18212
|
+
join16(process.cwd(), "assets", "skills", "inbox", "SKILL.md")
|
|
18213
|
+
];
|
|
18214
|
+
const found = candidates.find((candidate) => existsSync13(candidate));
|
|
18215
|
+
if (!found) {
|
|
18216
|
+
throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
|
|
18217
|
+
}
|
|
18218
|
+
return found;
|
|
18219
|
+
}
|
|
18220
|
+
function readCanonicalSkill(explicitPath) {
|
|
18221
|
+
const assetPath = packagedInboxSkillPath(explicitPath);
|
|
18222
|
+
const stat = lstatOrNull(assetPath);
|
|
18223
|
+
if (!stat?.isFile()) {
|
|
18224
|
+
throw new Error("packaged inbox skill contract is not a regular file");
|
|
18225
|
+
}
|
|
18226
|
+
const content = readFileSync11(assetPath, "utf8");
|
|
18227
|
+
if (!content.includes("conversations watch --from <agent> --all")) {
|
|
18228
|
+
throw new Error("packaged inbox skill contract does not declare the canonical conversations watcher");
|
|
18229
|
+
}
|
|
18230
|
+
if (!content.includes("There is no separate")) {
|
|
18231
|
+
throw new Error("packaged inbox skill contract does not retire the legacy executable");
|
|
18232
|
+
}
|
|
18233
|
+
return { content, sha256: sha2569(content) };
|
|
18234
|
+
}
|
|
18235
|
+
function runProbe(command, args) {
|
|
18236
|
+
const result = spawnSync(command, args, {
|
|
18237
|
+
encoding: "utf8",
|
|
18238
|
+
timeout: 5000,
|
|
18239
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
18240
|
+
});
|
|
18241
|
+
if (result.error || result.status !== 0) {
|
|
18242
|
+
return { ok: false, output: "" };
|
|
18243
|
+
}
|
|
18244
|
+
return {
|
|
18245
|
+
ok: true,
|
|
18246
|
+
output: `${result.stdout ?? ""}
|
|
18247
|
+
${result.stderr ?? ""}`.trim()
|
|
18248
|
+
};
|
|
18249
|
+
}
|
|
18250
|
+
function parseVersion(output) {
|
|
18251
|
+
return output.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
|
|
18252
|
+
}
|
|
18253
|
+
function compareVersions(left, right) {
|
|
18254
|
+
const a = left.split(".").map(Number);
|
|
18255
|
+
const b = right.split(".").map(Number);
|
|
18256
|
+
for (let i = 0;i < Math.max(a.length, b.length); i++) {
|
|
18257
|
+
const delta = (a[i] ?? 0) - (b[i] ?? 0);
|
|
18258
|
+
if (delta !== 0)
|
|
18259
|
+
return delta;
|
|
18260
|
+
}
|
|
18261
|
+
return 0;
|
|
18262
|
+
}
|
|
18263
|
+
function inspectSkillMarkers(homeDir2) {
|
|
18264
|
+
return INBOX_SKILL_MARKERS.map((parts) => join16(homeDir2, ...parts)).map((path) => {
|
|
18265
|
+
const stat = lstatOrNull(path);
|
|
18266
|
+
if (!stat)
|
|
18267
|
+
return null;
|
|
18268
|
+
if (!stat.isFile()) {
|
|
18269
|
+
return { path, content: null, mode: null, regular: false };
|
|
18270
|
+
}
|
|
18271
|
+
return {
|
|
18272
|
+
path,
|
|
18273
|
+
content: readFileSync11(path, "utf8"),
|
|
18274
|
+
mode: stat.mode & 511,
|
|
18275
|
+
regular: true
|
|
18276
|
+
};
|
|
18277
|
+
}).filter((snapshot) => snapshot !== null);
|
|
18278
|
+
}
|
|
18279
|
+
function inspectInbox(options) {
|
|
18280
|
+
const homeDir2 = options.homeDir ?? homedir8();
|
|
18281
|
+
const runtimeCommand = options.conversationsCommand ?? "conversations";
|
|
18282
|
+
const snapshots = inspectSkillMarkers(homeDir2);
|
|
18283
|
+
const skillPresent = snapshots.length > 0;
|
|
18284
|
+
let canonicalContent = null;
|
|
18285
|
+
let canonicalSha256 = null;
|
|
18286
|
+
let assetError = null;
|
|
18287
|
+
try {
|
|
18288
|
+
const canonical = readCanonicalSkill(options.assetPath);
|
|
18289
|
+
canonicalContent = canonical.content;
|
|
18290
|
+
canonicalSha256 = canonical.sha256;
|
|
18291
|
+
} catch (error) {
|
|
18292
|
+
assetError = error instanceof Error ? error.message : String(error);
|
|
18293
|
+
}
|
|
18294
|
+
const versionProbe = skillPresent ? runProbe(runtimeCommand, ["--version"]) : { ok: false, output: "" };
|
|
18295
|
+
const helpProbe = versionProbe.ok ? runProbe(runtimeCommand, ["watch", "--help"]) : { ok: false, output: "" };
|
|
18296
|
+
const runtimeVersion = versionProbe.ok ? parseVersion(versionProbe.output) : null;
|
|
18297
|
+
const supportsFrom = helpProbe.ok && helpProbe.output.includes(REQUIRED_WATCH_FLAGS[0]);
|
|
18298
|
+
const supportsAll = helpProbe.ok && helpProbe.output.includes(REQUIRED_WATCH_FLAGS[1]);
|
|
18299
|
+
const supportsFullContent = helpProbe.ok && helpProbe.output.includes(REQUIRED_WATCH_FLAGS[2]);
|
|
18300
|
+
const packageReady = versionProbe.ok && runtimeVersion !== null && compareVersions(runtimeVersion, INBOX_CONVERSATIONS_MINIMUM_VERSION) >= 0 && helpProbe.ok && supportsFrom && supportsAll && supportsFullContent;
|
|
18301
|
+
const heartbeatProbe = skillPresent && packageReady && options.agent ? runProbe(runtimeCommand, ["agents", "heartbeat", "--from", options.agent, "--json"]) : null;
|
|
18302
|
+
const hostedHeartbeat = heartbeatProbe === null ? "unverified" : heartbeatProbe.ok ? "passed" : "failed";
|
|
18303
|
+
const deliveryVerified = hostedHeartbeat === "passed" && options.deliveryVerified === true;
|
|
18304
|
+
const staleMarkers = canonicalContent === null ? snapshots.map((snapshot) => snapshot.path) : snapshots.filter((snapshot) => !snapshot.regular || snapshot.content !== canonicalContent).map((snapshot) => snapshot.path);
|
|
18305
|
+
let reason = "skill not installed";
|
|
18306
|
+
if (skillPresent) {
|
|
18307
|
+
const nonRegular = snapshots.some((snapshot) => !snapshot.regular);
|
|
18308
|
+
const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname6(path))).find((found) => found !== null);
|
|
18309
|
+
if (nonRegular)
|
|
18310
|
+
reason = "managed skill target is not a regular file";
|
|
18311
|
+
else if (symlinkAncestor)
|
|
18312
|
+
reason = `managed skill path uses a symlink ancestor: ${symlinkAncestor}`;
|
|
18313
|
+
else if (assetError)
|
|
18314
|
+
reason = assetError;
|
|
18315
|
+
else if (!versionProbe.ok)
|
|
18316
|
+
reason = "conversations command unavailable";
|
|
18317
|
+
else if (!runtimeVersion)
|
|
18318
|
+
reason = "conversations version is unreadable";
|
|
18319
|
+
else if (compareVersions(runtimeVersion, INBOX_CONVERSATIONS_MINIMUM_VERSION) < 0) {
|
|
18320
|
+
reason = `conversations ${runtimeVersion} is older than ${INBOX_CONVERSATIONS_MINIMUM_VERSION}`;
|
|
18321
|
+
} else if (!helpProbe.ok)
|
|
18322
|
+
reason = "conversations watch help is unavailable";
|
|
18323
|
+
else if (!supportsFrom || !supportsAll || !supportsFullContent) {
|
|
18324
|
+
const missing = [
|
|
18325
|
+
!supportsFrom ? "--from" : null,
|
|
18326
|
+
!supportsAll ? "--all" : null,
|
|
18327
|
+
!supportsFullContent ? "--full-content" : null
|
|
18328
|
+
].filter((flag) => flag !== null);
|
|
18329
|
+
reason = `conversations watch is missing required flags: ${missing.join(", ")}`;
|
|
18330
|
+
} else if (staleMarkers.length > 0)
|
|
18331
|
+
reason = "skill contract stale";
|
|
18332
|
+
else if (hostedHeartbeat === "failed")
|
|
18333
|
+
reason = "hosted heartbeat failed; manual fallback required";
|
|
18334
|
+
else if (hostedHeartbeat === "unverified")
|
|
18335
|
+
reason = "hosted heartbeat unverified; manual fallback required";
|
|
18336
|
+
else if (!deliveryVerified)
|
|
18337
|
+
reason = "hosted heartbeat passed; channel and DM delivery verification required";
|
|
18338
|
+
else
|
|
18339
|
+
reason = "ready";
|
|
18340
|
+
}
|
|
18341
|
+
return {
|
|
18342
|
+
status: {
|
|
18343
|
+
skill: "inbox",
|
|
18344
|
+
runtime: "conversations watch",
|
|
18345
|
+
minimum_version: INBOX_CONVERSATIONS_MINIMUM_VERSION,
|
|
18346
|
+
skill_present: skillPresent,
|
|
18347
|
+
skill_markers: snapshots.map((snapshot) => snapshot.path),
|
|
18348
|
+
skill_contracts_current: snapshots.length - staleMarkers.length,
|
|
18349
|
+
stale_skill_markers: staleMarkers,
|
|
18350
|
+
expected_skill_sha256: canonicalSha256,
|
|
18351
|
+
runtime_command: runtimeCommand,
|
|
18352
|
+
runtime_present: versionProbe.ok,
|
|
18353
|
+
runtime_version: runtimeVersion,
|
|
18354
|
+
watch_supports_from: supportsFrom,
|
|
18355
|
+
watch_supports_all: supportsAll,
|
|
18356
|
+
watch_supports_full_content: supportsFullContent,
|
|
18357
|
+
hosted_heartbeat: hostedHeartbeat,
|
|
18358
|
+
delivery_verified: deliveryVerified,
|
|
18359
|
+
manual_fallback_ready: skillPresent && staleMarkers.length === 0 && packageReady,
|
|
18360
|
+
healthy: !skillPresent || reason === "ready",
|
|
18361
|
+
reason
|
|
18362
|
+
},
|
|
18363
|
+
canonicalContent,
|
|
18364
|
+
snapshots
|
|
18365
|
+
};
|
|
18366
|
+
}
|
|
18367
|
+
function inspectManagedSkillRuntimes(options = {}) {
|
|
18368
|
+
const runtime = inspectInbox(options).status;
|
|
18369
|
+
const installed = runtime.skill_present ? [runtime] : [];
|
|
18370
|
+
return {
|
|
18371
|
+
runtimes: [runtime],
|
|
18372
|
+
skills_present: installed.length,
|
|
18373
|
+
healthy: installed.filter((item) => item.healthy).length,
|
|
18374
|
+
missing: installed.filter((item) => !item.healthy).length
|
|
18375
|
+
};
|
|
18376
|
+
}
|
|
18377
|
+
function runtimeReadyForWrite(status) {
|
|
18378
|
+
return status.runtime_present && status.runtime_version !== null && compareVersions(status.runtime_version, INBOX_CONVERSATIONS_MINIMUM_VERSION) >= 0 && status.watch_supports_from && status.watch_supports_all && status.watch_supports_full_content;
|
|
18379
|
+
}
|
|
18380
|
+
function projectUpdatedStatus(status, contractCount) {
|
|
18381
|
+
const healthy = status.hosted_heartbeat === "passed" && status.delivery_verified;
|
|
18382
|
+
return {
|
|
18383
|
+
...status,
|
|
18384
|
+
skill_contracts_current: contractCount,
|
|
18385
|
+
stale_skill_markers: [],
|
|
18386
|
+
manual_fallback_ready: true,
|
|
18387
|
+
healthy,
|
|
18388
|
+
reason: healthy ? "ready" : status.hosted_heartbeat === "failed" ? "hosted heartbeat failed; manual fallback required" : status.hosted_heartbeat === "unverified" ? "hosted heartbeat unverified; manual fallback required" : "hosted heartbeat passed; channel and DM delivery verification required"
|
|
18389
|
+
};
|
|
18390
|
+
}
|
|
18391
|
+
function cleanup(path) {
|
|
18392
|
+
rmSync5(path, { force: true });
|
|
18393
|
+
}
|
|
18394
|
+
function writeAtomic(path, content, mode) {
|
|
18395
|
+
assertNoSymlinkAncestors3(dirname6(path));
|
|
18396
|
+
const tempPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
18397
|
+
try {
|
|
18398
|
+
mkdirSync7(dirname6(path), { recursive: true, mode: 493 });
|
|
18399
|
+
writeFileSync4(tempPath, content, { mode, flag: "wx" });
|
|
18400
|
+
renameSync2(tempPath, path);
|
|
18401
|
+
} finally {
|
|
18402
|
+
cleanup(tempPath);
|
|
18403
|
+
}
|
|
18404
|
+
}
|
|
18405
|
+
var DEFAULT_SKILL_WRITE_FILE_OPERATIONS = {
|
|
18406
|
+
lstat: lstatOrNull,
|
|
18407
|
+
read: (path) => readFileSync11(path, "utf8"),
|
|
18408
|
+
write: writeAtomic
|
|
18409
|
+
};
|
|
18410
|
+
function writeSkillContractsTransactional(snapshots, canonicalContent, fileOperations = DEFAULT_SKILL_WRITE_FILE_OPERATIONS) {
|
|
18411
|
+
const written = [];
|
|
18412
|
+
try {
|
|
18413
|
+
for (const snapshot of snapshots) {
|
|
18414
|
+
const currentStat = fileOperations.lstat(snapshot.path);
|
|
18415
|
+
if (!currentStat?.isFile() || fileOperations.read(snapshot.path) !== snapshot.content) {
|
|
18416
|
+
throw new Error("managed skill changed after inspection; refusing a stale write");
|
|
18417
|
+
}
|
|
18418
|
+
fileOperations.write(snapshot.path, canonicalContent, snapshot.mode);
|
|
18419
|
+
written.push(snapshot);
|
|
18420
|
+
}
|
|
18421
|
+
return { ok: true, error: null, rollback_conflicts: [] };
|
|
18422
|
+
} catch (error) {
|
|
18423
|
+
const rollbackConflicts = [];
|
|
18424
|
+
for (const snapshot of written.reverse()) {
|
|
18425
|
+
const currentStat = fileOperations.lstat(snapshot.path);
|
|
18426
|
+
if (!currentStat?.isFile()) {
|
|
18427
|
+
rollbackConflicts.push(`${snapshot.path}: no longer a regular file`);
|
|
18428
|
+
continue;
|
|
18429
|
+
}
|
|
18430
|
+
let currentContent;
|
|
18431
|
+
try {
|
|
18432
|
+
currentContent = fileOperations.read(snapshot.path);
|
|
18433
|
+
} catch {
|
|
18434
|
+
rollbackConflicts.push(`${snapshot.path}: could not read the current file`);
|
|
18435
|
+
continue;
|
|
18436
|
+
}
|
|
18437
|
+
if (currentContent !== canonicalContent) {
|
|
18438
|
+
rollbackConflicts.push(`${snapshot.path}: changed after this reconciliation wrote it`);
|
|
18439
|
+
continue;
|
|
18440
|
+
}
|
|
18441
|
+
try {
|
|
18442
|
+
fileOperations.write(snapshot.path, snapshot.content, snapshot.mode);
|
|
18443
|
+
} catch {
|
|
18444
|
+
rollbackConflicts.push(`${snapshot.path}: still owned but could not be restored`);
|
|
18445
|
+
}
|
|
18446
|
+
}
|
|
18447
|
+
return {
|
|
18448
|
+
ok: false,
|
|
18449
|
+
error: error instanceof Error ? error.message : String(error),
|
|
18450
|
+
rollback_conflicts: rollbackConflicts
|
|
18451
|
+
};
|
|
18452
|
+
}
|
|
18453
|
+
}
|
|
18454
|
+
async function reconcileManagedSkillRuntimes(options = {}) {
|
|
18455
|
+
const dryRun = options.dryRun ?? false;
|
|
18456
|
+
const before = inspectInbox(options);
|
|
18457
|
+
const status = before.status;
|
|
18458
|
+
if (!status.skill_present) {
|
|
18459
|
+
return {
|
|
18460
|
+
runtimes: [{ ...status, action: "skipped", dry_run: dryRun, skill_contracts_changed: 0 }],
|
|
18461
|
+
changed: 0,
|
|
18462
|
+
failed: 0,
|
|
18463
|
+
dry_run: dryRun
|
|
18464
|
+
};
|
|
18465
|
+
}
|
|
18466
|
+
if (before.snapshots.some((snapshot) => !snapshot.regular)) {
|
|
18467
|
+
return {
|
|
18468
|
+
runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
|
|
18469
|
+
changed: 0,
|
|
18470
|
+
failed: 1,
|
|
18471
|
+
dry_run: dryRun
|
|
18472
|
+
};
|
|
18473
|
+
}
|
|
18474
|
+
const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname6(path))).find((found) => found !== null);
|
|
18475
|
+
if (symlinkedAncestor) {
|
|
18476
|
+
return {
|
|
18477
|
+
runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
|
|
18478
|
+
changed: 0,
|
|
18479
|
+
failed: 1,
|
|
18480
|
+
dry_run: dryRun
|
|
18481
|
+
};
|
|
18482
|
+
}
|
|
18483
|
+
if (!before.canonicalContent || !runtimeReadyForWrite(status)) {
|
|
18484
|
+
return {
|
|
18485
|
+
runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
|
|
18486
|
+
changed: 0,
|
|
18487
|
+
failed: 1,
|
|
18488
|
+
dry_run: dryRun
|
|
18489
|
+
};
|
|
18490
|
+
}
|
|
18491
|
+
const staleSnapshots = before.snapshots.filter((snapshot) => snapshot.content !== before.canonicalContent);
|
|
18492
|
+
if (staleSnapshots.length === 0) {
|
|
18493
|
+
return {
|
|
18494
|
+
runtimes: [{ ...status, action: "unchanged", dry_run: dryRun, skill_contracts_changed: 0 }],
|
|
18495
|
+
changed: 0,
|
|
18496
|
+
failed: 0,
|
|
18497
|
+
dry_run: dryRun
|
|
18498
|
+
};
|
|
18499
|
+
}
|
|
18500
|
+
if (dryRun) {
|
|
18501
|
+
const projected = projectUpdatedStatus(status, before.snapshots.length);
|
|
18502
|
+
return {
|
|
18503
|
+
runtimes: [{
|
|
18504
|
+
...projected,
|
|
18505
|
+
action: "update",
|
|
18506
|
+
dry_run: true,
|
|
18507
|
+
skill_contracts_changed: staleSnapshots.length
|
|
18508
|
+
}],
|
|
18509
|
+
changed: 1,
|
|
18510
|
+
failed: 0,
|
|
18511
|
+
dry_run: true
|
|
18512
|
+
};
|
|
18513
|
+
}
|
|
18514
|
+
const transaction = writeSkillContractsTransactional(staleSnapshots.map((snapshot) => ({
|
|
18515
|
+
path: snapshot.path,
|
|
18516
|
+
content: snapshot.content,
|
|
18517
|
+
mode: snapshot.mode ?? 420
|
|
18518
|
+
})), before.canonicalContent);
|
|
18519
|
+
if (!transaction.ok) {
|
|
18520
|
+
const rollbackConflictReason = transaction.rollback_conflicts.length > 0 ? `; rollback conflicts: ${transaction.rollback_conflicts.join("; ")}` : "";
|
|
18521
|
+
const reason = `${transaction.error ?? "managed skill reconciliation failed"}${rollbackConflictReason}`;
|
|
18522
|
+
return {
|
|
18523
|
+
runtimes: [{
|
|
18524
|
+
...status,
|
|
18525
|
+
action: "failed",
|
|
18526
|
+
dry_run: false,
|
|
18527
|
+
skill_contracts_changed: 0,
|
|
18528
|
+
reason
|
|
18529
|
+
}],
|
|
18530
|
+
changed: 0,
|
|
18531
|
+
failed: 1,
|
|
18532
|
+
dry_run: false
|
|
18533
|
+
};
|
|
18534
|
+
}
|
|
18535
|
+
const after = inspectInbox(options).status;
|
|
18536
|
+
const accepted = after.healthy || after.manual_fallback_ready;
|
|
18537
|
+
return {
|
|
18538
|
+
runtimes: [{
|
|
18539
|
+
...after,
|
|
18540
|
+
action: accepted ? "update" : "failed",
|
|
18541
|
+
dry_run: false,
|
|
18542
|
+
skill_contracts_changed: accepted ? staleSnapshots.length : 0
|
|
18543
|
+
}],
|
|
18544
|
+
changed: accepted ? 1 : 0,
|
|
18545
|
+
failed: accepted ? 0 : 1,
|
|
18546
|
+
dry_run: false
|
|
18547
|
+
};
|
|
18548
|
+
}
|
|
18549
|
+
|
|
18550
|
+
// src/cli/index.tsx
|
|
18028
18551
|
init_project_context();
|
|
18029
18552
|
|
|
18030
18553
|
// src/status.ts
|
|
18031
18554
|
init_config_store();
|
|
18032
18555
|
init_apply();
|
|
18033
18556
|
init_config_agents();
|
|
18034
|
-
import { existsSync as
|
|
18557
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
18035
18558
|
|
|
18036
18559
|
// src/lib/package-version.ts
|
|
18037
|
-
import { existsSync as
|
|
18038
|
-
import { dirname as
|
|
18560
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
18561
|
+
import { dirname as dirname7, join as join17 } from "path";
|
|
18039
18562
|
import { fileURLToPath } from "url";
|
|
18040
18563
|
var cached = null;
|
|
18041
18564
|
function getPackageVersion() {
|
|
18042
18565
|
if (cached)
|
|
18043
18566
|
return cached;
|
|
18044
18567
|
try {
|
|
18045
|
-
let dir =
|
|
18568
|
+
let dir = dirname7(fileURLToPath(import.meta.url));
|
|
18046
18569
|
for (let i = 0;i < 8; i++) {
|
|
18047
|
-
const pkgPath =
|
|
18048
|
-
if (
|
|
18049
|
-
const pkg = JSON.parse(
|
|
18570
|
+
const pkgPath = join17(dir, "package.json");
|
|
18571
|
+
if (existsSync14(pkgPath)) {
|
|
18572
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
18050
18573
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
18051
18574
|
cached = pkg.version;
|
|
18052
18575
|
return cached;
|
|
18053
18576
|
}
|
|
18054
18577
|
}
|
|
18055
|
-
const parent =
|
|
18578
|
+
const parent = dirname7(dir);
|
|
18056
18579
|
if (parent === dir)
|
|
18057
18580
|
break;
|
|
18058
18581
|
dir = parent;
|
|
@@ -18085,7 +18608,7 @@ function countBy(items, getValue) {
|
|
|
18085
18608
|
}
|
|
18086
18609
|
return counts;
|
|
18087
18610
|
}
|
|
18088
|
-
async function getConfigsStatus(store = resolveConfigStore()) {
|
|
18611
|
+
async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
18089
18612
|
let databaseReachable = true;
|
|
18090
18613
|
let configs = [];
|
|
18091
18614
|
let categoryStats = { total: 0 };
|
|
@@ -18109,11 +18632,11 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
18109
18632
|
continue;
|
|
18110
18633
|
knownTargets += 1;
|
|
18111
18634
|
const targetPath = expandPath(config.target_path);
|
|
18112
|
-
if (!
|
|
18635
|
+
if (!existsSync15(targetPath)) {
|
|
18113
18636
|
missingTargets += 1;
|
|
18114
18637
|
continue;
|
|
18115
18638
|
}
|
|
18116
|
-
const disk =
|
|
18639
|
+
const disk = readFileSync13(targetPath, "utf-8");
|
|
18117
18640
|
const { content: redactedDisk } = redactContent(disk, config.format);
|
|
18118
18641
|
if (redactedDisk !== config.content) {
|
|
18119
18642
|
driftedTargets += 1;
|
|
@@ -18139,7 +18662,11 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
18139
18662
|
}
|
|
18140
18663
|
}
|
|
18141
18664
|
const byCategory = Object.fromEntries(Object.entries(categoryStats).filter(([key]) => key !== "total"));
|
|
18142
|
-
const
|
|
18665
|
+
const managedSkillRuntimes = inspectManagedSkillRuntimes({
|
|
18666
|
+
homeDir: options.homeDir,
|
|
18667
|
+
conversationsCommand: options.conversationsCommand
|
|
18668
|
+
});
|
|
18669
|
+
const status = databaseReachable && driftedTargets === 0 && missingTargets === 0 && unredactedSecretFindings === 0 && retiredAgentRows === 0 && managedSkillRuntimes.missing === 0 ? "ok" : "warn";
|
|
18143
18670
|
return {
|
|
18144
18671
|
service: "configs",
|
|
18145
18672
|
schemaVersion: "1.0",
|
|
@@ -18169,7 +18696,12 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
18169
18696
|
profileLinks,
|
|
18170
18697
|
machines,
|
|
18171
18698
|
snapshots,
|
|
18172
|
-
knownTargets
|
|
18699
|
+
knownTargets,
|
|
18700
|
+
managedSkillRuntimes: {
|
|
18701
|
+
skillsPresent: managedSkillRuntimes.skills_present,
|
|
18702
|
+
healthy: managedSkillRuntimes.healthy,
|
|
18703
|
+
missing: managedSkillRuntimes.missing
|
|
18704
|
+
}
|
|
18173
18705
|
},
|
|
18174
18706
|
health: {
|
|
18175
18707
|
status,
|
|
@@ -18178,10 +18710,12 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
18178
18710
|
missingTargets,
|
|
18179
18711
|
unredactedSecretFindings,
|
|
18180
18712
|
retiredAgentRows,
|
|
18713
|
+
missingManagedSkillRuntimes: managedSkillRuntimes.missing,
|
|
18181
18714
|
hasDrift: driftedTargets > 0,
|
|
18182
18715
|
hasMissingTargets: missingTargets > 0,
|
|
18183
18716
|
hasUnredactedSecrets: unredactedSecretFindings > 0,
|
|
18184
|
-
hasRetiredAgentRows: retiredAgentRows > 0
|
|
18717
|
+
hasRetiredAgentRows: retiredAgentRows > 0,
|
|
18718
|
+
hasMissingManagedSkillRuntimes: managedSkillRuntimes.missing > 0
|
|
18185
18719
|
},
|
|
18186
18720
|
safety: {
|
|
18187
18721
|
includesConfigValues: false,
|
|
@@ -18223,6 +18757,17 @@ function printLine(text = "") {
|
|
|
18223
18757
|
function printJson(value) {
|
|
18224
18758
|
printLine(JSON.stringify(value, null, 2));
|
|
18225
18759
|
}
|
|
18760
|
+
function printManagedSkillRuntimeReport(report) {
|
|
18761
|
+
for (const runtime of report.runtimes) {
|
|
18762
|
+
if (!runtime.skill_present)
|
|
18763
|
+
continue;
|
|
18764
|
+
const prefix = runtime.action === "failed" ? chalk.red("[failed]") : runtime.dry_run ? chalk.yellow("[dry-run]") : runtime.manual_fallback_ready && !runtime.healthy ? chalk.yellow("[degraded]") : runtime.action === "unchanged" ? chalk.dim("=") : chalk.green("\u2713");
|
|
18765
|
+
console.log(`${prefix} ${runtime.skill} via ${runtime.runtime} \u2014 ${runtime.reason}`);
|
|
18766
|
+
if (runtime.action === "update") {
|
|
18767
|
+
console.log(chalk.dim(` skill contracts: ${runtime.skill_contracts_changed}`));
|
|
18768
|
+
}
|
|
18769
|
+
}
|
|
18770
|
+
}
|
|
18226
18771
|
function fmtConfig(c, format) {
|
|
18227
18772
|
if (format === "json")
|
|
18228
18773
|
return JSON.stringify(c, null, 2);
|
|
@@ -18289,7 +18834,7 @@ function parseSessionSource(value, order) {
|
|
|
18289
18834
|
if (!path)
|
|
18290
18835
|
throw new Error(`Invalid --source "${value}" (expected path or id=path)`);
|
|
18291
18836
|
const absPath = resolveSessionPath(path);
|
|
18292
|
-
if (!
|
|
18837
|
+
if (!existsSync17(absPath))
|
|
18293
18838
|
throw new Error(`Instruction source file not found: ${absPath}`);
|
|
18294
18839
|
const content = readSessionInstructionSourceFile(absPath);
|
|
18295
18840
|
const source = sourceFromFilePath(absPath, content, order);
|
|
@@ -18333,7 +18878,7 @@ function sessionSourceReplacements(values) {
|
|
|
18333
18878
|
return replacements;
|
|
18334
18879
|
}
|
|
18335
18880
|
function readSessionInstructionSourceFile(path) {
|
|
18336
|
-
const stat =
|
|
18881
|
+
const stat = lstatSync7(path);
|
|
18337
18882
|
if (stat.isSymbolicLink()) {
|
|
18338
18883
|
throw new Error("SESSION_SOURCE_SYMLINK_REJECTED: instruction source file must be a regular non-symlink file");
|
|
18339
18884
|
}
|
|
@@ -18343,7 +18888,7 @@ function readSessionInstructionSourceFile(path) {
|
|
|
18343
18888
|
if (stat.size > SESSION_MANAGED_INPUT_MAX_BYTES) {
|
|
18344
18889
|
throw new Error(`SESSION_SOURCE_INPUT_TOO_LARGE: instruction source file exceeds ${SESSION_MANAGED_INPUT_MAX_BYTES} bytes`);
|
|
18345
18890
|
}
|
|
18346
|
-
return
|
|
18891
|
+
return readFileSync15(path, "utf-8");
|
|
18347
18892
|
}
|
|
18348
18893
|
function parseLayeredReference(value) {
|
|
18349
18894
|
const trimmed = value.trim();
|
|
@@ -18370,9 +18915,9 @@ async function collectSessionSources(opts, tool, store) {
|
|
|
18370
18915
|
}
|
|
18371
18916
|
for (const value of opts.identityExport ?? []) {
|
|
18372
18917
|
const path = resolveSessionPath(value);
|
|
18373
|
-
if (!
|
|
18918
|
+
if (!existsSync17(path))
|
|
18374
18919
|
throw new Error(`Identity instruction export not found: ${path}`);
|
|
18375
|
-
const parsed = JSON.parse(
|
|
18920
|
+
const parsed = JSON.parse(readFileSync15(path, "utf-8"));
|
|
18376
18921
|
sources.push(...sourcesFromIdentityExport(parsed, { path, tool, orderOffset: sources.length }));
|
|
18377
18922
|
}
|
|
18378
18923
|
return sources.map((source) => {
|
|
@@ -18493,19 +19038,19 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
18493
19038
|
if (value === "-")
|
|
18494
19039
|
return { json: readBoundedProjectContextStdin() };
|
|
18495
19040
|
const path = resolveSessionPath(value);
|
|
18496
|
-
if (!
|
|
19041
|
+
if (!existsSync17(path)) {
|
|
18497
19042
|
if (allowMissing)
|
|
18498
19043
|
return {};
|
|
18499
19044
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_MISSING", `bundle file not found: ${path}`);
|
|
18500
19045
|
}
|
|
18501
|
-
const stat =
|
|
19046
|
+
const stat = lstatSync7(path);
|
|
18502
19047
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
18503
19048
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", "bundle input must be a regular non-symlink file");
|
|
18504
19049
|
}
|
|
18505
19050
|
if (stat.size > PROJECT_CONTEXT_MAX_INPUT_BYTES) {
|
|
18506
19051
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_TOO_LARGE", `bundle exceeds ${PROJECT_CONTEXT_MAX_INPUT_BYTES} bytes`);
|
|
18507
19052
|
}
|
|
18508
|
-
return { json:
|
|
19053
|
+
return { json: readFileSync15(path, "utf8"), sourcePath: path };
|
|
18509
19054
|
}
|
|
18510
19055
|
function readBoundedProjectContextStdin() {
|
|
18511
19056
|
const chunks = [];
|
|
@@ -18664,15 +19209,15 @@ program.command("tag <id>").description("Add or remove tags on a stored config (
|
|
|
18664
19209
|
console.log(chalk.green("\u2713") + ` Tags on ${chalk.bold(updated.name)} ${chalk.dim(`(${updated.slug})`)}: ${nextTags.join(", ") || chalk.dim("(none)")}`);
|
|
18665
19210
|
});
|
|
18666
19211
|
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) => {
|
|
18667
|
-
const abs =
|
|
18668
|
-
if (!
|
|
19212
|
+
const abs = resolve13(filePath);
|
|
19213
|
+
if (!existsSync17(abs)) {
|
|
18669
19214
|
console.error(chalk.red(`File not found: ${abs}`));
|
|
18670
19215
|
process.exit(1);
|
|
18671
19216
|
}
|
|
18672
|
-
const rawContent =
|
|
19217
|
+
const rawContent = readFileSync15(abs, "utf-8");
|
|
18673
19218
|
const fmt = detectFormat(abs);
|
|
18674
19219
|
const { content, redacted, isTemplate: isTemplate2 } = redactContent(rawContent, fmt);
|
|
18675
|
-
const targetPath = abs.startsWith(
|
|
19220
|
+
const targetPath = abs.startsWith(homedir10()) ? abs.replace(homedir10(), "~") : abs;
|
|
18676
19221
|
const name = opts.name || filePath.split("/").pop();
|
|
18677
19222
|
const store = resolveConfigStore();
|
|
18678
19223
|
const allConfigs = await store.listConfigs();
|
|
@@ -18847,7 +19392,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
18847
19392
|
for (const entry of entries) {
|
|
18848
19393
|
if (!entry.isDirectory())
|
|
18849
19394
|
continue;
|
|
18850
|
-
const projDir =
|
|
19395
|
+
const projDir = join19(absDir, entry.name);
|
|
18851
19396
|
const hasAgentConfig = [
|
|
18852
19397
|
"CLAUDE.md",
|
|
18853
19398
|
".mcp.json",
|
|
@@ -18860,7 +19405,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
18860
19405
|
".aicopilot",
|
|
18861
19406
|
".cursor",
|
|
18862
19407
|
".agents"
|
|
18863
|
-
].some((marker) =>
|
|
19408
|
+
].some((marker) => existsSync17(join19(projDir, marker)));
|
|
18864
19409
|
if (!hasAgentConfig)
|
|
18865
19410
|
continue;
|
|
18866
19411
|
const result2 = await syncProject({ projectDir: projDir, dryRun: opts.dryRun, store });
|
|
@@ -18911,7 +19456,7 @@ program.command("import <file>").description("Import configs from a tar.gz bundl
|
|
|
18911
19456
|
});
|
|
18912
19457
|
program.command("whoami").description("Show setup summary").action(async () => {
|
|
18913
19458
|
const store = resolveConfigStore();
|
|
18914
|
-
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
19459
|
+
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join19(getRawStoreRoot(), "instructions.db");
|
|
18915
19460
|
const stats = await store.getConfigStats();
|
|
18916
19461
|
console.log(chalk.bold("@hasna/instructions") + chalk.dim(" v" + pkg.version));
|
|
18917
19462
|
console.log(chalk.cyan(isApiTransport() ? "API:" : "DB:") + " " + dbPath);
|
|
@@ -19134,7 +19679,7 @@ profileCmd.command("binding <profile> <config>").description("Set a config's sch
|
|
|
19134
19679
|
process.exit(1);
|
|
19135
19680
|
}
|
|
19136
19681
|
});
|
|
19137
|
-
profileCmd.command("apply [id]").description("Apply all configs in a profile to disk").option("--dry-run", "preview without writing").option("--auto", "resolve the matching profile for the current machine").option("--hostname <hostname>", "override detected hostname for auto resolution").option("--os <os>", "override detected OS for auto resolution").option("--arch <arch>", "override detected arch for auto resolution").action(async (id, opts) => {
|
|
19682
|
+
profileCmd.command("apply [id]").description("Apply all configs in a profile to disk").option("--dry-run", "preview without writing").option("--from <agent>", "verify the hosted Conversations heartbeat for this agent").option("--delivery-verified", "assert channel and direct-message canaries were observed in this acceptance pass").option("--auto", "resolve the matching profile for the current machine").option("--hostname <hostname>", "override detected hostname for auto resolution").option("--os <os>", "override detected OS for auto resolution").option("--arch <arch>", "override detected arch for auto resolution").action(async (id, opts) => {
|
|
19138
19683
|
try {
|
|
19139
19684
|
const store = resolveConfigStore();
|
|
19140
19685
|
const { machine, profile } = await getMachineProfileContext(opts, store);
|
|
@@ -19171,6 +19716,14 @@ profileCmd.command("apply [id]").description("Apply all configs in a profile to
|
|
|
19171
19716
|
}
|
|
19172
19717
|
if (report.failures.length > 0)
|
|
19173
19718
|
process.exitCode = 1;
|
|
19719
|
+
const runtimeReport = await reconcileManagedSkillRuntimes({
|
|
19720
|
+
dryRun: opts.dryRun,
|
|
19721
|
+
agent: opts.from,
|
|
19722
|
+
deliveryVerified: opts.deliveryVerified
|
|
19723
|
+
});
|
|
19724
|
+
printManagedSkillRuntimeReport(runtimeReport);
|
|
19725
|
+
if (runtimeReport.failed > 0)
|
|
19726
|
+
process.exitCode = 1;
|
|
19174
19727
|
console.log(chalk.dim(`
|
|
19175
19728
|
${changed}/${results.length} changed (${selected.slug} on ${machine.hostname} ${machine.os_family}/${machine.arch})`));
|
|
19176
19729
|
} catch (e) {
|
|
@@ -19302,6 +19855,8 @@ sessionCmd.command("plan").description("Produce a dry-run render plan for profil
|
|
|
19302
19855
|
...planJsonForOutput(plan),
|
|
19303
19856
|
...globalCoverage ? { globalSourceCoverage: globalCoverage } : {}
|
|
19304
19857
|
});
|
|
19858
|
+
if (plan.blocked)
|
|
19859
|
+
process.exitCode = 1;
|
|
19305
19860
|
return;
|
|
19306
19861
|
}
|
|
19307
19862
|
console.log(chalk.bold(`${plan.tool} session render plan`) + chalk.dim(` (${plan.adapter.mode})`));
|
|
@@ -19328,6 +19883,8 @@ sessionCmd.command("plan").description("Produce a dry-run render plan for profil
|
|
|
19328
19883
|
if (globalCoverage.complete)
|
|
19329
19884
|
console.log(chalk.dim(`global source coverage: ${globalCoverage.expectedSlugs.length}/${globalCoverage.expectedSlugs.length} complete`));
|
|
19330
19885
|
}
|
|
19886
|
+
if (plan.blocked)
|
|
19887
|
+
process.exitCode = 1;
|
|
19331
19888
|
console.log(chalk.dim("Dry run only. No files were written."));
|
|
19332
19889
|
} catch (e) {
|
|
19333
19890
|
console.error(chalk.red(formatCliError(e)));
|
|
@@ -19654,14 +20211,14 @@ mcpCmd.command("install").alias("add").description("Install configs MCP server i
|
|
|
19654
20211
|
} else if (target === "codex") {
|
|
19655
20212
|
const { appendFileSync, existsSync: ex } = await import("fs");
|
|
19656
20213
|
const { join: j } = await import("path");
|
|
19657
|
-
const configPath = j(
|
|
20214
|
+
const configPath = j(homedir10(), ".codex", "config.toml");
|
|
19658
20215
|
const block = `
|
|
19659
20216
|
[mcp_servers.configs]
|
|
19660
20217
|
command = "${mcpBinary}"
|
|
19661
20218
|
args = []
|
|
19662
20219
|
`;
|
|
19663
20220
|
if (ex(configPath)) {
|
|
19664
|
-
const content =
|
|
20221
|
+
const content = readFileSync15(configPath, "utf-8");
|
|
19665
20222
|
if (content.includes("[mcp_servers.configs]")) {
|
|
19666
20223
|
console.log(chalk.dim("= Already installed in Codex"));
|
|
19667
20224
|
continue;
|
|
@@ -19672,7 +20229,7 @@ args = []
|
|
|
19672
20229
|
} else if (target === "antigravity") {
|
|
19673
20230
|
const { mkdirSync: md, readFileSync: rf, writeFileSync: wf, existsSync: ex } = await import("fs");
|
|
19674
20231
|
const { dirname: dn, join: j } = await import("path");
|
|
19675
|
-
const configPath = j(
|
|
20232
|
+
const configPath = j(homedir10(), ".gemini", "config", "mcp_config.json");
|
|
19676
20233
|
let settings = {};
|
|
19677
20234
|
if (ex(configPath)) {
|
|
19678
20235
|
try {
|
|
@@ -19756,7 +20313,7 @@ DB stats:`));
|
|
|
19756
20313
|
if (count > 0)
|
|
19757
20314
|
console.log(` ${key.padEnd(18)} ${count}`);
|
|
19758
20315
|
}
|
|
19759
|
-
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
20316
|
+
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join19(getRawStoreRoot(), "instructions.db");
|
|
19760
20317
|
console.log(chalk.dim(`
|
|
19761
20318
|
${isApiTransport() ? "API" : "DB"}: ${location}`));
|
|
19762
20319
|
});
|
|
@@ -19774,14 +20331,53 @@ program.command("status").description("Health check: total configs, drift from d
|
|
|
19774
20331
|
console.log(chalk.cyan("Missing:") + ` ${status.health.missingTargets === 0 ? chalk.green("0") : chalk.yellow(String(status.health.missingTargets))} (file not on disk)`);
|
|
19775
20332
|
console.log(chalk.cyan("Secrets:") + ` ${status.health.unredactedSecretFindings === 0 ? chalk.green("0 \u2713") : chalk.red(String(status.health.unredactedSecretFindings) + " \u26A0")} unredacted`);
|
|
19776
20333
|
console.log(chalk.cyan("Retired agents:") + ` ${status.health.retiredAgentRows === 0 ? chalk.green("0") : chalk.yellow(String(status.health.retiredAgentRows))} row(s)`);
|
|
20334
|
+
console.log(chalk.cyan("Skill runtimes:") + ` ${status.health.missingManagedSkillRuntimes === 0 ? chalk.green(`${status.counts.managedSkillRuntimes.healthy} ready`) : chalk.yellow(`${status.health.missingManagedSkillRuntimes} missing`)} (${status.counts.managedSkillRuntimes.skillsPresent} managed skill(s) present)`);
|
|
19777
20335
|
console.log(chalk.cyan("Templates:") + ` ${status.counts.configs.templates} (with {{VAR}} placeholders)`);
|
|
19778
20336
|
});
|
|
20337
|
+
var managedSkillsCmd = program.command("managed-skills").description("Inspect or reconcile package-owned runtime contracts for installed managed skills");
|
|
20338
|
+
managedSkillsCmd.command("status").option("--from <agent>", "verify the hosted Conversations heartbeat for this agent").option("--delivery-verified", "assert channel and direct-message canaries were observed in this acceptance pass").option("--json", "output the full local runtime status as JSON").action((opts) => {
|
|
20339
|
+
const report = inspectManagedSkillRuntimes({
|
|
20340
|
+
agent: opts.from,
|
|
20341
|
+
deliveryVerified: opts.deliveryVerified
|
|
20342
|
+
});
|
|
20343
|
+
if (opts.json) {
|
|
20344
|
+
printJson(report);
|
|
20345
|
+
if (report.missing > 0)
|
|
20346
|
+
process.exitCode = 1;
|
|
20347
|
+
return;
|
|
20348
|
+
}
|
|
20349
|
+
if (report.skills_present === 0) {
|
|
20350
|
+
console.log(chalk.dim("No managed skills with package-owned runtime contracts are installed."));
|
|
20351
|
+
return;
|
|
20352
|
+
}
|
|
20353
|
+
for (const runtime of report.runtimes) {
|
|
20354
|
+
if (!runtime.skill_present)
|
|
20355
|
+
continue;
|
|
20356
|
+
const prefix = runtime.healthy ? chalk.green("\u2713") : chalk.yellow("!");
|
|
20357
|
+
console.log(`${prefix} ${runtime.skill} via ${runtime.runtime} \u2014 ${runtime.reason}`);
|
|
20358
|
+
}
|
|
20359
|
+
if (report.missing > 0)
|
|
20360
|
+
process.exitCode = 1;
|
|
20361
|
+
});
|
|
20362
|
+
managedSkillsCmd.command("apply").option("--dry-run", "preview without writing").option("--from <agent>", "verify the hosted Conversations heartbeat for this agent").option("--delivery-verified", "assert channel and direct-message canaries were observed in this acceptance pass").option("--json", "output the reconcile report as JSON").action(async (opts) => {
|
|
20363
|
+
const report = await reconcileManagedSkillRuntimes({
|
|
20364
|
+
dryRun: opts.dryRun,
|
|
20365
|
+
agent: opts.from,
|
|
20366
|
+
deliveryVerified: opts.deliveryVerified
|
|
20367
|
+
});
|
|
20368
|
+
if (opts.json)
|
|
20369
|
+
printJson(report);
|
|
20370
|
+
else
|
|
20371
|
+
printManagedSkillRuntimeReport(report);
|
|
20372
|
+
if (report.failed > 0)
|
|
20373
|
+
process.exitCode = 1;
|
|
20374
|
+
});
|
|
19779
20375
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
19780
20376
|
const { mkdirSync: mk } = await import("fs");
|
|
19781
|
-
const backupDir =
|
|
20377
|
+
const backupDir = join19(getRawStoreRoot(), "backups");
|
|
19782
20378
|
mk(backupDir, { recursive: true });
|
|
19783
20379
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
19784
|
-
const outPath =
|
|
20380
|
+
const outPath = join19(backupDir, `configs-${ts}.tar.gz`);
|
|
19785
20381
|
const result = await exportConfigs(outPath, { store: resolveConfigStore() });
|
|
19786
20382
|
const { statSync: st } = await import("fs");
|
|
19787
20383
|
const size = st(outPath).size;
|
|
@@ -19809,9 +20405,9 @@ program.command("doctor").description("Validate configs: syntax, permissions, mi
|
|
|
19809
20405
|
console.log(chalk.cyan("Known files on disk:"));
|
|
19810
20406
|
for (const k of KNOWN_CONFIGS) {
|
|
19811
20407
|
if (k.rulesDir) {
|
|
19812
|
-
|
|
20408
|
+
existsSync17(expandPath(k.rulesDir)) ? pass(`${k.rulesDir}/ exists`) : k.optional ? skip(`${k.rulesDir}/ (optional)`) : fail2(`${k.rulesDir}/ not found`);
|
|
19813
20409
|
} else {
|
|
19814
|
-
|
|
20410
|
+
existsSync17(expandPath(k.path)) ? pass(k.path) : k.optional ? skip(`${k.path} (optional)`) : fail2(`${k.path} not found`);
|
|
19815
20411
|
}
|
|
19816
20412
|
}
|
|
19817
20413
|
const allConfigs = await store.listConfigs();
|
|
@@ -19964,16 +20560,16 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
19964
20560
|
for (const k of KNOWN_CONFIGS) {
|
|
19965
20561
|
if (k.rulesDir) {
|
|
19966
20562
|
const absDir = expandPath2(k.rulesDir);
|
|
19967
|
-
if (!
|
|
20563
|
+
if (!existsSync17(absDir))
|
|
19968
20564
|
continue;
|
|
19969
20565
|
const { readdirSync: readdirSync5 } = await import("fs");
|
|
19970
20566
|
for (const f of readdirSync5(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
19971
|
-
const abs =
|
|
20567
|
+
const abs = join19(absDir, f);
|
|
19972
20568
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
19973
20569
|
}
|
|
19974
20570
|
} else {
|
|
19975
20571
|
const abs = expandPath2(k.path);
|
|
19976
|
-
if (
|
|
20572
|
+
if (existsSync17(abs))
|
|
19977
20573
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
19978
20574
|
}
|
|
19979
20575
|
}
|
|
@@ -19981,7 +20577,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
19981
20577
|
const tick = async () => {
|
|
19982
20578
|
let changed = 0;
|
|
19983
20579
|
for (const [abs, oldMtime] of mtimes) {
|
|
19984
|
-
if (!
|
|
20580
|
+
if (!existsSync17(abs))
|
|
19985
20581
|
continue;
|
|
19986
20582
|
const newMtime = st(abs).mtimeMs;
|
|
19987
20583
|
if (newMtime !== oldMtime) {
|
|
@@ -19993,10 +20589,10 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
19993
20589
|
for (const k of KNOWN_CONFIGS) {
|
|
19994
20590
|
if (k.rulesDir) {
|
|
19995
20591
|
const absDir = expandPath2(k.rulesDir);
|
|
19996
|
-
if (!
|
|
20592
|
+
if (!existsSync17(absDir))
|
|
19997
20593
|
continue;
|
|
19998
20594
|
for (const f of rd(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
19999
|
-
const abs =
|
|
20595
|
+
const abs = join19(absDir, f);
|
|
20000
20596
|
if (!mtimes.has(abs)) {
|
|
20001
20597
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
20002
20598
|
changed++;
|
|
@@ -20004,7 +20600,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
20004
20600
|
}
|
|
20005
20601
|
} else {
|
|
20006
20602
|
const abs = expandPath2(k.path);
|
|
20007
|
-
if (
|
|
20603
|
+
if (existsSync17(abs) && !mtimes.has(abs)) {
|
|
20008
20604
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
20009
20605
|
changed++;
|
|
20010
20606
|
}
|
|
@@ -20032,11 +20628,11 @@ program.command("report").description("Summary of stored configs, drift, and eco
|
|
|
20032
20628
|
if (!c.target_path)
|
|
20033
20629
|
continue;
|
|
20034
20630
|
const abs = expandPath(c.target_path);
|
|
20035
|
-
if (!
|
|
20631
|
+
if (!existsSync17(abs)) {
|
|
20036
20632
|
missing++;
|
|
20037
20633
|
continue;
|
|
20038
20634
|
}
|
|
20039
|
-
const disk =
|
|
20635
|
+
const disk = readFileSync15(abs, "utf-8");
|
|
20040
20636
|
const { content: redactedDisk } = redactContent(disk, c.format);
|
|
20041
20637
|
if (redactedDisk !== c.content)
|
|
20042
20638
|
drifted++;
|
|
@@ -20103,7 +20699,7 @@ program.command("clean").description("Remove configs from DB whose target files
|
|
|
20103
20699
|
if (!c.target_path)
|
|
20104
20700
|
continue;
|
|
20105
20701
|
const abs = expandPath(c.target_path);
|
|
20106
|
-
if (!
|
|
20702
|
+
if (!existsSync17(abs)) {
|
|
20107
20703
|
if (printed < maxPrinted) {
|
|
20108
20704
|
if (opts.dryRun) {
|
|
20109
20705
|
console.log(chalk.yellow(" would remove:") + ` ${c.slug} ${chalk.dim(`(${truncateMiddle(c.target_path, 88)})`)}`);
|
|
@@ -20127,7 +20723,7 @@ ${removed} orphaned config(s) ${opts.dryRun ? "found" : "removed"}${omitted > 0
|
|
|
20127
20723
|
console.log(chalk.dim(`Use --limit ${removed} to print every orphan row.`));
|
|
20128
20724
|
}
|
|
20129
20725
|
});
|
|
20130
|
-
program.command("bootstrap").description("Install the full @hasna ecosystem: CLI tools + MCP servers + configs").option("--dry-run", "show what would be installed without doing it").option("--skip-mcp", "skip MCP server registration").action(async (opts) => {
|
|
20726
|
+
program.command("bootstrap").description("Install the full @hasna ecosystem: CLI tools + MCP servers + configs").option("--dry-run", "show what would be installed without doing it").option("--from <agent>", "verify the hosted Conversations heartbeat for this agent").option("--delivery-verified", "assert channel and direct-message canaries were observed in this acceptance pass").option("--skip-mcp", "skip MCP server registration").action(async (opts) => {
|
|
20131
20727
|
const store = resolveConfigStore();
|
|
20132
20728
|
const packages = [
|
|
20133
20729
|
{ name: "@hasna/todos", bin: "todos", mcp: "todos-mcp" },
|
|
@@ -20190,8 +20786,22 @@ Initializing configs:`));
|
|
|
20190
20786
|
} else {
|
|
20191
20787
|
console.log(chalk.dim(" would run: configs init"));
|
|
20192
20788
|
}
|
|
20789
|
+
console.log(chalk.cyan(`
|
|
20790
|
+
Reconciling managed skill runtimes:`));
|
|
20791
|
+
const runtimeReport = await reconcileManagedSkillRuntimes({
|
|
20792
|
+
dryRun: opts.dryRun,
|
|
20793
|
+
agent: opts.from,
|
|
20794
|
+
deliveryVerified: opts.deliveryVerified
|
|
20795
|
+
});
|
|
20796
|
+
printManagedSkillRuntimeReport(runtimeReport);
|
|
20797
|
+
if (runtimeReport.failed > 0) {
|
|
20798
|
+
console.error(chalk.red(`
|
|
20799
|
+
Bootstrap incomplete: a managed skill runtime could not be reconciled.`));
|
|
20800
|
+
process.exitCode = 1;
|
|
20801
|
+
return;
|
|
20802
|
+
}
|
|
20193
20803
|
console.log(chalk.bold(`
|
|
20194
|
-
\u2713 Bootstrap complete.`) + chalk.dim(" Restart
|
|
20804
|
+
\u2713 Bootstrap complete.`) + chalk.dim(" Restart agent sessions to load updated integrations."));
|
|
20195
20805
|
});
|
|
20196
20806
|
program.command("pull").description("Alias for sync (read from disk into DB)").option("-a, --agent <agent>", "only sync this agent").option("--dry-run", "preview without writing").action(async (opts) => {
|
|
20197
20807
|
const result = await syncKnown({ dryRun: opts.dryRun, agent: opts.agent, store: resolveConfigStore() });
|