@hasna/skills 0.10.46 → 0.10.48
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/bin/index.js +1211 -926
- package/bin/maintenance.js +7 -2
- package/bin/mcp.js +577 -360
- package/bin/migrate.js +7 -5
- package/bin/server.js +29 -10
- package/bin/worker.js +12 -7
- package/dist/index.js +826 -613
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/claude-plugin-manifest-witness.d.ts +9 -0
- package/dist/lib/profile-client.d.ts +6 -2
- package/dist/lib/remote-files.d.ts +1 -1
- package/dist/lib/selection-cache.d.ts +5 -0
- package/dist/lib/selection-resolver.d.ts +1 -0
- package/dist/lib/session-reconciliation.d.ts +6 -3
- package/dist/sdk/index.js +796 -564
- package/dist/server/skills-api.d.ts +1 -0
- package/dist/server/types.d.ts +3 -1
- package/dist/types/skill-selection.d.ts +2 -0
- package/migrations/postgres/0011_skill_authorization_epoch.sql +22 -0
- package/migrations/sqlite/0011_skill_authorization_epoch.sql +17 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3724,10 +3724,10 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3724
3724
|
let offset = bm.offset;
|
|
3725
3725
|
let commentEnd = null;
|
|
3726
3726
|
for (const collItem of bm.items) {
|
|
3727
|
-
const { start, key, sep:
|
|
3727
|
+
const { start, key, sep: sep3, value } = collItem;
|
|
3728
3728
|
const keyProps = resolveProps.resolveProps(start, {
|
|
3729
3729
|
indicator: "explicit-key-ind",
|
|
3730
|
-
next: key ??
|
|
3730
|
+
next: key ?? sep3?.[0],
|
|
3731
3731
|
offset,
|
|
3732
3732
|
onError,
|
|
3733
3733
|
parentIndent: bm.indent,
|
|
@@ -3741,7 +3741,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3741
3741
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
3742
3742
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
3743
3743
|
}
|
|
3744
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
3744
|
+
if (!keyProps.anchor && !keyProps.tag && !sep3) {
|
|
3745
3745
|
commentEnd = keyProps.end;
|
|
3746
3746
|
if (keyProps.comment) {
|
|
3747
3747
|
if (map.comment)
|
|
@@ -3766,7 +3766,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3766
3766
|
ctx.atKey = false;
|
|
3767
3767
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
3768
3768
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
3769
|
-
const valueProps = resolveProps.resolveProps(
|
|
3769
|
+
const valueProps = resolveProps.resolveProps(sep3 ?? [], {
|
|
3770
3770
|
indicator: "map-value-ind",
|
|
3771
3771
|
next: value,
|
|
3772
3772
|
offset: keyNode.range[2],
|
|
@@ -3782,7 +3782,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3782
3782
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
3783
3783
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
3784
3784
|
}
|
|
3785
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
3785
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
|
|
3786
3786
|
if (ctx.schema.compat)
|
|
3787
3787
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
3788
3788
|
offset = valueNode.range[2];
|
|
@@ -3868,7 +3868,7 @@ var require_resolve_end = __commonJS((exports) => {
|
|
|
3868
3868
|
let comment = "";
|
|
3869
3869
|
if (end) {
|
|
3870
3870
|
let hasSpace = false;
|
|
3871
|
-
let
|
|
3871
|
+
let sep3 = "";
|
|
3872
3872
|
for (const token of end) {
|
|
3873
3873
|
const { source, type } = token;
|
|
3874
3874
|
switch (type) {
|
|
@@ -3882,13 +3882,13 @@ var require_resolve_end = __commonJS((exports) => {
|
|
|
3882
3882
|
if (!comment)
|
|
3883
3883
|
comment = cb;
|
|
3884
3884
|
else
|
|
3885
|
-
comment +=
|
|
3886
|
-
|
|
3885
|
+
comment += sep3 + cb;
|
|
3886
|
+
sep3 = "";
|
|
3887
3887
|
break;
|
|
3888
3888
|
}
|
|
3889
3889
|
case "newline":
|
|
3890
3890
|
if (comment)
|
|
3891
|
-
|
|
3891
|
+
sep3 += source;
|
|
3892
3892
|
hasSpace = true;
|
|
3893
3893
|
break;
|
|
3894
3894
|
default:
|
|
@@ -3928,18 +3928,18 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
3928
3928
|
let offset = fc.offset + fc.start.source.length;
|
|
3929
3929
|
for (let i = 0;i < fc.items.length; ++i) {
|
|
3930
3930
|
const collItem = fc.items[i];
|
|
3931
|
-
const { start, key, sep:
|
|
3931
|
+
const { start, key, sep: sep3, value } = collItem;
|
|
3932
3932
|
const props = resolveProps.resolveProps(start, {
|
|
3933
3933
|
flow: fcName,
|
|
3934
3934
|
indicator: "explicit-key-ind",
|
|
3935
|
-
next: key ??
|
|
3935
|
+
next: key ?? sep3?.[0],
|
|
3936
3936
|
offset,
|
|
3937
3937
|
onError,
|
|
3938
3938
|
parentIndent: fc.indent,
|
|
3939
3939
|
startOnNewline: false
|
|
3940
3940
|
});
|
|
3941
3941
|
if (!props.found) {
|
|
3942
|
-
if (!props.anchor && !props.tag && !
|
|
3942
|
+
if (!props.anchor && !props.tag && !sep3 && !value) {
|
|
3943
3943
|
if (i === 0 && props.comma)
|
|
3944
3944
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
3945
3945
|
else if (i < fc.items.length - 1)
|
|
@@ -3991,8 +3991,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
3991
3991
|
}
|
|
3992
3992
|
}
|
|
3993
3993
|
}
|
|
3994
|
-
if (!isMap && !
|
|
3995
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
3994
|
+
if (!isMap && !sep3 && !props.found) {
|
|
3995
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
|
|
3996
3996
|
coll.items.push(valueNode);
|
|
3997
3997
|
offset = valueNode.range[2];
|
|
3998
3998
|
if (isBlock(value))
|
|
@@ -4004,7 +4004,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
4004
4004
|
if (isBlock(key))
|
|
4005
4005
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
4006
4006
|
ctx.atKey = false;
|
|
4007
|
-
const valueProps = resolveProps.resolveProps(
|
|
4007
|
+
const valueProps = resolveProps.resolveProps(sep3 ?? [], {
|
|
4008
4008
|
flow: fcName,
|
|
4009
4009
|
indicator: "map-value-ind",
|
|
4010
4010
|
next: value,
|
|
@@ -4015,8 +4015,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
4015
4015
|
});
|
|
4016
4016
|
if (valueProps.found) {
|
|
4017
4017
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
4018
|
-
if (
|
|
4019
|
-
for (const st of
|
|
4018
|
+
if (sep3)
|
|
4019
|
+
for (const st of sep3) {
|
|
4020
4020
|
if (st === valueProps.found)
|
|
4021
4021
|
break;
|
|
4022
4022
|
if (st.type === "newline") {
|
|
@@ -4033,7 +4033,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
4033
4033
|
else
|
|
4034
4034
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
4035
4035
|
}
|
|
4036
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
4036
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
|
|
4037
4037
|
if (valueNode) {
|
|
4038
4038
|
if (isBlock(value))
|
|
4039
4039
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -4210,7 +4210,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
|
|
|
4210
4210
|
chompStart = i + 1;
|
|
4211
4211
|
}
|
|
4212
4212
|
let value = "";
|
|
4213
|
-
let
|
|
4213
|
+
let sep3 = "";
|
|
4214
4214
|
let prevMoreIndented = false;
|
|
4215
4215
|
for (let i = 0;i < contentStart; ++i)
|
|
4216
4216
|
value += lines[i][0].slice(trimIndent) + `
|
|
@@ -4228,33 +4228,33 @@ var require_resolve_block_scalar = __commonJS((exports) => {
|
|
|
4228
4228
|
indent = "";
|
|
4229
4229
|
}
|
|
4230
4230
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
4231
|
-
value +=
|
|
4232
|
-
|
|
4231
|
+
value += sep3 + indent.slice(trimIndent) + content;
|
|
4232
|
+
sep3 = `
|
|
4233
4233
|
`;
|
|
4234
4234
|
} else if (indent.length > trimIndent || content[0] === "\t") {
|
|
4235
|
-
if (
|
|
4236
|
-
|
|
4235
|
+
if (sep3 === " ")
|
|
4236
|
+
sep3 = `
|
|
4237
4237
|
`;
|
|
4238
|
-
else if (!prevMoreIndented &&
|
|
4238
|
+
else if (!prevMoreIndented && sep3 === `
|
|
4239
4239
|
`)
|
|
4240
|
-
|
|
4240
|
+
sep3 = `
|
|
4241
4241
|
|
|
4242
4242
|
`;
|
|
4243
|
-
value +=
|
|
4244
|
-
|
|
4243
|
+
value += sep3 + indent.slice(trimIndent) + content;
|
|
4244
|
+
sep3 = `
|
|
4245
4245
|
`;
|
|
4246
4246
|
prevMoreIndented = true;
|
|
4247
4247
|
} else if (content === "") {
|
|
4248
|
-
if (
|
|
4248
|
+
if (sep3 === `
|
|
4249
4249
|
`)
|
|
4250
4250
|
value += `
|
|
4251
4251
|
`;
|
|
4252
4252
|
else
|
|
4253
|
-
|
|
4253
|
+
sep3 = `
|
|
4254
4254
|
`;
|
|
4255
4255
|
} else {
|
|
4256
|
-
value +=
|
|
4257
|
-
|
|
4256
|
+
value += sep3 + content;
|
|
4257
|
+
sep3 = " ";
|
|
4258
4258
|
prevMoreIndented = false;
|
|
4259
4259
|
}
|
|
4260
4260
|
}
|
|
@@ -4435,27 +4435,27 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
|
|
|
4435
4435
|
if (!match)
|
|
4436
4436
|
return source;
|
|
4437
4437
|
let res = match[1];
|
|
4438
|
-
let
|
|
4438
|
+
let sep3 = " ";
|
|
4439
4439
|
let pos = first.lastIndex;
|
|
4440
4440
|
line.lastIndex = pos;
|
|
4441
4441
|
while (match = line.exec(source)) {
|
|
4442
4442
|
if (match[1] === "") {
|
|
4443
|
-
if (
|
|
4443
|
+
if (sep3 === `
|
|
4444
4444
|
`)
|
|
4445
|
-
res +=
|
|
4445
|
+
res += sep3;
|
|
4446
4446
|
else
|
|
4447
|
-
|
|
4447
|
+
sep3 = `
|
|
4448
4448
|
`;
|
|
4449
4449
|
} else {
|
|
4450
|
-
res +=
|
|
4451
|
-
|
|
4450
|
+
res += sep3 + match[1];
|
|
4451
|
+
sep3 = " ";
|
|
4452
4452
|
}
|
|
4453
4453
|
pos = line.lastIndex;
|
|
4454
4454
|
}
|
|
4455
4455
|
const last = /[ \t]*(.*)/sy;
|
|
4456
4456
|
last.lastIndex = pos;
|
|
4457
4457
|
match = last.exec(source);
|
|
4458
|
-
return res +
|
|
4458
|
+
return res + sep3 + (match?.[1] ?? "");
|
|
4459
4459
|
}
|
|
4460
4460
|
function doubleQuotedValue(source, onError) {
|
|
4461
4461
|
let res = "";
|
|
@@ -5236,14 +5236,14 @@ var require_cst_stringify = __commonJS((exports) => {
|
|
|
5236
5236
|
}
|
|
5237
5237
|
}
|
|
5238
5238
|
}
|
|
5239
|
-
function stringifyItem({ start, key, sep:
|
|
5239
|
+
function stringifyItem({ start, key, sep: sep3, value }) {
|
|
5240
5240
|
let res = "";
|
|
5241
5241
|
for (const st of start)
|
|
5242
5242
|
res += st.source;
|
|
5243
5243
|
if (key)
|
|
5244
5244
|
res += stringifyToken(key);
|
|
5245
|
-
if (
|
|
5246
|
-
for (const st of
|
|
5245
|
+
if (sep3)
|
|
5246
|
+
for (const st of sep3)
|
|
5247
5247
|
res += st.source;
|
|
5248
5248
|
if (value)
|
|
5249
5249
|
res += stringifyToken(value);
|
|
@@ -6391,18 +6391,18 @@ var require_parser = __commonJS((exports) => {
|
|
|
6391
6391
|
if (this.type === "map-value-ind") {
|
|
6392
6392
|
const prev = getPrevProps(this.peek(2));
|
|
6393
6393
|
const start = getFirstKeyStartProps(prev);
|
|
6394
|
-
let
|
|
6394
|
+
let sep3;
|
|
6395
6395
|
if (scalar.end) {
|
|
6396
|
-
|
|
6397
|
-
|
|
6396
|
+
sep3 = scalar.end;
|
|
6397
|
+
sep3.push(this.sourceToken);
|
|
6398
6398
|
delete scalar.end;
|
|
6399
6399
|
} else
|
|
6400
|
-
|
|
6400
|
+
sep3 = [this.sourceToken];
|
|
6401
6401
|
const map = {
|
|
6402
6402
|
type: "block-map",
|
|
6403
6403
|
offset: scalar.offset,
|
|
6404
6404
|
indent: scalar.indent,
|
|
6405
|
-
items: [{ start, key: scalar, sep:
|
|
6405
|
+
items: [{ start, key: scalar, sep: sep3 }]
|
|
6406
6406
|
};
|
|
6407
6407
|
this.onKeyLine = true;
|
|
6408
6408
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6556,15 +6556,15 @@ var require_parser = __commonJS((exports) => {
|
|
|
6556
6556
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
6557
6557
|
const start2 = getFirstKeyStartProps(it.start);
|
|
6558
6558
|
const key = it.key;
|
|
6559
|
-
const
|
|
6560
|
-
|
|
6559
|
+
const sep3 = it.sep;
|
|
6560
|
+
sep3.push(this.sourceToken);
|
|
6561
6561
|
delete it.key;
|
|
6562
6562
|
delete it.sep;
|
|
6563
6563
|
this.stack.push({
|
|
6564
6564
|
type: "block-map",
|
|
6565
6565
|
offset: this.offset,
|
|
6566
6566
|
indent: this.indent,
|
|
6567
|
-
items: [{ start: start2, key, sep:
|
|
6567
|
+
items: [{ start: start2, key, sep: sep3 }]
|
|
6568
6568
|
});
|
|
6569
6569
|
} else if (start.length > 0) {
|
|
6570
6570
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -6758,13 +6758,13 @@ var require_parser = __commonJS((exports) => {
|
|
|
6758
6758
|
const prev = getPrevProps(parent);
|
|
6759
6759
|
const start = getFirstKeyStartProps(prev);
|
|
6760
6760
|
fixFlowSeqItems(fc);
|
|
6761
|
-
const
|
|
6762
|
-
|
|
6761
|
+
const sep3 = fc.end.splice(1, fc.end.length);
|
|
6762
|
+
sep3.push(this.sourceToken);
|
|
6763
6763
|
const map = {
|
|
6764
6764
|
type: "block-map",
|
|
6765
6765
|
offset: fc.offset,
|
|
6766
6766
|
indent: fc.indent,
|
|
6767
|
-
items: [{ start, key: fc, sep:
|
|
6767
|
+
items: [{ start, key: fc, sep: sep3 }]
|
|
6768
6768
|
};
|
|
6769
6769
|
this.onKeyLine = true;
|
|
6770
6770
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6989,7 +6989,7 @@ var require_public_api = __commonJS((exports) => {
|
|
|
6989
6989
|
|
|
6990
6990
|
// src/lib/registry.ts
|
|
6991
6991
|
import { existsSync as existsSync15, readFileSync as readFileSync20, readdirSync as readdirSync12 } from "fs";
|
|
6992
|
-
import { join as
|
|
6992
|
+
import { join as join35 } from "path";
|
|
6993
6993
|
|
|
6994
6994
|
// src/lib/config.ts
|
|
6995
6995
|
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
@@ -7262,7 +7262,7 @@ import {
|
|
|
7262
7262
|
statSync as statSync11,
|
|
7263
7263
|
writeFileSync as writeFileSync9
|
|
7264
7264
|
} from "fs";
|
|
7265
|
-
import { basename as basename12, dirname as dirname21, isAbsolute as isAbsolute19, join as
|
|
7265
|
+
import { basename as basename12, dirname as dirname21, isAbsolute as isAbsolute19, join as join34, normalize as normalize4, relative as relative7, resolve as resolve20, sep as sep10 } from "path";
|
|
7266
7266
|
|
|
7267
7267
|
// src/lib/registry-data/index.ts
|
|
7268
7268
|
var SKILLS = [];
|
|
@@ -8922,7 +8922,7 @@ var NATIVE_SKILL_ROOTS = [
|
|
|
8922
8922
|
// src/lib/authoring-path.ts
|
|
8923
8923
|
import { lstatSync as lstatSync25, realpathSync as realpathSync11 } from "fs";
|
|
8924
8924
|
import { homedir as homedir6 } from "os";
|
|
8925
|
-
import { basename as basename11, dirname as dirname20, join as
|
|
8925
|
+
import { basename as basename11, dirname as dirname20, join as join33, resolve as resolve19, sep as sep9 } from "path";
|
|
8926
8926
|
|
|
8927
8927
|
// src/lib/agent-adapters.ts
|
|
8928
8928
|
var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
|
|
@@ -9355,7 +9355,7 @@ function upgradeSumiSettingsWitness(previous, reviewedSettingsPath) {
|
|
|
9355
9355
|
}
|
|
9356
9356
|
|
|
9357
9357
|
// src/lib/agent-policy-limits.ts
|
|
9358
|
-
import { basename as basename3, isAbsolute as isAbsolute3, resolve as resolve3 } from "path";
|
|
9358
|
+
import { basename as basename3, isAbsolute as isAbsolute3, join as join7, resolve as resolve3, sep as sep2 } from "path";
|
|
9359
9359
|
var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
|
|
9360
9360
|
function object(value) {
|
|
9361
9361
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -9497,7 +9497,9 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9497
9497
|
text(source.path);
|
|
9498
9498
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9499
9499
|
if (source.hashMode !== undefined)
|
|
9500
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9500
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9501
|
+
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
9502
|
+
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && source.path.endsWith(join7(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
9501
9503
|
if (source.hashMode === "sumi-settings-v1")
|
|
9502
9504
|
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "sumi.json");
|
|
9503
9505
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
@@ -9536,9 +9538,9 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9536
9538
|
}
|
|
9537
9539
|
|
|
9538
9540
|
// src/lib/agent-discovery.ts
|
|
9539
|
-
import { createHash as
|
|
9541
|
+
import { createHash as createHash23 } from "crypto";
|
|
9540
9542
|
import { lstatSync as lstatSync24, readFileSync as readFileSync19, readdirSync as readdirSync10, realpathSync as realpathSync10 } from "fs";
|
|
9541
|
-
import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute18, join as
|
|
9543
|
+
import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute18, join as join32, resolve as resolve18, sep as sep8 } from "path";
|
|
9542
9544
|
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
9543
9545
|
|
|
9544
9546
|
// node_modules/yaml/dist/index.js
|
|
@@ -9588,7 +9590,7 @@ var $visit = visit.visit;
|
|
|
9588
9590
|
var $visitAsync = visit.visitAsync;
|
|
9589
9591
|
|
|
9590
9592
|
// src/lib/agent-hermes.ts
|
|
9591
|
-
import { dirname as dirname4, isAbsolute as isAbsolute4, join as
|
|
9593
|
+
import { dirname as dirname4, isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "path";
|
|
9592
9594
|
import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
|
|
9593
9595
|
var HERMES_OPT_OUT = ".no-bundled-skills";
|
|
9594
9596
|
var EVENTS = ["pre_llm_call", "pre_tool_call"];
|
|
@@ -9662,7 +9664,7 @@ function assertHermesEnvironment(home) {
|
|
|
9662
9664
|
throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
|
|
9663
9665
|
}
|
|
9664
9666
|
const selected = process.env.HERMES_HOME?.trim();
|
|
9665
|
-
if (selected && (!isAbsolute4(selected) || resolve4(selected) !==
|
|
9667
|
+
if (selected && (!isAbsolute4(selected) || resolve4(selected) !== join8(resolve4(home), ".hermes")))
|
|
9666
9668
|
throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
|
|
9667
9669
|
if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
|
|
9668
9670
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
|
|
@@ -9683,11 +9685,11 @@ function read(path) {
|
|
|
9683
9685
|
}
|
|
9684
9686
|
function assertHermesProtection(home, config, command, profile, supervisor) {
|
|
9685
9687
|
assertHermesEnvironment(home);
|
|
9686
|
-
if (read(
|
|
9688
|
+
if (read(join8(home, ".hermes", HERMES_OPT_OUT)) === null)
|
|
9687
9689
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
|
|
9688
9690
|
if (!supervisor || !isAbsolute4(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
|
|
9689
9691
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
|
|
9690
|
-
const raw = read(
|
|
9692
|
+
const raw = read(join8(home, ".hermes/shell-hooks-allowlist.json"));
|
|
9691
9693
|
let trust;
|
|
9692
9694
|
try {
|
|
9693
9695
|
trust = raw === null ? undefined : JSON.parse(raw);
|
|
@@ -9730,15 +9732,15 @@ function assertNoHermesLegacyShadow(root) {
|
|
|
9730
9732
|
throw new Error("NATIVE_SKILL_DRIFT: unsupported Hermes legacy discovery input");
|
|
9731
9733
|
return;
|
|
9732
9734
|
}
|
|
9733
|
-
if (lstatSync4(
|
|
9735
|
+
if (lstatSync4(join8(path, "skills-cli.md"), { throwIfNoEntry: false }))
|
|
9734
9736
|
throw new Error("NATIVE_SKILL_DRIFT: legacy Hermes skills-cli.md can shadow the owned bridge; preserve and retire it before continuing");
|
|
9735
|
-
const hasDescriptor = Boolean(lstatSync4(
|
|
9737
|
+
const hasDescriptor = Boolean(lstatSync4(join8(path, "SKILL.md"), { throwIfNoEntry: false }));
|
|
9736
9738
|
const directory = opendirSync(path);
|
|
9737
9739
|
try {
|
|
9738
9740
|
for (let child = directory.readSync();child; child = directory.readSync()) {
|
|
9739
9741
|
if (hasDescriptor && ["references", "templates", "assets", "scripts"].includes(child.name))
|
|
9740
9742
|
continue;
|
|
9741
|
-
scan(
|
|
9743
|
+
scan(join8(path, child.name), depth + 1);
|
|
9742
9744
|
}
|
|
9743
9745
|
} finally {
|
|
9744
9746
|
directory.closeSync();
|
|
@@ -9797,7 +9799,7 @@ if (failed) process.exitCode = 2;
|
|
|
9797
9799
|
// src/lib/agent-discovery-directories.ts
|
|
9798
9800
|
import { createHash as createHash4 } from "crypto";
|
|
9799
9801
|
import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
|
|
9800
|
-
import { dirname as dirname5, isAbsolute as isAbsolute5, join as
|
|
9802
|
+
import { dirname as dirname5, isAbsolute as isAbsolute5, join as join9, resolve as resolve5 } from "path";
|
|
9801
9803
|
function need2(value, message) {
|
|
9802
9804
|
if (!value)
|
|
9803
9805
|
throw new Error(message);
|
|
@@ -9835,7 +9837,7 @@ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
|
|
|
9835
9837
|
}
|
|
9836
9838
|
need2(new Set(names).size === names.length, "Ambiguous native discovery directory names");
|
|
9837
9839
|
for (const name of names.sort()) {
|
|
9838
|
-
const child =
|
|
9840
|
+
const child = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
|
|
9839
9841
|
if (omittedRoots.includes(child))
|
|
9840
9842
|
continue;
|
|
9841
9843
|
need2(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
|
|
@@ -9883,7 +9885,7 @@ function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
|
|
|
9883
9885
|
need2(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute5(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
|
|
9884
9886
|
`, "Invalid reviewed directory member");
|
|
9885
9887
|
hash.update(row);
|
|
9886
|
-
const path =
|
|
9888
|
+
const path = join9(directory.path, value[0]);
|
|
9887
9889
|
if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
|
|
9888
9890
|
kept.push(row);
|
|
9889
9891
|
}
|
|
@@ -9981,7 +9983,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
|
|
|
9981
9983
|
// src/lib/agent-discovery-path-bytes.ts
|
|
9982
9984
|
import { createHash as createHash6 } from "crypto";
|
|
9983
9985
|
import { lstatSync as lstatSync7, readlinkSync } from "fs";
|
|
9984
|
-
import { dirname as dirname7, isAbsolute as isAbsolute7, join as
|
|
9986
|
+
import { dirname as dirname7, isAbsolute as isAbsolute7, join as join10, parse as parse2, resolve as resolve7, sep as sep3 } from "path";
|
|
9985
9987
|
function identity2(stat, mutable = false) {
|
|
9986
9988
|
return {
|
|
9987
9989
|
dev: String(stat.dev),
|
|
@@ -9999,7 +10001,7 @@ function canonical2(path) {
|
|
|
9999
10001
|
function trace(path) {
|
|
10000
10002
|
canonical2(path);
|
|
10001
10003
|
const rows = [], paths = [];
|
|
10002
|
-
let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(
|
|
10004
|
+
let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep3);
|
|
10003
10005
|
function record2(path2, row) {
|
|
10004
10006
|
const value = { path: path2, ...row };
|
|
10005
10007
|
metadataBytes += Buffer.byteLength(JSON.stringify(value));
|
|
@@ -10025,7 +10027,7 @@ function trace(path) {
|
|
|
10025
10027
|
cursor = dirname7(cursor);
|
|
10026
10028
|
continue;
|
|
10027
10029
|
}
|
|
10028
|
-
const candidate =
|
|
10030
|
+
const candidate = join10(cursor, component);
|
|
10029
10031
|
if (candidate.length > AGENT_POLICY_LIMITS.pathCharacters)
|
|
10030
10032
|
throw new Error("Discovery resolved path length limit exceeded");
|
|
10031
10033
|
const stat = lstatSync7(candidate, { bigint: true, throwIfNoEntry: false });
|
|
@@ -10047,7 +10049,7 @@ function trace(path) {
|
|
|
10047
10049
|
root();
|
|
10048
10050
|
}
|
|
10049
10051
|
const remainder = isAbsolute7(target) ? target.slice(parse2(target).root.length) : target;
|
|
10050
|
-
pending = [...remainder.split(
|
|
10052
|
+
pending = [...remainder.split(sep3), ...pending];
|
|
10051
10053
|
if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
|
|
10052
10054
|
throw new Error("Discovery path step limit exceeded");
|
|
10053
10055
|
continue;
|
|
@@ -10071,7 +10073,7 @@ function hashDiscoveryPathFile(path, budget2, changes) {
|
|
|
10071
10073
|
let createsMissingPath = false;
|
|
10072
10074
|
if (before.missing !== null && changes) {
|
|
10073
10075
|
for (const target of changes.keys()) {
|
|
10074
|
-
if (target === before.missing || target.startsWith(before.missing +
|
|
10076
|
+
if (target === before.missing || target.startsWith(before.missing + sep3)) {
|
|
10075
10077
|
createsMissingPath = true;
|
|
10076
10078
|
break;
|
|
10077
10079
|
}
|
|
@@ -10092,26 +10094,161 @@ function hashDiscoveryPathFile(path, budget2, changes) {
|
|
|
10092
10094
|
return createHash6("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
|
|
10093
10095
|
}
|
|
10094
10096
|
|
|
10097
|
+
// src/lib/claude-plugin-manifest-witness.ts
|
|
10098
|
+
import { createHash as createHash7 } from "crypto";
|
|
10099
|
+
var CLAUDE_PLUGIN_MANIFEST_LIMITS = Object.freeze({ bytes: 1024 * 1024, depth: 32, nodes: 65536, stringCharacters: 16384 });
|
|
10100
|
+
function requireValue(value, reason) {
|
|
10101
|
+
if (!value)
|
|
10102
|
+
throw new Error("Claude plugin manifest " + reason);
|
|
10103
|
+
}
|
|
10104
|
+
function parse3(text2) {
|
|
10105
|
+
requireValue(Buffer.byteLength(text2, "utf8") <= CLAUDE_PLUGIN_MANIFEST_LIMITS.bytes, "exceeds its byte limit");
|
|
10106
|
+
let at = 0, nodes = 0;
|
|
10107
|
+
const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
10108
|
+
const space = () => {
|
|
10109
|
+
while (at < text2.length && /[\x20\t\r\n]/.test(text2[at]))
|
|
10110
|
+
at++;
|
|
10111
|
+
};
|
|
10112
|
+
function string() {
|
|
10113
|
+
requireValue(text2[at] === '"', "contains invalid JSON");
|
|
10114
|
+
const start = at++;
|
|
10115
|
+
while (at < text2.length) {
|
|
10116
|
+
const character = text2[at++];
|
|
10117
|
+
if (character === '"') {
|
|
10118
|
+
let value2;
|
|
10119
|
+
try {
|
|
10120
|
+
value2 = JSON.parse(text2.slice(start, at));
|
|
10121
|
+
} catch {
|
|
10122
|
+
throw new Error("Claude plugin manifest contains invalid JSON string");
|
|
10123
|
+
}
|
|
10124
|
+
requireValue(value2.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "exceeds its string limit");
|
|
10125
|
+
return value2;
|
|
10126
|
+
}
|
|
10127
|
+
if (character === "\\")
|
|
10128
|
+
at++;
|
|
10129
|
+
}
|
|
10130
|
+
throw new Error("Claude plugin manifest contains an unterminated JSON string");
|
|
10131
|
+
}
|
|
10132
|
+
function value(depth) {
|
|
10133
|
+
requireValue(depth <= CLAUDE_PLUGIN_MANIFEST_LIMITS.depth && ++nodes <= CLAUDE_PLUGIN_MANIFEST_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
10134
|
+
space();
|
|
10135
|
+
const character = text2[at];
|
|
10136
|
+
if (character === '"')
|
|
10137
|
+
return { kind: "string", value: string() };
|
|
10138
|
+
if (character === "{" || character === "[") {
|
|
10139
|
+
at++;
|
|
10140
|
+
space();
|
|
10141
|
+
const object3 = character === "{", end = object3 ? "}" : "]", entries = [], items = [], keys = new Set;
|
|
10142
|
+
if (text2[at] !== end)
|
|
10143
|
+
while (true) {
|
|
10144
|
+
if (object3) {
|
|
10145
|
+
space();
|
|
10146
|
+
const key = string();
|
|
10147
|
+
requireValue(!keys.has(key), "contains a duplicate JSON key");
|
|
10148
|
+
keys.add(key);
|
|
10149
|
+
space();
|
|
10150
|
+
requireValue(text2[at++] === ":", "contains invalid JSON");
|
|
10151
|
+
entries.push([key, value(depth + 1)]);
|
|
10152
|
+
} else
|
|
10153
|
+
items.push(value(depth + 1));
|
|
10154
|
+
space();
|
|
10155
|
+
if (text2[at] === end)
|
|
10156
|
+
break;
|
|
10157
|
+
requireValue(text2[at++] === ",", "contains invalid JSON");
|
|
10158
|
+
}
|
|
10159
|
+
at++;
|
|
10160
|
+
return object3 ? { kind: "object", entries } : { kind: "array", items };
|
|
10161
|
+
}
|
|
10162
|
+
for (const literal of ["true", "false", "null"])
|
|
10163
|
+
if (text2.startsWith(literal, at)) {
|
|
10164
|
+
at += literal.length;
|
|
10165
|
+
return { kind: "literal", value: literal };
|
|
10166
|
+
}
|
|
10167
|
+
numberPattern.lastIndex = at;
|
|
10168
|
+
const number = numberPattern.exec(text2);
|
|
10169
|
+
requireValue(number, "contains invalid JSON");
|
|
10170
|
+
at += number[0].length;
|
|
10171
|
+
return { kind: "number", value: number[0] };
|
|
10172
|
+
}
|
|
10173
|
+
const root = value(0);
|
|
10174
|
+
space();
|
|
10175
|
+
requireValue(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
|
|
10176
|
+
return root;
|
|
10177
|
+
}
|
|
10178
|
+
function canonical3(value) {
|
|
10179
|
+
if (value.kind === "string")
|
|
10180
|
+
return JSON.stringify(value.value);
|
|
10181
|
+
if (value.kind === "number")
|
|
10182
|
+
return value.value;
|
|
10183
|
+
if (value.kind === "literal")
|
|
10184
|
+
return value.value;
|
|
10185
|
+
if (value.kind === "array")
|
|
10186
|
+
return "[" + value.items.map(canonical3).join(",") + "]";
|
|
10187
|
+
return "{" + value.entries.map(([key, item]) => JSON.stringify(key) + ":" + canonical3(item)).join(",") + "}";
|
|
10188
|
+
}
|
|
10189
|
+
var DESCRIPTIVE_FIELDS = new Set(["description", "version", "author", "homepage", "repository", "license", "keywords"]);
|
|
10190
|
+
function validateText(value, field) {
|
|
10191
|
+
requireValue(value?.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "has an invalid " + field + " field");
|
|
10192
|
+
}
|
|
10193
|
+
function validateDescriptiveFields(manifest) {
|
|
10194
|
+
const values = new Map(manifest.entries);
|
|
10195
|
+
for (const field of ["description", "version", "homepage", "license"]) {
|
|
10196
|
+
const value = values.get(field);
|
|
10197
|
+
if (value !== undefined)
|
|
10198
|
+
validateText(value, field);
|
|
10199
|
+
}
|
|
10200
|
+
const author = values.get("author");
|
|
10201
|
+
if (author !== undefined) {
|
|
10202
|
+
if (author.kind === "string")
|
|
10203
|
+
validateText(author, "author");
|
|
10204
|
+
else {
|
|
10205
|
+
requireValue(author.kind === "object" && author.entries.every(([key, value]) => ["name", "email", "url"].includes(key) && value.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters), "has an invalid author field");
|
|
10206
|
+
}
|
|
10207
|
+
}
|
|
10208
|
+
const repository = values.get("repository");
|
|
10209
|
+
if (repository !== undefined) {
|
|
10210
|
+
if (repository.kind === "string")
|
|
10211
|
+
validateText(repository, "repository");
|
|
10212
|
+
else
|
|
10213
|
+
requireValue(repository.kind === "object" && repository.entries.every(([key, value]) => ["type", "url", "directory"].includes(key) && value.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters), "has an invalid repository field");
|
|
10214
|
+
}
|
|
10215
|
+
const keywords = values.get("keywords");
|
|
10216
|
+
if (keywords !== undefined)
|
|
10217
|
+
requireValue(keywords.kind === "array" && keywords.items.every((value) => value.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters), "has an invalid keywords field");
|
|
10218
|
+
}
|
|
10219
|
+
function hashClaudePluginManifest(text2) {
|
|
10220
|
+
const manifest = parse3(text2);
|
|
10221
|
+
validateDescriptiveFields(manifest);
|
|
10222
|
+
const discovery = { kind: "object", entries: manifest.entries.filter(([key]) => !DESCRIPTIVE_FIELDS.has(key)) };
|
|
10223
|
+
return createHash7("sha256").update(canonical3(discovery)).digest("hex");
|
|
10224
|
+
}
|
|
10225
|
+
function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
|
|
10226
|
+
if (!/^[a-f0-9]{64}$/.test(reviewedSha256) || createHash7("sha256").update(text2).digest("hex") !== reviewedSha256) {
|
|
10227
|
+
throw new Error("Reviewed Claude plugin manifest changed before semantic projection");
|
|
10228
|
+
}
|
|
10229
|
+
return hashClaudePluginManifest(text2);
|
|
10230
|
+
}
|
|
10231
|
+
|
|
10095
10232
|
// src/lib/plugin-discovery.ts
|
|
10096
10233
|
import { closeSync as closeSync12, constants as constants12, fstatSync as fstatSync12, lstatSync as lstatSync22, openSync as openSync12, opendirSync as opendirSync5, readSync as readSync10 } from "fs";
|
|
10097
|
-
import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute16, join as
|
|
10234
|
+
import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute16, join as join31, resolve as resolve16 } from "path";
|
|
10098
10235
|
|
|
10099
10236
|
// src/lib/plugin-admission.ts
|
|
10100
10237
|
import { homedir as homedir5 } from "os";
|
|
10101
|
-
import { isAbsolute as isAbsolute15, join as
|
|
10238
|
+
import { isAbsolute as isAbsolute15, join as join30, resolve as resolve15 } from "path";
|
|
10102
10239
|
|
|
10103
10240
|
// src/lib/selection-cache.ts
|
|
10104
|
-
import { createHash as
|
|
10241
|
+
import { createHash as createHash9, randomUUID } from "crypto";
|
|
10105
10242
|
import { spawnSync } from "child_process";
|
|
10106
10243
|
import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, fsyncSync, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync8, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
10107
10244
|
import { hostname } from "os";
|
|
10108
|
-
import { dirname as dirname8, join as
|
|
10245
|
+
import { dirname as dirname8, join as join12, parse as parse4, resolve as resolve8 } from "path";
|
|
10109
10246
|
import { isDeepStrictEqual } from "util";
|
|
10110
10247
|
|
|
10111
10248
|
// src/lib/skill-bundle.ts
|
|
10112
|
-
import { createHash as
|
|
10249
|
+
import { createHash as createHash8 } from "crypto";
|
|
10113
10250
|
import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
10114
|
-
import { join as
|
|
10251
|
+
import { join as join11, relative as relative2 } from "path";
|
|
10115
10252
|
import { createGunzip } from "zlib";
|
|
10116
10253
|
var BLOCK = 512;
|
|
10117
10254
|
var ANY_SEGMENT_EXCLUDES = new Set([
|
|
@@ -10264,7 +10401,7 @@ function ownBytes(view) {
|
|
|
10264
10401
|
return out;
|
|
10265
10402
|
}
|
|
10266
10403
|
function sha256Hex(bytes) {
|
|
10267
|
-
return
|
|
10404
|
+
return createHash8("sha256").update(bytes).digest("hex");
|
|
10268
10405
|
}
|
|
10269
10406
|
function collectSkillBundleEntries(dir) {
|
|
10270
10407
|
const entries = [];
|
|
@@ -10273,7 +10410,7 @@ function collectSkillBundleEntries(dir) {
|
|
|
10273
10410
|
}
|
|
10274
10411
|
function walk(root, current, out) {
|
|
10275
10412
|
for (const entry of readdirSync4(current, { withFileTypes: true })) {
|
|
10276
|
-
const absolute =
|
|
10413
|
+
const absolute = join11(current, entry.name);
|
|
10277
10414
|
const rel = relative2(root, absolute).split("\\").join("/");
|
|
10278
10415
|
const isRootLevel = !rel.includes("/");
|
|
10279
10416
|
if (ANY_SEGMENT_EXCLUDES.has(entry.name.toLowerCase()))
|
|
@@ -10748,17 +10885,17 @@ class SkillSelectionError extends Error {
|
|
|
10748
10885
|
var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
10749
10886
|
var SELECTION_LOCK_FILE = "selection.lock.json";
|
|
10750
10887
|
function selectionCacheRoot(options = {}) {
|
|
10751
|
-
return resolve8(options.cacheDir ??
|
|
10888
|
+
return resolve8(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
|
|
10752
10889
|
}
|
|
10753
10890
|
function selectionKey(selection) {
|
|
10754
10891
|
validateSelection(selection);
|
|
10755
10892
|
return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
|
|
10756
10893
|
}
|
|
10757
10894
|
function hash(value) {
|
|
10758
|
-
return
|
|
10895
|
+
return createHash9("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10759
10896
|
}
|
|
10760
10897
|
function validateSelection(selection) {
|
|
10761
|
-
if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
|
|
10898
|
+
if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || selection.authorizationEpoch !== undefined && (typeof selection.authorizationEpoch !== "string" || !/^[a-f0-9]{32}$/.test(selection.authorizationEpoch)) || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
|
|
10762
10899
|
throw new SkillSelectionError("INVALID_SELECTION", "A skill selection must carry an exact version, digest, workspace and profile revision.");
|
|
10763
10900
|
}
|
|
10764
10901
|
const aliasError = selectionAliasError([selection]);
|
|
@@ -10792,15 +10929,15 @@ function validateResolvedProfile(profile, authority) {
|
|
|
10792
10929
|
}
|
|
10793
10930
|
function selectionBundlePath(selection, options = {}) {
|
|
10794
10931
|
validateSelection(selection);
|
|
10795
|
-
return
|
|
10932
|
+
return join12(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
|
|
10796
10933
|
}
|
|
10797
10934
|
function assertRegularPath(path, createParents = false) {
|
|
10798
10935
|
const absolute = resolve8(path);
|
|
10799
|
-
const root =
|
|
10936
|
+
const root = parse4(absolute).root;
|
|
10800
10937
|
let cursor = root;
|
|
10801
10938
|
const segments = absolute.slice(root.length).split("/");
|
|
10802
10939
|
for (let index = 0;index < segments.length; index++) {
|
|
10803
|
-
cursor =
|
|
10940
|
+
cursor = join12(cursor, segments[index]);
|
|
10804
10941
|
try {
|
|
10805
10942
|
const stat = lstatSync8(cursor);
|
|
10806
10943
|
if (stat.isSymbolicLink() || index < segments.length - 1 && !stat.isDirectory()) {
|
|
@@ -10852,7 +10989,7 @@ function writeSelectionJson(path, value) {
|
|
|
10852
10989
|
}
|
|
10853
10990
|
function atomicWrite(path, bytes, mode, durable = false) {
|
|
10854
10991
|
assertRegularPath(path, true);
|
|
10855
|
-
const temporary =
|
|
10992
|
+
const temporary = join12(dirname8(path), `.selection-${randomUUID()}.tmp`);
|
|
10856
10993
|
try {
|
|
10857
10994
|
const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
|
|
10858
10995
|
try {
|
|
@@ -10884,17 +11021,21 @@ function readSelectionJson(path) {
|
|
|
10884
11021
|
throw new SkillSelectionError("INVALID_RECEIPT", "The Skills selection receipt is unreadable; sync the profile again.");
|
|
10885
11022
|
}
|
|
10886
11023
|
}
|
|
10887
|
-
async function verifiedEntries(selection, bytes) {
|
|
11024
|
+
async function verifiedEntries(selection, bytes, signal, deadline) {
|
|
10888
11025
|
if (`sha256:${sha256Hex(bytes)}` !== selection.bundleDigest)
|
|
10889
11026
|
throw new SkillSelectionError("BUNDLE_DIGEST_MISMATCH", "The skill bundle does not match its selected digest.");
|
|
10890
|
-
const
|
|
11027
|
+
const remaining = deadline === undefined ? undefined : Math.floor(deadline - performance.now());
|
|
11028
|
+
signal?.throwIfAborted();
|
|
11029
|
+
if (remaining !== undefined && remaining <= 0)
|
|
11030
|
+
throw new SkillSelectionError("SKILLS_API_UNAVAILABLE", "Skills session authorization deadline exceeded.");
|
|
11031
|
+
const bundle = await inspectSkillBundle(bytes, { signal, ...remaining === undefined ? {} : { limits: { timeoutMs: remaining } } });
|
|
10891
11032
|
return bundle.entries;
|
|
10892
11033
|
}
|
|
10893
11034
|
async function readCachedSelection(selection, options = {}) {
|
|
10894
11035
|
const bytes = readRegularFile(selectionBundlePath(selection, options), SKILL_BUNDLE_INSPECTION_LIMITS.compressedBytes);
|
|
10895
11036
|
return bytes ? verifiedEntries(selection, bytes) : null;
|
|
10896
11037
|
}
|
|
10897
|
-
async function
|
|
11038
|
+
async function verifySelectionBundleResponse(selection, response, signal, deadline) {
|
|
10898
11039
|
validateSelection(selection);
|
|
10899
11040
|
if (!response?.ok)
|
|
10900
11041
|
throw new SkillSelectionError("BUNDLE_UNAVAILABLE", `The selected skill bundle is unavailable${response ? ` (HTTP ${response.status})` : ""}.`);
|
|
@@ -10904,15 +11045,27 @@ async function cacheSelectionBundle(selection, response, options = {}) {
|
|
|
10904
11045
|
await response.body?.cancel();
|
|
10905
11046
|
throw new SkillSelectionError("BUNDLE_IDENTITY_MISMATCH", "The returned bundle does not match the selected version and digest.");
|
|
10906
11047
|
}
|
|
11048
|
+
const epoch = response.headers.get("X-Skill-Authorization-Epoch");
|
|
11049
|
+
if (selection.authorizationEpoch !== undefined && epoch !== selection.authorizationEpoch) {
|
|
11050
|
+
response.body?.cancel().catch(() => {});
|
|
11051
|
+
throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "The selected bundle lifecycle authorization changed or is unavailable.");
|
|
11052
|
+
}
|
|
10907
11053
|
const maximum = SKILL_BUNDLE_INSPECTION_LIMITS.compressedBytes;
|
|
10908
11054
|
const reader = response.body?.getReader();
|
|
10909
11055
|
if (!reader)
|
|
10910
11056
|
throw new SkillSelectionError("BUNDLE_UNAVAILABLE", "The selected bundle response has no body.");
|
|
11057
|
+
signal?.throwIfAborted();
|
|
11058
|
+
const abort = () => {
|
|
11059
|
+
reader.cancel().catch(() => {});
|
|
11060
|
+
};
|
|
11061
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
10911
11062
|
const chunks = [];
|
|
10912
11063
|
let length = 0;
|
|
10913
11064
|
try {
|
|
10914
11065
|
while (true) {
|
|
11066
|
+
signal?.throwIfAborted();
|
|
10915
11067
|
const next = await reader.read();
|
|
11068
|
+
signal?.throwIfAborted();
|
|
10916
11069
|
if (next.done)
|
|
10917
11070
|
break;
|
|
10918
11071
|
length += next.value.byteLength;
|
|
@@ -10921,9 +11074,10 @@ async function cacheSelectionBundle(selection, response, options = {}) {
|
|
|
10921
11074
|
chunks.push(next.value);
|
|
10922
11075
|
}
|
|
10923
11076
|
} catch (error) {
|
|
10924
|
-
|
|
11077
|
+
reader.cancel().catch(() => {});
|
|
10925
11078
|
throw error;
|
|
10926
11079
|
} finally {
|
|
11080
|
+
signal?.removeEventListener("abort", abort);
|
|
10927
11081
|
reader.releaseLock();
|
|
10928
11082
|
}
|
|
10929
11083
|
const bytes = new Uint8Array(length);
|
|
@@ -10932,7 +11086,12 @@ async function cacheSelectionBundle(selection, response, options = {}) {
|
|
|
10932
11086
|
bytes.set(chunk, offset);
|
|
10933
11087
|
offset += chunk.byteLength;
|
|
10934
11088
|
}
|
|
10935
|
-
const entries = await verifiedEntries(selection, bytes);
|
|
11089
|
+
const entries = await verifiedEntries(selection, bytes, signal, deadline);
|
|
11090
|
+
return { bytes, entries };
|
|
11091
|
+
}
|
|
11092
|
+
async function cacheSelectionBundle(selection, response, options = {}) {
|
|
11093
|
+
const { bytes, entries } = await verifySelectionBundleResponse(selection, response);
|
|
11094
|
+
const maximum = SKILL_BUNDLE_INSPECTION_LIMITS.compressedBytes;
|
|
10936
11095
|
const path = selectionBundlePath(selection, options);
|
|
10937
11096
|
const existing = readRegularFile(path, maximum);
|
|
10938
11097
|
if (existing)
|
|
@@ -10942,7 +11101,7 @@ async function cacheSelectionBundle(selection, response, options = {}) {
|
|
|
10942
11101
|
return entries;
|
|
10943
11102
|
}
|
|
10944
11103
|
function profilePath(profileId, options) {
|
|
10945
|
-
return
|
|
11104
|
+
return join12(selectionCacheRoot(options), "profiles", `${hash(profileId)}.json`);
|
|
10946
11105
|
}
|
|
10947
11106
|
function activateSelectionProfile(profile, options = {}) {
|
|
10948
11107
|
validateResolvedProfile(profile);
|
|
@@ -10974,7 +11133,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
|
|
|
10974
11133
|
}
|
|
10975
11134
|
}
|
|
10976
11135
|
function projectSelectionLockPath(projectDir) {
|
|
10977
|
-
return
|
|
11136
|
+
return join12(resolve8(projectDir), ".skills", SELECTION_LOCK_FILE);
|
|
10978
11137
|
}
|
|
10979
11138
|
function readProjectSelection(projectDir) {
|
|
10980
11139
|
const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
|
|
@@ -10985,7 +11144,7 @@ function readProjectSelection(projectDir) {
|
|
|
10985
11144
|
function sessionReceiptPath(sessionId, options = {}) {
|
|
10986
11145
|
if (!sessionId.trim() || sessionId.length > MAX_SKILL_SESSION_ID_CHARS)
|
|
10987
11146
|
throw new SkillSelectionError("INVALID_SESSION", `A Skills session id must contain 1\u2013${MAX_SKILL_SESSION_ID_CHARS} characters.`);
|
|
10988
|
-
return
|
|
11147
|
+
return join12(selectionCacheRoot(options), "sessions", `${hash(sessionId)}.json`);
|
|
10989
11148
|
}
|
|
10990
11149
|
function skillSessionGeneration(receipt) {
|
|
10991
11150
|
return receipt.generation ?? 0;
|
|
@@ -11191,9 +11350,9 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11191
11350
|
const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
|
|
11192
11351
|
if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
|
|
11193
11352
|
return null;
|
|
11194
|
-
const directory =
|
|
11195
|
-
const archivePath =
|
|
11196
|
-
const recordPath =
|
|
11353
|
+
const directory = join12(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
|
|
11354
|
+
const archivePath = join12(directory, "original.write-lock");
|
|
11355
|
+
const recordPath = join12(directory, "recovery.json");
|
|
11197
11356
|
assertRegularPath(archivePath, true);
|
|
11198
11357
|
archiveBytes(recordPath, Buffer.from(`${JSON.stringify({
|
|
11199
11358
|
schemaVersion: 1,
|
|
@@ -11218,7 +11377,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11218
11377
|
moved = true;
|
|
11219
11378
|
const archivedPaths = [archivePath];
|
|
11220
11379
|
if (finalLock.stagingPath) {
|
|
11221
|
-
const publicationPath =
|
|
11380
|
+
const publicationPath = join12(directory, "publication.write-lock");
|
|
11222
11381
|
renameSync(finalLock.stagingPath, publicationPath);
|
|
11223
11382
|
archivedPaths.push(publicationPath);
|
|
11224
11383
|
}
|
|
@@ -11384,8 +11543,8 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11384
11543
|
if (before.receipt.profile.authority !== replacement.profile.authority || before.receipt.profile.workspaceId !== replacement.profile.workspaceId) {
|
|
11385
11544
|
throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "A session reconciliation cannot change its Skills authority or workspace.");
|
|
11386
11545
|
}
|
|
11387
|
-
const directory =
|
|
11388
|
-
const archivePath =
|
|
11546
|
+
const directory = join12(selectionCacheRoot(options), "session-reconciliations", randomUUID());
|
|
11547
|
+
const archivePath = join12(directory, "original.json"), replacementPath = join12(directory, "replacement.json"), receiptPath = join12(directory, "receipt.json");
|
|
11389
11548
|
const afterSha256 = sha256Hex(bytes);
|
|
11390
11549
|
archiveBytes(archivePath, before.bytes);
|
|
11391
11550
|
archiveBytes(replacementPath, bytes);
|
|
@@ -11413,7 +11572,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11413
11572
|
// package.json
|
|
11414
11573
|
var package_default = {
|
|
11415
11574
|
name: "@hasna/skills",
|
|
11416
|
-
version: "0.10.
|
|
11575
|
+
version: "0.10.48",
|
|
11417
11576
|
description: "Skills library for AI coding agents",
|
|
11418
11577
|
type: "module",
|
|
11419
11578
|
bin: {
|
|
@@ -11555,7 +11714,7 @@ import { createRequire } from "module";
|
|
|
11555
11714
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11556
11715
|
import { closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync9, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
|
|
11557
11716
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
11558
|
-
import { basename as basename4, dirname as dirname9, join as
|
|
11717
|
+
import { basename as basename4, dirname as dirname9, join as join13, relative as relative3, sep as sep4 } from "path";
|
|
11559
11718
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
11560
11719
|
function envToken(name) {
|
|
11561
11720
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -11633,7 +11792,7 @@ var PACKAGE = "@hasna/secrets";
|
|
|
11633
11792
|
var MAX_PACKAGE_BYTES = 1024 * 1024;
|
|
11634
11793
|
function entryPoint(directory) {
|
|
11635
11794
|
const root = realpathSync2(directory);
|
|
11636
|
-
const fd = openSync4(
|
|
11795
|
+
const fd = openSync4(join13(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
|
|
11637
11796
|
let pkg;
|
|
11638
11797
|
try {
|
|
11639
11798
|
const stat = fstatSync4(fd);
|
|
@@ -11653,9 +11812,9 @@ function entryPoint(directory) {
|
|
|
11653
11812
|
if (/[\\%?#\0]/.test(target) || parts.some((part) => !part || part === "." || part === ".." || part === "node_modules")) {
|
|
11654
11813
|
throw new Error("Invalid Secrets import export");
|
|
11655
11814
|
}
|
|
11656
|
-
const file = realpathSync2(
|
|
11815
|
+
const file = realpathSync2(join13(root, ...parts));
|
|
11657
11816
|
const within = relative3(root, file);
|
|
11658
|
-
if (!within || within === ".." || within.startsWith(`..${
|
|
11817
|
+
if (!within || within === ".." || within.startsWith(`..${sep4}`) || !statSync6(file).isFile()) {
|
|
11659
11818
|
throw new Error("Secrets import export escapes its package or is not a file");
|
|
11660
11819
|
}
|
|
11661
11820
|
return pathToFileURL(file).href;
|
|
@@ -11664,7 +11823,7 @@ function resolveInstalledSecrets(parentUrl) {
|
|
|
11664
11823
|
let directory = dirname9(fileURLToPath(parentUrl));
|
|
11665
11824
|
for (;; ) {
|
|
11666
11825
|
if (basename4(directory) !== "node_modules") {
|
|
11667
|
-
const candidate =
|
|
11826
|
+
const candidate = join13(directory, "node_modules", PACKAGE);
|
|
11668
11827
|
let absent = false;
|
|
11669
11828
|
try {
|
|
11670
11829
|
lstatSync9(candidate);
|
|
@@ -12811,7 +12970,7 @@ class MissingSkillsFleetError extends Error {
|
|
|
12811
12970
|
}
|
|
12812
12971
|
|
|
12813
12972
|
// src/lib/remote-files.ts
|
|
12814
|
-
import { createHash as
|
|
12973
|
+
import { createHash as createHash10 } from "crypto";
|
|
12815
12974
|
var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
12816
12975
|
function describeRemoteFiles(files) {
|
|
12817
12976
|
if (files.length > 10)
|
|
@@ -12829,9 +12988,9 @@ function describeRemoteFiles(files) {
|
|
|
12829
12988
|
});
|
|
12830
12989
|
}
|
|
12831
12990
|
function sha256(bytes) {
|
|
12832
|
-
return
|
|
12991
|
+
return createHash10("sha256").update(bytes).digest("hex");
|
|
12833
12992
|
}
|
|
12834
|
-
async function readBoundedResponse(response, maximum) {
|
|
12993
|
+
async function readBoundedResponse(response, maximum, signal) {
|
|
12835
12994
|
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
12836
12995
|
throw new Error("Invalid artifact size limit");
|
|
12837
12996
|
const length = response.headers.get("content-length");
|
|
@@ -12842,11 +13001,18 @@ async function readBoundedResponse(response, maximum) {
|
|
|
12842
13001
|
const reader = response.body?.getReader();
|
|
12843
13002
|
if (!reader)
|
|
12844
13003
|
return new Uint8Array;
|
|
13004
|
+
signal?.throwIfAborted();
|
|
13005
|
+
const abort = () => {
|
|
13006
|
+
reader.cancel().catch(() => {});
|
|
13007
|
+
};
|
|
13008
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
12845
13009
|
const chunks = [];
|
|
12846
13010
|
let size = 0;
|
|
12847
13011
|
try {
|
|
12848
13012
|
while (true) {
|
|
13013
|
+
signal?.throwIfAborted();
|
|
12849
13014
|
const next = await reader.read();
|
|
13015
|
+
signal?.throwIfAborted();
|
|
12850
13016
|
if (next.done)
|
|
12851
13017
|
break;
|
|
12852
13018
|
size += next.value.byteLength;
|
|
@@ -12858,6 +13024,7 @@ async function readBoundedResponse(response, maximum) {
|
|
|
12858
13024
|
await reader.cancel().catch(() => {});
|
|
12859
13025
|
throw error;
|
|
12860
13026
|
} finally {
|
|
13027
|
+
signal?.removeEventListener("abort", abort);
|
|
12861
13028
|
reader.releaseLock();
|
|
12862
13029
|
}
|
|
12863
13030
|
const bytes = new Uint8Array(size);
|
|
@@ -12899,10 +13066,12 @@ function validateSelection2(value) {
|
|
|
12899
13066
|
|
|
12900
13067
|
class HttpProfileClient {
|
|
12901
13068
|
key;
|
|
13069
|
+
signal;
|
|
12902
13070
|
authority;
|
|
12903
13071
|
origin;
|
|
12904
|
-
constructor(key, origin) {
|
|
13072
|
+
constructor(key, origin, signal) {
|
|
12905
13073
|
this.key = key;
|
|
13074
|
+
this.signal = signal;
|
|
12906
13075
|
if (!key.trim())
|
|
12907
13076
|
throw new Error("A Skills credential is required");
|
|
12908
13077
|
this.origin = normalizeSkillsApiOrigin(origin);
|
|
@@ -12916,7 +13085,7 @@ class HttpProfileClient {
|
|
|
12916
13085
|
...init,
|
|
12917
13086
|
redirect: "error",
|
|
12918
13087
|
credentials: "omit",
|
|
12919
|
-
signal: AbortSignal.timeout(15000),
|
|
13088
|
+
signal: this.signal ? AbortSignal.any([this.signal, AbortSignal.timeout(15000)]) : AbortSignal.timeout(15000),
|
|
12920
13089
|
headers: {
|
|
12921
13090
|
"Content-Type": "application/json",
|
|
12922
13091
|
"User-Agent": `hasna-skills/${package_default.version}`,
|
|
@@ -12941,22 +13110,28 @@ class HttpProfileClient {
|
|
|
12941
13110
|
}
|
|
12942
13111
|
async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
|
|
12943
13112
|
try {
|
|
12944
|
-
return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit)));
|
|
13113
|
+
return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit, this.signal)));
|
|
12945
13114
|
} catch {
|
|
12946
13115
|
invalid();
|
|
12947
13116
|
}
|
|
12948
13117
|
}
|
|
13118
|
+
async supportsPinRenewal() {
|
|
13119
|
+
const result = await this.read(await this.request("/capabilities"));
|
|
13120
|
+
if (!object3(result) || !Array.isArray(result.capabilities))
|
|
13121
|
+
invalid();
|
|
13122
|
+
return result.capabilities.includes("skills.session-pin-renewal");
|
|
13123
|
+
}
|
|
12949
13124
|
async resolveProfile(id) {
|
|
12950
13125
|
if (!identifier(id))
|
|
12951
13126
|
throw new Error("Invalid selection profile id");
|
|
12952
|
-
const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve`), MAX_RESOLVED_PROFILE_BYTES);
|
|
13127
|
+
const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve?pinAuthorization=epoch-v1`), MAX_RESOLVED_PROFILE_BYTES);
|
|
12953
13128
|
if (!object3(result) || result.profileId !== id || result.authority !== this.authority || !identifier(result.workspaceId) || !identifier(result.profileRevision) || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
|
|
12954
13129
|
invalid();
|
|
12955
13130
|
const seen = new Set;
|
|
12956
13131
|
for (const entry of result.selections) {
|
|
12957
13132
|
validateSelection2(entry);
|
|
12958
13133
|
const item = entry;
|
|
12959
|
-
if (seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
|
|
13134
|
+
if (item.authorizationEpoch !== undefined && (typeof item.authorizationEpoch !== "string" || !/^[a-f0-9]{32}$/.test(item.authorizationEpoch)) || seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
|
|
12960
13135
|
invalid();
|
|
12961
13136
|
seen.add(item.slug);
|
|
12962
13137
|
}
|
|
@@ -13003,7 +13178,7 @@ class HttpProfileClient {
|
|
|
13003
13178
|
return this.request(`/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/bundle`);
|
|
13004
13179
|
}
|
|
13005
13180
|
}
|
|
13006
|
-
async function createProfileClient() {
|
|
13181
|
+
async function createProfileClient(signal) {
|
|
13007
13182
|
let connection;
|
|
13008
13183
|
try {
|
|
13009
13184
|
connection = await resolveSkillsConnection();
|
|
@@ -13015,12 +13190,12 @@ async function createProfileClient() {
|
|
|
13015
13190
|
}
|
|
13016
13191
|
if (!connection)
|
|
13017
13192
|
throw new Error("Selection profiles require a configured Skills API");
|
|
13018
|
-
return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
|
|
13193
|
+
return new HttpProfileClient(connection.apiKey, connection.apiOrigin, signal);
|
|
13019
13194
|
}
|
|
13020
13195
|
|
|
13021
13196
|
// src/lib/dependency-preparation.ts
|
|
13022
13197
|
import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSync6, lstatSync as lstatSync11, mkdirSync as mkdirSync4, openSync as openSync6, readFileSync as readFileSync10, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
|
|
13023
|
-
import { join as
|
|
13198
|
+
import { join as join14 } from "path";
|
|
13024
13199
|
|
|
13025
13200
|
// src/lib/preparation-process.ts
|
|
13026
13201
|
function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
|
|
@@ -13067,15 +13242,15 @@ var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation";
|
|
|
13067
13242
|
var RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.";
|
|
13068
13243
|
function hasDependencyPreparationMarker(skillPath) {
|
|
13069
13244
|
try {
|
|
13070
|
-
lstatSync11(
|
|
13245
|
+
lstatSync11(join14(skillPath, DEPENDENCY_PREPARATION_MARKER));
|
|
13071
13246
|
return true;
|
|
13072
13247
|
} catch (error) {
|
|
13073
13248
|
return error.code !== "ENOENT";
|
|
13074
13249
|
}
|
|
13075
13250
|
}
|
|
13076
13251
|
function claimPreparation(skillPath) {
|
|
13077
|
-
const marker =
|
|
13078
|
-
const state =
|
|
13252
|
+
const marker = join14(skillPath, DEPENDENCY_PREPARATION_MARKER);
|
|
13253
|
+
const state = join14(marker, "state.json"), active = join14(marker, "active");
|
|
13079
13254
|
let created = false;
|
|
13080
13255
|
try {
|
|
13081
13256
|
mkdirSync4(marker, { mode: 448 });
|
|
@@ -13227,14 +13402,14 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
13227
13402
|
|
|
13228
13403
|
// src/lib/skillinfo.ts
|
|
13229
13404
|
import { existsSync as existsSync13, readFileSync as readFileSync18 } from "fs";
|
|
13230
|
-
import { join as
|
|
13405
|
+
import { join as join28 } from "path";
|
|
13231
13406
|
|
|
13232
13407
|
// src/lib/codex-corpus-write.ts
|
|
13233
13408
|
import { KernelLock } from "@hasna/contracts/kernel-lock";
|
|
13234
13409
|
import { execFileSync } from "child_process";
|
|
13235
|
-
import { createHash as
|
|
13410
|
+
import { createHash as createHash11 } from "crypto";
|
|
13236
13411
|
import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
|
|
13237
|
-
import { basename as basename5, delimiter, dirname as dirname10, isAbsolute as isAbsolute9, join as
|
|
13412
|
+
import { basename as basename5, delimiter, dirname as dirname10, isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve9, sep as sep5 } from "path";
|
|
13238
13413
|
var KEY = ".native-corpus-admission";
|
|
13239
13414
|
var fail = () => {
|
|
13240
13415
|
throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
|
|
@@ -13243,7 +13418,7 @@ function physicalTarget(path) {
|
|
|
13243
13418
|
const suffix = [];
|
|
13244
13419
|
for (let cursor = path;; cursor = dirname10(cursor)) {
|
|
13245
13420
|
try {
|
|
13246
|
-
return
|
|
13421
|
+
return join15(realpathSync3(cursor), ...suffix);
|
|
13247
13422
|
} catch (error) {
|
|
13248
13423
|
if (error.code !== "ENOENT" || cursor === dirname10(cursor))
|
|
13249
13424
|
throw error;
|
|
@@ -13264,17 +13439,17 @@ function codexCorpusRootForPath(path) {
|
|
|
13264
13439
|
const selectedRoots = selected && isAbsolute9(selected) ? [resolve9(selected), physicalTarget(resolve9(selected))] : [];
|
|
13265
13440
|
const classify = (candidate) => {
|
|
13266
13441
|
const checked = (root2) => {
|
|
13267
|
-
if (candidate === root2 || candidate ===
|
|
13442
|
+
if (candidate === root2 || candidate === join15(root2, `${KEY}.flock-v1`))
|
|
13268
13443
|
fail();
|
|
13269
13444
|
return root2;
|
|
13270
13445
|
};
|
|
13271
13446
|
for (const root2 of selectedRoots) {
|
|
13272
13447
|
const distance = relative4(root2, candidate);
|
|
13273
|
-
if (distance === "" || !distance.startsWith(`..${
|
|
13448
|
+
if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute9(distance))
|
|
13274
13449
|
return checked(root2);
|
|
13275
13450
|
}
|
|
13276
13451
|
for (let cursor = candidate;cursor !== dirname10(cursor); cursor = dirname10(cursor)) {
|
|
13277
|
-
if (cursor.endsWith(`${
|
|
13452
|
+
if (cursor.endsWith(`${sep5}.codex`))
|
|
13278
13453
|
return checked(cursor);
|
|
13279
13454
|
}
|
|
13280
13455
|
return;
|
|
@@ -13301,8 +13476,8 @@ function executable(options) {
|
|
|
13301
13476
|
let found;
|
|
13302
13477
|
if (isAbsolute9(command))
|
|
13303
13478
|
found = command;
|
|
13304
|
-
else if (!command.includes(
|
|
13305
|
-
found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute9).map((dir) =>
|
|
13479
|
+
else if (!command.includes(sep5)) {
|
|
13480
|
+
found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute9).map((dir) => join15(dir, command)).find((candidate) => {
|
|
13306
13481
|
try {
|
|
13307
13482
|
accessSync(candidate, constants6.X_OK);
|
|
13308
13483
|
return true;
|
|
@@ -13316,7 +13491,7 @@ function executable(options) {
|
|
|
13316
13491
|
const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
|
|
13317
13492
|
if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
|
|
13318
13493
|
return fail();
|
|
13319
|
-
const hash2 = () =>
|
|
13494
|
+
const hash2 = () => createHash11("sha256").update(readFileSync11(path)).digest("hex");
|
|
13320
13495
|
const digest = hash2();
|
|
13321
13496
|
if (options.codexSha256 && digest !== options.codexSha256)
|
|
13322
13497
|
return fail();
|
|
@@ -13353,7 +13528,7 @@ function acquireCodexCorpusWrite(roots, options) {
|
|
|
13353
13528
|
for (const { root } of held) {
|
|
13354
13529
|
assertCurrent();
|
|
13355
13530
|
native.assertCurrent();
|
|
13356
|
-
const rootIdentity = identity3(root), lockIdentity = identity3(
|
|
13531
|
+
const rootIdentity = identity3(root), lockIdentity = identity3(join15(root, `${KEY}.flock-v1`));
|
|
13357
13532
|
assertCurrent();
|
|
13358
13533
|
const output = execFileSync(native.path, ["corpus-admission-inspect", "--home", root], {
|
|
13359
13534
|
encoding: "utf8",
|
|
@@ -13392,7 +13567,7 @@ function withCodexCorpusWrite(roots, run, options = {}) {
|
|
|
13392
13567
|
|
|
13393
13568
|
// src/lib/installer.ts
|
|
13394
13569
|
import { existsSync as existsSync12, readFileSync as readFileSync17, rmSync as rmSync2 } from "fs";
|
|
13395
|
-
import { join as
|
|
13570
|
+
import { join as join27 } from "path";
|
|
13396
13571
|
import { homedir as homedir4 } from "os";
|
|
13397
13572
|
|
|
13398
13573
|
// src/lib/agent-sync.ts
|
|
@@ -13410,10 +13585,10 @@ import {
|
|
|
13410
13585
|
writeFileSync as writeFileSync7
|
|
13411
13586
|
} from "fs";
|
|
13412
13587
|
import { homedir as homedir3 } from "os";
|
|
13413
|
-
import { basename as basename7, dirname as dirname15, join as
|
|
13588
|
+
import { basename as basename7, dirname as dirname15, join as join25 } from "path";
|
|
13414
13589
|
|
|
13415
13590
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13416
|
-
import { createHash as
|
|
13591
|
+
import { createHash as createHash12 } from "crypto";
|
|
13417
13592
|
|
|
13418
13593
|
// src/lib/codex-local-mcp-controls.ts
|
|
13419
13594
|
function isCodexLocalStdioMcpServer(name, server) {
|
|
@@ -13482,7 +13657,7 @@ function isCodexLocalStdioMcpServer(name, server) {
|
|
|
13482
13657
|
}
|
|
13483
13658
|
|
|
13484
13659
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13485
|
-
import { basename as basename6, dirname as dirname11, join as
|
|
13660
|
+
import { basename as basename6, dirname as dirname11, join as join17, relative as relative5, resolve as resolve10, sep as sep6 } from "path";
|
|
13486
13661
|
import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
|
|
13487
13662
|
|
|
13488
13663
|
// src/lib/codex-native-compatibility.ts
|
|
@@ -13509,7 +13684,7 @@ import { realpathSync as realpathSync5 } from "fs";
|
|
|
13509
13684
|
import { isAbsolute as isAbsolute11, normalize as normalize2 } from "path";
|
|
13510
13685
|
|
|
13511
13686
|
// src/lib/codex-hook-rpc.ts
|
|
13512
|
-
import { join as
|
|
13687
|
+
import { join as join16 } from "path";
|
|
13513
13688
|
import { execFile, spawn } from "child_process";
|
|
13514
13689
|
import { realpathSync as realpathSync4, statSync as statSync8 } from "fs";
|
|
13515
13690
|
import { isAbsolute as isAbsolute10 } from "path";
|
|
@@ -13541,7 +13716,7 @@ async function connectCodexHookRpc(options) {
|
|
|
13541
13716
|
delete env.CODEX_HOME;
|
|
13542
13717
|
else
|
|
13543
13718
|
env.CODEX_HOME = options.codexHome;
|
|
13544
|
-
const corpusLease = acquireCodexCorpusWrite([options.codexHome ??
|
|
13719
|
+
const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join16(options.home, ".codex")], { codexCommand: binary });
|
|
13545
13720
|
const child = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
|
|
13546
13721
|
cwd: options.home,
|
|
13547
13722
|
env,
|
|
@@ -13821,7 +13996,7 @@ function remoteInstallationMatches(parent, remotePluginId, read2) {
|
|
|
13821
13996
|
try {
|
|
13822
13997
|
if (!remotePluginIdentifier(remotePluginId) || !lstatSync13(parent).isDirectory() || realpathSync6(parent) !== parent)
|
|
13823
13998
|
return false;
|
|
13824
|
-
const path =
|
|
13999
|
+
const path = join17(parent, ".codex-remote-plugin-install.json"), stat = lstatSync13(path, { throwIfNoEntry: false });
|
|
13825
14000
|
if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
|
|
13826
14001
|
return false;
|
|
13827
14002
|
const text2 = read2(path);
|
|
@@ -13897,8 +14072,8 @@ function isBundledCleanupHooks(root, namespace, value) {
|
|
|
13897
14072
|
return true;
|
|
13898
14073
|
}
|
|
13899
14074
|
function rootControls(root, read2) {
|
|
13900
|
-
const manifestPath =
|
|
13901
|
-
if (!lstatSync13(
|
|
14075
|
+
const manifestPath = join17(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync13(manifestPath, { throwIfNoEntry: false });
|
|
14076
|
+
if (!lstatSync13(join17(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
13902
14077
|
refuse2();
|
|
13903
14078
|
let manifest, manifestSha256;
|
|
13904
14079
|
try {
|
|
@@ -13913,9 +14088,9 @@ function rootControls(root, read2) {
|
|
|
13913
14088
|
const cacheVersion = basename6(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
13914
14089
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
13915
14090
|
refuse2();
|
|
13916
|
-
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync13(
|
|
14091
|
+
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync13(join17(root, "hooks"), { throwIfNoEntry: false }))
|
|
13917
14092
|
refuse2();
|
|
13918
|
-
const mcp =
|
|
14093
|
+
const mcp = join17(root, ".mcp.json"), mcpPresent = lstatSync13(mcp, { throwIfNoEntry: false });
|
|
13919
14094
|
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
13920
14095
|
refuse2();
|
|
13921
14096
|
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync6(mcp) !== mcp))
|
|
@@ -13923,7 +14098,7 @@ function rootControls(root, read2) {
|
|
|
13923
14098
|
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
|
13924
14099
|
refuse2();
|
|
13925
14100
|
const mcpSha256 = mcpPresent ? mcpControls(mcp, read2) : undefined;
|
|
13926
|
-
const app =
|
|
14101
|
+
const app = join17(root, ".app.json"), present = lstatSync13(app, { throwIfNoEntry: false });
|
|
13927
14102
|
if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
|
|
13928
14103
|
refuse2();
|
|
13929
14104
|
if (manifest.apps !== undefined && !present)
|
|
@@ -13932,7 +14107,7 @@ function rootControls(root, read2) {
|
|
|
13932
14107
|
return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
|
|
13933
14108
|
}
|
|
13934
14109
|
function documentIdentity(document2, cache, read2) {
|
|
13935
|
-
if (!document2.startsWith(cache +
|
|
14110
|
+
if (!document2.startsWith(cache + sep6) || !document2.endsWith(sep6 + "SKILL.md"))
|
|
13936
14111
|
refuse2();
|
|
13937
14112
|
const text2 = read2(document2);
|
|
13938
14113
|
if (Buffer.byteLength(text2) > 1024 * 1024)
|
|
@@ -13955,12 +14130,12 @@ function documentIdentity(document2, cache, read2) {
|
|
|
13955
14130
|
}
|
|
13956
14131
|
if (!front || typeof front !== "object" || !identifier2(front.name) || typeof front.description !== "string" || !front.description.trim())
|
|
13957
14132
|
refuse2();
|
|
13958
|
-
for (let root = dirname11(document2), depth = 0;root.startsWith(cache +
|
|
13959
|
-
const path =
|
|
14133
|
+
for (let root = dirname11(document2), depth = 0;root.startsWith(cache + sep6) && depth < 12; root = dirname11(root), depth++) {
|
|
14134
|
+
const path = join17(root, ".codex-plugin", "plugin.json");
|
|
13960
14135
|
if (!lstatSync13(path, { throwIfNoEntry: false }))
|
|
13961
14136
|
continue;
|
|
13962
14137
|
const manifestFile = lstatSync13(path);
|
|
13963
|
-
if (!lstatSync13(
|
|
14138
|
+
if (!lstatSync13(join17(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
|
|
13964
14139
|
refuse2();
|
|
13965
14140
|
let manifest;
|
|
13966
14141
|
try {
|
|
@@ -13987,13 +14162,13 @@ function identity4(document2, cache, read2) {
|
|
|
13987
14162
|
return { name: parsed.name, pluginParent: parsed.pluginParent, ...controls };
|
|
13988
14163
|
}
|
|
13989
14164
|
function classifyCodexPluginCacheDocument(document2, cache, read2) {
|
|
13990
|
-
if (!document2.startsWith(cache +
|
|
14165
|
+
if (!document2.startsWith(cache + sep6))
|
|
13991
14166
|
return null;
|
|
13992
14167
|
try {
|
|
13993
14168
|
const located = documentIdentity(document2, cache, read2), parsed = identity4(document2, cache, read2);
|
|
13994
|
-
if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(
|
|
14169
|
+
if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(join17(located.root, ".codex-plugin/plugin.json")) !== join17(located.root, ".codex-plugin/plugin.json"))
|
|
13995
14170
|
return null;
|
|
13996
|
-
const parts = relative5(cache, parsed.pluginParent).split(
|
|
14171
|
+
const parts = relative5(cache, parsed.pluginParent).split(sep6);
|
|
13997
14172
|
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== parsed.namespace)
|
|
13998
14173
|
return null;
|
|
13999
14174
|
return { name: parsed.name, namespace: parsed.namespace, pluginId: `${parts[1]}@${parts[0]}`, pluginParent: parsed.pluginParent, root: located.root, manifestSha256: parsed.manifestSha256 };
|
|
@@ -14003,7 +14178,7 @@ function classifyCodexPluginCacheDocument(document2, cache, read2) {
|
|
|
14003
14178
|
}
|
|
14004
14179
|
function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
14005
14180
|
try {
|
|
14006
|
-
const parts = relative5(cache, parent).split(
|
|
14181
|
+
const parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
14007
14182
|
if (resolve10(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
|
|
14008
14183
|
return false;
|
|
14009
14184
|
for (let cursor = parent;; cursor = dirname11(cursor)) {
|
|
@@ -14013,7 +14188,7 @@ function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
|
14013
14188
|
if (dirname11(cursor) === cursor)
|
|
14014
14189
|
break;
|
|
14015
14190
|
}
|
|
14016
|
-
return enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path.startsWith(parent +
|
|
14191
|
+
return enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path.startsWith(parent + sep6))) && rule.enabled !== false));
|
|
14017
14192
|
} catch {
|
|
14018
14193
|
return false;
|
|
14019
14194
|
}
|
|
@@ -14023,7 +14198,7 @@ function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
|
14023
14198
|
}
|
|
14024
14199
|
function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbsent = false) {
|
|
14025
14200
|
try {
|
|
14026
|
-
const parent = dirname11(root), parts = relative5(cache, parent).split(
|
|
14201
|
+
const parent = dirname11(root), parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
14027
14202
|
if (resolve10(root) !== root || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !basename6(root) || basename6(root) === "latest" || !enrolled.length || !Array.isArray(rules))
|
|
14028
14203
|
return false;
|
|
14029
14204
|
for (let cursor = root;; cursor = dirname11(cursor)) {
|
|
@@ -14033,14 +14208,14 @@ function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbse
|
|
|
14033
14208
|
if (dirname11(cursor) === cursor)
|
|
14034
14209
|
break;
|
|
14035
14210
|
}
|
|
14036
|
-
if (!enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path === root || rule.path.startsWith(root +
|
|
14211
|
+
if (!enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path === root || rule.path.startsWith(root + sep6))) && rule.enabled !== false)))
|
|
14037
14212
|
return false;
|
|
14038
14213
|
if (!lstatSync13(root, { throwIfNoEntry: false }))
|
|
14039
14214
|
return allowAbsent && (lstatSync13(parent, { throwIfNoEntry: false }) ? enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read2)) : absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
14040
14215
|
if (!enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read2)))
|
|
14041
14216
|
return false;
|
|
14042
14217
|
const parsed = rootControls(root, read2);
|
|
14043
|
-
if (JSON.parse(read2(
|
|
14218
|
+
if (JSON.parse(read2(join17(root, ".codex-plugin/plugin.json"))).hooks !== undefined)
|
|
14044
14219
|
return false;
|
|
14045
14220
|
if (!enrolled.some((control) => control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && !parsed.appSha256 && !parsed.mcpSha256))
|
|
14046
14221
|
return false;
|
|
@@ -14048,7 +14223,7 @@ function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbse
|
|
|
14048
14223
|
const emptySkills = (directory, depth) => depth <= 64 && readdirSync6(directory).every((name) => {
|
|
14049
14224
|
if (++entries > 4096 || name === "SKILL.md")
|
|
14050
14225
|
return false;
|
|
14051
|
-
const path =
|
|
14226
|
+
const path = join17(directory, name), stat = lstatSync13(path);
|
|
14052
14227
|
return !stat.isSymbolicLink() && (stat.isFile() || stat.isDirectory() && emptySkills(path, depth + 1));
|
|
14053
14228
|
});
|
|
14054
14229
|
return emptySkills(root, 0);
|
|
@@ -14064,7 +14239,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
|
|
|
14064
14239
|
for (const parent of new Set(controls.map((control) => control.pluginParent))) {
|
|
14065
14240
|
if (absentDisabledCodexPluginParent(cache, parent, controls, rules))
|
|
14066
14241
|
continue;
|
|
14067
|
-
if (!parent.startsWith(cache +
|
|
14242
|
+
if (!parent.startsWith(cache + sep6) || !lstatSync13(parent, { throwIfNoEntry: false })?.isDirectory() || realpathSync6(parent) !== parent)
|
|
14068
14243
|
return false;
|
|
14069
14244
|
for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
|
|
14070
14245
|
if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read2))
|
|
@@ -14075,7 +14250,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
|
|
|
14075
14250
|
if (entries > 4096)
|
|
14076
14251
|
return false;
|
|
14077
14252
|
for (const name of names) {
|
|
14078
|
-
let root =
|
|
14253
|
+
let root = join17(parent, name), stat = lstatSync13(root);
|
|
14079
14254
|
if (stat.isSymbolicLink()) {
|
|
14080
14255
|
const target = resolve10(parent, readlinkSync3(root));
|
|
14081
14256
|
if (name !== "latest" || dirname11(target) !== parent || !lstatSync13(target).isDirectory() || realpathSync6(target) !== target)
|
|
@@ -14083,7 +14258,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
|
|
|
14083
14258
|
root = target;
|
|
14084
14259
|
stat = lstatSync13(root);
|
|
14085
14260
|
}
|
|
14086
|
-
if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync13(
|
|
14261
|
+
if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync13(join17(root, path), { throwIfNoEntry: false })))
|
|
14087
14262
|
continue;
|
|
14088
14263
|
const parsed = rootControls(root, read2);
|
|
14089
14264
|
if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
|
|
@@ -14113,12 +14288,12 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14113
14288
|
}
|
|
14114
14289
|
const candidates = catalog.plugins === undefined ? skills.map((skill) => skill.path).filter((path) => allowed.has(path)) : allowed;
|
|
14115
14290
|
for (const document2 of candidates) {
|
|
14116
|
-
if (!document2.startsWith(cache +
|
|
14291
|
+
if (!document2.startsWith(cache + sep6))
|
|
14117
14292
|
continue;
|
|
14118
14293
|
const located = documentIdentity(document2, cache, read2);
|
|
14119
14294
|
if (located.name === "skills-cli" || located.name.endsWith(":skills-cli"))
|
|
14120
14295
|
refuse2();
|
|
14121
|
-
const cacheParts = relative5(cache, located.pluginParent).split(
|
|
14296
|
+
const cacheParts = relative5(cache, located.pluginParent).split(sep6);
|
|
14122
14297
|
if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier2(part)))
|
|
14123
14298
|
refuse2();
|
|
14124
14299
|
const [marketplace, pluginName] = cacheParts;
|
|
@@ -14131,8 +14306,8 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14131
14306
|
const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
|
|
14132
14307
|
if (catalog.version === "codex-cli 0.160.0" && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename6(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
|
|
14133
14308
|
const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
|
|
14134
|
-
if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache +
|
|
14135
|
-
if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot +
|
|
14309
|
+
if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep6) && (lstatSync13(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep6)))) {
|
|
14310
|
+
if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep6)) || !sourceInputUnchanged(input, cache, read2))
|
|
14136
14311
|
refuse2();
|
|
14137
14312
|
sourceInputs.set(expectedPluginId, input);
|
|
14138
14313
|
}
|
|
@@ -14142,7 +14317,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14142
14317
|
if (!Array.isArray(rules))
|
|
14143
14318
|
refuse2();
|
|
14144
14319
|
const exactPathDeny = rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
14145
|
-
if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(
|
|
14320
|
+
if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(join17(located.root, ".codex-plugin/plugin.json")) !== join17(located.root, ".codex-plugin/plugin.json") || !exactPathDeny || rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === located.name && rule?.enabled !== false) || !isCodexNativeSkillDisabled({ name: located.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
|
|
14146
14321
|
refuse2();
|
|
14147
14322
|
inertDocuments.add(document2);
|
|
14148
14323
|
const plugin = installed[0];
|
|
@@ -14171,7 +14346,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14171
14346
|
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
14172
14347
|
const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
|
|
14173
14348
|
const denial = denials.find((item) => item.path === document2);
|
|
14174
|
-
const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 ===
|
|
14349
|
+
const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 === createHash12("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
|
|
14175
14350
|
if (denial && !explicitDeny)
|
|
14176
14351
|
refuse2();
|
|
14177
14352
|
if (explicitDeny)
|
|
@@ -14180,21 +14355,21 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14180
14355
|
if (!explicitDeny && (!exactPathDeny && !plannedPathDeny || !isCodexNativeSkillDisabled(nativeSkill, rules)))
|
|
14181
14356
|
refuse2();
|
|
14182
14357
|
}
|
|
14183
|
-
const skillsOnly = !parsed.appSha256 && !parsed.mcpSha256 && JSON.parse(read2(
|
|
14358
|
+
const skillsOnly = !parsed.appSha256 && !parsed.mcpSha256 && JSON.parse(read2(join17(located.root, ".codex-plugin/plugin.json"))).hooks === undefined;
|
|
14184
14359
|
result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {}, ...skillsOnly ? { skillsOnly: true } : {} });
|
|
14185
14360
|
}
|
|
14186
14361
|
if (usedDenials.size !== denials.length)
|
|
14187
14362
|
refuse2();
|
|
14188
|
-
if (!result.length && documents.some((document2) => document2.startsWith(cache +
|
|
14363
|
+
if (!result.length && documents.some((document2) => document2.startsWith(cache + sep6) && !inertDocuments.has(document2)) || new Set(result.map((item) => item.name)).size !== result.length)
|
|
14189
14364
|
refuse2();
|
|
14190
14365
|
return { skills: result, inactivePlugins: [...inactivePlugins.values()], sourceInputs: [...sourceInputs.values()] };
|
|
14191
14366
|
}
|
|
14192
14367
|
function sourceInputUnchanged(input, cache, read2) {
|
|
14193
14368
|
try {
|
|
14194
|
-
const parts = relative5(cache, input.pluginParent).split(
|
|
14195
|
-
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) || resolve10(input.sourceRoot) !== input.sourceRoot || !lstatSync13(input.sourceRoot).isDirectory() || realpathSync6(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache +
|
|
14369
|
+
const parts = relative5(cache, input.pluginParent).split(sep6);
|
|
14370
|
+
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) || resolve10(input.sourceRoot) !== input.sourceRoot || !lstatSync13(input.sourceRoot).isDirectory() || realpathSync6(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep6) || cache.startsWith(input.sourceRoot + sep6))
|
|
14196
14371
|
return false;
|
|
14197
|
-
const path =
|
|
14372
|
+
const path = join17(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync13(path);
|
|
14198
14373
|
if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync6(path) !== path)
|
|
14199
14374
|
return false;
|
|
14200
14375
|
const text2 = read2(path);
|
|
@@ -14209,7 +14384,7 @@ function reviewedCodexPluginSourceRoots(cache, inputs, read2) {
|
|
|
14209
14384
|
if (!Array.isArray(inputs) || inputs.length > 4096 || new Set(inputs.map((input) => input.pluginId)).size !== inputs.length || new Set(inputs.map((input) => input.sourceRoot)).size !== inputs.length || inputs.some((input) => !sourceInputUnchanged(input, cache, read2)))
|
|
14210
14385
|
refuse2();
|
|
14211
14386
|
const roots = inputs.map((input) => input.sourceRoot);
|
|
14212
|
-
if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other +
|
|
14387
|
+
if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep6))))
|
|
14213
14388
|
refuse2();
|
|
14214
14389
|
return roots;
|
|
14215
14390
|
}
|
|
@@ -14218,9 +14393,9 @@ function isReviewedCodexPluginInactive(document2, cache, controls, pluginSetting
|
|
|
14218
14393
|
return false;
|
|
14219
14394
|
try {
|
|
14220
14395
|
const parsed = documentIdentity(document2, cache, read2), matches = controls.filter((control2) => control2.pluginParent === parsed.pluginParent && control2.namespace === parsed.namespace);
|
|
14221
|
-
if (matches.length !== 1 || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(parsed.root) !== parsed.root || realpathSync6(parsed.pluginParent) !== parsed.pluginParent || realpathSync6(
|
|
14396
|
+
if (matches.length !== 1 || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(parsed.root) !== parsed.root || realpathSync6(parsed.pluginParent) !== parsed.pluginParent || realpathSync6(join17(parsed.root, ".codex-plugin/plugin.json")) !== join17(parsed.root, ".codex-plugin/plugin.json"))
|
|
14222
14397
|
return false;
|
|
14223
|
-
const control = matches[0], parts = relative5(cache, parsed.pluginParent).split(
|
|
14398
|
+
const control = matches[0], parts = relative5(cache, parsed.pluginParent).split(sep6);
|
|
14224
14399
|
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== parsed.namespace || control.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ ...control, id: control.pluginId }) || !Object.hasOwn(pluginSettings, control.pluginId) || pluginSettings[control.pluginId]?.enabled !== false)
|
|
14225
14400
|
return false;
|
|
14226
14401
|
return !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule?.enabled !== false);
|
|
@@ -14229,7 +14404,7 @@ function isReviewedCodexPluginInactive(document2, cache, controls, pluginSetting
|
|
|
14229
14404
|
}
|
|
14230
14405
|
}
|
|
14231
14406
|
function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, read2) {
|
|
14232
|
-
if (!document2.startsWith(cache +
|
|
14407
|
+
if (!document2.startsWith(cache + sep6) || !Array.isArray(controls) || controls.length > 4096)
|
|
14233
14408
|
return false;
|
|
14234
14409
|
let parsed;
|
|
14235
14410
|
try {
|
|
@@ -14258,9 +14433,9 @@ enabled = false
|
|
|
14258
14433
|
}
|
|
14259
14434
|
|
|
14260
14435
|
// src/lib/codex-native-skill-policy.ts
|
|
14261
|
-
import { createHash as
|
|
14436
|
+
import { createHash as createHash13, randomUUID as randomUUID2 } from "crypto";
|
|
14262
14437
|
import { closeSync as closeSync7, constants as constants7, fstatSync as fstatSync7, fsyncSync as fsyncSync2, lstatSync as lstatSync14, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync12, readSync as readSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
14263
|
-
import { dirname as dirname12, isAbsolute as isAbsolute12, join as
|
|
14438
|
+
import { dirname as dirname12, isAbsolute as isAbsolute12, join as join18, resolve as resolve11 } from "path";
|
|
14264
14439
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
14265
14440
|
var CODEX_NATIVE_POLICY_CAPABILITY = "host-path-allowlist-v1";
|
|
14266
14441
|
var CODEX_NATIVE_POLICY_RECEIPT_SCHEMA = "skills.codex-native-policy-acceptance/v1";
|
|
@@ -14386,7 +14561,7 @@ function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256)
|
|
|
14386
14561
|
if (realpathSync7.native(path) !== path)
|
|
14387
14562
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
|
|
14388
14563
|
const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
|
|
14389
|
-
const sha2562 =
|
|
14564
|
+
const sha2562 = createHash13("sha256").update(bytes).digest("hex");
|
|
14390
14565
|
if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
|
|
14391
14566
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
|
|
14392
14567
|
return { path, sha256: sha2562 };
|
|
@@ -14469,7 +14644,7 @@ function executableWitness(path) {
|
|
|
14469
14644
|
var witnessKey = (path, witness) => JSON.stringify([path, witness.dev, witness.ino, witness.size, witness.mtimeNs, witness.ctimeNs, witness.uid]);
|
|
14470
14645
|
var executableDigestMemory = new Map;
|
|
14471
14646
|
function executableCachePath(dataDir2) {
|
|
14472
|
-
return
|
|
14647
|
+
return join18(resolve11(dataDir2), "agent-hooks", "codex-native-policy-executable-cache.json");
|
|
14473
14648
|
}
|
|
14474
14649
|
var currentUid = () => process.getuid?.() ?? -1;
|
|
14475
14650
|
function readTrustedCacheBytes(path, ownerUid) {
|
|
@@ -14632,7 +14807,7 @@ function assertOperatorTrustRoot(dataDir2) {
|
|
|
14632
14807
|
};
|
|
14633
14808
|
checkDirectory(resolve11(dataDir2), "the Skills data directory", true);
|
|
14634
14809
|
checkDirectory(dirname12(executableCachePath(dataDir2)), "the agent-hooks directory", false);
|
|
14635
|
-
check(
|
|
14810
|
+
check(join18(resolve11(dataDir2), "agent-policy.json"), "the managed policy", true);
|
|
14636
14811
|
check(executableCachePath(dataDir2), "the executable identity cache", false);
|
|
14637
14812
|
}
|
|
14638
14813
|
function runCodexNativePolicyHelper(request) {
|
|
@@ -14803,7 +14978,7 @@ function defaultProcessInspector() {
|
|
|
14803
14978
|
return { platform: process.platform, arch: process.arch, pid: process.pid, parentOf: () => null, startTime: () => null, executablePath: () => null };
|
|
14804
14979
|
}
|
|
14805
14980
|
function codexNativePolicyReceiptPath(dataDir2) {
|
|
14806
|
-
return
|
|
14981
|
+
return join18(resolve11(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
|
|
14807
14982
|
}
|
|
14808
14983
|
function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
|
|
14809
14984
|
const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname12(path);
|
|
@@ -14854,8 +15029,8 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
|
|
|
14854
15029
|
}
|
|
14855
15030
|
|
|
14856
15031
|
// src/lib/codex-settings-witness-v1.ts
|
|
14857
|
-
import { createHash as
|
|
14858
|
-
var sha = (text2) =>
|
|
15032
|
+
import { createHash as createHash14 } from "crypto";
|
|
15033
|
+
var sha = (text2) => createHash14("sha256").update(text2).digest("hex");
|
|
14859
15034
|
var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
14860
15035
|
var VERBOSITY = new Set(["low", "medium", "high"]);
|
|
14861
15036
|
function need3(value) {
|
|
@@ -14901,7 +15076,7 @@ function numericTokens(text2) {
|
|
|
14901
15076
|
}
|
|
14902
15077
|
return result;
|
|
14903
15078
|
}
|
|
14904
|
-
function
|
|
15079
|
+
function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
14905
15080
|
need3(depth <= 32 && --budget2.remaining >= 0);
|
|
14906
15081
|
if (value === null)
|
|
14907
15082
|
return ["null"];
|
|
@@ -14918,9 +15093,9 @@ function canonical3(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
|
14918
15093
|
return ["date", value.toISOString()];
|
|
14919
15094
|
}
|
|
14920
15095
|
if (Array.isArray(value))
|
|
14921
|
-
return ["array", value.map((child) =>
|
|
15096
|
+
return ["array", value.map((child) => canonical4(child, depth + 1, budget2))];
|
|
14922
15097
|
need3(value && typeof value === "object");
|
|
14923
|
-
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key,
|
|
15098
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget2)])];
|
|
14924
15099
|
}
|
|
14925
15100
|
function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
14926
15101
|
need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
@@ -14941,7 +15116,7 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
|
|
|
14941
15116
|
need3(typeof config[key] === "string" && values.has(config[key]));
|
|
14942
15117
|
delete config[key];
|
|
14943
15118
|
}
|
|
14944
|
-
return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([
|
|
15119
|
+
return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical4(config), numericTokens(text2)])}`);
|
|
14945
15120
|
}
|
|
14946
15121
|
function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
14947
15122
|
const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
@@ -15012,9 +15187,9 @@ function projectRegularCodexMcp(config, source) {
|
|
|
15012
15187
|
}
|
|
15013
15188
|
|
|
15014
15189
|
// src/lib/codex-settings-witness.ts
|
|
15015
|
-
import { createHash as
|
|
15190
|
+
import { createHash as createHash15 } from "crypto";
|
|
15016
15191
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
15017
|
-
var sha2 = (text2) =>
|
|
15192
|
+
var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex");
|
|
15018
15193
|
var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
15019
15194
|
var VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
15020
15195
|
var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
@@ -15069,7 +15244,7 @@ function numericTokens2(text2) {
|
|
|
15069
15244
|
}
|
|
15070
15245
|
return result.sort();
|
|
15071
15246
|
}
|
|
15072
|
-
function
|
|
15247
|
+
function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
15073
15248
|
need4(depth <= 32 && --budget2.remaining >= 0);
|
|
15074
15249
|
if (value === null)
|
|
15075
15250
|
return ["null"];
|
|
@@ -15086,9 +15261,9 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
|
15086
15261
|
return ["date", value.toISOString()];
|
|
15087
15262
|
}
|
|
15088
15263
|
if (Array.isArray(value))
|
|
15089
|
-
return ["array", value.map((child) =>
|
|
15264
|
+
return ["array", value.map((child) => canonical5(child, depth + 1, budget2))];
|
|
15090
15265
|
need4(isRecord3(value));
|
|
15091
|
-
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key,
|
|
15266
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical5(child, depth + 1, budget2)])];
|
|
15092
15267
|
}
|
|
15093
15268
|
function normalize3(config, version = 2) {
|
|
15094
15269
|
if (isRecord3(config.hooks)) {
|
|
@@ -15262,7 +15437,7 @@ function settingsDigest2(text2, version = 2) {
|
|
|
15262
15437
|
const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
|
|
15263
15438
|
const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
|
|
15264
15439
|
normalize3(projected.config, version);
|
|
15265
|
-
return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([
|
|
15440
|
+
return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical5(projected.config), numericTokens2(projected.numericSource)])}`);
|
|
15266
15441
|
}
|
|
15267
15442
|
function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15268
15443
|
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
@@ -15326,15 +15501,15 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
|
15326
15501
|
}
|
|
15327
15502
|
|
|
15328
15503
|
// src/lib/agent-integration.ts
|
|
15329
|
-
import { createHash as
|
|
15504
|
+
import { createHash as createHash18, randomUUID as randomUUID3 } from "crypto";
|
|
15330
15505
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
15331
15506
|
import { existsSync as existsSync9, lstatSync as lstatSync19, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync14, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync5, realpathSync as realpathSync9, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync4, fstatSync as fstatSync10, readSync as readSync8, constants as constants10, linkSync as linkSync2 } from "fs";
|
|
15332
|
-
import { dirname as dirname14, join as
|
|
15507
|
+
import { dirname as dirname14, join as join23, resolve as resolve13, relative as relative6, isAbsolute as isAbsolute13, sep as sep7 } from "path";
|
|
15333
15508
|
import { homedir as homedir2 } from "os";
|
|
15334
15509
|
|
|
15335
15510
|
// src/lib/managed-policy.ts
|
|
15336
15511
|
import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, lstatSync as lstatSync15, openSync as openSync8, readSync as readSync6 } from "fs";
|
|
15337
|
-
import { join as
|
|
15512
|
+
import { join as join19 } from "path";
|
|
15338
15513
|
function refusal(reason = "is unreadable") {
|
|
15339
15514
|
return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason}; refusing legacy fallback.`);
|
|
15340
15515
|
}
|
|
@@ -15365,7 +15540,7 @@ function serializeManagedSkillPolicy(value) {
|
|
|
15365
15540
|
return text2;
|
|
15366
15541
|
}
|
|
15367
15542
|
function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
|
|
15368
|
-
const path =
|
|
15543
|
+
const path = join19(dataDir2, "agent-policy.json");
|
|
15369
15544
|
let descriptor, observed = false;
|
|
15370
15545
|
try {
|
|
15371
15546
|
const stat = lstatSync15(path);
|
|
@@ -15413,9 +15588,9 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
|
15413
15588
|
}
|
|
15414
15589
|
|
|
15415
15590
|
// src/lib/agent-bridge.ts
|
|
15416
|
-
import { createHash as
|
|
15591
|
+
import { createHash as createHash16 } from "crypto";
|
|
15417
15592
|
import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
|
|
15418
|
-
import { join as
|
|
15593
|
+
import { join as join20 } from "path";
|
|
15419
15594
|
var CLI_BRIDGE_NAME = "skills-cli";
|
|
15420
15595
|
var CLI_BRIDGE_VERSION = 1;
|
|
15421
15596
|
var markdown = `---
|
|
@@ -15432,7 +15607,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
15432
15607
|
|
|
15433
15608
|
This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
|
|
15434
15609
|
`;
|
|
15435
|
-
var CLI_BRIDGE_DIGEST =
|
|
15610
|
+
var CLI_BRIDGE_DIGEST = createHash16("sha256").update(markdown).digest("hex");
|
|
15436
15611
|
var CLI_BRIDGE_FILES = Object.freeze({
|
|
15437
15612
|
"SKILL.md": markdown,
|
|
15438
15613
|
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
@@ -15447,7 +15622,7 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
15447
15622
|
if (JSON.stringify(readdirSync7(path).sort()) !== JSON.stringify(Object.keys(CLI_BRIDGE_FILES).sort()))
|
|
15448
15623
|
return false;
|
|
15449
15624
|
return Object.entries(CLI_BRIDGE_FILES).every(([name, content]) => {
|
|
15450
|
-
const file =
|
|
15625
|
+
const file = join20(path, name), stat = lstatSync16(file);
|
|
15451
15626
|
return stat.isFile() && !stat.isSymbolicLink() && stat.size === Buffer.byteLength(content) && readFileSync13(file, "utf8") === content;
|
|
15452
15627
|
});
|
|
15453
15628
|
} catch {
|
|
@@ -15725,36 +15900,36 @@ function disableCodexBundledSkills(text2) {
|
|
|
15725
15900
|
|
|
15726
15901
|
// src/lib/agent-sumi.ts
|
|
15727
15902
|
import { existsSync as existsSync8, lstatSync as lstatSync17, realpathSync as realpathSync8 } from "fs";
|
|
15728
|
-
import { join as
|
|
15903
|
+
import { join as join21, resolve as resolve12 } from "path";
|
|
15729
15904
|
function sumiConfigDirectory(home, env = process.env) {
|
|
15730
15905
|
for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
|
|
15731
15906
|
if (env[key] !== undefined)
|
|
15732
15907
|
throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
|
|
15733
15908
|
const selected = (key) => env[key]?.trim() ? env[key] : undefined;
|
|
15734
|
-
const expand = (value) => resolve12(value === "~" ? home : value.startsWith("~/") ?
|
|
15909
|
+
const expand = (value) => resolve12(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
|
|
15735
15910
|
const explicit = selected("SUMI_CONFIG_DIR");
|
|
15736
15911
|
const xdg = selected("XDG_CONFIG_HOME");
|
|
15737
15912
|
const custom = selected("SUMI_HOME");
|
|
15738
|
-
const
|
|
15913
|
+
const canonical6 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
|
|
15739
15914
|
if (explicit || xdg || custom)
|
|
15740
|
-
return
|
|
15741
|
-
const legacy =
|
|
15742
|
-
const a = lstatSync17(
|
|
15743
|
-
if (a && b && realpathSync8(
|
|
15915
|
+
return canonical6;
|
|
15916
|
+
const legacy = join21(home, ".config/sumi");
|
|
15917
|
+
const a = lstatSync17(canonical6, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
|
|
15918
|
+
if (a && b && realpathSync8(canonical6) !== realpathSync8(legacy))
|
|
15744
15919
|
throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
|
|
15745
|
-
const path = a ?
|
|
15920
|
+
const path = a ? canonical6 : b ? legacy : canonical6;
|
|
15746
15921
|
if (!existsSync8(path))
|
|
15747
15922
|
return path;
|
|
15748
15923
|
const physical = realpathSync8(path);
|
|
15749
|
-
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![
|
|
15924
|
+
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical6, legacy].includes(physical))
|
|
15750
15925
|
throw new Error("Sumi config alias must resolve to its canonical or legacy root");
|
|
15751
15926
|
return physical;
|
|
15752
15927
|
}
|
|
15753
15928
|
function sumiBridgeRoot(home) {
|
|
15754
|
-
return
|
|
15929
|
+
return join21(sumiConfigDirectory(home), "skills");
|
|
15755
15930
|
}
|
|
15756
15931
|
function sumiConfigPath(home) {
|
|
15757
|
-
return
|
|
15932
|
+
return join21(sumiConfigDirectory(home), "sumi.json");
|
|
15758
15933
|
}
|
|
15759
15934
|
function renderSumiPlugin(command, profile) {
|
|
15760
15935
|
return renderSumiPluginVersion(command, profile, false);
|
|
@@ -15847,7 +16022,7 @@ var SUMI_SKILL_PERMISSIONS = [
|
|
|
15847
16022
|
|
|
15848
16023
|
// src/lib/codex-hook-trust-files.ts
|
|
15849
16024
|
import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
15850
|
-
import { createHash as
|
|
16025
|
+
import { createHash as createHash17 } from "crypto";
|
|
15851
16026
|
import { dirname as dirname13 } from "path";
|
|
15852
16027
|
function need5(value, code) {
|
|
15853
16028
|
if (!value)
|
|
@@ -15883,7 +16058,7 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
15883
16058
|
}
|
|
15884
16059
|
need5(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
|
|
15885
16060
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
15886
|
-
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256:
|
|
16061
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash17("sha256").update(content).digest("hex") };
|
|
15887
16062
|
} finally {
|
|
15888
16063
|
closeSync9(fd);
|
|
15889
16064
|
}
|
|
@@ -15894,7 +16069,7 @@ function unchanged2(before) {
|
|
|
15894
16069
|
}
|
|
15895
16070
|
|
|
15896
16071
|
// src/lib/agent-integration.ts
|
|
15897
|
-
var sha3 = (data) =>
|
|
16072
|
+
var sha3 = (data) => createHash18("sha256").update(data).digest("hex");
|
|
15898
16073
|
var ROOTS = NATIVE_SKILL_ROOTS;
|
|
15899
16074
|
function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
|
|
15900
16075
|
try {
|
|
@@ -15972,7 +16147,7 @@ function isSystemRootAlias(path) {
|
|
|
15972
16147
|
}
|
|
15973
16148
|
}
|
|
15974
16149
|
function canonicalSystemPath(path) {
|
|
15975
|
-
const absolute = resolve13(path), root = `/${absolute.split(
|
|
16150
|
+
const absolute = resolve13(path), root = `/${absolute.split(sep7)[1]}`;
|
|
15976
16151
|
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync19(root, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
15977
16152
|
return absolute;
|
|
15978
16153
|
if (!isSystemRootAlias(root))
|
|
@@ -15992,7 +16167,7 @@ function assertSafePath(path) {
|
|
|
15992
16167
|
}
|
|
15993
16168
|
function readRootAlias(home, agent) {
|
|
15994
16169
|
assertSafePath(home);
|
|
15995
|
-
const realHome = realpathSync9(home), alias =
|
|
16170
|
+
const realHome = realpathSync9(home), alias = join23(realHome, `.${agent}`);
|
|
15996
16171
|
const stat = lstatSync19(alias, { throwIfNoEntry: false, bigint: true });
|
|
15997
16172
|
if (!stat?.isSymbolicLink())
|
|
15998
16173
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
@@ -16002,7 +16177,7 @@ function readRootAlias(home, agent) {
|
|
|
16002
16177
|
if (!targetStat?.isDirectory())
|
|
16003
16178
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
16004
16179
|
const target = realpathSync9(targetPath), inside = relative6(realHome, target);
|
|
16005
|
-
if (!inside || inside === ".." || inside.startsWith(`..${
|
|
16180
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute13(inside))
|
|
16006
16181
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
16007
16182
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
16008
16183
|
}
|
|
@@ -16011,15 +16186,15 @@ function rootAliases(home, allowed = false) {
|
|
|
16011
16186
|
return [];
|
|
16012
16187
|
const result = [];
|
|
16013
16188
|
for (const agent of ["claude", "codex"])
|
|
16014
|
-
if (lstatSync19(
|
|
16189
|
+
if (lstatSync19(join23(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
16015
16190
|
result.push(readRootAlias(home, agent));
|
|
16016
|
-
if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target +
|
|
16191
|
+
if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep7) || other.target.startsWith(item.target + sep7))))
|
|
16017
16192
|
throw new Error("Agent root alias targets must not overlap");
|
|
16018
16193
|
return result;
|
|
16019
16194
|
}
|
|
16020
16195
|
function canonicalAgentPath(path, aliases) {
|
|
16021
|
-
const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias +
|
|
16022
|
-
return binding ?
|
|
16196
|
+
const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep7));
|
|
16197
|
+
return binding ? join23(binding.target, relative6(binding.alias, absolute)) : absolute;
|
|
16023
16198
|
}
|
|
16024
16199
|
function disabledCodexSkillPaths(config, aliases) {
|
|
16025
16200
|
const skills = config.skills;
|
|
@@ -16042,7 +16217,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
16042
16217
|
} catch {
|
|
16043
16218
|
continue;
|
|
16044
16219
|
}
|
|
16045
|
-
if (!document2.endsWith(`${
|
|
16220
|
+
if (!document2.endsWith(`${sep7}SKILL.md`))
|
|
16046
16221
|
continue;
|
|
16047
16222
|
settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
|
|
16048
16223
|
}
|
|
@@ -16050,7 +16225,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
16050
16225
|
}
|
|
16051
16226
|
function recheckRootAliases(aliases) {
|
|
16052
16227
|
for (const binding of aliases) {
|
|
16053
|
-
if (!["claude", "codex"].includes(binding.agent) || binding.alias !==
|
|
16228
|
+
if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join23(binding.home, `.${binding.agent}`))
|
|
16054
16229
|
throw new Error("Invalid agent root alias binding");
|
|
16055
16230
|
const current = readRootAlias(binding.home, binding.agent);
|
|
16056
16231
|
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
@@ -16088,7 +16263,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
16088
16263
|
}
|
|
16089
16264
|
}
|
|
16090
16265
|
function treeHash(root) {
|
|
16091
|
-
const hash2 =
|
|
16266
|
+
const hash2 = createHash18("sha256");
|
|
16092
16267
|
let bytes = 0, files = 0;
|
|
16093
16268
|
function visit2(path) {
|
|
16094
16269
|
const stat = lstatSync19(path);
|
|
@@ -16098,7 +16273,7 @@ function treeHash(root) {
|
|
|
16098
16273
|
if (stat.isDirectory()) {
|
|
16099
16274
|
hash2.update(`d\x00${rel}\x00`);
|
|
16100
16275
|
for (const name of readdirSync8(path).sort())
|
|
16101
|
-
visit2(
|
|
16276
|
+
visit2(join23(path, name));
|
|
16102
16277
|
return;
|
|
16103
16278
|
}
|
|
16104
16279
|
if (!stat.isFile())
|
|
@@ -16126,7 +16301,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
16126
16301
|
if (!existsSync9(projectRoot) || !lstatSync19(projectRoot).isDirectory())
|
|
16127
16302
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
16128
16303
|
assertSafePath(projectRoot);
|
|
16129
|
-
if (isAbsolute13(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${
|
|
16304
|
+
if (isAbsolute13(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep7}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
16130
16305
|
throw new Error(`Invalid native migration target: ${target.path}`);
|
|
16131
16306
|
const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
|
|
16132
16307
|
if (identities.has(identity5))
|
|
@@ -16162,7 +16337,7 @@ function projectAncestorDirectories(projects) {
|
|
|
16162
16337
|
}
|
|
16163
16338
|
function isCodexSkillDiscoveryInput(path) {
|
|
16164
16339
|
for (let cursor = canonicalSystemPath(path);cursor !== dirname14(cursor); cursor = dirname14(cursor)) {
|
|
16165
|
-
if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(
|
|
16340
|
+
if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join23(sep7, root))))
|
|
16166
16341
|
return true;
|
|
16167
16342
|
}
|
|
16168
16343
|
return false;
|
|
@@ -16179,18 +16354,18 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16179
16354
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
16180
16355
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
16181
16356
|
const rootDefinitions = selectedAgents ? ROOTS.filter(([agent, path]) => selectedAgents.has(agent) || selectedAgents.has("hermes") && agent === "codex" && path === ".agents/skills") : ROOTS;
|
|
16182
|
-
const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(
|
|
16183
|
-
const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesAgent("sumi")).map(([agent, adapter]) => canonicalAgentPath(
|
|
16357
|
+
const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join23(home, path), aliases)]);
|
|
16358
|
+
const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesAgent("sumi")).map(([agent, adapter]) => canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, adapter.root), CLI_BRIDGE_NAME), aliases));
|
|
16184
16359
|
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
16185
16360
|
for (const [agent, path] of rootDefinitions)
|
|
16186
|
-
roots.push([agent, canonicalAgentPath(
|
|
16361
|
+
roots.push([agent, canonicalAgentPath(join23(project, path), aliases)]);
|
|
16187
16362
|
if (selectedAgents?.has("sumi"))
|
|
16188
16363
|
for (const directory of [".claude", ".agents"])
|
|
16189
16364
|
for (const name of ["skill", "skills"])
|
|
16190
|
-
roots.push(["sumi", canonicalAgentPath(
|
|
16365
|
+
roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
|
|
16191
16366
|
}
|
|
16192
16367
|
const installationInputs = options.codexInstallationInputRoots ?? [];
|
|
16193
|
-
if (installationInputs.some((input) => resolve13(input) !== input || realpathSync9(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input +
|
|
16368
|
+
if (installationInputs.some((input) => resolve13(input) !== input || realpathSync9(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep7) || input.startsWith(path + sep7)))))
|
|
16194
16369
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
16195
16370
|
const entries = [], seen = new Set;
|
|
16196
16371
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
@@ -16216,7 +16391,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16216
16391
|
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
16217
16392
|
return false;
|
|
16218
16393
|
if (scan.hasSkills) {
|
|
16219
|
-
const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target +
|
|
16394
|
+
const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep7)));
|
|
16220
16395
|
if (path === reviewedCacheAlias && targetSkills.length > 0)
|
|
16221
16396
|
reviewedCacheAliasFound = true;
|
|
16222
16397
|
if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path) || options.disabledVendorSkill?.(entry) === true))
|
|
@@ -16231,7 +16406,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16231
16406
|
admit(path);
|
|
16232
16407
|
const scan = { complete: true, hasSkills: false, entries: 1 };
|
|
16233
16408
|
assertSafePath(path);
|
|
16234
|
-
if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${
|
|
16409
|
+
if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep7}`)))
|
|
16235
16410
|
return scan;
|
|
16236
16411
|
if (!existsSync9(path))
|
|
16237
16412
|
return scan;
|
|
@@ -16243,21 +16418,21 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16243
16418
|
}
|
|
16244
16419
|
if (!stat.isDirectory())
|
|
16245
16420
|
throw new Error(`Unsupported native discovery entry: ${path}`);
|
|
16246
|
-
if (existsSync9(
|
|
16421
|
+
if (existsSync9(join23(path, "SKILL.md"))) {
|
|
16247
16422
|
scan.hasSkills = true;
|
|
16248
16423
|
if (seen.has(path))
|
|
16249
16424
|
return scan;
|
|
16250
16425
|
seen.add(path);
|
|
16251
16426
|
let managed = false;
|
|
16252
|
-
const marker =
|
|
16427
|
+
const marker = join23(path, ".hasna-skills.json");
|
|
16253
16428
|
if (existsSync9(marker)) {
|
|
16254
16429
|
try {
|
|
16255
16430
|
managed = JSON.parse(readFileSync14(marker, "utf8")).managedBy === "@hasna/skills";
|
|
16256
16431
|
} catch {}
|
|
16257
16432
|
}
|
|
16258
16433
|
const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
|
|
16259
|
-
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target +
|
|
16260
|
-
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ??
|
|
16434
|
+
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep7));
|
|
16435
|
+
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join23(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve13(home) } : {}, ...rootAlias ? { rootAlias } : {} });
|
|
16261
16436
|
return scan;
|
|
16262
16437
|
}
|
|
16263
16438
|
if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
|
|
@@ -16266,7 +16441,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16266
16441
|
try {
|
|
16267
16442
|
let child;
|
|
16268
16443
|
while ((child = directory.readSync()) !== null) {
|
|
16269
|
-
admit(
|
|
16444
|
+
admit(join23(path, child.name));
|
|
16270
16445
|
children.push(child);
|
|
16271
16446
|
}
|
|
16272
16447
|
} finally {
|
|
@@ -16278,12 +16453,12 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16278
16453
|
for (const { name } of children) {
|
|
16279
16454
|
if (name === "node_modules")
|
|
16280
16455
|
continue;
|
|
16281
|
-
if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${
|
|
16456
|
+
if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep7}`.includes(`${sep7}.agents${sep7}skills${sep7}`)))
|
|
16282
16457
|
continue;
|
|
16283
16458
|
const isVendor = vendor || name === ".system";
|
|
16284
16459
|
if (isVendor && !options.includeVendor)
|
|
16285
16460
|
continue;
|
|
16286
|
-
const child =
|
|
16461
|
+
const child = join23(path, name);
|
|
16287
16462
|
if (isVendor && lstatSync19(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
16288
16463
|
const target = statSync9(child, { throwIfNoEntry: false });
|
|
16289
16464
|
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
|
|
@@ -16301,7 +16476,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16301
16476
|
return scan;
|
|
16302
16477
|
}
|
|
16303
16478
|
function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
|
|
16304
|
-
if (agent === "hermes" || options.guardHermes && `${path}${
|
|
16479
|
+
if (agent === "hermes" || options.guardHermes && `${path}${sep7}`.includes(`${sep7}.agents${sep7}skills${sep7}`))
|
|
16305
16480
|
assertNoHermesLegacyShadow(path);
|
|
16306
16481
|
if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
|
|
16307
16482
|
throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
|
|
@@ -16310,11 +16485,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16310
16485
|
scanRoot(agent, path);
|
|
16311
16486
|
if (options.includeVendor) {
|
|
16312
16487
|
for (const agent of ["codex", "claude"].filter(includesAgent))
|
|
16313
|
-
scanRoot(agent, canonicalAgentPath(
|
|
16488
|
+
scanRoot(agent, canonicalAgentPath(join23(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
16314
16489
|
if (includesAgent("claude"))
|
|
16315
|
-
scanRoot("claude", canonicalAgentPath(
|
|
16490
|
+
scanRoot("claude", canonicalAgentPath(join23(home, ".claude", "plugins", "synced"), aliases), true);
|
|
16316
16491
|
if (includesAgent("gemini"))
|
|
16317
|
-
scanRoot("gemini",
|
|
16492
|
+
scanRoot("gemini", join23(home, ".gemini", "extensions"), true);
|
|
16318
16493
|
}
|
|
16319
16494
|
const configured = options.configured ? INTEGRATION_AGENTS.filter(includesAgent).flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
16320
16495
|
for (const root of [...options.agentRoots ?? [], ...configured])
|
|
@@ -16427,14 +16602,14 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
16427
16602
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
16428
16603
|
const bundled = disableCodexBundledSkills(text2);
|
|
16429
16604
|
let result = normalizeCodexInlinePathConfig(bundled);
|
|
16430
|
-
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve13(bridgePath), resolve13(
|
|
16605
|
+
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve13(bridgePath), resolve13(join23(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
16431
16606
|
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
16432
16607
|
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
16433
16608
|
throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
|
|
16434
16609
|
const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
|
|
16435
16610
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
16436
16611
|
for (const skill of selections) {
|
|
16437
|
-
const path =
|
|
16612
|
+
const path = join23(skill.path, "SKILL.md");
|
|
16438
16613
|
let found = false;
|
|
16439
16614
|
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
16440
16615
|
const parsed = Bun.TOML.parse(section);
|
|
@@ -16461,7 +16636,7 @@ enabled = false
|
|
|
16461
16636
|
}
|
|
16462
16637
|
function assertCodexInstallationInputs(home, projectDirs, inputs) {
|
|
16463
16638
|
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve13(directory, path)));
|
|
16464
|
-
if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input +
|
|
16639
|
+
if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep7) || input.startsWith(root + sep7))))
|
|
16465
16640
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
16466
16641
|
}
|
|
16467
16642
|
function planAgentIntegration(options) {
|
|
@@ -16469,7 +16644,7 @@ function planAgentIntegration(options) {
|
|
|
16469
16644
|
throw new Error("Explicit Codex skill denials require a fresh native catalog and discovery review");
|
|
16470
16645
|
const home = options.home ?? homedir2(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
16471
16646
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
16472
|
-
const policyPath =
|
|
16647
|
+
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
16473
16648
|
assertSafePath(policyPath);
|
|
16474
16649
|
const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
|
|
16475
16650
|
if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
|
|
@@ -16501,28 +16676,28 @@ function planAgentIntegration(options) {
|
|
|
16501
16676
|
}
|
|
16502
16677
|
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
16503
16678
|
});
|
|
16504
|
-
const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(
|
|
16679
|
+
const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) : null;
|
|
16505
16680
|
const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
|
|
16506
16681
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
|
|
16507
|
-
const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(
|
|
16682
|
+
const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join23(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
|
|
16508
16683
|
const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
|
|
16509
16684
|
const observedNativeSources = [];
|
|
16510
|
-
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) =>
|
|
16685
|
+
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), resolve13(options.projectDir ?? home), (path) => {
|
|
16511
16686
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
16512
16687
|
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
16513
16688
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
16514
16689
|
}, codexConfig.skills?.config ?? [], codexConfig.plugins, plannedCodexRules, options.codexSkillDenials) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
|
|
16515
|
-
const cache = canonicalAgentPath(
|
|
16690
|
+
const cache = canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases);
|
|
16516
16691
|
const retired = (policy.bridge?.codexPluginSkills ?? []).filter((control) => absentDisabledCodexPluginParent(cache, control.pluginParent, policy.bridge.codexPluginSkills, codexConfig.skills?.config ?? []));
|
|
16517
16692
|
for (const denial of options.codexSkillDenials ?? []) {
|
|
16518
16693
|
const control = codexPluginControls.skills.find((item) => item.name === denial.name);
|
|
16519
16694
|
if (!control)
|
|
16520
16695
|
throw new Error("Explicit Codex denial did not resolve a reviewed identity");
|
|
16521
|
-
const root =
|
|
16696
|
+
const root = join23(control.pluginParent, relative6(control.pluginParent, denial.path).split(sep7)[0]);
|
|
16522
16697
|
const review = options.discoveryInputs.agents.find((item) => item.agent === "codex");
|
|
16523
|
-
for (const path of [
|
|
16698
|
+
for (const path of [join23(root, ".app.json"), join23(root, ".mcp.json"), join23(root, "hooks")])
|
|
16524
16699
|
observedNativeSources.push({ path, sha256: nativeSourceDigest(path) });
|
|
16525
|
-
const files = [
|
|
16700
|
+
const files = [join23(root, ".codex-plugin/plugin.json"), ...control.appSha256 ? [join23(root, ".app.json")] : [], ...control.mcpSha256 ? [join23(root, ".mcp.json")] : []];
|
|
16526
16701
|
for (const path of files)
|
|
16527
16702
|
if (!review?.sources.some((source) => source.path === path && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode) && source.sha256 === sha3(readNativeBytes(path, 1024 * 1024))))
|
|
16528
16703
|
throw new Error("Explicit Codex denial requires current full manifest and capability discovery witnesses");
|
|
@@ -16531,12 +16706,12 @@ function planAgentIntegration(options) {
|
|
|
16531
16706
|
for (const discovery of discoveries)
|
|
16532
16707
|
if (discovery.agent === "codex" && discovery.method === "reviewed")
|
|
16533
16708
|
discovery.codexDisabledPluginSkills = codexPluginSkills;
|
|
16534
|
-
if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(
|
|
16709
|
+
if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), codexPluginSkills, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? [])) {
|
|
16535
16710
|
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; provide a fresh native catalog and discovery review");
|
|
16536
16711
|
}
|
|
16537
16712
|
const codexInactivePlugins = codexPluginControls.inactivePlugins;
|
|
16538
16713
|
const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
|
|
16539
|
-
const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot +
|
|
16714
|
+
const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep7) || input.sourceRoot.startsWith(root + sep7)) && codexDiscovery.sources.some((source) => source.path === join23(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
|
|
16540
16715
|
assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
|
|
16541
16716
|
const changes = [];
|
|
16542
16717
|
let observedSettings;
|
|
@@ -16545,19 +16720,19 @@ function planAgentIntegration(options) {
|
|
|
16545
16720
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
16546
16721
|
const { command, profileId: profileId2 } = bindings.get(agent);
|
|
16547
16722
|
const adapter = AGENT_ADAPTERS[agent];
|
|
16548
|
-
const bridgePath = canonicalAgentPath(
|
|
16723
|
+
const bridgePath = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, adapter.root), CLI_BRIDGE_NAME), aliases);
|
|
16549
16724
|
assertSafePath(bridgePath);
|
|
16550
16725
|
if (existsSync9(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
|
|
16551
16726
|
throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
|
|
16552
16727
|
for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
|
|
16553
|
-
const path2 =
|
|
16728
|
+
const path2 = join23(bridgePath, name), before2 = readOptional(path2);
|
|
16554
16729
|
if (before2 !== after2)
|
|
16555
16730
|
changes.push({ path: path2, before: before2, after: after2 });
|
|
16556
16731
|
}
|
|
16557
|
-
const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) :
|
|
16732
|
+
const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join23(home, adapter.config), aliases);
|
|
16558
16733
|
const before = readOptional(path);
|
|
16559
16734
|
if (agent === "hermes") {
|
|
16560
|
-
const supervisorPath =
|
|
16735
|
+
const supervisorPath = join23(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
|
|
16561
16736
|
const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
|
|
16562
16737
|
const previous = policy.bridge?.supervisors?.hermes;
|
|
16563
16738
|
if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
|
|
@@ -16568,7 +16743,7 @@ function planAgentIntegration(options) {
|
|
|
16568
16743
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
16569
16744
|
if (before !== after2)
|
|
16570
16745
|
changes.push({ path, before, after: after2 });
|
|
16571
|
-
const optOut =
|
|
16746
|
+
const optOut = join23(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
|
|
16572
16747
|
if (optOutBefore === null)
|
|
16573
16748
|
changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
|
|
16574
16749
|
` });
|
|
@@ -16579,7 +16754,7 @@ function planAgentIntegration(options) {
|
|
|
16579
16754
|
if (config.permissions !== undefined && (!Array.isArray(config.permissions) || config.permissions.some((rule) => !rule || typeof rule.action !== "string" || typeof rule.resource !== "string" || !["allow", "deny", "ask"].includes(rule.effect))))
|
|
16580
16755
|
throw new Error("Expected Sumi native permission rules");
|
|
16581
16756
|
config.permissions = [...(config.permissions ?? []).filter((rule) => rule.action !== "skill"), ...SUMI_SKILL_PERMISSIONS];
|
|
16582
|
-
const pluginPath =
|
|
16757
|
+
const pluginPath = join23(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
16583
16758
|
const pluginAfter = renderSumiPlugin(command, profileId2);
|
|
16584
16759
|
const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
|
|
16585
16760
|
if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && isManagedSumiPlugin(pluginBefore, priorCommand, priorProfile)))
|
|
@@ -16591,7 +16766,7 @@ function planAgentIntegration(options) {
|
|
|
16591
16766
|
if (!permission || typeof permission !== "object" || Array.isArray(permission))
|
|
16592
16767
|
throw new Error("Expected OpenCode permission object");
|
|
16593
16768
|
config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
|
|
16594
|
-
const pluginPath =
|
|
16769
|
+
const pluginPath = join23(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
16595
16770
|
const pluginAfter = renderOpenCodePlugin(command, profileId2);
|
|
16596
16771
|
const priorCommand = policy.bridge?.commands?.opencode;
|
|
16597
16772
|
const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
|
|
@@ -16608,7 +16783,7 @@ function planAgentIntegration(options) {
|
|
|
16608
16783
|
if (before !== after)
|
|
16609
16784
|
changes.push({ path, before, after });
|
|
16610
16785
|
if (agent === "codex") {
|
|
16611
|
-
const configPath = canonicalAgentPath(
|
|
16786
|
+
const configPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
|
|
16612
16787
|
if (previous !== codexConfigBefore)
|
|
16613
16788
|
throw new Error(`Configuration changed during planning: ${configPath}`);
|
|
16614
16789
|
const next = disableReviewedCodexPluginNames(codexPathConfig, codexPluginSkills);
|
|
@@ -16620,16 +16795,16 @@ function planAgentIntegration(options) {
|
|
|
16620
16795
|
}
|
|
16621
16796
|
}
|
|
16622
16797
|
}
|
|
16623
|
-
const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(
|
|
16798
|
+
const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join23(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join23(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
|
|
16624
16799
|
const discoveryAfter = discoveries.map((binding) => {
|
|
16625
16800
|
const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
|
|
16626
16801
|
if (binding.agent !== "codex")
|
|
16627
16802
|
return rebound;
|
|
16628
16803
|
const { codexInstallationInputs: previousInputs, ...current } = rebound;
|
|
16629
16804
|
const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
|
|
16630
|
-
const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path +
|
|
16805
|
+
const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep7))).map((directory) => directory.path), inputs) : previousInputs.directories;
|
|
16631
16806
|
const installed = codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: "codex-cli 0.160.0", catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
|
|
16632
|
-
return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(
|
|
16807
|
+
return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(join23(home, ".codex/config.toml"), aliases))?.after ?? codexConfigBefore ?? "").skills?.config ?? []);
|
|
16633
16808
|
});
|
|
16634
16809
|
const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
|
|
16635
16810
|
...policy.bridge,
|
|
@@ -16638,10 +16813,10 @@ function planAgentIntegration(options) {
|
|
|
16638
16813
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
16639
16814
|
home: resolve13(home),
|
|
16640
16815
|
includeVendor: true,
|
|
16641
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path:
|
|
16816
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join23(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
16642
16817
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
16643
16818
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
16644
|
-
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(
|
|
16819
|
+
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
16645
16820
|
rootAliases: aliases,
|
|
16646
16821
|
...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
|
|
16647
16822
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
@@ -16693,10 +16868,10 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
16693
16868
|
if (expectedResolved !== undefined && resolved !== expectedResolved)
|
|
16694
16869
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
16695
16870
|
const resolvedParentIdentity = parents2(resolved);
|
|
16696
|
-
const manifest = snapshot(
|
|
16871
|
+
const manifest = snapshot(join23(dirname14(dirname14(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
16697
16872
|
projectedHookAcl(manifest.file, manifest.stat);
|
|
16698
16873
|
const pkg = JSON.parse(manifest.text);
|
|
16699
|
-
if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !==
|
|
16874
|
+
if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join23(dirname14(manifest.file), "bin/index.js"))
|
|
16700
16875
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
16701
16876
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
16702
16877
|
projectedHookAcl(cli.file, cli.stat);
|
|
@@ -16733,7 +16908,7 @@ function planClaudeManagedHookProjection(options) {
|
|
|
16733
16908
|
hookProjectionRefusal("TARGET_INVALID");
|
|
16734
16909
|
}
|
|
16735
16910
|
const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
16736
|
-
const policyPath =
|
|
16911
|
+
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
16737
16912
|
let policyBytes, settingsBytes, settingsPath, command, profile;
|
|
16738
16913
|
try {
|
|
16739
16914
|
policyBytes = readNativeBytes(policyPath, 1024 * 1024);
|
|
@@ -16741,7 +16916,7 @@ function planClaudeManagedHookProjection(options) {
|
|
|
16741
16916
|
if (!snapshot2 || snapshot2.text !== policyBytes.toString("utf8"))
|
|
16742
16917
|
hookProjectionRefusal("SOURCE_UNVERIFIED");
|
|
16743
16918
|
const binding = snapshot2.value.bridge;
|
|
16744
|
-
settingsPath = canonicalAgentPath(
|
|
16919
|
+
settingsPath = canonicalAgentPath(join23(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
|
|
16745
16920
|
settingsBytes = readNativeBytes(settingsPath, 1024 * 1024);
|
|
16746
16921
|
command = binding?.commands?.claude;
|
|
16747
16922
|
profile = binding?.profiles?.claude;
|
|
@@ -16824,12 +16999,12 @@ function planClaudePreToolUseHookUpdate(options) {
|
|
|
16824
16999
|
return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
|
|
16825
17000
|
}
|
|
16826
17001
|
function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
16827
|
-
const
|
|
17002
|
+
const canonical6 = (value) => canonicalAgentPath(value, aliases);
|
|
16828
17003
|
const containsSettings = (root) => {
|
|
16829
|
-
const distance = relative6(
|
|
16830
|
-
return distance === "" || distance !== ".." && !distance.startsWith(`..${
|
|
17004
|
+
const distance = relative6(canonical6(root), path);
|
|
17005
|
+
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute13(distance);
|
|
16831
17006
|
};
|
|
16832
|
-
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" ||
|
|
17007
|
+
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical6(join23(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical6(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
|
|
16833
17008
|
}
|
|
16834
17009
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
16835
17010
|
try {
|
|
@@ -16847,7 +17022,7 @@ function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
|
16847
17022
|
}
|
|
16848
17023
|
function planClaudeHookEventsUpdate(options) {
|
|
16849
17024
|
const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
16850
|
-
const policyPath =
|
|
17025
|
+
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
16851
17026
|
assertSafePath(policyPath);
|
|
16852
17027
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
16853
17028
|
if (!snapshot2)
|
|
@@ -16858,7 +17033,7 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
16858
17033
|
}
|
|
16859
17034
|
const aliases = bridge.rootAliases ?? [];
|
|
16860
17035
|
recheckRootAliases(aliases);
|
|
16861
|
-
const path = canonicalAgentPath(
|
|
17036
|
+
const path = canonicalAgentPath(join23(home, ".claude", "settings.json"), aliases);
|
|
16862
17037
|
assertSafePath(path);
|
|
16863
17038
|
const before = lstatSync19(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
|
|
16864
17039
|
if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
|
|
@@ -16936,7 +17111,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
16936
17111
|
const binding = policy.bridge?.discovery?.[options.agent];
|
|
16937
17112
|
if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
|
|
16938
17113
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
16939
|
-
const configPath = canonicalAgentPath(options.agent === "sumi" ? sumiConfigPath(home) :
|
|
17114
|
+
const configPath = canonicalAgentPath(options.agent === "sumi" ? sumiConfigPath(home) : join23(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
16940
17115
|
const settings = readOptional(configPath);
|
|
16941
17116
|
if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
|
|
16942
17117
|
throw new Error("Native settings preimage changed");
|
|
@@ -16987,7 +17162,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
16987
17162
|
const replacement = { ...binding, sources: rebound };
|
|
16988
17163
|
verifyAgentDiscovery(replacement);
|
|
16989
17164
|
const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
16990
|
-
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path:
|
|
17165
|
+
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
|
|
16991
17166
|
}
|
|
16992
17167
|
function applyAgentIntegration(plan, options = {}) {
|
|
16993
17168
|
const roots = plan.changes.flatMap((change) => {
|
|
@@ -17003,7 +17178,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17003
17178
|
throw new Error("Native identity source changed after planning");
|
|
17004
17179
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
17005
17180
|
throw new Error("Native settings changed after witness planning");
|
|
17006
|
-
const policyPath =
|
|
17181
|
+
const policyPath = join23(plan.dataDir, "agent-policy.json");
|
|
17007
17182
|
assertSafePath(policyPath);
|
|
17008
17183
|
const currentText = (path) => resolve13(path) === resolve13(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
17009
17184
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
@@ -17038,7 +17213,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17038
17213
|
for (const change of plan.changes)
|
|
17039
17214
|
if (currentText(change.path) !== change.before)
|
|
17040
17215
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
17041
|
-
const backupRoot =
|
|
17216
|
+
const backupRoot = join23(plan.dataDir, "migration", randomUUID3()), backups = [], written = [], createdDirectories = new Set;
|
|
17042
17217
|
if (!plan.changes.length) {
|
|
17043
17218
|
assertCurrent();
|
|
17044
17219
|
recheckRootAliases(aliases);
|
|
@@ -17049,7 +17224,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17049
17224
|
chmodSync(backupRoot, 448);
|
|
17050
17225
|
for (const [index, change] of plan.changes.entries())
|
|
17051
17226
|
if (change.before !== null) {
|
|
17052
|
-
const backup =
|
|
17227
|
+
const backup = join23(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
|
|
17053
17228
|
writeFileSync6(backup, change.before, { mode: 384, flag: "wx" });
|
|
17054
17229
|
if (readFileSync14(backup, "utf8") !== change.before)
|
|
17055
17230
|
throw new Error("Native configuration preservation readback failed");
|
|
@@ -17082,7 +17257,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17082
17257
|
}
|
|
17083
17258
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
17084
17259
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
17085
|
-
atomicWrite2(
|
|
17260
|
+
atomicWrite2(join23(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
17086
17261
|
`);
|
|
17087
17262
|
} catch (error) {
|
|
17088
17263
|
assertCurrent();
|
|
@@ -17145,7 +17320,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
17145
17320
|
const selected = options.targetManifest ? selectNativeMigrationTargets(inventory, options.targetManifest) : inventory.filter((entry) => !entry.bridge && !entry.system && (entry.vendor ? options.includeVendor : entry.managed || options.includeUnmanaged));
|
|
17146
17321
|
const roots = selected.flatMap((entry) => {
|
|
17147
17322
|
const root = (entry.rootAlias?.agent === "codex" ? entry.rootAlias.alias : undefined) ?? codexCorpusRootForPath(entry.path);
|
|
17148
|
-
const owner = entry.codexHome ?? (entry.agent === "codex" && entry.bridgeHome ?
|
|
17323
|
+
const owner = entry.codexHome ?? (entry.agent === "codex" && entry.bridgeHome ? join23(entry.bridgeHome, ".codex") : undefined);
|
|
17149
17324
|
if (!root && !owner && (entry.agent === "codex" || isCodexSkillDiscoveryInput(entry.path)))
|
|
17150
17325
|
throw new Error("CODEX_CORPUS_ROOT_UNVERIFIED");
|
|
17151
17326
|
return [...new Set([root, owner].filter((value) => value !== undefined))];
|
|
@@ -17160,7 +17335,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17160
17335
|
recheckRootAliases(aliases);
|
|
17161
17336
|
for (const entry of inventory.filter((entry2) => entry2.bridge)) {
|
|
17162
17337
|
const adapter = AGENT_ADAPTERS[entry.agent];
|
|
17163
|
-
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(
|
|
17338
|
+
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join23(entry.agent === "sumi" ? sumiBridgeRoot(entry.bridgeHome) : join23(entry.bridgeHome, adapter.root), CLI_BRIDGE_NAME), aliases) : undefined;
|
|
17164
17339
|
if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
|
|
17165
17340
|
throw new Error(`Skills bridge changed after planning: ${entry.path}`);
|
|
17166
17341
|
}
|
|
@@ -17171,11 +17346,11 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17171
17346
|
const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
|
|
17172
17347
|
if (!selected.length)
|
|
17173
17348
|
return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
17174
|
-
const operationId = randomUUID3(), archiveRoot =
|
|
17349
|
+
const operationId = randomUUID3(), archiveRoot = join23(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join23(archiveRoot, "receipt.json");
|
|
17175
17350
|
const entries = selected.map((entry, index) => ({
|
|
17176
|
-
source: entry.vendor ?
|
|
17177
|
-
archive:
|
|
17178
|
-
hash: entry.vendor ? sha3(readNativeBytes(
|
|
17351
|
+
source: entry.vendor ? join23(entry.path, "SKILL.md") : entry.path,
|
|
17352
|
+
archive: join23(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
|
|
17353
|
+
hash: entry.vendor ? sha3(readNativeBytes(join23(entry.path, "SKILL.md"))) : entry.hash,
|
|
17179
17354
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
17180
17355
|
status: "planned"
|
|
17181
17356
|
}));
|
|
@@ -17282,9 +17457,9 @@ function isInertCodexPluginCacheCopy(entry, cache, allowedHostPaths, read2) {
|
|
|
17282
17457
|
try {
|
|
17283
17458
|
if (entry.agent !== "codex" || !entry.vendor || entry.bridge || entry.system)
|
|
17284
17459
|
return false;
|
|
17285
|
-
const document2 =
|
|
17460
|
+
const document2 = join23(entry.path, "SKILL.md");
|
|
17286
17461
|
const inside = relative6(cache, entry.path);
|
|
17287
|
-
if (!inside || isAbsolute13(inside) || inside.split(
|
|
17462
|
+
if (!inside || isAbsolute13(inside) || inside.split(sep7).some((segment) => segment === ".."))
|
|
17288
17463
|
return false;
|
|
17289
17464
|
assertSafePath(entry.path);
|
|
17290
17465
|
if (!lstatSync19(cache).isDirectory() || !lstatSync19(entry.path).isDirectory() || !lstatSync19(document2).isFile())
|
|
@@ -17305,7 +17480,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
17305
17480
|
}
|
|
17306
17481
|
function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
|
|
17307
17482
|
const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17308
|
-
assertSafePath(
|
|
17483
|
+
assertSafePath(join23(dataDir2, "agent-policy.json"));
|
|
17309
17484
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
17310
17485
|
if (!snapshot2)
|
|
17311
17486
|
throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
|
|
@@ -17316,14 +17491,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17316
17491
|
if (!Array.isArray(aliases))
|
|
17317
17492
|
throw new Error("NATIVE_SKILL_DRIFT: invalid root alias binding");
|
|
17318
17493
|
recheckRootAliases(aliases);
|
|
17319
|
-
const expected = canonicalAgentPath(
|
|
17494
|
+
const expected = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, AGENT_ADAPTERS[agent].root), CLI_BRIDGE_NAME), aliases);
|
|
17320
17495
|
if (!isOwnedCliBridge(expected, [expected]))
|
|
17321
17496
|
throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
|
|
17322
17497
|
const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
|
|
17323
|
-
const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${
|
|
17498
|
+
const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep7}.agents${sep7}skills${sep7}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
|
|
17324
17499
|
const discovery = provenDiscovery ?? binding.discovery?.[agent];
|
|
17325
17500
|
assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
|
|
17326
|
-
const configPath = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) :
|
|
17501
|
+
const configPath = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join23(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
|
|
17327
17502
|
const command = binding.commands?.[agent], profile = binding.profiles?.[agent];
|
|
17328
17503
|
if (typeof command !== "string" || typeof profile !== "string")
|
|
17329
17504
|
throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
|
|
@@ -17331,14 +17506,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17331
17506
|
throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
|
|
17332
17507
|
if (agent === "hermes") {
|
|
17333
17508
|
const supervisor = binding.supervisors?.hermes;
|
|
17334
|
-
if (supervisor?.path !==
|
|
17509
|
+
if (supervisor?.path !== join23(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
|
|
17335
17510
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
17336
17511
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
17337
17512
|
} else if (agent === "sumi") {
|
|
17338
|
-
if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(
|
|
17513
|
+
if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join23(sumiConfigDirectory(home), "plugins", "skills-cli.js")), command, profile))
|
|
17339
17514
|
throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
|
|
17340
17515
|
} else if (agent === "opencode") {
|
|
17341
|
-
if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(
|
|
17516
|
+
if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join23(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
|
|
17342
17517
|
throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
|
|
17343
17518
|
} else {
|
|
17344
17519
|
const required = {};
|
|
@@ -17355,42 +17530,42 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17355
17530
|
if (agent === "gemini" && (config.hooksConfig?.enabled === false || config.skills?.enabled !== true || !["antigravity-support", "skill-creator"].every((name) => config.skills?.disabled?.includes(name)) || config.skills?.disabled?.includes(CLI_BRIDGE_NAME)))
|
|
17356
17531
|
throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
|
|
17357
17532
|
}
|
|
17358
|
-
const codexPath = canonicalAgentPath(
|
|
17533
|
+
const codexPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases);
|
|
17359
17534
|
if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
|
|
17360
17535
|
throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
|
|
17361
17536
|
const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
|
|
17362
17537
|
if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
|
|
17363
17538
|
throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
|
|
17364
|
-
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path,
|
|
17539
|
+
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join23(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
17365
17540
|
if (agent === "codex" && [...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
|
|
17366
17541
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
17367
|
-
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(
|
|
17542
|
+
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? []))
|
|
17368
17543
|
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
|
|
17369
17544
|
const disabledPlugin = (entry) => {
|
|
17370
17545
|
if (agent !== "codex" || !entry.vendor)
|
|
17371
17546
|
return false;
|
|
17372
|
-
const document2 =
|
|
17547
|
+
const document2 = join23(entry.path, "SKILL.md"), cache = canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
|
|
17373
17548
|
return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
|
|
17374
17549
|
};
|
|
17375
17550
|
const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
|
|
17376
17551
|
const disabledVendorSkill = (entry) => {
|
|
17377
17552
|
if (agent !== "codex" || !entry.vendor)
|
|
17378
17553
|
return false;
|
|
17379
|
-
if ((binding.codexPluginSkills ?? []).some((control) => entry.path.startsWith(control.pluginParent +
|
|
17554
|
+
if ((binding.codexPluginSkills ?? []).some((control) => entry.path.startsWith(control.pluginParent + sep7)))
|
|
17380
17555
|
return disabledPlugin(entry);
|
|
17381
17556
|
if (disabledPlugin(entry))
|
|
17382
17557
|
return true;
|
|
17383
17558
|
if (!disabledPaths.has(entry.path))
|
|
17384
17559
|
return false;
|
|
17385
|
-
if (entry.path.startsWith(canonicalAgentPath(
|
|
17560
|
+
if (entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "plugins", "cache"), aliases) + sep7))
|
|
17386
17561
|
return true;
|
|
17387
|
-
const runtimeCache = canonicalAgentPath(
|
|
17388
|
-
if (entry.path.startsWith(runtimeCache +
|
|
17562
|
+
const runtimeCache = canonicalAgentPath(join23(home, ".cache", "codex-runtimes"), aliases);
|
|
17563
|
+
if (entry.path.startsWith(runtimeCache + sep7) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep7)))
|
|
17389
17564
|
return true;
|
|
17390
|
-
const bundledMarketplaces = canonicalAgentPath(
|
|
17391
|
-
if (entry.path.startsWith(bundledMarketplaces +
|
|
17565
|
+
const bundledMarketplaces = canonicalAgentPath(join23(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
|
|
17566
|
+
if (entry.path.startsWith(bundledMarketplaces + sep7) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep7)))
|
|
17392
17567
|
return true;
|
|
17393
|
-
if (!entry.path.startsWith(canonicalAgentPath(
|
|
17568
|
+
if (!entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7))
|
|
17394
17569
|
return false;
|
|
17395
17570
|
return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
17396
17571
|
};
|
|
@@ -17413,7 +17588,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17413
17588
|
let installationInputRoots = [];
|
|
17414
17589
|
if (agent === "codex") {
|
|
17415
17590
|
try {
|
|
17416
|
-
installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(
|
|
17591
|
+
installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
|
|
17417
17592
|
} catch {
|
|
17418
17593
|
throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
|
|
17419
17594
|
}
|
|
@@ -17423,7 +17598,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17423
17598
|
let adapterRefusal, inert = [];
|
|
17424
17599
|
if (agent === "codex" && unexpected.length && options.codexNativePolicy) {
|
|
17425
17600
|
const { inspector, helperRunner, deadlineMs, ...envelope } = options.codexNativePolicy;
|
|
17426
|
-
const cache = canonicalAgentPath(
|
|
17601
|
+
const cache = canonicalAgentPath(join23(home, ".codex", "plugins", "cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), bridgeDocument = join23(expected, "SKILL.md");
|
|
17427
17602
|
try {
|
|
17428
17603
|
const claimed = parseCodexNativeHookEnvelope(envelope, bridgeDocument).policy;
|
|
17429
17604
|
inert = unexpected.filter((entry) => isInertCodexPluginCacheCopy(entry, cache, claimed.allowedHostPaths, read2));
|
|
@@ -17464,7 +17639,7 @@ function isSkillsOwnershipMarker(marker) {
|
|
|
17464
17639
|
return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
|
|
17465
17640
|
}
|
|
17466
17641
|
function hasSkillsOwnershipMarker(dir) {
|
|
17467
|
-
const path =
|
|
17642
|
+
const path = join25(dir, SYNC_MARKER_FILE);
|
|
17468
17643
|
try {
|
|
17469
17644
|
if (!lstatSync20(path).isFile())
|
|
17470
17645
|
return false;
|
|
@@ -17488,9 +17663,9 @@ function resolveSyncAgents(arg) {
|
|
|
17488
17663
|
function agentGlobalSkillsDir(agent, homeDir2 = homedir3()) {
|
|
17489
17664
|
switch (agent) {
|
|
17490
17665
|
case "opencode":
|
|
17491
|
-
return
|
|
17666
|
+
return join25(homeDir2, ".config", "opencode", "skills");
|
|
17492
17667
|
default:
|
|
17493
|
-
return
|
|
17668
|
+
return join25(homeDir2, `.${agent}`, "skills");
|
|
17494
17669
|
}
|
|
17495
17670
|
}
|
|
17496
17671
|
function adaptSkillMdForAgent(skillMd, agent) {
|
|
@@ -17549,7 +17724,7 @@ function resolveSyncCorpus(options = {}) {
|
|
|
17549
17724
|
function packageSourceRoots(source) {
|
|
17550
17725
|
const roots = [];
|
|
17551
17726
|
for (const sub of ["skills"]) {
|
|
17552
|
-
const candidate =
|
|
17727
|
+
const candidate = join25(source, sub);
|
|
17553
17728
|
if (existsSync10(candidate) && isDirectory(candidate))
|
|
17554
17729
|
roots.push(candidate);
|
|
17555
17730
|
}
|
|
@@ -17565,10 +17740,10 @@ function containsSkillDirectories(path) {
|
|
|
17565
17740
|
return false;
|
|
17566
17741
|
}
|
|
17567
17742
|
return entries.some((entry) => {
|
|
17568
|
-
const candidate =
|
|
17743
|
+
const candidate = join25(path, entry);
|
|
17569
17744
|
if (!isDirectory(candidate))
|
|
17570
17745
|
return false;
|
|
17571
|
-
return existsSync10(
|
|
17746
|
+
return existsSync10(join25(candidate, "SKILL.md")) || existsSync10(join25(candidate, "skill.json")) || existsSync10(join25(candidate, "package.json"));
|
|
17572
17747
|
});
|
|
17573
17748
|
}
|
|
17574
17749
|
function isDirectory(path) {
|
|
@@ -17608,7 +17783,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
17608
17783
|
actions.push({
|
|
17609
17784
|
skill: name,
|
|
17610
17785
|
agent,
|
|
17611
|
-
path:
|
|
17786
|
+
path: join25(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
|
|
17612
17787
|
action: "skip",
|
|
17613
17788
|
reason: "not found in this machine's corpus"
|
|
17614
17789
|
});
|
|
@@ -17640,7 +17815,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
17640
17815
|
}
|
|
17641
17816
|
function writeManagedAgentSkill(params) {
|
|
17642
17817
|
const homeDir2 = params.homeDir ?? homedir3();
|
|
17643
|
-
const dir =
|
|
17818
|
+
const dir = join25(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
|
|
17644
17819
|
const result = writeManagedSkillDir(dir, params.skillMd, {
|
|
17645
17820
|
skill: params.skill,
|
|
17646
17821
|
source: params.source,
|
|
@@ -17663,8 +17838,8 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
17663
17838
|
return withCodexCorpusWrite(root ? [root] : [], (assertCurrent) => writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent), options);
|
|
17664
17839
|
}
|
|
17665
17840
|
function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
|
|
17666
|
-
const skillMdPath =
|
|
17667
|
-
const markerPath =
|
|
17841
|
+
const skillMdPath = join25(dir, "SKILL.md");
|
|
17842
|
+
const markerPath = join25(dir, SYNC_MARKER_FILE);
|
|
17668
17843
|
const dirExists = existsSync10(dir);
|
|
17669
17844
|
const managed = hasSkillsOwnershipMarker(dir);
|
|
17670
17845
|
const hasSkillMd = existsSync10(skillMdPath);
|
|
@@ -17702,11 +17877,11 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
|
|
|
17702
17877
|
return { action, path: skillMdPath };
|
|
17703
17878
|
const parentDir = dirname15(dir);
|
|
17704
17879
|
mkdirSync7(parentDir, { recursive: true });
|
|
17705
|
-
const transactionDir = mkdtempSync(
|
|
17706
|
-
const candidateDir =
|
|
17707
|
-
const backupDir =
|
|
17708
|
-
const candidateSkillMdPath =
|
|
17709
|
-
const candidateMarkerPath =
|
|
17880
|
+
const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename7(dir)}-`));
|
|
17881
|
+
const candidateDir = join25(transactionDir, "candidate");
|
|
17882
|
+
const backupDir = join25(transactionDir, "backup");
|
|
17883
|
+
const candidateSkillMdPath = join25(candidateDir, "SKILL.md");
|
|
17884
|
+
const candidateMarkerPath = join25(candidateDir, SYNC_MARKER_FILE);
|
|
17710
17885
|
const marker = {
|
|
17711
17886
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
17712
17887
|
skill: options.skill,
|
|
@@ -17764,12 +17939,12 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
|
|
|
17764
17939
|
return { action, path: skillMdPath };
|
|
17765
17940
|
}
|
|
17766
17941
|
function removeManagedAgentSkill(skill, agent, homeDir2 = homedir3(), options = {}) {
|
|
17767
|
-
const dir =
|
|
17942
|
+
const dir = join25(agentGlobalSkillsDir(agent, homeDir2), skill);
|
|
17768
17943
|
const root = codexCorpusRootForPath(dir);
|
|
17769
17944
|
return withCodexCorpusWrite(root ? [root] : [], () => removeManagedAgentSkillUnlocked(skill, agent, homeDir2, options), options);
|
|
17770
17945
|
}
|
|
17771
17946
|
function removeManagedAgentSkillUnlocked(skill, agent, homeDir2 = homedir3(), options = {}) {
|
|
17772
|
-
const dir =
|
|
17947
|
+
const dir = join25(agentGlobalSkillsDir(agent, homeDir2), skill);
|
|
17773
17948
|
if (!hasSkillsOwnershipMarker(dir))
|
|
17774
17949
|
return false;
|
|
17775
17950
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -17777,7 +17952,7 @@ function removeManagedAgentSkillUnlocked(skill, agent, homeDir2 = homedir3(), op
|
|
|
17777
17952
|
}
|
|
17778
17953
|
function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
|
|
17779
17954
|
if (kind === undefined || kind === "instruction" || preferBundledDocs) {
|
|
17780
|
-
const skillMdPath =
|
|
17955
|
+
const skillMdPath = join25(skillPath, "SKILL.md");
|
|
17781
17956
|
if (existsSync10(skillMdPath))
|
|
17782
17957
|
return readFileSync15(skillMdPath, "utf-8");
|
|
17783
17958
|
}
|
|
@@ -17820,7 +17995,7 @@ function resolveSkillAlias(name) {
|
|
|
17820
17995
|
|
|
17821
17996
|
// src/lib/project-state.ts
|
|
17822
17997
|
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
|
|
17823
|
-
import { join as
|
|
17998
|
+
import { join as join26 } from "path";
|
|
17824
17999
|
var VALID_PIN_SOURCES = [
|
|
17825
18000
|
"official",
|
|
17826
18001
|
"custom",
|
|
@@ -17835,10 +18010,10 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
17835
18010
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
17836
18011
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
17837
18012
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
17838
|
-
return
|
|
18013
|
+
return join26(targetDir, SKILLS_PROJECT_DIR);
|
|
17839
18014
|
}
|
|
17840
18015
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
17841
|
-
return
|
|
18016
|
+
return join26(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
17842
18017
|
}
|
|
17843
18018
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
17844
18019
|
const path = getProjectConfigPath(targetDir);
|
|
@@ -17967,7 +18142,7 @@ function getSkillPath(name) {
|
|
|
17967
18142
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
17968
18143
|
if (extensionPath)
|
|
17969
18144
|
return extensionPath;
|
|
17970
|
-
return
|
|
18145
|
+
return join27(getPortableSkillsRoot(), skillName);
|
|
17971
18146
|
}
|
|
17972
18147
|
function getCanonicalSkillName(name) {
|
|
17973
18148
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -18030,7 +18205,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
|
|
|
18030
18205
|
if (!existsSync12(sourcePath))
|
|
18031
18206
|
return null;
|
|
18032
18207
|
let skillMd = "";
|
|
18033
|
-
const skillMdPath =
|
|
18208
|
+
const skillMdPath = join27(sourcePath, "SKILL.md");
|
|
18034
18209
|
if (existsSync12(skillMdPath)) {
|
|
18035
18210
|
skillMd = readFileSync17(skillMdPath, "utf-8");
|
|
18036
18211
|
} else if (generateSkillMd) {
|
|
@@ -18104,16 +18279,16 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
18104
18279
|
const base = projectDir || process.cwd();
|
|
18105
18280
|
switch (agent) {
|
|
18106
18281
|
case "pi":
|
|
18107
|
-
return scope === "project" ?
|
|
18282
|
+
return scope === "project" ? join27(base, ".pi", "skills") : join27(homedir4(), ".pi", "agent", "skills");
|
|
18108
18283
|
case "opencode":
|
|
18109
|
-
return scope === "project" ?
|
|
18284
|
+
return scope === "project" ? join27(base, ".opencode", "skills") : join27(homedir4(), ".config", "opencode", "skills");
|
|
18110
18285
|
default:
|
|
18111
|
-
return scope === "project" ?
|
|
18286
|
+
return scope === "project" ? join27(base, `.${agent}`, "skills") : join27(homedir4(), `.${agent}`, "skills");
|
|
18112
18287
|
}
|
|
18113
18288
|
}
|
|
18114
18289
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
18115
18290
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
18116
|
-
return
|
|
18291
|
+
return join27(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
18117
18292
|
}
|
|
18118
18293
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
18119
18294
|
assertNativeExportAllowed();
|
|
@@ -18156,7 +18331,7 @@ function removeSkillForAgentUnlocked(name, options) {
|
|
|
18156
18331
|
}
|
|
18157
18332
|
function resolveAgentSkillMd(name, generateSkillMd) {
|
|
18158
18333
|
const sourcePath = getSkillPath(name);
|
|
18159
|
-
const skillMdPath =
|
|
18334
|
+
const skillMdPath = join27(sourcePath, "SKILL.md");
|
|
18160
18335
|
if (existsSync12(skillMdPath))
|
|
18161
18336
|
return readFileSync17(skillMdPath, "utf-8");
|
|
18162
18337
|
if (generateSkillMd)
|
|
@@ -18191,7 +18366,7 @@ function generateMinimalSkillMd(name) {
|
|
|
18191
18366
|
"---",
|
|
18192
18367
|
""
|
|
18193
18368
|
].filter(Boolean);
|
|
18194
|
-
const fallbackDoc = readFileIfExists(
|
|
18369
|
+
const fallbackDoc = readFileIfExists(join27(sourcePath, "README.md")) || readFileIfExists(join27(sourcePath, "CLAUDE.md"));
|
|
18195
18370
|
if (fallbackDoc)
|
|
18196
18371
|
return `${frontmatter.join(`
|
|
18197
18372
|
`)}${fallbackDoc.trim()}
|
|
@@ -18210,7 +18385,7 @@ skills run ${canonicalName}
|
|
|
18210
18385
|
`;
|
|
18211
18386
|
}
|
|
18212
18387
|
function readBundledSkillVersion(name) {
|
|
18213
|
-
const pkgPath =
|
|
18388
|
+
const pkgPath = join27(getSkillPath(name), "package.json");
|
|
18214
18389
|
if (!existsSync12(pkgPath))
|
|
18215
18390
|
return "unknown";
|
|
18216
18391
|
try {
|
|
@@ -18265,7 +18440,7 @@ function selectSkillDoc(docs, file) {
|
|
|
18265
18440
|
function isInstructionSkillDir(skillPath, meta) {
|
|
18266
18441
|
if (meta?.kind === "instruction")
|
|
18267
18442
|
return true;
|
|
18268
|
-
const skillMdPath =
|
|
18443
|
+
const skillMdPath = join28(skillPath, "SKILL.md");
|
|
18269
18444
|
if (!existsSync13(skillMdPath))
|
|
18270
18445
|
return false;
|
|
18271
18446
|
try {
|
|
@@ -18295,9 +18470,9 @@ function getSkillDocs(name) {
|
|
|
18295
18470
|
if (!existsSync13(skillPath))
|
|
18296
18471
|
return null;
|
|
18297
18472
|
return {
|
|
18298
|
-
skillMd: readIfExists(
|
|
18299
|
-
readme: readIfExists(
|
|
18300
|
-
claudeMd: readIfExists(
|
|
18473
|
+
skillMd: readIfExists(join28(skillPath, "SKILL.md")),
|
|
18474
|
+
readme: readIfExists(join28(skillPath, "README.md")),
|
|
18475
|
+
claudeMd: readIfExists(join28(skillPath, "CLAUDE.md"))
|
|
18301
18476
|
};
|
|
18302
18477
|
}
|
|
18303
18478
|
function getSkillBestDoc(name, file) {
|
|
@@ -18313,14 +18488,14 @@ function getSkillRequirements(name) {
|
|
|
18313
18488
|
return null;
|
|
18314
18489
|
const texts = [];
|
|
18315
18490
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
18316
|
-
const content = readIfExists(
|
|
18491
|
+
const content = readIfExists(join28(skillPath, file));
|
|
18317
18492
|
if (content)
|
|
18318
18493
|
texts.push(content);
|
|
18319
18494
|
}
|
|
18320
18495
|
const meta = getSkill(name);
|
|
18321
18496
|
let dependencies = {};
|
|
18322
18497
|
try {
|
|
18323
|
-
dependencies = JSON.parse(readFileSync18(
|
|
18498
|
+
dependencies = JSON.parse(readFileSync18(join28(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
18324
18499
|
} catch {}
|
|
18325
18500
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta);
|
|
18326
18501
|
}
|
|
@@ -18382,7 +18557,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
18382
18557
|
error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
|
|
18383
18558
|
};
|
|
18384
18559
|
}
|
|
18385
|
-
const pkgPath =
|
|
18560
|
+
const pkgPath = join28(skillPath, "package.json");
|
|
18386
18561
|
if (!existsSync13(pkgPath)) {
|
|
18387
18562
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
18388
18563
|
}
|
|
@@ -18402,12 +18577,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
18402
18577
|
} catch {
|
|
18403
18578
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
18404
18579
|
}
|
|
18405
|
-
const entryPath =
|
|
18580
|
+
const entryPath = join28(skillPath, entryPoint2);
|
|
18406
18581
|
if (!existsSync13(entryPath)) {
|
|
18407
18582
|
return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
|
|
18408
18583
|
}
|
|
18409
18584
|
const env = { ...process.env, ...options.env };
|
|
18410
|
-
if (!existsSync13(
|
|
18585
|
+
if (!existsSync13(join28(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
|
|
18411
18586
|
const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
|
|
18412
18587
|
if (failure)
|
|
18413
18588
|
return failure;
|
|
@@ -18485,10 +18660,10 @@ function generateSkillMd(name) {
|
|
|
18485
18660
|
"---"
|
|
18486
18661
|
].join(`
|
|
18487
18662
|
`);
|
|
18488
|
-
const readme = readIfExists(
|
|
18489
|
-
const claudeMd = readIfExists(
|
|
18663
|
+
const readme = readIfExists(join28(skillPath, "README.md"));
|
|
18664
|
+
const claudeMd = readIfExists(join28(skillPath, "CLAUDE.md"));
|
|
18490
18665
|
let cliCommand = null;
|
|
18491
|
-
const pkgPath =
|
|
18666
|
+
const pkgPath = join28(skillPath, "package.json");
|
|
18492
18667
|
if (existsSync13(pkgPath)) {
|
|
18493
18668
|
try {
|
|
18494
18669
|
const pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
|
|
@@ -18694,7 +18869,7 @@ function exactProfileSelection(spec, profile) {
|
|
|
18694
18869
|
}
|
|
18695
18870
|
|
|
18696
18871
|
// src/lib/plugin-projection.ts
|
|
18697
|
-
import { createHash as
|
|
18872
|
+
import { createHash as createHash19 } from "crypto";
|
|
18698
18873
|
import { posix } from "path";
|
|
18699
18874
|
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
|
|
18700
18875
|
var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -18705,7 +18880,7 @@ function pluginNeed(value, message) {
|
|
|
18705
18880
|
if (!value)
|
|
18706
18881
|
pluginRefusal(message);
|
|
18707
18882
|
}
|
|
18708
|
-
var pluginHash = (bytes) =>
|
|
18883
|
+
var pluginHash = (bytes) => createHash19("sha256").update(bytes).digest("hex");
|
|
18709
18884
|
function pluginObject(value) {
|
|
18710
18885
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
18711
18886
|
}
|
|
@@ -18721,7 +18896,7 @@ function pluginId(value) {
|
|
|
18721
18896
|
function pluginDigest(value) {
|
|
18722
18897
|
pluginNeed(typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value), "Invalid plugin tree digest");
|
|
18723
18898
|
}
|
|
18724
|
-
function
|
|
18899
|
+
function parse5(bytes) {
|
|
18725
18900
|
pluginNeed(bytes.byteLength <= PLUGIN_PROJECTION_LIMITS.metadataBytes, "Plugin metadata exceeds its limit");
|
|
18726
18901
|
try {
|
|
18727
18902
|
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
@@ -18812,7 +18987,7 @@ function buildPluginProjection(entries) {
|
|
|
18812
18987
|
pluginFileWitnesses(entries);
|
|
18813
18988
|
const contract = entries.filter((item) => item.path === PLUGIN_PROJECTION_MANIFEST);
|
|
18814
18989
|
pluginNeed(contract.length === 1, "The selected bundle has no unique plugin projection manifest");
|
|
18815
|
-
const manifest =
|
|
18990
|
+
const manifest = parse5(contract[0].bytes);
|
|
18816
18991
|
validatePluginManifest(manifest);
|
|
18817
18992
|
const original = entries.filter((item) => item.path.startsWith("original/")).map((item) => ({ ...item, path: item.path.slice(9) }));
|
|
18818
18993
|
pluginNeed(!original.some((item) => [".in_use", ".orphaned_at", ".gcs-sha", ".links_materialized"].some((path) => within(item.path, path))), "Original plugin packages cannot supply native runtime metadata");
|
|
@@ -18820,7 +18995,7 @@ function buildPluginProjection(entries) {
|
|
|
18820
18995
|
pluginNeed(originalTreeDigest === manifest.upstream.treeDigest, "The original plugin package differs from its immutable provenance");
|
|
18821
18996
|
const native = original.find((item) => item.path === ".claude-plugin/plugin.json");
|
|
18822
18997
|
pluginNeed(native, "The original plugin manifest is missing");
|
|
18823
|
-
const config =
|
|
18998
|
+
const config = parse5(native.bytes);
|
|
18824
18999
|
pluginKeys(config, MANIFEST_FIELDS);
|
|
18825
19000
|
pluginNeed(config.name === manifest.pluginId.split("@")[0] && (manifest.upstream.version === null ? !Object.hasOwn(config, "version") : config.version === manifest.upstream.version), "Original plugin identity differs from its contract");
|
|
18826
19001
|
const skillRoots = ["skills", ...declaredPaths(config.skills)], commandRoots = ["commands", ...declaredPaths(config.commands)];
|
|
@@ -18864,7 +19039,7 @@ function buildPluginProjection(entries) {
|
|
|
18864
19039
|
if (item.path === native.path)
|
|
18865
19040
|
continue;
|
|
18866
19041
|
if (item.path.toLowerCase() === "package.json") {
|
|
18867
|
-
const pkg =
|
|
19042
|
+
const pkg = parse5(item.bytes);
|
|
18868
19043
|
pluginNeed(pluginObject(pkg), "Invalid plugin package metadata");
|
|
18869
19044
|
for (const key of ["scripts", "dependencies", "optionalDependencies", "devDependencies", "peerDependencies", "workspaces", "bundledDependencies", "bundleDependencies", "packageManager"])
|
|
18870
19045
|
pluginNeed(pkg[key] === undefined, "Upstream package installation is outside plugin admission");
|
|
@@ -18893,9 +19068,9 @@ function buildPluginProjection(entries) {
|
|
|
18893
19068
|
}
|
|
18894
19069
|
|
|
18895
19070
|
// src/lib/plugin-projection-store.ts
|
|
18896
|
-
import { createHash as
|
|
19071
|
+
import { createHash as createHash20, randomUUID as randomUUID4 } from "crypto";
|
|
18897
19072
|
import { constants as constants11, closeSync as closeSync11, fchmodSync, fstatSync as fstatSync11, fsyncSync as fsyncSync5, linkSync as linkSync3, lstatSync as lstatSync21, mkdirSync as mkdirSync9, openSync as openSync11, opendirSync as opendirSync4, readSync as readSync9, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "fs";
|
|
18898
|
-
import { dirname as dirname16, isAbsolute as isAbsolute14, join as
|
|
19073
|
+
import { dirname as dirname16, isAbsolute as isAbsolute14, join as join29, parse as parse6, resolve as resolve14 } from "path";
|
|
18899
19074
|
var READ_FLAGS = constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
|
|
18900
19075
|
var WRITE_FLAGS = constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
|
|
18901
19076
|
var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
|
|
@@ -18909,7 +19084,7 @@ function stat(path) {
|
|
|
18909
19084
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
18910
19085
|
}
|
|
18911
19086
|
}
|
|
18912
|
-
function
|
|
19087
|
+
function canonical6(path) {
|
|
18913
19088
|
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute14(path) && resolve14(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
18914
19089
|
}
|
|
18915
19090
|
function owner(stats, system = false) {
|
|
@@ -18920,12 +19095,12 @@ function same3(left, right, identityOnly = false) {
|
|
|
18920
19095
|
pluginNeed(right && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode && left.uid === right.uid && left.gid === right.gid && (identityOnly || left.size === right.size && left.nlink === right.nlink && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs), "Plugin filesystem entry changed during access");
|
|
18921
19096
|
}
|
|
18922
19097
|
function ancestors(path, create = false) {
|
|
18923
|
-
|
|
18924
|
-
const result = [], parent = dirname16(path), filesystemRoot =
|
|
19098
|
+
canonical6(path);
|
|
19099
|
+
const result = [], parent = dirname16(path), filesystemRoot = parse6(parent).root;
|
|
18925
19100
|
let current = filesystemRoot;
|
|
18926
19101
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
18927
19102
|
if (segment)
|
|
18928
|
-
current =
|
|
19103
|
+
current = join29(current, segment);
|
|
18929
19104
|
let currentStat = stat(current);
|
|
18930
19105
|
if (!currentStat && create) {
|
|
18931
19106
|
try {
|
|
@@ -19040,7 +19215,7 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
19040
19215
|
pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
|
|
19041
19216
|
const path = relative7 ? `${relative7}/${name}` : name;
|
|
19042
19217
|
pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
|
|
19043
|
-
const absolute =
|
|
19218
|
+
const absolute = join29(directory, name), entryStat = stat(absolute);
|
|
19044
19219
|
pluginNeed(entryStat, "Plugin tree member disappeared");
|
|
19045
19220
|
if (relative7 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
19046
19221
|
const bytes2 = readBytes(absolute, 32, "native-pin");
|
|
@@ -19057,7 +19232,7 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
19057
19232
|
directories.push({ path: absolute, stat: entryStat, names: pins });
|
|
19058
19233
|
for (const pin of pins) {
|
|
19059
19234
|
pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
|
|
19060
|
-
const pinPath =
|
|
19235
|
+
const pinPath = join29(absolute, pin), pinStat = stat(pinPath);
|
|
19061
19236
|
pluginNeed(pinStat, "Native plugin pin disappeared");
|
|
19062
19237
|
const bytes2 = readBytes(pinPath, 4096, "native-pin");
|
|
19063
19238
|
let value;
|
|
@@ -19155,7 +19330,7 @@ function materializePluginTree(root, entries) {
|
|
|
19155
19330
|
mkdirSync9(staging, { mode: 448 });
|
|
19156
19331
|
staged = stat(staging);
|
|
19157
19332
|
for (const entry of entries) {
|
|
19158
|
-
const path =
|
|
19333
|
+
const path = join29(staging, entry.path);
|
|
19159
19334
|
ancestors(path, true);
|
|
19160
19335
|
writeExclusive(path, entry.bytes, entry.mode);
|
|
19161
19336
|
}
|
|
@@ -19235,7 +19410,7 @@ function pluginExecutableDigest(path) {
|
|
|
19235
19410
|
const opened = fstatSync11(fd, { bigint: true });
|
|
19236
19411
|
same3(before, opened);
|
|
19237
19412
|
fileShape(opened, maximum, "executable");
|
|
19238
|
-
const hash2 =
|
|
19413
|
+
const hash2 = createHash20("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
19239
19414
|
let remaining = Number(opened.size);
|
|
19240
19415
|
while (remaining > 0) {
|
|
19241
19416
|
const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -19282,7 +19457,7 @@ function describeEntries(entries) {
|
|
|
19282
19457
|
|
|
19283
19458
|
// src/lib/plugin-admission.ts
|
|
19284
19459
|
function pluginAdmissionRoot() {
|
|
19285
|
-
return
|
|
19460
|
+
return join30(homedir5(), ".hasna", "skills", "plugin-admission");
|
|
19286
19461
|
}
|
|
19287
19462
|
function validatePluginTarget(value) {
|
|
19288
19463
|
pluginKeys(value, ["schemaVersion", "pluginId", "registrations", "native", "resolver"]);
|
|
@@ -19458,12 +19633,12 @@ function root(options) {
|
|
|
19458
19633
|
}
|
|
19459
19634
|
function pluginBindingPath(storeRoot, id) {
|
|
19460
19635
|
pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
|
|
19461
|
-
return
|
|
19636
|
+
return join30(storeRoot, "bindings", `${id}.json`);
|
|
19462
19637
|
}
|
|
19463
19638
|
function pluginReceiptPath(storeRoot, id, digest) {
|
|
19464
19639
|
pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
|
|
19465
19640
|
pluginDigest(digest);
|
|
19466
|
-
return
|
|
19641
|
+
return join30(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
|
|
19467
19642
|
}
|
|
19468
19643
|
function readPluginBinding(storeRoot, id) {
|
|
19469
19644
|
const value = readPluginJson(pluginBindingPath(storeRoot, id));
|
|
@@ -19483,7 +19658,7 @@ function readPluginAdmissionReceipt(storeRoot, id, digest) {
|
|
|
19483
19658
|
pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest === digest, "Plugin receipt identity differs from its filename");
|
|
19484
19659
|
const binding = readPluginBinding(storeRoot, id);
|
|
19485
19660
|
pluginNeed(canonicalJson(binding) === canonicalJson(receipt.plan.binding) && receipt.plan.sourceCommand === pluginResolverCommand(binding), "Plugin receipt binding changed");
|
|
19486
|
-
pluginNeed(receipt.materializedPath ===
|
|
19661
|
+
pluginNeed(receipt.materializedPath === join30(storeRoot, "objects", id, digest.slice(7)), "Plugin receipt points outside its immutable materialization");
|
|
19487
19662
|
pluginText(receipt.admittedAt, 64);
|
|
19488
19663
|
pluginNeed(Number.isFinite(Date.parse(receipt.admittedAt)), "Invalid plugin admission time");
|
|
19489
19664
|
return receipt;
|
|
@@ -19624,7 +19799,7 @@ async function admitPlugin(spec, profileId, target, approvedDigest, approvedEvid
|
|
|
19624
19799
|
const { plan, projection } = await candidate(spec, profileId, target, options);
|
|
19625
19800
|
pluginNeed(plan.planDigest === approvedDigest, "The plugin plan changed since review; obtain and review a new plan");
|
|
19626
19801
|
pluginNeed(plan.evidenceDigest === approvedEvidenceDigest, "The plugin routing evidence changed since review; obtain and review a new plan");
|
|
19627
|
-
const store = root(options), materializedPath =
|
|
19802
|
+
const store = root(options), materializedPath = join30(store, "objects", plan.bindingId, plan.planDigest.slice(7));
|
|
19628
19803
|
materializePluginTree(materializedPath, projection.files);
|
|
19629
19804
|
writePluginJsonImmutable(pluginBindingPath(store, plan.bindingId), plan.binding);
|
|
19630
19805
|
const path = pluginReceiptPath(store, plan.bindingId, plan.planDigest), existing = readPluginJson(path);
|
|
@@ -19716,7 +19891,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
19716
19891
|
seen.add(target.pluginId);
|
|
19717
19892
|
const rows = plugins[target.pluginId];
|
|
19718
19893
|
pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
|
|
19719
|
-
const [name, market] = target.pluginId.split("@"), cacheRoot =
|
|
19894
|
+
const [name, market] = target.pluginId.split("@"), cacheRoot = join31(dirname17(path), "cache", market, name);
|
|
19720
19895
|
const scopes = new Set, approvals = new Map, currentFiles = new Map;
|
|
19721
19896
|
plugins[target.pluginId] = rows.map((row) => {
|
|
19722
19897
|
pluginNeed(pluginObject(row), "Invalid managed plugin registration");
|
|
@@ -19729,7 +19904,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
19729
19904
|
pluginText(row.sourceProducerPath);
|
|
19730
19905
|
pluginText(row.lastUpdated, 64);
|
|
19731
19906
|
pluginNeed(Number.isFinite(Date.parse(row.lastUpdated)), "Invalid managed plugin update timestamp");
|
|
19732
|
-
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath ===
|
|
19907
|
+
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join31(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
|
|
19733
19908
|
const approval = (producer) => {
|
|
19734
19909
|
pluginText(producer);
|
|
19735
19910
|
const digest = `sha256:${basename8(producer)}`;
|
|
@@ -19772,11 +19947,11 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
19772
19947
|
directory.closeSync();
|
|
19773
19948
|
}
|
|
19774
19949
|
for (const version of names.sort()) {
|
|
19775
|
-
const files = pluginFileWitnesses(snapshotPluginTree(
|
|
19950
|
+
const files = pluginFileWitnesses(snapshotPluginTree(join31(cacheRoot, version), { nativeRuntime: `claude-${target.native.version}` }));
|
|
19776
19951
|
verifiedBytes += files.reduce((total, file) => total + file.size, 0);
|
|
19777
19952
|
pluginNeed(verifiedBytes <= 256 * 1024 * 1024, "Managed plugin verification exceeds its aggregate byte budget");
|
|
19778
19953
|
const serialized = JSON.stringify(files);
|
|
19779
|
-
const expected = currentFiles.get(
|
|
19954
|
+
const expected = currentFiles.get(join31(cacheRoot, version));
|
|
19780
19955
|
pluginNeed(expected === undefined || serialized === expected, "Current native cache differs from its exact registered producer receipt");
|
|
19781
19956
|
pluginNeed([...approvals.values()].some((receipt) => nativeVersionMatches(version, receipt) && JSON.stringify(receipt.plan.files) === serialized), "A retained native plugin cache version differs from every approved projection");
|
|
19782
19957
|
}
|
|
@@ -19794,7 +19969,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
|
|
|
19794
19969
|
}
|
|
19795
19970
|
|
|
19796
19971
|
// src/lib/claude-marketplace-registry.ts
|
|
19797
|
-
import { createHash as
|
|
19972
|
+
import { createHash as createHash21 } from "crypto";
|
|
19798
19973
|
import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync23, openSync as openSync13, readSync as readSync11 } from "fs";
|
|
19799
19974
|
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute17, resolve as resolve17 } from "path";
|
|
19800
19975
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
@@ -19851,7 +20026,7 @@ function readRegistry2(path, budget2) {
|
|
|
19851
20026
|
closeSync13(fd);
|
|
19852
20027
|
}
|
|
19853
20028
|
}
|
|
19854
|
-
function
|
|
20029
|
+
function parse7(text2) {
|
|
19855
20030
|
let at = 0, nodes = 0;
|
|
19856
20031
|
const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
19857
20032
|
const space = () => {
|
|
@@ -19960,22 +20135,22 @@ function normalizeRow(row, version = 1) {
|
|
|
19960
20135
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
19961
20136
|
}
|
|
19962
20137
|
function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
19963
|
-
const value =
|
|
20138
|
+
const value = parse7(readRegistry2(path, budget2));
|
|
19964
20139
|
for (const [, row] of value.entries)
|
|
19965
20140
|
normalizeRow(row);
|
|
19966
|
-
const sha2562 =
|
|
20141
|
+
const sha2562 = createHash21("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
19967
20142
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
19968
20143
|
}
|
|
19969
20144
|
function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
19970
|
-
const value =
|
|
20145
|
+
const value = parse7(readRegistry2(path, budget2));
|
|
19971
20146
|
for (const [, row] of value.entries)
|
|
19972
20147
|
normalizeRow(row, 2);
|
|
19973
|
-
const sha2562 =
|
|
20148
|
+
const sha2562 = createHash21("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
19974
20149
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
19975
20150
|
}
|
|
19976
20151
|
|
|
19977
20152
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
19978
|
-
import { createHash as
|
|
20153
|
+
import { createHash as createHash22 } from "crypto";
|
|
19979
20154
|
|
|
19980
20155
|
// src/lib/codex-hook-additions-review.ts
|
|
19981
20156
|
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
@@ -19983,7 +20158,7 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
|
19983
20158
|
var proofs = new WeakMap;
|
|
19984
20159
|
|
|
19985
20160
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
19986
|
-
var sha4 = (text2) =>
|
|
20161
|
+
var sha4 = (text2) => createHash22("sha256").update(text2).digest("hex");
|
|
19987
20162
|
var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
19988
20163
|
var proofs2 = new WeakMap;
|
|
19989
20164
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
@@ -20002,7 +20177,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
20002
20177
|
}
|
|
20003
20178
|
|
|
20004
20179
|
// src/lib/agent-discovery.ts
|
|
20005
|
-
var digest = (text2) =>
|
|
20180
|
+
var digest = (text2) => createHash23("sha256").update(text2).digest("hex");
|
|
20006
20181
|
function parseConfig(text2, path, toml = false) {
|
|
20007
20182
|
try {
|
|
20008
20183
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -20088,6 +20263,12 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
20088
20263
|
throw new Error("Managed plugin registry witnesses require verified native registrations");
|
|
20089
20264
|
return hashManagedPluginRegistry(source.path, source.managedPlugins);
|
|
20090
20265
|
}
|
|
20266
|
+
if (source.hashMode === "claude-plugin-manifest-v1") {
|
|
20267
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || changes?.has(source.path))
|
|
20268
|
+
throw new Error("Claude plugin manifest witnesses require a native discovery source without synthetic changes");
|
|
20269
|
+
const text3 = read2(source.path);
|
|
20270
|
+
return text3 === null ? null : hashClaudePluginManifest(text3);
|
|
20271
|
+
}
|
|
20091
20272
|
if (source.managedPlugins !== undefined)
|
|
20092
20273
|
throw new Error("Managed plugin rules require a typed registry witness");
|
|
20093
20274
|
if (source.hashMode !== undefined) {
|
|
@@ -20114,7 +20295,7 @@ function absentCodexSkillRoots(binding) {
|
|
|
20114
20295
|
throw new Error("Invalid disabled Codex discovery proof");
|
|
20115
20296
|
if (binding.method !== "reviewed" || controls.every((control) => lstatSync24(control.pluginParent, { throwIfNoEntry: false })))
|
|
20116
20297
|
return [];
|
|
20117
|
-
const cache = dirname19(dirname19(controls[0].pluginParent)), config =
|
|
20298
|
+
const cache = dirname19(dirname19(controls[0].pluginParent)), config = join32(dirname19(dirname19(cache)), "config.toml");
|
|
20118
20299
|
const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
20119
20300
|
if (!source || projected(source) !== source.sha256)
|
|
20120
20301
|
throw new Error("Missing current disabled Codex configuration witness");
|
|
@@ -20122,8 +20303,8 @@ function absentCodexSkillRoots(binding) {
|
|
|
20122
20303
|
return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
20123
20304
|
}
|
|
20124
20305
|
function codexDirectHookSelects(cache, root2) {
|
|
20125
|
-
const codex = dirname19(dirname19(cache)), config = Bun.TOML.parse(read2(
|
|
20126
|
-
const hooks = read2(
|
|
20306
|
+
const codex = dirname19(dirname19(cache)), config = Bun.TOML.parse(read2(join32(codex, "config.toml")) ?? "");
|
|
20307
|
+
const hooks = read2(join32(codex, "hooks.json"));
|
|
20127
20308
|
if (hooks !== null)
|
|
20128
20309
|
hashNativeJsonControls(hooks);
|
|
20129
20310
|
return JSON.stringify(config.hooks ?? {}).includes(root2) || hooks !== null && JSON.stringify(JSON.parse(hooks)).includes(root2);
|
|
@@ -20135,7 +20316,7 @@ function retiredCodexRoots(binding) {
|
|
|
20135
20316
|
const controls = binding.codexDisabledPluginSkills;
|
|
20136
20317
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !Array.isArray(controls) || !controls.length || !Array.isArray(proof.roots) || proof.roots.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.roots).size !== proof.roots.length || !Array.isArray(proof.directories) || proof.parents !== undefined && (!Array.isArray(proof.parents) || proof.parents.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.parents).size !== proof.parents.length) || !proof.roots.length && !proof.parents?.length)
|
|
20137
20318
|
throw new Error("Invalid retired Codex materialization proof");
|
|
20138
|
-
const cache = dirname19(dirname19(controls[0].pluginParent)), config =
|
|
20319
|
+
const cache = dirname19(dirname19(controls[0].pluginParent)), config = join32(dirname19(dirname19(cache)), "config.toml");
|
|
20139
20320
|
const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
20140
20321
|
if (!source || projected(source) !== source.sha256)
|
|
20141
20322
|
throw new Error("Missing current retired Codex configuration witness");
|
|
@@ -20143,7 +20324,7 @@ function retiredCodexRoots(binding) {
|
|
|
20143
20324
|
if ((proof.parents ?? []).some((parent) => codexDirectHookSelects(cache, parent) || !reviewedDisabledCodexPluginParent(cache, parent, controls, rules)) || !reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read2(path), rules))
|
|
20144
20325
|
throw new Error("Retired Codex parent capabilities changed");
|
|
20145
20326
|
for (const root2 of proof.roots) {
|
|
20146
|
-
const manifest = binding.sources.find((source2) => source2.path ===
|
|
20327
|
+
const manifest = binding.sources.find((source2) => source2.path === join32(root2, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
|
|
20147
20328
|
if (!manifest || codexDirectHookSelects(cache, root2) || !reviewedRetiredCodexRoot(cache, root2, controls, rules, (path) => read2(path), true))
|
|
20148
20329
|
throw new Error("Retired Codex materialization changed");
|
|
20149
20330
|
}
|
|
@@ -20153,14 +20334,14 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
|
20153
20334
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls.length)
|
|
20154
20335
|
return binding;
|
|
20155
20336
|
const cache = dirname19(dirname19(controls[0].pluginParent));
|
|
20156
|
-
const candidates = binding.sources.filter((source) => source.path.endsWith(
|
|
20337
|
+
const candidates = binding.sources.filter((source) => source.path.endsWith(sep8 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname19(dirname19(source.path)));
|
|
20157
20338
|
const roots = [...new Set(candidates)].filter((root2) => !codexDirectHookSelects(cache, root2) && reviewedRetiredCodexRoot(cache, root2, controls, rules, (path) => read2(path), binding.codexRetiredMaterializations?.roots.includes(root2) ?? false)).sort();
|
|
20158
20339
|
const parents3 = reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read2(path), rules) ? [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && reviewedDisabledCodexPluginParent(cache, parent, controls, rules)).sort() : [];
|
|
20159
20340
|
const { codexRetiredMaterializations: previous, ...current } = binding;
|
|
20160
20341
|
if (!roots.length && !parents3.length)
|
|
20161
20342
|
return current;
|
|
20162
20343
|
const installation = codexInstallationRoots(binding);
|
|
20163
|
-
const directories = (binding.directories ?? []).filter((directory) => !installation.some((root2) => directory.path === root2 || directory.path.startsWith(root2 +
|
|
20344
|
+
const directories = (binding.directories ?? []).filter((directory) => !installation.some((root2) => directory.path === root2 || directory.path.startsWith(root2 + sep8) || root2.startsWith(directory.path + sep8)));
|
|
20164
20345
|
if (previous && isDeepStrictEqual7(previous.roots, roots) && isDeepStrictEqual7(previous.parents, parents3) && isDeepStrictEqual7(previous.directories.map((item) => item.path), directories.map((item) => item.path)))
|
|
20165
20346
|
return { ...current, codexRetiredMaterializations: previous };
|
|
20166
20347
|
return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
|
|
@@ -20172,17 +20353,17 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
20172
20353
|
for (const source of binding.sources) {
|
|
20173
20354
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || ![undefined, "bytes"].includes(source.hashMode) || source.sha256 === null)
|
|
20174
20355
|
continue;
|
|
20175
|
-
const marker =
|
|
20356
|
+
const marker = sep8 + ".claude" + sep8 + "plugins" + sep8 + "cache" + sep8, at = source.path.indexOf(marker);
|
|
20176
20357
|
if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
|
|
20177
20358
|
continue;
|
|
20178
|
-
const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(
|
|
20359
|
+
const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep8);
|
|
20179
20360
|
if (parts.length < 4 || parts.slice(0, 3).some((part) => !part || part === "." || part === ".."))
|
|
20180
20361
|
continue;
|
|
20181
|
-
const root2 =
|
|
20362
|
+
const root2 = join32(cache, ...parts.slice(0, 3));
|
|
20182
20363
|
safe4(root2);
|
|
20183
20364
|
if (lstatSync24(root2, { throwIfNoEntry: false }))
|
|
20184
20365
|
continue;
|
|
20185
|
-
const claude = dirname19(dirname19(cache)), settings =
|
|
20366
|
+
const claude = dirname19(dirname19(cache)), settings = join32(claude, "settings.json"), registry = join32(claude, "plugins/installed_plugins.json");
|
|
20186
20367
|
const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
|
|
20187
20368
|
const configSource = full(settings), registrySource = full(registry);
|
|
20188
20369
|
if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
|
|
@@ -20201,10 +20382,10 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
20201
20382
|
const active = rows[0].installPath;
|
|
20202
20383
|
if (rows[0].version !== basename10(active) || resolve18(active) !== active || dirname19(active) !== dirname19(root2) || active === root2 || !lstatSync24(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync10(active) !== active)
|
|
20203
20384
|
continue;
|
|
20204
|
-
const activeManifest = full(
|
|
20385
|
+
const activeManifest = full(join32(active, ".claude-plugin/plugin.json"));
|
|
20205
20386
|
if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
|
|
20206
20387
|
continue;
|
|
20207
|
-
if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root2 || row.installPath.startsWith(root2 +
|
|
20388
|
+
if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root2 || row.installPath.startsWith(root2 + sep8) || root2.startsWith(row.installPath + sep8))))
|
|
20208
20389
|
continue;
|
|
20209
20390
|
result.add(root2);
|
|
20210
20391
|
}
|
|
@@ -20219,10 +20400,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
20219
20400
|
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
20220
20401
|
const installationRoots = codexInstallationRoots(binding);
|
|
20221
20402
|
const retiredRoots = retiredCodexRoots(binding);
|
|
20222
|
-
const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 +
|
|
20403
|
+
const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep8));
|
|
20223
20404
|
if (binding.directories !== undefined) {
|
|
20224
20405
|
const projected2 = binding.codexInstallationInputs?.directories ?? [];
|
|
20225
|
-
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path +
|
|
20406
|
+
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path + sep8)));
|
|
20226
20407
|
if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
|
|
20227
20408
|
throw new Error("Missing installation input directory projection");
|
|
20228
20409
|
const ordinary = binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory));
|
|
@@ -20258,7 +20439,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
20258
20439
|
safe4(source.path);
|
|
20259
20440
|
continue;
|
|
20260
20441
|
}
|
|
20261
|
-
if (absentRoots.some((root2) => source.path.startsWith(root2 +
|
|
20442
|
+
if (absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)) {
|
|
20262
20443
|
safe4(source.path);
|
|
20263
20444
|
continue;
|
|
20264
20445
|
}
|
|
@@ -20281,12 +20462,12 @@ function codexInstallationRoots(binding) {
|
|
|
20281
20462
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
|
|
20282
20463
|
throw new Error("Invalid native installation input proof");
|
|
20283
20464
|
const cache = dirname19(dirname19(proof.plugins[0].pluginParent));
|
|
20284
|
-
const config =
|
|
20465
|
+
const config = join32(dirname19(dirname19(cache)), "config.toml"), home = dirname19(dirname19(dirname19(cache)));
|
|
20285
20466
|
const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
20286
|
-
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) =>
|
|
20287
|
-
if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot +
|
|
20467
|
+
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
|
|
20468
|
+
if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep8) || input.sourceRoot.startsWith(root2 + sep8)) || binding.sources.some((source) => basename10(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep8)))))
|
|
20288
20469
|
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
20289
|
-
if (proof.plugins.some((input) => dirname19(dirname19(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path ===
|
|
20470
|
+
if (proof.plugins.some((input) => dirname19(dirname19(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join32(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
|
|
20290
20471
|
throw new Error("Missing native installation input source witness");
|
|
20291
20472
|
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
20292
20473
|
const text2 = read2(path);
|
|
@@ -20296,6 +20477,8 @@ function codexInstallationRoots(binding) {
|
|
|
20296
20477
|
});
|
|
20297
20478
|
}
|
|
20298
20479
|
function assertMarketplaceBinding(binding, source) {
|
|
20480
|
+
if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json"))))
|
|
20481
|
+
throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
|
|
20299
20482
|
if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename10(source.path) !== "sumi.json"))
|
|
20300
20483
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
20301
20484
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
@@ -20310,7 +20493,7 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
20310
20493
|
const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync24(root2, { throwIfNoEntry: false }))];
|
|
20311
20494
|
const rebound = { ...binding, sources: binding.sources.map((source) => {
|
|
20312
20495
|
assertMarketplaceBinding(binding, source);
|
|
20313
|
-
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 +
|
|
20496
|
+
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep8)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)))
|
|
20314
20497
|
return source;
|
|
20315
20498
|
const sha2562 = projected(source, changes, budget2);
|
|
20316
20499
|
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
@@ -20336,8 +20519,8 @@ function captureDiscoveryPathSources(paths) {
|
|
|
20336
20519
|
const budget2 = discoveryByteBudget();
|
|
20337
20520
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
|
|
20338
20521
|
}
|
|
20339
|
-
function assertProjectDiscovery(agent, directories, home,
|
|
20340
|
-
const homeConfigPath =
|
|
20522
|
+
function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18, reviewed) {
|
|
20523
|
+
const homeConfigPath = canonical7(agentDiscoveryConfigPath(home, agent));
|
|
20341
20524
|
for (const directory of directories) {
|
|
20342
20525
|
const isHome = resolve18(directory) === resolve18(home);
|
|
20343
20526
|
if (agent === "hermes") {
|
|
@@ -20348,7 +20531,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
|
|
|
20348
20531
|
continue;
|
|
20349
20532
|
const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : agent === "sumi" ? ["sumi.json", "sumi.jsonc", ".sumi/sumi.json", ".sumi/sumi.jsonc"] : [".cursor/hooks.json"];
|
|
20350
20533
|
for (const suffix of paths) {
|
|
20351
|
-
const path =
|
|
20534
|
+
const path = canonical7(join32(directory, suffix)), raw = read2(path);
|
|
20352
20535
|
if (raw === null)
|
|
20353
20536
|
continue;
|
|
20354
20537
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -20364,7 +20547,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
|
|
|
20364
20547
|
const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
|
|
20365
20548
|
if (exact && projected(exact) === exact.sha256 && managed.length) {
|
|
20366
20549
|
const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
|
|
20367
|
-
admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath ===
|
|
20550
|
+
admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical7(directory))));
|
|
20368
20551
|
}
|
|
20369
20552
|
}
|
|
20370
20553
|
if (keys.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
|
|
@@ -20372,13 +20555,13 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
|
|
|
20372
20555
|
}
|
|
20373
20556
|
if (agent === "sumi")
|
|
20374
20557
|
for (const name of ["plugin", "plugins"]) {
|
|
20375
|
-
const path =
|
|
20558
|
+
const path = canonical7(join32(directory, ".sumi", name));
|
|
20376
20559
|
safe4(path);
|
|
20377
20560
|
if (lstatSync24(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(path).length)
|
|
20378
20561
|
throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
|
|
20379
20562
|
}
|
|
20380
20563
|
if (agent === "claude") {
|
|
20381
|
-
const commands =
|
|
20564
|
+
const commands = canonical7(join32(directory, ".claude/commands"));
|
|
20382
20565
|
safe4(commands);
|
|
20383
20566
|
if (lstatSync24(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
20384
20567
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
@@ -20388,13 +20571,13 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
|
|
|
20388
20571
|
function agentDiscoveryConfigPath(home, agent) {
|
|
20389
20572
|
if (agent === "sumi")
|
|
20390
20573
|
return sumiConfigPath(home);
|
|
20391
|
-
return
|
|
20574
|
+
return join32(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
20392
20575
|
}
|
|
20393
20576
|
function resolveAgentDiscovery(options) {
|
|
20394
|
-
const
|
|
20577
|
+
const canonical7 = options.canonical ?? resolve18, home = resolve18(options.home), agent = options.agent;
|
|
20395
20578
|
const sources = [], roots = new Set, builtinNames = [];
|
|
20396
|
-
const witness = (path, format, fields2) => {
|
|
20397
|
-
const source = { path:
|
|
20579
|
+
const witness = (path, format, fields2, hashMode) => {
|
|
20580
|
+
const source = { path: canonical7(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
|
|
20398
20581
|
source.sha256 = projected(source);
|
|
20399
20582
|
sources.push(source);
|
|
20400
20583
|
return read2(source.path);
|
|
@@ -20407,31 +20590,34 @@ function resolveAgentDiscovery(options) {
|
|
|
20407
20590
|
const unresolved = (detail) => {
|
|
20408
20591
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
20409
20592
|
};
|
|
20593
|
+
if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
|
|
20594
|
+
throw new Error("Invalid --discovery-inputs version or agents");
|
|
20595
|
+
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
20410
20596
|
if (agent === "sumi") {
|
|
20411
20597
|
const directory = sumiConfigDirectory(home);
|
|
20412
|
-
if (witness(
|
|
20598
|
+
if (witness(join32(directory, "sumi.jsonc")) !== null)
|
|
20413
20599
|
unresolved("JSONC configuration requires a supported format adapter");
|
|
20414
|
-
for (const root2 of [directory,
|
|
20600
|
+
for (const root2 of [directory, join32(home, ".claude"), join32(home, ".agents")])
|
|
20415
20601
|
for (const name of ["skill", "skills"])
|
|
20416
|
-
roots.add(
|
|
20602
|
+
roots.add(canonical7(join32(root2, name)));
|
|
20417
20603
|
if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
|
|
20418
20604
|
unresolved("skills must be a string array");
|
|
20419
20605
|
for (const value of config.skills ?? []) {
|
|
20420
20606
|
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute18(value) || value.startsWith("~/")))
|
|
20421
20607
|
unresolved("remote or relative skill sources need a dedicated discovery adapter");
|
|
20422
|
-
roots.add(
|
|
20608
|
+
roots.add(canonical7(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
|
|
20423
20609
|
}
|
|
20424
20610
|
for (const key of ["plugin", "plugins"])
|
|
20425
20611
|
if (config[key] !== undefined && !Array.isArray(config[key]))
|
|
20426
20612
|
unresolved(`${key} must be an array`);
|
|
20427
|
-
const plugins = [
|
|
20613
|
+
const plugins = [join32(directory, "plugin"), join32(directory, "plugins")];
|
|
20428
20614
|
for (const path of plugins) {
|
|
20429
20615
|
safe4(path);
|
|
20430
20616
|
if (lstatSync24(path, { throwIfNoEntry: false })?.isDirectory())
|
|
20431
20617
|
for (const name of readdirSync10(path).sort()) {
|
|
20432
20618
|
if (name === "skills-cli.js" && path === plugins[1])
|
|
20433
20619
|
continue;
|
|
20434
|
-
const target =
|
|
20620
|
+
const target = join32(path, name);
|
|
20435
20621
|
if (!lstatSync24(target).isFile())
|
|
20436
20622
|
unresolved("local plugin packages need a dedicated discovery adapter");
|
|
20437
20623
|
witness(target);
|
|
@@ -20444,7 +20630,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20444
20630
|
}
|
|
20445
20631
|
}
|
|
20446
20632
|
if (agent === "hermes") {
|
|
20447
|
-
const profile = witness(
|
|
20633
|
+
const profile = witness(join32(home, ".hermes/active_profile"));
|
|
20448
20634
|
if (profile?.trim() && profile.trim() !== "default")
|
|
20449
20635
|
unresolved("a non-default profile is active");
|
|
20450
20636
|
const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
|
|
@@ -20458,7 +20644,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20458
20644
|
continue;
|
|
20459
20645
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
20460
20646
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
20461
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ?
|
|
20647
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join32(home, normalized.slice(2)) : resolve18(home, ".hermes", normalized);
|
|
20462
20648
|
safe4(path);
|
|
20463
20649
|
roots.add(path);
|
|
20464
20650
|
}
|
|
@@ -20468,7 +20654,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20468
20654
|
if (executable2) {
|
|
20469
20655
|
let packageRoot;
|
|
20470
20656
|
for (let directory = dirname19(realpathSync10(executable2)), depth = 0;depth < 8; directory = dirname19(directory), depth++) {
|
|
20471
|
-
const path =
|
|
20657
|
+
const path = join32(directory, "package.json"), raw = read2(path);
|
|
20472
20658
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
20473
20659
|
packageRoot = directory;
|
|
20474
20660
|
witness(path);
|
|
@@ -20479,12 +20665,12 @@ function resolveAgentDiscovery(options) {
|
|
|
20479
20665
|
}
|
|
20480
20666
|
if (!packageRoot)
|
|
20481
20667
|
unresolved("installed Gemini package layout is not recognized");
|
|
20482
|
-
const builtinRoot =
|
|
20668
|
+
const builtinRoot = join32(packageRoot, "bundle/builtin");
|
|
20483
20669
|
safe4(builtinRoot);
|
|
20484
20670
|
if (!lstatSync24(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
20485
20671
|
unresolved("installed Gemini builtin root is missing");
|
|
20486
20672
|
for (const name of readdirSync10(builtinRoot).sort()) {
|
|
20487
|
-
const text2 = witness(
|
|
20673
|
+
const text2 = witness(join32(builtinRoot, name, "SKILL.md"));
|
|
20488
20674
|
const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
20489
20675
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
20490
20676
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
@@ -20497,18 +20683,15 @@ function resolveAgentDiscovery(options) {
|
|
|
20497
20683
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
20498
20684
|
unresolved("invalid retained review");
|
|
20499
20685
|
verifyAgentDiscovery(retained);
|
|
20500
|
-
if (!retained.sources.some((source) => source.path ===
|
|
20686
|
+
if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
|
|
20501
20687
|
unresolved("retained review is missing the full agent configuration source");
|
|
20502
|
-
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !==
|
|
20688
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
20503
20689
|
unresolved("retained settings witness names another configuration source");
|
|
20504
|
-
const covered = supersedesSettingsProjection(agent, retained.sources,
|
|
20690
|
+
const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
|
|
20505
20691
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
20506
20692
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
20507
20693
|
return retained;
|
|
20508
20694
|
}
|
|
20509
|
-
if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
|
|
20510
|
-
throw new Error("Invalid --discovery-inputs version or agents");
|
|
20511
|
-
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
20512
20695
|
if (review) {
|
|
20513
20696
|
if (review.pluginHooks !== "reviewed-no-skill-injection" || !Array.isArray(review.sources) || !review.sources.length || !Array.isArray(review.roots))
|
|
20514
20697
|
throw new Error("Discovery review must bind sources and confirm plugin hooks do not inject retired skills");
|
|
@@ -20516,47 +20699,77 @@ function resolveAgentDiscovery(options) {
|
|
|
20516
20699
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
20517
20700
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
20518
20701
|
verifyAgentDiscovery(supplied);
|
|
20519
|
-
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !==
|
|
20702
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
|
|
20520
20703
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
20521
|
-
if (!review.sources.some((source) => source.path ===
|
|
20704
|
+
if (!review.sources.some((source) => source.path === canonical7(configPath)))
|
|
20522
20705
|
throw new Error("Discovery review must include the agent configuration source");
|
|
20523
|
-
const reviewedSources = supersedesSettingsProjection(agent, review.sources,
|
|
20524
|
-
|
|
20706
|
+
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
|
|
20707
|
+
const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
|
|
20708
|
+
if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json")))
|
|
20709
|
+
return source;
|
|
20710
|
+
const pluginRoot = dirname19(dirname19(source.path)), hooksPath = join32(pluginRoot, "hooks/hooks.json");
|
|
20711
|
+
const hookStat = lstatSync24(hooksPath, { throwIfNoEntry: false });
|
|
20712
|
+
const text2 = read2(source.path);
|
|
20713
|
+
if (text2 === null)
|
|
20714
|
+
throw new Error("Reviewed Claude plugin manifest is missing");
|
|
20715
|
+
const manifestSha256 = projectReviewedClaudePluginManifest(text2, source.sha256 ?? "");
|
|
20716
|
+
const manifest = JSON.parse(text2);
|
|
20717
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
20718
|
+
throw new Error("Reviewed Claude plugin manifest must be an object");
|
|
20719
|
+
const reviewedHookPaths = new Set;
|
|
20720
|
+
if (hookStat)
|
|
20721
|
+
reviewedHookPaths.add(hooksPath);
|
|
20722
|
+
if (manifest.hooks !== undefined) {
|
|
20723
|
+
if (typeof manifest.hooks !== "string")
|
|
20724
|
+
throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
|
|
20725
|
+
const declaredHookPath = resolve18(pluginRoot, manifest.hooks);
|
|
20726
|
+
if (!declaredHookPath.startsWith(pluginRoot + sep8))
|
|
20727
|
+
throw new Error("Reviewed Claude plugin hooks target escapes its root");
|
|
20728
|
+
reviewedHookPaths.add(declaredHookPath);
|
|
20729
|
+
}
|
|
20730
|
+
for (const hooksFile of reviewedHookPaths)
|
|
20731
|
+
if (!review.sources.some((hook) => hook.path === canonical7(hooksFile) && hook.sha256 !== null && hook.format === undefined && hook.fields === undefined && hook.managedPlugins === undefined && (hook.hashMode === undefined || hook.hashMode === "bytes")))
|
|
20732
|
+
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
20733
|
+
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
20734
|
+
});
|
|
20735
|
+
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
20736
|
+
const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual7(candidate2, source)) === index);
|
|
20737
|
+
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
20525
20738
|
}
|
|
20526
20739
|
function plugin(root2) {
|
|
20527
|
-
root2 =
|
|
20740
|
+
root2 = canonical7(root2);
|
|
20528
20741
|
safe4(root2);
|
|
20529
|
-
const manifestPath =
|
|
20530
|
-
const raw = witness(manifestPath);
|
|
20742
|
+
const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
20743
|
+
const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
|
|
20531
20744
|
if (raw === null)
|
|
20532
20745
|
unresolved("plugin manifest missing");
|
|
20533
20746
|
const manifest = parseConfig(raw, manifestPath);
|
|
20534
|
-
const hooks = witness(
|
|
20747
|
+
const hooks = witness(join32(root2, "hooks/hooks.json"));
|
|
20535
20748
|
if (manifest.hooks !== undefined || hooks !== null)
|
|
20536
20749
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
20537
|
-
const commands =
|
|
20750
|
+
const commands = join32(root2, "commands");
|
|
20538
20751
|
safe4(commands);
|
|
20539
20752
|
if (agent === "claude" && (manifest.commands !== undefined || lstatSync24(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length))
|
|
20540
20753
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
20541
|
-
const rootSkill = witness(
|
|
20754
|
+
const rootSkill = witness(join32(root2, "SKILL.md"));
|
|
20542
20755
|
if (rootSkill !== null)
|
|
20543
20756
|
roots.add(root2);
|
|
20544
20757
|
const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
20545
20758
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
20546
20759
|
unresolved("unsupported plugin skill paths");
|
|
20547
20760
|
if (agent === "claude")
|
|
20548
|
-
roots.add(
|
|
20761
|
+
roots.add(join32(root2, "skills"));
|
|
20549
20762
|
for (const value of declared) {
|
|
20550
20763
|
const path = resolve18(root2, value);
|
|
20551
|
-
if (path !== root2 && !path.startsWith(root2 +
|
|
20764
|
+
if (path !== root2 && !path.startsWith(root2 + sep8))
|
|
20552
20765
|
unresolved("plugin skill path escapes its root");
|
|
20553
|
-
roots.add(path.endsWith(`${
|
|
20766
|
+
roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname19(path) : path);
|
|
20554
20767
|
}
|
|
20555
20768
|
}
|
|
20556
20769
|
if (agent === "claude") {
|
|
20557
|
-
const registrations = witness(
|
|
20558
|
-
const installed = registrations === null ? {} : parseConfig(registrations,
|
|
20559
|
-
const commands =
|
|
20770
|
+
const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
|
|
20771
|
+
const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
20772
|
+
const commands = canonical7(join32(home, ".claude/commands"));
|
|
20560
20773
|
safe4(commands);
|
|
20561
20774
|
if (lstatSync24(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
20562
20775
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -20580,16 +20793,16 @@ function resolveAgentDiscovery(options) {
|
|
|
20580
20793
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
20581
20794
|
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute18(market.source))
|
|
20582
20795
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
20583
|
-
const catalogText = witness(
|
|
20796
|
+
const catalogText = witness(join32(market.source, ".agents/plugins/marketplace.json"));
|
|
20584
20797
|
if (catalogText === null)
|
|
20585
20798
|
unresolved("marketplace catalog missing");
|
|
20586
|
-
const catalog = parseConfig(catalogText,
|
|
20799
|
+
const catalog = parseConfig(catalogText, join32(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
20587
20800
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
20588
20801
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
20589
20802
|
plugin(resolve18(market.source, matches[0].source.path));
|
|
20590
20803
|
}
|
|
20591
20804
|
} else if (agent === "opencode") {
|
|
20592
|
-
if (witness(
|
|
20805
|
+
if (witness(join32(home, ".config/opencode/opencode.jsonc")) !== null || witness(join32(home, ".config/opencode/config.json")) !== null)
|
|
20593
20806
|
unresolved("additional config layers require review");
|
|
20594
20807
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
20595
20808
|
unresolved("external plugin or remote skill source");
|
|
@@ -20598,15 +20811,15 @@ function resolveAgentDiscovery(options) {
|
|
|
20598
20811
|
unresolved("relative or malformed added skill root");
|
|
20599
20812
|
roots.add(path);
|
|
20600
20813
|
}
|
|
20601
|
-
const localPlugins =
|
|
20814
|
+
const localPlugins = join32(home, ".config/opencode/plugins");
|
|
20602
20815
|
safe4(localPlugins);
|
|
20603
20816
|
if (lstatSync24(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
20604
20817
|
unresolved("additional local plugin hooks require review");
|
|
20605
20818
|
} else if (agent === "sumi") {
|
|
20606
|
-
if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !==
|
|
20819
|
+
if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical7(configPath) && source.path !== canonical7(join32(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
|
|
20607
20820
|
unresolved("external or foreign local plugins require review");
|
|
20608
20821
|
} else if (agent === "hermes") {
|
|
20609
|
-
for (const directory of [
|
|
20822
|
+
for (const directory of [join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")]) {
|
|
20610
20823
|
safe4(directory);
|
|
20611
20824
|
if (lstatSync24(directory, { throwIfNoEntry: false }))
|
|
20612
20825
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
@@ -20616,30 +20829,30 @@ function resolveAgentDiscovery(options) {
|
|
|
20616
20829
|
} else if (agent === "gemini") {
|
|
20617
20830
|
if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
|
|
20618
20831
|
unresolved("custom extension or skill paths");
|
|
20619
|
-
const directory =
|
|
20832
|
+
const directory = join32(home, ".gemini/extensions");
|
|
20620
20833
|
safe4(directory);
|
|
20621
20834
|
if (lstatSync24(directory, { throwIfNoEntry: false }))
|
|
20622
20835
|
for (const name of readdirSync10(directory)) {
|
|
20623
|
-
const root2 =
|
|
20836
|
+
const root2 = join32(directory, name);
|
|
20624
20837
|
safe4(root2);
|
|
20625
20838
|
if (!lstatSync24(root2)?.isDirectory())
|
|
20626
20839
|
continue;
|
|
20627
|
-
const raw = witness(
|
|
20840
|
+
const raw = witness(join32(root2, "gemini-extension.json"));
|
|
20628
20841
|
if (raw === null)
|
|
20629
20842
|
unresolved("extension registration missing");
|
|
20630
|
-
const manifest = parseConfig(raw,
|
|
20631
|
-
if (manifest.hooks !== undefined || witness(
|
|
20843
|
+
const manifest = parseConfig(raw, join32(root2, "gemini-extension.json"));
|
|
20844
|
+
if (manifest.hooks !== undefined || witness(join32(root2, "hooks/hooks.json")) !== null || witness(join32(root2, ".gemini-extension-install.json")) !== null)
|
|
20632
20845
|
unresolved("extension hooks or linked registration require review");
|
|
20633
|
-
roots.add(
|
|
20846
|
+
roots.add(join32(root2, "skills"));
|
|
20634
20847
|
}
|
|
20635
20848
|
}
|
|
20636
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([
|
|
20849
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) => canonical7(path))) } : {} };
|
|
20637
20850
|
}
|
|
20638
20851
|
|
|
20639
20852
|
// src/lib/authoring-path.ts
|
|
20640
20853
|
var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
|
|
20641
20854
|
function pathParts(path) {
|
|
20642
|
-
return resolve19(path).split(
|
|
20855
|
+
return resolve19(path).split(sep9).filter(Boolean);
|
|
20643
20856
|
}
|
|
20644
20857
|
function nativeRootMatch(path) {
|
|
20645
20858
|
const parts = pathParts(path);
|
|
@@ -20665,10 +20878,10 @@ function physicalPath(path) {
|
|
|
20665
20878
|
return resolve19(realpathSync11(cursor), ...suffix);
|
|
20666
20879
|
}
|
|
20667
20880
|
function contains(root2, path) {
|
|
20668
|
-
return path === root2 || path.startsWith(root2 +
|
|
20881
|
+
return path === root2 || path.startsWith(root2 + sep9);
|
|
20669
20882
|
}
|
|
20670
20883
|
function configuredRoots(home, explicitHome) {
|
|
20671
|
-
const homeData =
|
|
20884
|
+
const homeData = join33(home, ".hasna", "skills");
|
|
20672
20885
|
const homePolicy = readManagedSkillPolicySnapshot(homeData);
|
|
20673
20886
|
const configuredPolicy = !explicitHome && resolve19(getDataDirReadOnly()) !== resolve19(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
|
|
20674
20887
|
const policy = homePolicy ?? configuredPolicy;
|
|
@@ -20699,7 +20912,7 @@ function configuredRoots(home, explicitHome) {
|
|
|
20699
20912
|
admitBinding(binding);
|
|
20700
20913
|
continue;
|
|
20701
20914
|
}
|
|
20702
|
-
const nativeHome =
|
|
20915
|
+
const nativeHome = join33(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
|
|
20703
20916
|
if (!lstatSync25(nativeHome, { throwIfNoEntry: false }))
|
|
20704
20917
|
continue;
|
|
20705
20918
|
roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
|
|
@@ -20714,8 +20927,8 @@ function assertPortableAuthoringPath(path, options = {}) {
|
|
|
20714
20927
|
const candidates = [lexical, physical];
|
|
20715
20928
|
let native = candidates.map(nativeRootMatch).find(Boolean);
|
|
20716
20929
|
const home = resolve19(options.homeDir ?? homedir6());
|
|
20717
|
-
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) =>
|
|
20718
|
-
defaults.push(
|
|
20930
|
+
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join33(home, ...parts));
|
|
20931
|
+
defaults.push(join33(home, ".codex/plugins/cache"), join33(home, ".claude/plugins/cache"), join33(home, ".claude/plugins/synced"), join33(home, ".gemini/extensions"));
|
|
20719
20932
|
for (const root2 of defaults) {
|
|
20720
20933
|
if (candidates.some((candidate2) => contains(resolve19(root2), candidate2) || contains(physicalPath(root2), candidate2)))
|
|
20721
20934
|
native ??= root2;
|
|
@@ -20736,15 +20949,15 @@ function getPortableSkillsRoot(options = {}) {
|
|
|
20736
20949
|
function resolvePortableSkillsRoot(options, migrate) {
|
|
20737
20950
|
if (options.rootDir)
|
|
20738
20951
|
return options.rootDir;
|
|
20739
|
-
const appDir = options.homeDir ?
|
|
20740
|
-
const cache =
|
|
20952
|
+
const appDir = options.homeDir ? join34(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
|
|
20953
|
+
const cache = join34(appDir, SKILLS_CACHE_DIRNAME);
|
|
20741
20954
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
|
|
20742
20955
|
return cache;
|
|
20743
|
-
const installed =
|
|
20956
|
+
const installed = join34(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
20744
20957
|
return installed;
|
|
20745
20958
|
}
|
|
20746
20959
|
function getPortableSkillPath(name, options = {}) {
|
|
20747
|
-
return
|
|
20960
|
+
return join34(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
20748
20961
|
}
|
|
20749
20962
|
function findPortableSkill(name, options = {}) {
|
|
20750
20963
|
let normalized;
|
|
@@ -20770,7 +20983,7 @@ function listPortableSkills(options = {}) {
|
|
|
20770
20983
|
for (const entry of readdirSync11(root2).sort()) {
|
|
20771
20984
|
if (entry.startsWith("."))
|
|
20772
20985
|
continue;
|
|
20773
|
-
const path =
|
|
20986
|
+
const path = join34(root2, entry);
|
|
20774
20987
|
if (!safeIsDirectory(path))
|
|
20775
20988
|
continue;
|
|
20776
20989
|
try {
|
|
@@ -20804,9 +21017,9 @@ function isOfficialSkillName(name) {
|
|
|
20804
21017
|
function scaffoldPortableSkill(name, options = {}) {
|
|
20805
21018
|
const skillName = normalizeNewPortableSkillName(name);
|
|
20806
21019
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
20807
|
-
assertPortableAuthoringPath(
|
|
21020
|
+
assertPortableAuthoringPath(join34(readOnlyRoot, skillName), options);
|
|
20808
21021
|
const root2 = getPortableSkillsRoot(options);
|
|
20809
|
-
const skillPath =
|
|
21022
|
+
const skillPath = join34(root2, skillName);
|
|
20810
21023
|
assertPortableAuthoringPath(skillPath, options);
|
|
20811
21024
|
if (existsSync14(skillPath)) {
|
|
20812
21025
|
if (!options.overwrite)
|
|
@@ -20840,7 +21053,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
20840
21053
|
const skipped = [];
|
|
20841
21054
|
const entries = readdirSync11(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
|
|
20842
21055
|
for (const entryName of entries) {
|
|
20843
|
-
const childPath =
|
|
21056
|
+
const childPath = join34(absoluteSource, entryName);
|
|
20844
21057
|
if (!safeIsDirectory(childPath))
|
|
20845
21058
|
continue;
|
|
20846
21059
|
if (!isSkillCandidate(childPath)) {
|
|
@@ -20869,7 +21082,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
20869
21082
|
};
|
|
20870
21083
|
}
|
|
20871
21084
|
function isSkillCandidate(dir) {
|
|
20872
|
-
return existsSync14(
|
|
21085
|
+
return existsSync14(join34(dir, "SKILL.md")) || existsSync14(join34(dir, "skill.json")) || existsSync14(join34(dir, "package.json"));
|
|
20873
21086
|
}
|
|
20874
21087
|
function physicalImportPath(path) {
|
|
20875
21088
|
let ancestor = resolve20(path);
|
|
@@ -20878,14 +21091,14 @@ function physicalImportPath(path) {
|
|
|
20878
21091
|
suffix.unshift(basename12(ancestor));
|
|
20879
21092
|
ancestor = dirname21(ancestor);
|
|
20880
21093
|
}
|
|
20881
|
-
return
|
|
21094
|
+
return join34(realpathSync12(ancestor), ...suffix);
|
|
20882
21095
|
}
|
|
20883
21096
|
function assertDisjointImportPaths(source, destination) {
|
|
20884
21097
|
const sourcePath = realpathSync12(source);
|
|
20885
21098
|
const destinationPath = physicalImportPath(destination);
|
|
20886
21099
|
const contains2 = (parent, child) => {
|
|
20887
21100
|
const path = relative7(parent, child);
|
|
20888
|
-
return path === "" || !isAbsolute19(path) && path !== ".." && !path.startsWith(`..${
|
|
21101
|
+
return path === "" || !isAbsolute19(path) && path !== ".." && !path.startsWith(`..${sep10}`);
|
|
20889
21102
|
};
|
|
20890
21103
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
20891
21104
|
throw new Error("Source and destination skill directories must not overlap");
|
|
@@ -20904,11 +21117,11 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
20904
21117
|
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename12(absoluteSource)}')` : ""} matches a bundled official skill.`;
|
|
20905
21118
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
20906
21119
|
}
|
|
20907
|
-
const readOnlyDestination =
|
|
21120
|
+
const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
|
|
20908
21121
|
assertPortableAuthoringPath(readOnlyDestination, options);
|
|
20909
21122
|
assertDisjointImportPaths(absoluteSource, readOnlyDestination);
|
|
20910
21123
|
const root2 = getPortableSkillsRoot(options);
|
|
20911
|
-
const destination =
|
|
21124
|
+
const destination = join34(root2, skillName);
|
|
20912
21125
|
assertPortableAuthoringPath(destination, options);
|
|
20913
21126
|
assertDisjointImportPaths(absoluteSource, destination);
|
|
20914
21127
|
if (existsSync14(destination)) {
|
|
@@ -20953,13 +21166,13 @@ function buildCorpusManifest(input, name) {
|
|
|
20953
21166
|
function writeCorpusSkill(input, options = {}) {
|
|
20954
21167
|
const name = normalizePortableSkillName(input.name);
|
|
20955
21168
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
20956
|
-
assertPortableAuthoringPath(
|
|
21169
|
+
assertPortableAuthoringPath(join34(readOnlyRoot, name), options);
|
|
20957
21170
|
const root2 = getPortableSkillsRoot(options);
|
|
20958
|
-
const skillPath =
|
|
21171
|
+
const skillPath = join34(root2, name);
|
|
20959
21172
|
assertPortableAuthoringPath(skillPath, options);
|
|
20960
21173
|
const created = !existsSync14(skillPath);
|
|
20961
21174
|
mkdirSync10(skillPath, { recursive: true });
|
|
20962
|
-
writeFileSync9(
|
|
21175
|
+
writeFileSync9(join34(skillPath, "SKILL.md"), input.skillMd);
|
|
20963
21176
|
const manifest = buildCorpusManifest(input, name);
|
|
20964
21177
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
20965
21178
|
return { name, path: skillPath, manifest, created };
|
|
@@ -20967,22 +21180,22 @@ function writeCorpusSkill(input, options = {}) {
|
|
|
20967
21180
|
function installCorpusSkillAtomically(input, options = {}) {
|
|
20968
21181
|
const name = normalizePortableSkillName(input.name);
|
|
20969
21182
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
20970
|
-
assertPortableAuthoringPath(
|
|
21183
|
+
assertPortableAuthoringPath(join34(readOnlyRoot, name), options);
|
|
20971
21184
|
const root2 = getPortableSkillsRoot(options);
|
|
20972
|
-
const target =
|
|
21185
|
+
const target = join34(root2, name);
|
|
20973
21186
|
assertPortableAuthoringPath(target, options);
|
|
20974
21187
|
const created = !existsSync14(target);
|
|
20975
21188
|
mkdirSync10(root2, { recursive: true });
|
|
20976
|
-
const staging = mkdtempSync2(
|
|
21189
|
+
const staging = mkdtempSync2(join34(root2, `.pull-${name}-`));
|
|
20977
21190
|
let moved = false;
|
|
20978
21191
|
let backup = null;
|
|
20979
21192
|
try {
|
|
20980
|
-
writeFileSync9(
|
|
21193
|
+
writeFileSync9(join34(staging, "SKILL.md"), input.skillMd);
|
|
20981
21194
|
const manifest = buildCorpusManifest(input, name);
|
|
20982
21195
|
writeSkillJsonWithHash(staging, manifest);
|
|
20983
21196
|
if (existsSync14(target)) {
|
|
20984
|
-
backup = mkdtempSync2(
|
|
20985
|
-
renameSync6(target,
|
|
21197
|
+
backup = mkdtempSync2(join34(root2, `.pull-backup-${name}-`));
|
|
21198
|
+
renameSync6(target, join34(backup, name));
|
|
20986
21199
|
moved = true;
|
|
20987
21200
|
}
|
|
20988
21201
|
renameSync6(staging, target);
|
|
@@ -20991,9 +21204,9 @@ function installCorpusSkillAtomically(input, options = {}) {
|
|
|
20991
21204
|
return { name, path: target, manifest, created };
|
|
20992
21205
|
} catch (error) {
|
|
20993
21206
|
rmSync4(staging, { recursive: true, force: true });
|
|
20994
|
-
if (moved && backup && existsSync14(
|
|
21207
|
+
if (moved && backup && existsSync14(join34(backup, name))) {
|
|
20995
21208
|
try {
|
|
20996
|
-
renameSync6(
|
|
21209
|
+
renameSync6(join34(backup, name), target);
|
|
20997
21210
|
} catch {}
|
|
20998
21211
|
}
|
|
20999
21212
|
throw error;
|
|
@@ -21006,8 +21219,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
21006
21219
|
const warnings = [...base.warnings];
|
|
21007
21220
|
let manifest;
|
|
21008
21221
|
if (existsSync14(skillPath)) {
|
|
21009
|
-
const skillJsonPath =
|
|
21010
|
-
const skillMdPath =
|
|
21222
|
+
const skillJsonPath = join34(skillPath, "skill.json");
|
|
21223
|
+
const skillMdPath = join34(skillPath, "SKILL.md");
|
|
21011
21224
|
if (!existsSync14(skillJsonPath) && !existsSync14(skillMdPath)) {
|
|
21012
21225
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
21013
21226
|
}
|
|
@@ -21027,7 +21240,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
21027
21240
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
21028
21241
|
}
|
|
21029
21242
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
21030
|
-
strict: existsSync14(
|
|
21243
|
+
strict: existsSync14(join34(skillPath, "skill.json")),
|
|
21031
21244
|
skillPath
|
|
21032
21245
|
});
|
|
21033
21246
|
for (const issue of contractIssues)
|
|
@@ -21067,7 +21280,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
21067
21280
|
add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
21068
21281
|
continue;
|
|
21069
21282
|
}
|
|
21070
|
-
const entryPath =
|
|
21283
|
+
const entryPath = join34(skillPath, command.entry);
|
|
21071
21284
|
if (!existsSync14(entryPath))
|
|
21072
21285
|
add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
21073
21286
|
else if (statSync11(entryPath).isDirectory())
|
|
@@ -21078,7 +21291,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
21078
21291
|
} catch (error) {
|
|
21079
21292
|
add3(issues, "portable.manifest_invalid", error.message);
|
|
21080
21293
|
}
|
|
21081
|
-
if (manifest?.kind !== "instruction" && !existsSync14(
|
|
21294
|
+
if (manifest?.kind !== "instruction" && !existsSync14(join34(skillPath, "AGENTS.md"))) {
|
|
21082
21295
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
21083
21296
|
}
|
|
21084
21297
|
}
|
|
@@ -21114,12 +21327,12 @@ async function runPortableSkill(name, args, options = {}) {
|
|
|
21114
21327
|
if (!isSafeRelativePath2(command.entry)) {
|
|
21115
21328
|
return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
|
|
21116
21329
|
}
|
|
21117
|
-
const entryPath =
|
|
21330
|
+
const entryPath = join34(skill.path, command.entry);
|
|
21118
21331
|
if (!existsSync14(entryPath)) {
|
|
21119
21332
|
return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
|
|
21120
21333
|
}
|
|
21121
|
-
const pkgPath =
|
|
21122
|
-
const nodeModules =
|
|
21334
|
+
const pkgPath = join34(skill.path, "package.json");
|
|
21335
|
+
const nodeModules = join34(skill.path, "node_modules");
|
|
21123
21336
|
if (existsSync14(pkgPath) && !existsSync14(nodeModules) && hasPackageDependencies(pkgPath)) {
|
|
21124
21337
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
21125
21338
|
cwd: skill.path,
|
|
@@ -21376,7 +21589,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
21376
21589
|
for (const entry of entries) {
|
|
21377
21590
|
if (!entry.isDirectory())
|
|
21378
21591
|
continue;
|
|
21379
|
-
const skillMdPath =
|
|
21592
|
+
const skillMdPath = join35(dir, entry.name, "SKILL.md");
|
|
21380
21593
|
if (!existsSync15(skillMdPath))
|
|
21381
21594
|
continue;
|
|
21382
21595
|
let content;
|
|
@@ -21396,7 +21609,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
21396
21609
|
category: fm.category || "Development Tools",
|
|
21397
21610
|
tags: fm.tags || [],
|
|
21398
21611
|
...fm.kind ? { kind: fm.kind } : {},
|
|
21399
|
-
...isHostedMetadataSkillDir(
|
|
21612
|
+
...isHostedMetadataSkillDir(join35(dir, entry.name)) ? { serverOwned: true } : {},
|
|
21400
21613
|
source
|
|
21401
21614
|
});
|
|
21402
21615
|
}
|
|
@@ -21412,8 +21625,8 @@ function findExtensionSkillPath(name) {
|
|
|
21412
21625
|
for (const entry of entries) {
|
|
21413
21626
|
if (!entry.isDirectory())
|
|
21414
21627
|
continue;
|
|
21415
|
-
const skillDir =
|
|
21416
|
-
const skillMdPath =
|
|
21628
|
+
const skillDir = join35(config.extensionsDir, entry.name);
|
|
21629
|
+
const skillMdPath = join35(skillDir, "SKILL.md");
|
|
21417
21630
|
if (!existsSync15(skillMdPath))
|
|
21418
21631
|
continue;
|
|
21419
21632
|
let content;
|
|
@@ -21482,21 +21695,21 @@ function getAllTags(catalog) {
|
|
|
21482
21695
|
return Array.from(tagSet).sort();
|
|
21483
21696
|
}
|
|
21484
21697
|
// src/lib/run-state.ts
|
|
21485
|
-
import { createHash as
|
|
21698
|
+
import { createHash as createHash24, randomBytes } from "crypto";
|
|
21486
21699
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync21, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
21487
|
-
import { extname, join as
|
|
21700
|
+
import { extname, join as join36, relative as relative8 } from "path";
|
|
21488
21701
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
21489
21702
|
const now = new Date;
|
|
21490
21703
|
const id = createRunId(now);
|
|
21491
21704
|
const day = now.toISOString().slice(0, 10);
|
|
21492
21705
|
const skillName = normalizeSkillName(params.skill);
|
|
21493
21706
|
const root2 = getProjectStateDir(targetDir);
|
|
21494
|
-
const runDir =
|
|
21495
|
-
const logsDir =
|
|
21496
|
-
const exportDir =
|
|
21707
|
+
const runDir = join36(root2, "runs", day, id);
|
|
21708
|
+
const logsDir = join36(runDir, "logs");
|
|
21709
|
+
const exportDir = join36(root2, "exports", skillName, id);
|
|
21497
21710
|
mkdirSync11(logsDir, { recursive: true });
|
|
21498
21711
|
mkdirSync11(exportDir, { recursive: true });
|
|
21499
|
-
mkdirSync11(
|
|
21712
|
+
mkdirSync11(join36(root2, "tmp"), { recursive: true });
|
|
21500
21713
|
const record3 = {
|
|
21501
21714
|
id,
|
|
21502
21715
|
skill: skillName,
|
|
@@ -21548,27 +21761,27 @@ function updateSkillRun(context, patch) {
|
|
|
21548
21761
|
return context.record;
|
|
21549
21762
|
}
|
|
21550
21763
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
21551
|
-
writeFileSync10(
|
|
21552
|
-
writeFileSync10(
|
|
21764
|
+
writeFileSync10(join36(context.logsDir, "stdout.log"), stdout);
|
|
21765
|
+
writeFileSync10(join36(context.logsDir, "stderr.log"), stderr);
|
|
21553
21766
|
}
|
|
21554
21767
|
function appendRunEvent(context, event, data = {}) {
|
|
21555
21768
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
21556
21769
|
`;
|
|
21557
|
-
const path =
|
|
21770
|
+
const path = join36(context.runDir, "events.ndjson");
|
|
21558
21771
|
const previous = existsSync16(path) ? readFileSync21(path, "utf-8") : "";
|
|
21559
21772
|
writeFileSync10(path, previous + line);
|
|
21560
21773
|
}
|
|
21561
21774
|
function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
21562
|
-
const runsRoot =
|
|
21775
|
+
const runsRoot = join36(getProjectStateDir(targetDir), "runs");
|
|
21563
21776
|
if (!existsSync16(runsRoot))
|
|
21564
21777
|
return [];
|
|
21565
21778
|
const records = [];
|
|
21566
21779
|
for (const day of readdirSync13(runsRoot).sort().reverse()) {
|
|
21567
|
-
const dayDir =
|
|
21780
|
+
const dayDir = join36(runsRoot, day);
|
|
21568
21781
|
if (!statSync12(dayDir).isDirectory())
|
|
21569
21782
|
continue;
|
|
21570
21783
|
for (const runId of readdirSync13(dayDir).sort().reverse()) {
|
|
21571
|
-
const record3 = readRunRecord(
|
|
21784
|
+
const record3 = readRunRecord(join36(dayDir, runId));
|
|
21572
21785
|
if (record3)
|
|
21573
21786
|
records.push(record3);
|
|
21574
21787
|
if (records.length >= limit)
|
|
@@ -21578,25 +21791,25 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
|
21578
21791
|
return records;
|
|
21579
21792
|
}
|
|
21580
21793
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
21581
|
-
const runsRoot =
|
|
21794
|
+
const runsRoot = join36(getProjectStateDir(targetDir), "runs");
|
|
21582
21795
|
if (!existsSync16(runsRoot))
|
|
21583
21796
|
return null;
|
|
21584
21797
|
for (const day of readdirSync13(runsRoot)) {
|
|
21585
|
-
const record3 = readRunRecord(
|
|
21798
|
+
const record3 = readRunRecord(join36(runsRoot, day, runId));
|
|
21586
21799
|
if (record3)
|
|
21587
21800
|
return record3;
|
|
21588
21801
|
}
|
|
21589
21802
|
return null;
|
|
21590
21803
|
}
|
|
21591
21804
|
function getRunExportDir(runId, skill, targetDir = process.cwd()) {
|
|
21592
|
-
return
|
|
21805
|
+
return join36(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
|
|
21593
21806
|
}
|
|
21594
21807
|
function writeRunRecord(context) {
|
|
21595
|
-
writeFileSync10(
|
|
21808
|
+
writeFileSync10(join36(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
21596
21809
|
`);
|
|
21597
21810
|
}
|
|
21598
21811
|
function writeArtifactsManifest(context, artifacts) {
|
|
21599
|
-
writeFileSync10(
|
|
21812
|
+
writeFileSync10(join36(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
21600
21813
|
`);
|
|
21601
21814
|
}
|
|
21602
21815
|
function collectRunArtifacts(context) {
|
|
@@ -21609,14 +21822,14 @@ function collectRunArtifacts(context) {
|
|
|
21609
21822
|
artifacts.push({
|
|
21610
21823
|
path: toProjectRelative(context.targetDir, path),
|
|
21611
21824
|
mime: mimeForPath(path),
|
|
21612
|
-
sha256:
|
|
21825
|
+
sha256: createHash24("sha256").update(bytes).digest("hex"),
|
|
21613
21826
|
sizeBytes: stat2.size
|
|
21614
21827
|
});
|
|
21615
21828
|
}
|
|
21616
21829
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
21617
21830
|
}
|
|
21618
21831
|
function readRunRecord(runDir) {
|
|
21619
|
-
const path =
|
|
21832
|
+
const path = join36(runDir, "run.json");
|
|
21620
21833
|
if (!existsSync16(path))
|
|
21621
21834
|
return null;
|
|
21622
21835
|
try {
|
|
@@ -21628,7 +21841,7 @@ function readRunRecord(runDir) {
|
|
|
21628
21841
|
function walkFiles(dir) {
|
|
21629
21842
|
const files = [];
|
|
21630
21843
|
for (const entry of readdirSync13(dir)) {
|
|
21631
|
-
const full =
|
|
21844
|
+
const full = join36(dir, entry);
|
|
21632
21845
|
if (statSync12(full).isDirectory())
|
|
21633
21846
|
files.push(...walkFiles(full));
|
|
21634
21847
|
else
|
|
@@ -21737,7 +21950,7 @@ __export(exports_external, {
|
|
|
21737
21950
|
pipe: () => pipe,
|
|
21738
21951
|
partialRecord: () => partialRecord,
|
|
21739
21952
|
parseAsync: () => parseAsync2,
|
|
21740
|
-
parse: () =>
|
|
21953
|
+
parse: () => parse10,
|
|
21741
21954
|
overwrite: () => _overwrite,
|
|
21742
21955
|
output: () => output,
|
|
21743
21956
|
optional: () => optional,
|
|
@@ -21957,7 +22170,7 @@ __export(exports_core2, {
|
|
|
21957
22170
|
prettifyError: () => prettifyError,
|
|
21958
22171
|
parseURLObject: () => parseURLObject,
|
|
21959
22172
|
parseAsync: () => parseAsync,
|
|
21960
|
-
parse: () =>
|
|
22173
|
+
parse: () => parse8,
|
|
21961
22174
|
meta: () => meta,
|
|
21962
22175
|
mergeValues: () => mergeValues,
|
|
21963
22176
|
memoizer: () => memoizer,
|
|
@@ -23411,7 +23624,7 @@ var _parse = (_Err) => {
|
|
|
23411
23624
|
};
|
|
23412
23625
|
return fn;
|
|
23413
23626
|
};
|
|
23414
|
-
var
|
|
23627
|
+
var parse8 = /* @__PURE__ */ _parse($ZodRealError);
|
|
23415
23628
|
var _parseAsync = (_Err) => {
|
|
23416
23629
|
const fn = async (schema, value, _ctx, params) => {
|
|
23417
23630
|
const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
|
|
@@ -23482,18 +23695,18 @@ var validateAsync = async (schema, value, _ctx) => {
|
|
|
23482
23695
|
return result.issues.length === 0;
|
|
23483
23696
|
};
|
|
23484
23697
|
var _encode = (_Err) => {
|
|
23485
|
-
const
|
|
23698
|
+
const parse9 = _parse(_Err);
|
|
23486
23699
|
const fn = (schema, value, _ctx, _params) => {
|
|
23487
23700
|
const ctx = _ctx ? { ..._ctx, direction: "backward" } : { direction: "backward" };
|
|
23488
|
-
return
|
|
23701
|
+
return parse9(schema, value, ctx, finalizeParams(fn, _params));
|
|
23489
23702
|
};
|
|
23490
23703
|
return fn;
|
|
23491
23704
|
};
|
|
23492
23705
|
var encode = /* @__PURE__ */ _encode($ZodRealError);
|
|
23493
23706
|
var _decode = (_Err) => {
|
|
23494
|
-
const
|
|
23707
|
+
const parse9 = _parse(_Err);
|
|
23495
23708
|
const fn = (schema, value, _ctx, _params) => {
|
|
23496
|
-
return
|
|
23709
|
+
return parse9(schema, value, _ctx, finalizeParams(fn, _params));
|
|
23497
23710
|
};
|
|
23498
23711
|
return fn;
|
|
23499
23712
|
};
|
|
@@ -26416,10 +26629,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
|
|
|
26416
26629
|
throw new Error("implement() must be called with a function");
|
|
26417
26630
|
}
|
|
26418
26631
|
return Object.defineProperty(function(...args) {
|
|
26419
|
-
const parsedArgs = inst._def.input ?
|
|
26632
|
+
const parsedArgs = inst._def.input ? parse8(inst._def.input, args) : args;
|
|
26420
26633
|
const result = Reflect.apply(func, this, parsedArgs);
|
|
26421
26634
|
if (inst._def.output) {
|
|
26422
|
-
return
|
|
26635
|
+
return parse8(inst._def.output, result);
|
|
26423
26636
|
}
|
|
26424
26637
|
return result;
|
|
26425
26638
|
}, "_zod", { value: inst._zod, enumerable: false });
|
|
@@ -38057,7 +38270,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, undefi
|
|
|
38057
38270
|
});
|
|
38058
38271
|
|
|
38059
38272
|
// node_modules/zod/v4/classic/parse.js
|
|
38060
|
-
var
|
|
38273
|
+
var parse10 = /* @__PURE__ */ _parse(ZodRealError);
|
|
38061
38274
|
var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
38062
38275
|
var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
38063
38276
|
var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
@@ -38193,7 +38406,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
|
|
38193
38406
|
exports_util.own(this, "~standard", value);
|
|
38194
38407
|
},
|
|
38195
38408
|
parse: function _parse2(data, params) {
|
|
38196
|
-
return
|
|
38409
|
+
return parse10(this, data, params, { callee: _parse2 });
|
|
38197
38410
|
},
|
|
38198
38411
|
parseAsync: async function _parseAsync2(data, params) {
|
|
38199
38412
|
return await parseAsync2(this, data, params, { callee: _parseAsync2 });
|
|
@@ -41028,7 +41241,7 @@ function clone2(value) {
|
|
|
41028
41241
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
41029
41242
|
}
|
|
41030
41243
|
// src/lib/canonical-json.ts
|
|
41031
|
-
import { createHash as
|
|
41244
|
+
import { createHash as createHash25 } from "crypto";
|
|
41032
41245
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
41033
41246
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
41034
41247
|
}
|
|
@@ -41072,7 +41285,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
41072
41285
|
return visit3(value, 0);
|
|
41073
41286
|
}
|
|
41074
41287
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
41075
|
-
return
|
|
41288
|
+
return createHash25("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
41076
41289
|
}
|
|
41077
41290
|
|
|
41078
41291
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -43136,9 +43349,9 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
43136
43349
|
}
|
|
43137
43350
|
// src/lib/scheduler.ts
|
|
43138
43351
|
import { existsSync as existsSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12 } from "fs";
|
|
43139
|
-
import { join as
|
|
43352
|
+
import { join as join37 } from "path";
|
|
43140
43353
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
43141
|
-
return
|
|
43354
|
+
return join37(targetDir, ".skills", "schedules.json");
|
|
43142
43355
|
}
|
|
43143
43356
|
function loadSchedules(targetDir = process.cwd()) {
|
|
43144
43357
|
const path = getSchedulesPath(targetDir);
|
|
@@ -43151,7 +43364,7 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
43151
43364
|
}
|
|
43152
43365
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
43153
43366
|
const path = getSchedulesPath(targetDir);
|
|
43154
|
-
const dir =
|
|
43367
|
+
const dir = join37(targetDir, ".skills");
|
|
43155
43368
|
if (!existsSync17(dir))
|
|
43156
43369
|
mkdirSync12(dir, { recursive: true });
|
|
43157
43370
|
writeFileSync11(path, JSON.stringify(data, null, 2));
|
|
@@ -43339,7 +43552,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
43339
43552
|
saveSchedules(data, targetDir);
|
|
43340
43553
|
}
|
|
43341
43554
|
// src/lib/revision.ts
|
|
43342
|
-
import { createHash as
|
|
43555
|
+
import { createHash as createHash26 } from "crypto";
|
|
43343
43556
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
43344
43557
|
function revisionIdOf(content) {
|
|
43345
43558
|
const canonicalContent = {
|
|
@@ -43360,15 +43573,15 @@ function revisionIdOf(content) {
|
|
|
43360
43573
|
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
43361
43574
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
43362
43575
|
}
|
|
43363
|
-
const
|
|
43364
|
-
return
|
|
43576
|
+
const canonical7 = JSON.stringify(canonicalContent);
|
|
43577
|
+
return createHash26("sha256").update(canonical7).digest("hex");
|
|
43365
43578
|
}
|
|
43366
43579
|
function revisionIdOfRecord(record8) {
|
|
43367
43580
|
return revisionIdOf(record8);
|
|
43368
43581
|
}
|
|
43369
43582
|
// src/lib/pull.ts
|
|
43370
43583
|
import { existsSync as existsSync18, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readFileSync as readFileSync25, renameSync as renameSync7, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
|
|
43371
|
-
import { dirname as dirname22, join as
|
|
43584
|
+
import { dirname as dirname22, join as join38 } from "path";
|
|
43372
43585
|
|
|
43373
43586
|
// src/lib/skill-bundles.ts
|
|
43374
43587
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -43462,7 +43675,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
|
|
|
43462
43675
|
return reconcileTombstone(slug2, corpusOptions);
|
|
43463
43676
|
}
|
|
43464
43677
|
if (bundleResponse.status === 404) {
|
|
43465
|
-
const marker = readPullMarker(
|
|
43678
|
+
const marker = readPullMarker(join38(getPortableSkillsRoot(corpusOptions), slug2));
|
|
43466
43679
|
if (isPublishedInstallMarker(marker)) {
|
|
43467
43680
|
return { name: slug2, success: true, purged: true, removed: false };
|
|
43468
43681
|
}
|
|
@@ -43501,7 +43714,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
|
|
|
43501
43714
|
return { name: slug2, success: false, error: `Skill '${slug2}' was not found on the configured Skills instance.` };
|
|
43502
43715
|
}
|
|
43503
43716
|
if (!meta3?.revisionId) {
|
|
43504
|
-
const marker = readPullMarker(
|
|
43717
|
+
const marker = readPullMarker(join38(getPortableSkillsRoot(corpusOptions), slug2));
|
|
43505
43718
|
if (isPublishedInstallMarker(marker)) {
|
|
43506
43719
|
return { name: slug2, success: true, purged: true, removed: false };
|
|
43507
43720
|
}
|
|
@@ -43558,7 +43771,7 @@ function provenRevision(meta3, slug2, bundle2) {
|
|
|
43558
43771
|
return declared;
|
|
43559
43772
|
}
|
|
43560
43773
|
function reconcileTombstone(slug2, corpusOptions) {
|
|
43561
|
-
const target =
|
|
43774
|
+
const target = join38(getPortableSkillsRoot(corpusOptions), slug2);
|
|
43562
43775
|
if (!hasSkillsOwnershipMarker(target)) {
|
|
43563
43776
|
return { name: slug2, success: true, tombstoned: true, removed: false, leftInPlace: true };
|
|
43564
43777
|
}
|
|
@@ -43567,7 +43780,7 @@ function reconcileTombstone(slug2, corpusOptions) {
|
|
|
43567
43780
|
}
|
|
43568
43781
|
function readPullMarker(dir) {
|
|
43569
43782
|
try {
|
|
43570
|
-
return JSON.parse(readFileSync25(
|
|
43783
|
+
return JSON.parse(readFileSync25(join38(dir, PULL_MARKER_FILE), "utf-8"));
|
|
43571
43784
|
} catch {
|
|
43572
43785
|
return null;
|
|
43573
43786
|
}
|
|
@@ -43702,14 +43915,14 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
|
|
|
43702
43915
|
function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
43703
43916
|
const root2 = getPortableSkillsRoot(options);
|
|
43704
43917
|
mkdirSync13(root2, { recursive: true });
|
|
43705
|
-
const target =
|
|
43918
|
+
const target = join38(root2, name);
|
|
43706
43919
|
const created = !existsSync18(target);
|
|
43707
|
-
const staging = mkdtempSync3(
|
|
43920
|
+
const staging = mkdtempSync3(join38(root2, `.pull-${name}-`));
|
|
43708
43921
|
let moved = false;
|
|
43709
43922
|
let backup = null;
|
|
43710
43923
|
try {
|
|
43711
43924
|
for (const entry of entries) {
|
|
43712
|
-
const destination =
|
|
43925
|
+
const destination = join38(staging, entry.path);
|
|
43713
43926
|
mkdirSync13(dirname22(destination), { recursive: true });
|
|
43714
43927
|
writeFileSync12(destination, entry.bytes, { mode: entry.mode });
|
|
43715
43928
|
}
|
|
@@ -43722,8 +43935,8 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
43722
43935
|
...marker.revisionId ? { revisionId: marker.revisionId } : {}
|
|
43723
43936
|
});
|
|
43724
43937
|
if (existsSync18(target)) {
|
|
43725
|
-
backup = mkdtempSync3(
|
|
43726
|
-
renameSync7(target,
|
|
43938
|
+
backup = mkdtempSync3(join38(root2, `.pull-backup-${name}-`));
|
|
43939
|
+
renameSync7(target, join38(backup, name));
|
|
43727
43940
|
moved = true;
|
|
43728
43941
|
}
|
|
43729
43942
|
renameSync7(staging, target);
|
|
@@ -43731,9 +43944,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
43731
43944
|
rmSync5(backup, { recursive: true, force: true });
|
|
43732
43945
|
} catch (error61) {
|
|
43733
43946
|
rmSync5(staging, { recursive: true, force: true });
|
|
43734
|
-
if (moved && backup && existsSync18(
|
|
43947
|
+
if (moved && backup && existsSync18(join38(backup, name))) {
|
|
43735
43948
|
try {
|
|
43736
|
-
renameSync7(
|
|
43949
|
+
renameSync7(join38(backup, name), target);
|
|
43737
43950
|
} catch {}
|
|
43738
43951
|
}
|
|
43739
43952
|
throw error61;
|
|
@@ -43752,7 +43965,7 @@ function writePullMarker(dir, record8) {
|
|
|
43752
43965
|
...record8.revisionId ? { revisionId: record8.revisionId } : {},
|
|
43753
43966
|
syncedAt: new Date().toISOString()
|
|
43754
43967
|
};
|
|
43755
|
-
writeFileSync12(
|
|
43968
|
+
writeFileSync12(join38(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
43756
43969
|
`);
|
|
43757
43970
|
}
|
|
43758
43971
|
async function safeMeta(client2, slug2) {
|
|
@@ -43902,7 +44115,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
43902
44115
|
}
|
|
43903
44116
|
|
|
43904
44117
|
// src/lib/remote-private-publications.ts
|
|
43905
|
-
import { createHash as
|
|
44118
|
+
import { createHash as createHash27 } from "crypto";
|
|
43906
44119
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
43907
44120
|
var failures2 = {
|
|
43908
44121
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -43949,7 +44162,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
43949
44162
|
var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
43950
44163
|
var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
43951
44164
|
var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
|
|
43952
|
-
var publicationSha256 = (bytes) =>
|
|
44165
|
+
var publicationSha256 = (bytes) => createHash27("sha256").update(bytes).digest("hex");
|
|
43953
44166
|
function checkedPublicationDeclaration(value) {
|
|
43954
44167
|
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
43955
44168
|
return bad();
|
|
@@ -45756,10 +45969,10 @@ function clone3(value) {
|
|
|
45756
45969
|
}
|
|
45757
45970
|
// src/lib/feedback.ts
|
|
45758
45971
|
import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
|
|
45759
|
-
import { dirname as dirname24, join as
|
|
45972
|
+
import { dirname as dirname24, join as join39 } from "path";
|
|
45760
45973
|
import { Database } from "bun:sqlite";
|
|
45761
45974
|
function getFeedbackDbPath() {
|
|
45762
|
-
return
|
|
45975
|
+
return join39(getDataDir(), "skills.db");
|
|
45763
45976
|
}
|
|
45764
45977
|
function getFeedbackDb() {
|
|
45765
45978
|
const dbPath = getFeedbackDbPath();
|
|
@@ -45791,7 +46004,7 @@ function saveFeedback(input2) {
|
|
|
45791
46004
|
throw new Error("Feedback message is required");
|
|
45792
46005
|
const category = input2.category ?? "general";
|
|
45793
46006
|
if (isApiMode()) {
|
|
45794
|
-
const path =
|
|
46007
|
+
const path = join39(getDataDir(), "feedback.jsonl");
|
|
45795
46008
|
const dir = dirname24(path);
|
|
45796
46009
|
if (!existsSync19(dir))
|
|
45797
46010
|
mkdirSync15(dir, { recursive: true });
|
|
@@ -45815,7 +46028,7 @@ function isApiMode(env = process.env) {
|
|
|
45815
46028
|
}
|
|
45816
46029
|
}
|
|
45817
46030
|
// src/lib/native-storage.ts
|
|
45818
|
-
import { createHash as
|
|
46031
|
+
import { createHash as createHash28, createHmac as createHmac2 } from "crypto";
|
|
45819
46032
|
import {
|
|
45820
46033
|
existsSync as existsSync20,
|
|
45821
46034
|
mkdirSync as mkdirSync16,
|
|
@@ -45824,7 +46037,7 @@ import {
|
|
|
45824
46037
|
statSync as statSync13,
|
|
45825
46038
|
writeFileSync as writeFileSync14
|
|
45826
46039
|
} from "fs";
|
|
45827
|
-
import { dirname as dirname25, join as
|
|
46040
|
+
import { dirname as dirname25, join as join40, normalize as normalize5, relative as relative10, sep as sep11 } from "path";
|
|
45828
46041
|
var SKILLS_STORAGE_TABLES = [
|
|
45829
46042
|
"skills_sync_records",
|
|
45830
46043
|
"skills_sync_cursors"
|
|
@@ -45961,7 +46174,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
45961
46174
|
local: {
|
|
45962
46175
|
dataDir: getDataDir(),
|
|
45963
46176
|
projectStateDir: getProjectStateDir(targetDir),
|
|
45964
|
-
feedbackDbPath:
|
|
46177
|
+
feedbackDbPath: join40(getDataDir(), "skills.db")
|
|
45965
46178
|
},
|
|
45966
46179
|
remote: {
|
|
45967
46180
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -45991,7 +46204,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
45991
46204
|
files.push({
|
|
45992
46205
|
path: relativePath,
|
|
45993
46206
|
sizeBytes: bytes.byteLength,
|
|
45994
|
-
sha256:
|
|
46207
|
+
sha256: createHash28("sha256").update(bytes).digest("hex"),
|
|
45995
46208
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
45996
46209
|
});
|
|
45997
46210
|
}
|
|
@@ -46016,7 +46229,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
|
|
|
46016
46229
|
continue;
|
|
46017
46230
|
}
|
|
46018
46231
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
46019
|
-
const hash4 =
|
|
46232
|
+
const hash4 = createHash28("sha256").update(bytes).digest("hex");
|
|
46020
46233
|
if (hash4 !== file2.sha256) {
|
|
46021
46234
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
46022
46235
|
}
|
|
@@ -46318,7 +46531,7 @@ function parsePositiveInteger(value) {
|
|
|
46318
46531
|
function walkFiles2(dir) {
|
|
46319
46532
|
const files = [];
|
|
46320
46533
|
for (const entry of readdirSync14(dir)) {
|
|
46321
|
-
const full =
|
|
46534
|
+
const full = join40(dir, entry);
|
|
46322
46535
|
const stats = statSync13(full);
|
|
46323
46536
|
if (stats.isDirectory())
|
|
46324
46537
|
files.push(...walkFiles2(full));
|
|
@@ -46329,13 +46542,13 @@ function walkFiles2(dir) {
|
|
|
46329
46542
|
}
|
|
46330
46543
|
function resolveSnapshotPath(targetDir, snapshotPath) {
|
|
46331
46544
|
const normalizedPath = normalize5(snapshotPath);
|
|
46332
|
-
if (normalizedPath.startsWith("..") || normalizedPath.includes(`${
|
|
46545
|
+
if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep11}..${sep11}`) || normalizedPath.startsWith(sep11)) {
|
|
46333
46546
|
throw new Error(`Unsafe snapshot path: ${snapshotPath}`);
|
|
46334
46547
|
}
|
|
46335
46548
|
if (!toPosix(normalizedPath).startsWith(".skills/")) {
|
|
46336
46549
|
throw new Error(`Snapshot path must stay inside .skills: ${snapshotPath}`);
|
|
46337
46550
|
}
|
|
46338
|
-
return
|
|
46551
|
+
return join40(targetDir, normalizedPath);
|
|
46339
46552
|
}
|
|
46340
46553
|
function normalizeS3Prefix(prefix) {
|
|
46341
46554
|
return (prefix ?? "").trim().replace(/^\/+|\/+$/g, "");
|
|
@@ -46348,7 +46561,7 @@ function toIsoString(value) {
|
|
|
46348
46561
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
46349
46562
|
}
|
|
46350
46563
|
function sha256Hex2(value) {
|
|
46351
|
-
return
|
|
46564
|
+
return createHash28("sha256").update(value).digest("hex");
|
|
46352
46565
|
}
|
|
46353
46566
|
function normalizeHeaders(headers) {
|
|
46354
46567
|
const result = {};
|
|
@@ -46400,7 +46613,7 @@ function toArrayBuffer(bytes) {
|
|
|
46400
46613
|
return buffer;
|
|
46401
46614
|
}
|
|
46402
46615
|
// src/lib/station-snapshot.ts
|
|
46403
|
-
import { createHash as
|
|
46616
|
+
import { createHash as createHash29 } from "crypto";
|
|
46404
46617
|
import {
|
|
46405
46618
|
copyFileSync,
|
|
46406
46619
|
mkdirSync as mkdirSync17,
|
|
@@ -46408,12 +46621,12 @@ import {
|
|
|
46408
46621
|
statSync as statSync15,
|
|
46409
46622
|
writeFileSync as writeFileSync15
|
|
46410
46623
|
} from "fs";
|
|
46411
|
-
import { dirname as dirname26, isAbsolute as isAbsolute21, relative as relative11, resolve as resolve21, sep as
|
|
46624
|
+
import { dirname as dirname26, isAbsolute as isAbsolute21, relative as relative11, resolve as resolve21, sep as sep13 } from "path";
|
|
46412
46625
|
|
|
46413
46626
|
// src/lib/portable-snapshot-filter.ts
|
|
46414
46627
|
import { readdirSync as readdirSync15, statSync as statSync14 } from "fs";
|
|
46415
46628
|
import { homedir as homedir7 } from "os";
|
|
46416
|
-
import { join as
|
|
46629
|
+
import { join as join41, sep as sep12 } from "path";
|
|
46417
46630
|
var SYNC_HOMES = [
|
|
46418
46631
|
{ name: "skills", subClass: "skills", agent: null },
|
|
46419
46632
|
{ name: "custom", subClass: "custom", agent: null },
|
|
@@ -46506,16 +46719,16 @@ function isPortableWithinSkill(relativeParts) {
|
|
|
46506
46719
|
function homePathFor(definition, homesRoot) {
|
|
46507
46720
|
const home = homesRoot ?? homedir7();
|
|
46508
46721
|
if (definition.subClass === "skills" || definition.subClass === "custom") {
|
|
46509
|
-
return
|
|
46722
|
+
return join41(skillsDataRootForHome(home), definition.name);
|
|
46510
46723
|
}
|
|
46511
46724
|
if (definition.agent === "opencode") {
|
|
46512
|
-
return
|
|
46725
|
+
return join41(home, ".config", "opencode", "skills");
|
|
46513
46726
|
}
|
|
46514
|
-
return
|
|
46727
|
+
return join41(home, `.${definition.agent}`, "skills");
|
|
46515
46728
|
}
|
|
46516
46729
|
function destinationFor(definition, stationId, relativePath) {
|
|
46517
|
-
const category = definition.subClass === "agent-homes" ?
|
|
46518
|
-
return
|
|
46730
|
+
const category = definition.subClass === "agent-homes" ? join41("agent-homes", definition.agent ?? "") : definition.name;
|
|
46731
|
+
return join41("resources", stationId, "skills", category, ...relativePath.split(sep12));
|
|
46519
46732
|
}
|
|
46520
46733
|
function walkEntries(absoluteRoot) {
|
|
46521
46734
|
let entries;
|
|
@@ -46526,7 +46739,7 @@ function walkEntries(absoluteRoot) {
|
|
|
46526
46739
|
}
|
|
46527
46740
|
const output2 = [];
|
|
46528
46741
|
for (const entry of entries) {
|
|
46529
|
-
const childFull =
|
|
46742
|
+
const childFull = join41(absoluteRoot, entry.name);
|
|
46530
46743
|
if (entry.isSymbolicLink()) {
|
|
46531
46744
|
output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
|
|
46532
46745
|
continue;
|
|
@@ -46537,7 +46750,7 @@ function walkEntries(absoluteRoot) {
|
|
|
46537
46750
|
}
|
|
46538
46751
|
const nested = walkEntries(childFull);
|
|
46539
46752
|
for (const item of nested) {
|
|
46540
|
-
output2.push({ ...item, relativePath:
|
|
46753
|
+
output2.push({ ...item, relativePath: join41(entry.name, item.relativePath) });
|
|
46541
46754
|
}
|
|
46542
46755
|
continue;
|
|
46543
46756
|
}
|
|
@@ -46575,7 +46788,7 @@ function validateStationId(stationId) {
|
|
|
46575
46788
|
}
|
|
46576
46789
|
}
|
|
46577
46790
|
function sha256File(filePath) {
|
|
46578
|
-
return
|
|
46791
|
+
return createHash29("sha256").update(readFileSync27(filePath)).digest("hex");
|
|
46579
46792
|
}
|
|
46580
46793
|
function scanHome(definition, homesRoot) {
|
|
46581
46794
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -46583,7 +46796,7 @@ function scanHome(definition, homesRoot) {
|
|
|
46583
46796
|
const portable = [];
|
|
46584
46797
|
const skipped = [];
|
|
46585
46798
|
for (const entry of entries) {
|
|
46586
|
-
const relativeParts = entry.relativePath.split(
|
|
46799
|
+
const relativeParts = entry.relativePath.split(sep13);
|
|
46587
46800
|
const fileName = relativeParts[relativeParts.length - 1];
|
|
46588
46801
|
if (entry.kind === "symlink") {
|
|
46589
46802
|
skipped.push({ relativePath: entry.relativePath, reason: "symlink" });
|
|
@@ -46674,7 +46887,7 @@ function writeStationSnapshot(options) {
|
|
|
46674
46887
|
for (const plan of plans) {
|
|
46675
46888
|
const destination = resolve21(repoRoot, plan.destination);
|
|
46676
46889
|
const destinationRelative = relative11(repoRoot, destination);
|
|
46677
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(
|
|
46890
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute21(destinationRelative)) {
|
|
46678
46891
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
46679
46892
|
}
|
|
46680
46893
|
let existingDigest = null;
|
|
@@ -46726,7 +46939,7 @@ function writeStationSnapshot(options) {
|
|
|
46726
46939
|
};
|
|
46727
46940
|
}
|
|
46728
46941
|
// src/lib/station-hydrate.ts
|
|
46729
|
-
import { createHash as
|
|
46942
|
+
import { createHash as createHash30 } from "crypto";
|
|
46730
46943
|
import {
|
|
46731
46944
|
copyFileSync as copyFileSync2,
|
|
46732
46945
|
mkdirSync as mkdirSync18,
|
|
@@ -46735,7 +46948,7 @@ import {
|
|
|
46735
46948
|
statSync as statSync16,
|
|
46736
46949
|
writeFileSync as writeFileSync16
|
|
46737
46950
|
} from "fs";
|
|
46738
|
-
import { dirname as dirname27, join as
|
|
46951
|
+
import { dirname as dirname27, join as join42, resolve as resolve22, sep as sep14 } from "path";
|
|
46739
46952
|
var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
|
|
46740
46953
|
var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
|
|
46741
46954
|
var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
|
|
@@ -46743,11 +46956,11 @@ function fail2(code2, message, detail = []) {
|
|
|
46743
46956
|
throw new StationSnapshotError(code2, message, detail);
|
|
46744
46957
|
}
|
|
46745
46958
|
function snapshotRootFor(repoRoot, stationId) {
|
|
46746
|
-
return
|
|
46959
|
+
return join42(repoRoot, "resources", stationId, "skills");
|
|
46747
46960
|
}
|
|
46748
46961
|
function readSnapshotManifest(repoRoot, stationId) {
|
|
46749
46962
|
const snapshotRoot = snapshotRootFor(repoRoot, stationId);
|
|
46750
|
-
const manifestPath =
|
|
46963
|
+
const manifestPath = join42(snapshotRoot, "sync-manifest.json");
|
|
46751
46964
|
let manifest;
|
|
46752
46965
|
try {
|
|
46753
46966
|
manifest = JSON.parse(readFileSync28(manifestPath, "utf8"));
|
|
@@ -46774,7 +46987,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
46774
46987
|
const hashMismatches = [];
|
|
46775
46988
|
const skippedByRule = [];
|
|
46776
46989
|
for (const agent of SYNC_AGENTS) {
|
|
46777
|
-
const agentRoot =
|
|
46990
|
+
const agentRoot = join42(snapshotRoot, "agent-homes", agent);
|
|
46778
46991
|
let identEntries;
|
|
46779
46992
|
try {
|
|
46780
46993
|
identEntries = readdirSync16(agentRoot, { withFileTypes: true });
|
|
@@ -46785,10 +46998,10 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
46785
46998
|
if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
|
|
46786
46999
|
continue;
|
|
46787
47000
|
}
|
|
46788
|
-
const identRoot =
|
|
47001
|
+
const identRoot = join42(agentRoot, identEntry.name);
|
|
46789
47002
|
const entries = walkEntries(identRoot);
|
|
46790
47003
|
for (const entry of entries) {
|
|
46791
|
-
const relativeParts = [identEntry.name, ...entry.relativePath.split(
|
|
47004
|
+
const relativeParts = [identEntry.name, ...entry.relativePath.split(sep14)];
|
|
46792
47005
|
if (entry.kind === "symlink") {
|
|
46793
47006
|
symlinks.push({ ident: identEntry.name, agent, relativePath: entry.relativePath });
|
|
46794
47007
|
continue;
|
|
@@ -46812,8 +47025,8 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
46812
47025
|
});
|
|
46813
47026
|
continue;
|
|
46814
47027
|
}
|
|
46815
|
-
const withinIdent = relativeParts.slice(1).join(
|
|
46816
|
-
const homeRelative = relativeParts.join(
|
|
47028
|
+
const withinIdent = relativeParts.slice(1).join(sep14);
|
|
47029
|
+
const homeRelative = relativeParts.join(sep14);
|
|
46817
47030
|
if (REFUSED_SCANNER_FLAGGED.has(homeRelative)) {
|
|
46818
47031
|
skippedByRule.push({
|
|
46819
47032
|
ident: identEntry.name,
|
|
@@ -46932,7 +47145,7 @@ function skillSha256(skill) {
|
|
|
46932
47145
|
return sha256File(skill.files[0].winner.fullPath);
|
|
46933
47146
|
}
|
|
46934
47147
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
46935
|
-
return
|
|
47148
|
+
return createHash30("sha256").update(joined.sort().join(`
|
|
46936
47149
|
`)).digest("hex");
|
|
46937
47150
|
}
|
|
46938
47151
|
function writeStationHydration(options) {
|
|
@@ -46970,7 +47183,7 @@ function writeStationHydration(options) {
|
|
|
46970
47183
|
const toWrite = [];
|
|
46971
47184
|
for (const skill of plan.winners) {
|
|
46972
47185
|
for (const file2 of skill.files) {
|
|
46973
|
-
const destination =
|
|
47186
|
+
const destination = join42(cacheRoot, skill.ident, file2.withinIdent);
|
|
46974
47187
|
const digest3 = sha256File(file2.winner.fullPath);
|
|
46975
47188
|
let existingDigest = null;
|
|
46976
47189
|
try {
|
|
@@ -47012,7 +47225,7 @@ function writeStationHydration(options) {
|
|
|
47012
47225
|
},
|
|
47013
47226
|
skills: resultSkills
|
|
47014
47227
|
};
|
|
47015
|
-
const hydrationManifestPath =
|
|
47228
|
+
const hydrationManifestPath = join42(dirname27(cacheRoot), `hydration-${options.stationId}.json`);
|
|
47016
47229
|
mkdirSync18(dirname27(hydrationManifestPath), { recursive: true });
|
|
47017
47230
|
writeFileSync16(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
47018
47231
|
`);
|
|
@@ -47025,7 +47238,7 @@ function writeStationHydration(options) {
|
|
|
47025
47238
|
}
|
|
47026
47239
|
// src/lib/private-publication-recovery.ts
|
|
47027
47240
|
import { constants as constants14, closeSync as closeSync14, fsyncSync as fsyncSync6, fstatSync as fstatSync14, lstatSync as lstatSync26, mkdirSync as mkdirSync19, openSync as openSync14, readSync as readSync12, realpathSync as realpathSync13, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
|
|
47028
|
-
import { dirname as dirname28, isAbsolute as isAbsolute22, join as
|
|
47241
|
+
import { dirname as dirname28, isAbsolute as isAbsolute22, join as join43, resolve as resolve23 } from "path";
|
|
47029
47242
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
47030
47243
|
var fail3 = () => {
|
|
47031
47244
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
|
|
@@ -47051,7 +47264,7 @@ function unchangedDirectory(directory2, identity5) {
|
|
|
47051
47264
|
return fail3();
|
|
47052
47265
|
}
|
|
47053
47266
|
function readOwned(directory2, name, max) {
|
|
47054
|
-
const identity5 = safeDirectory(directory2), file2 =
|
|
47267
|
+
const identity5 = safeDirectory(directory2), file2 = join43(directory2, name);
|
|
47055
47268
|
const fd = openSync14(file2, constants14.O_RDONLY | constants14.O_NOFOLLOW);
|
|
47056
47269
|
try {
|
|
47057
47270
|
const stat2 = fstatSync14(fd);
|
|
@@ -47075,7 +47288,7 @@ function readOwned(directory2, name, max) {
|
|
|
47075
47288
|
}
|
|
47076
47289
|
}
|
|
47077
47290
|
function writeOwned(directory2, name, bytes) {
|
|
47078
|
-
const fd = openSync14(
|
|
47291
|
+
const fd = openSync14(join43(directory2, name), constants14.O_WRONLY | constants14.O_CREAT | constants14.O_EXCL | constants14.O_NOFOLLOW, 384);
|
|
47079
47292
|
try {
|
|
47080
47293
|
writeFileSync17(fd, bytes);
|
|
47081
47294
|
fsyncSync6(fd);
|
|
@@ -47088,7 +47301,7 @@ function save(directory2, value) {
|
|
|
47088
47301
|
writeOwned(directory2, temporary, JSON.stringify(value) + `
|
|
47089
47302
|
`);
|
|
47090
47303
|
unchangedDirectory(directory2, identity5);
|
|
47091
|
-
renameSync8(
|
|
47304
|
+
renameSync8(join43(directory2, temporary), join43(directory2, "receipt.json"));
|
|
47092
47305
|
const fd = openSync14(directory2, constants14.O_RDONLY | constants14.O_NOFOLLOW);
|
|
47093
47306
|
try {
|
|
47094
47307
|
fsyncSync6(fd);
|
|
@@ -47101,7 +47314,7 @@ function bind(client2, receipt) {
|
|
|
47101
47314
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
47102
47315
|
}
|
|
47103
47316
|
async function locked(directory2, action) {
|
|
47104
|
-
const identity5 = safeDirectory(directory2), file2 =
|
|
47317
|
+
const identity5 = safeDirectory(directory2), file2 = join43(directory2, "operation.lock");
|
|
47105
47318
|
let fd;
|
|
47106
47319
|
try {
|
|
47107
47320
|
fd = openSync14(file2, constants14.O_WRONLY | constants14.O_CREAT | constants14.O_EXCL | constants14.O_NOFOLLOW, 384);
|
|
@@ -47260,7 +47473,7 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
47260
47473
|
return privatePublicationResult(directory2, receipt);
|
|
47261
47474
|
}
|
|
47262
47475
|
// src/lib/skill-context.ts
|
|
47263
|
-
import { createHash as
|
|
47476
|
+
import { createHash as createHash31 } from "crypto";
|
|
47264
47477
|
import { resolve as resolve24 } from "path";
|
|
47265
47478
|
function words(value) {
|
|
47266
47479
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
@@ -47353,7 +47566,7 @@ ${content}`;
|
|
|
47353
47566
|
`);
|
|
47354
47567
|
const receipt = {
|
|
47355
47568
|
schemaVersion: 1,
|
|
47356
|
-
id:
|
|
47569
|
+
id: createHash31("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
47357
47570
|
profileId: profile.profileId,
|
|
47358
47571
|
profileRevision: profile.profileRevision,
|
|
47359
47572
|
authority: profile.authority,
|
|
@@ -47369,9 +47582,9 @@ ${content}`;
|
|
|
47369
47582
|
return { context: context2, selections, omitted, receipt };
|
|
47370
47583
|
}
|
|
47371
47584
|
// src/lib/session-reconciliation.ts
|
|
47372
|
-
import { createHash as
|
|
47585
|
+
import { createHash as createHash32 } from "crypto";
|
|
47373
47586
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
47374
|
-
var digest3 = (value) =>
|
|
47587
|
+
var digest3 = (value) => createHash32("sha256").update(JSON.stringify(value)).digest("hex");
|
|
47375
47588
|
function exactTimestamp(value) {
|
|
47376
47589
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
47377
47590
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|