@stratusagent/plugins 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +21 -8
- package/dist/loader.js.map +1 -1
- package/dist/provenance.d.ts +144 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +316 -0
- package/dist/provenance.js.map +1 -0
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -12,4 +12,5 @@ export { declaredRiskFor, parsePluginManifest, parseToolRiskOverrides, resolvePl
|
|
|
12
12
|
export { createManifestBoundCredentialResolver } from './credentials.ts';
|
|
13
13
|
export { ManifestBoundToolRegistry, riskFloorFor, type PluginToolRecord, } from './registry.ts';
|
|
14
14
|
export { isFirstPartyPackage, loadOptionalModule, loadPlugins, type CreatePlugin, type LoadedPlugin, type LoadPluginsOptions, type LoadPluginsResult, type OptionalModuleHost, type PluginLoadFailure, type PluginSkillRecord, } from './loader.ts';
|
|
15
|
+
export { createFileLedger, createProcessLocalLedger, isLedgerPath, LEDGER_FILENAME, ledgerContentTrust, ledgerGuard, ledgerTrustOfContent, nameIdentifiesHandle, type FileIdentity, type LedgerGuard, type TaintedWriteLedger, } from './provenance.ts';
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,qCAAqC,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EACL,yBAAyB,EACzB,YAAY,EACZ,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,qCAAqC,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EACL,yBAAyB,EACzB,YAAY,EACZ,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,4 +12,5 @@ export { declaredRiskFor, parsePluginManifest, parseToolRiskOverrides, resolvePl
|
|
|
12
12
|
export { createManifestBoundCredentialResolver } from "./credentials.js";
|
|
13
13
|
export { ManifestBoundToolRegistry, riskFloorFor, } from "./registry.js";
|
|
14
14
|
export { isFirstPartyPackage, loadOptionalModule, loadPlugins, } from "./loader.js";
|
|
15
|
+
export { createFileLedger, createProcessLocalLedger, isLedgerPath, LEDGER_FILENAME, ledgerContentTrust, ledgerGuard, ledgerTrustOfContent, nameIdentifiesHandle, } from "./provenance.js";
|
|
15
16
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GAMpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,qCAAqC,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EACL,yBAAyB,EACzB,YAAY,GAEb,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,GAQZ,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GAMpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,qCAAqC,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EACL,yBAAyB,EACzB,YAAY,GAEb,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,GAQZ,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,oBAAoB,GAIrB,MAAM,iBAAiB,CAAC"}
|
package/dist/loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAClB,QAAQ,EACR,UAAU,EACV,MAAM,EAEN,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7C;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAAU,CAAC,GAAG,OAAO,EAClD,WAAW,MAAM,EACjB,MAAM,kBAAkB,KACvB,OAAO,CAAC,CAAC,GAAG,SAAS,CASvB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AA8B5E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM,KAAG,OACmB,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC;IAC/C,IAAI,EAAE,kBAAkB,CAAC;IACzB,4EAA4E;IAC5E,KAAK,EAAE,YAAY,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,GAAG,EAAE,QAAQ,CAAC;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,yEAAyE;IACzE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAClB,QAAQ,EACR,UAAU,EACV,MAAM,EAEN,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7C;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAAU,CAAC,GAAG,OAAO,EAClD,WAAW,MAAM,EACjB,MAAM,kBAAkB,KACvB,OAAO,CAAC,CAAC,GAAG,SAAS,CASvB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AA8B5E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM,KAAG,OACmB,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC;IAC/C,IAAI,EAAE,kBAAkB,CAAC;IACzB,4EAA4E;IAC5E,KAAK,EAAE,YAAY,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,GAAG,EAAE,QAAQ,CAAC;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,yEAAyE;IACzE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAyGD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW,GAAU,SAAS,kBAAkB,KAAG,OAAO,CAAC,iBAAiB,CAsHxF,CAAC"}
|
package/dist/loader.js
CHANGED
|
@@ -66,20 +66,33 @@ const packageJsonFor = async (resolvedUrl, specifier) => {
|
|
|
66
66
|
*/
|
|
67
67
|
export const isFirstPartyPackage = (packageName) => packageName === '@stratusagent' || packageName.startsWith('@stratusagent/');
|
|
68
68
|
const WORKSPACE_ROOT_KEY = 'workspaceRoot';
|
|
69
|
+
/**
|
|
70
|
+
* Where the filesystem provenance ledger lives — always the host's
|
|
71
|
+
* workspace root, whatever a plugin's own `workspaceRoot` says. Two plugins
|
|
72
|
+
* write into the ledger (`tool-fs`, `plugin-mcp`), and an operator who
|
|
73
|
+
* points one of them at a different workspace must not thereby give it a
|
|
74
|
+
* different ledger: `fs.read` consults one, so a file recorded in another
|
|
75
|
+
* reads back unlabelled. Overwritten rather than defaulted, for that reason.
|
|
76
|
+
*/
|
|
77
|
+
const LEDGER_ROOT_KEY = 'ledgerRoot';
|
|
78
|
+
const declares = (manifest, key) => Boolean(manifest.config
|
|
79
|
+
&& typeof manifest.config.properties === 'object'
|
|
80
|
+
&& manifest.config.properties !== null
|
|
81
|
+
&& !Array.isArray(manifest.config.properties)
|
|
82
|
+
&& key in manifest.config.properties);
|
|
69
83
|
const configFor = (block, manifest, workspaceRoot) => {
|
|
70
84
|
// `toolRisks` is the host's key, applied by the view at registration —
|
|
71
85
|
// stripped here so the plugin's code never sees, and so can never
|
|
72
86
|
// second-guess, the operator's risk word.
|
|
73
87
|
const { enabled: _enabled, toolRisks: _toolRisks, ...rest } = block;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return { ...rest, [WORKSPACE_ROOT_KEY]: workspaceRoot };
|
|
88
|
+
let config = rest;
|
|
89
|
+
if (declares(manifest, WORKSPACE_ROOT_KEY) && workspaceRoot !== undefined && config[WORKSPACE_ROOT_KEY] === undefined) {
|
|
90
|
+
config = { ...config, [WORKSPACE_ROOT_KEY]: workspaceRoot };
|
|
91
|
+
}
|
|
92
|
+
if (declares(manifest, LEDGER_ROOT_KEY) && workspaceRoot !== undefined) {
|
|
93
|
+
config = { ...config, [LEDGER_ROOT_KEY]: workspaceRoot };
|
|
81
94
|
}
|
|
82
|
-
return
|
|
95
|
+
return config;
|
|
83
96
|
};
|
|
84
97
|
/**
|
|
85
98
|
* Read and validate every skill a manifest declares — staged, not
|
package/dist/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAWzC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,GAEpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qCAAqC,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAyB,MAAM,eAAe,CAAC;AAkBjF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,SAAiB,EACjB,IAAwB,EACA,EAAE;IAC1B,IAAI,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,yEAAyE;IACzE,yBAAyB;IACzB,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAM,CAAC;AAC7C,CAAC,CAAC;AAKF;;;;GAIG;AACH,MAAM,cAAc,GAAG,KAAK,EAC1B,WAAmB,EACnB,SAAiB,EACqC,EAAE;IACxD,IAAI,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,uEAAuE;IACvE,qEAAqE;IACrE,qCAAqC;IACrC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;YACzE,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,EAAE,SAAS,EAAE,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM;YACR,CAAC;YACD,SAAS,GAAG,MAAM,CAAC;QACrB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,mBAAmB,CAAC,qCAAqC,SAAS,GAAG,CAAC,CAAC;AACnF,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,WAAmB,EAAW,EAAE,CAClE,WAAW,KAAK,eAAe,IAAI,WAAW,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAgG9E,MAAM,kBAAkB,GAAG,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAWzC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,GAEpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qCAAqC,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAyB,MAAM,eAAe,CAAC;AAkBjF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,SAAiB,EACjB,IAAwB,EACA,EAAE;IAC1B,IAAI,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,yEAAyE;IACzE,yBAAyB;IACzB,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAM,CAAC;AAC7C,CAAC,CAAC;AAKF;;;;GAIG;AACH,MAAM,cAAc,GAAG,KAAK,EAC1B,WAAmB,EACnB,SAAiB,EACqC,EAAE;IACxD,IAAI,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,uEAAuE;IACvE,qEAAqE;IACrE,qCAAqC;IACrC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;YACzE,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,EAAE,SAAS,EAAE,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM;YACR,CAAC;YACD,SAAS,GAAG,MAAM,CAAC;QACrB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,mBAAmB,CAAC,qCAAqC,SAAS,GAAG,CAAC,CAAC;AACnF,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,WAAmB,EAAW,EAAE,CAClE,WAAW,KAAK,eAAe,IAAI,WAAW,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAgG9E,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAC3C;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,YAAY,CAAC;AAErC,MAAM,QAAQ,GAAG,CAAC,QAAwB,EAAE,GAAW,EAAW,EAAE,CAAC,OAAO,CAC1E,QAAQ,CAAC,MAAM;OACZ,OAAO,QAAQ,CAAC,MAAM,CAAC,UAAU,KAAK,QAAQ;OAC9C,QAAQ,CAAC,MAAM,CAAC,UAAU,KAAK,IAAI;OACnC,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;OAC1C,GAAG,IAAK,QAAQ,CAAC,MAAM,CAAC,UAAyB,CACrD,CAAC;AAEF,MAAM,SAAS,GAAG,CAChB,KAAiB,EACjB,QAAwB,EACxB,aAAiC,EACrB,EAAE;IACd,uEAAuE;IACvE,kEAAkE;IAClE,0CAA0C;IAC1C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IACpE,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC,IAAI,aAAa,KAAK,SAAS,IAAI,MAAM,CAAC,kBAAkB,CAAC,KAAK,SAAS,EAAE,CAAC;QACtH,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,EAAE,aAAa,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QACvE,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AASF;;;;;;;GAOG;AACH,MAAM,mBAAmB,GAAG,KAAK,EAC/B,QAAwB,EACxB,gBAAwB,EACM,EAAE;IAChC,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,KAAK,MAAM,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;QAClE,uEAAuE;QACvE,qEAAqE;QACrE,gBAAgB;QAChB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,mBAAmB,CAC3B,UAAU,QAAQ,CAAC,WAAW,WAAW,WAAW,CAAC,EAAE,yCAAyC,WAAW,CAAC,IAAI,EAAE,CACnH,CAAC;QACJ,CAAC;QACD,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,mBAAmB,CAC3B,UAAU,QAAQ,CAAC,WAAW,WAAW,WAAW,CAAC,EAAE,yBAAyB,WAAW,CAAC,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC9J,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,CAAC;QACb,IAAI,CAAC;YACH,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,mBAAmB,CAC3B,UAAU,QAAQ,CAAC,WAAW,WAAW,WAAW,CAAC,EAAE,6BAA6B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7I,CAAC;QACJ,CAAC;QACD,uEAAuE;QACvE,oEAAoE;QACpE,MAAM,SAAS,GAAG,GAAG,QAAQ,CAAC,WAAW,IAAI,WAAW,CAAC,EAAE,EAAE,CAAC;QAC9D,MAAM,CAAC,IAAI,CAAC;YACV,MAAM,EAAE,WAAW,CAAC,EAAE;YACtB,KAAK,EAAE,eAAe,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;YAC3F,MAAM,EAAE;gBACN,EAAE,EAAE,SAAS;gBACb,MAAM,EAAE,WAAW,CAAC,EAAE;gBACtB,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,WAAW,CAAC,EAAE;gBACrC,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,OAAO,EAAE,QAAQ,CAAC,WAAW;gBAC7B,IAAI,EAAE,QAAQ;aACf;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,OAA2B,EAA8B,EAAE;IAC3F,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,mBAAmB,CAAC;IACvD,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,qEAAqE;IACrE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,KAAK,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,OAAO,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,QAAQ,IAAI,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,mEAAmE;QACnE,sEAAsE;QACtE,uEAAuE;QACvE,uEAAuE;QACvE,iEAAiE;QACjE,IAAI,QAA4B,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAC7E,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC7D,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAChD,oEAAoE;YACpE,sEAAsE;YACtE,oEAAoE;YACpE,kDAAkD;YAClD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;YACjE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACvC,MAAM,aAAa,GAAG,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAE9D,gEAAgE;YAChE,+DAA+D;YAC/D,0CAA0C;YAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,mBAAmB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAE1F,MAAM,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAoC,CAAC;YACzF,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;gBAC9C,MAAM,IAAI,mBAAmB,CAC3B,UAAU,QAAQ,CAAC,WAAW,0EAA0E,CACzG,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBAClD,MAAM,IAAI,mBAAmB,CAC3B,UAAU,QAAQ,CAAC,WAAW,+DAA+D,CAC9F,CAAC;YACJ,CAAC;YACD,QAAQ,GAAG,MAAM,CAAC;YAElB,MAAM,IAAI,GAAG,IAAI,yBAAyB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;YACnH,MAAM,MAAM,CAAC,KAAK,CAAC;gBACjB,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,KAAK,EAAE,IAAI;gBACX,kEAAkE;gBAClE,gEAAgE;gBAChE,8DAA8D;gBAC9D,kBAAkB;gBAClB,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS;oBACnC,CAAC,CAAC,EAAE,WAAW,EAAE,qCAAqC,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE;oBACvF,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1D,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9D,CAAC,CAAC;YAEH,mEAAmE;YACnE,8DAA8D;YAC9D,8DAA8D;YAC9D,4DAA4D;YAC5D,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,YAAY,EAAE,CAAC;oBACrC,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;wBACjC,MAAM,IAAI,mBAAmB,CAC3B,uBAAuB,KAAK,CAAC,EAAE,2DAA2D,CAC3F,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,MAAM,GAAwB,EAAE,CAAC;YACvC,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;gBACrD,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAChC,gEAAgE;gBAChE,gEAAgE;gBAChE,gEAAgE;gBAChE,6DAA6D;gBAC7D,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;gBAChD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;YAED,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,QAAQ,CAAC,WAAW;gBAC7B,IAAI,EAAE,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW;gBACpG,QAAQ;gBACR,OAAO,EAAE,SAAS;gBAClB,KAAK;gBACL,MAAM;gBACN,QAAQ,EAAE,MAAM;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sEAAsE;YACtE,kEAAkE;YAClE,kDAAkD;YAClD,IAAI,CAAC;gBACH,MAAM,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,2DAA2D;YAC7D,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC;gBACZ,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC/D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { FileHandle } from 'node:fs/promises';
|
|
2
|
+
import { type TrustLevel } from '@stratusagent/core';
|
|
3
|
+
/**
|
|
4
|
+
* The tainted-write ledger: which paths an agent wrote while its session
|
|
5
|
+
* was at `external` or `unknown`, and at which label.
|
|
6
|
+
*
|
|
7
|
+
* The filesystem is a laundering channel by construction — a durable store
|
|
8
|
+
* the agent controls. An agent that `fs.write`s fetched text into its
|
|
9
|
+
* workspace and `fs.read`s it back next week gets a file with no
|
|
10
|
+
* provenance, arriving as its own notes. This closes the sequence an agent
|
|
11
|
+
* can perform by itself: a write from a tainted session records the path,
|
|
12
|
+
* and a later read of that path taints the reading session at the recorded
|
|
13
|
+
* label.
|
|
14
|
+
*
|
|
15
|
+
* What it does not cover, said plainly: a copy under another name, a file a
|
|
16
|
+
* different process wrote, content pasted through some path the ledger
|
|
17
|
+
* never saw. Full filesystem provenance means carrying labels on bytes
|
|
18
|
+
* across a surface the agent does not exclusively own, which is a different
|
|
19
|
+
* project.
|
|
20
|
+
*
|
|
21
|
+
* One file per agent, under the agent's own workspace — next to the roots
|
|
22
|
+
* `tool-fs` already resolves per call, and keyed the same way, because two
|
|
23
|
+
* agents with different roots is the whole point of the per-agent block.
|
|
24
|
+
* It lives in the plugin host rather than in `tool-fs` because `tool-fs`
|
|
25
|
+
* is not the only plugin that puts a server's bytes on disk: `plugin-mcp`
|
|
26
|
+
* writes a bridged tool's image and audio blocks into the same workspace,
|
|
27
|
+
* and a file that bypassed `fs.write` would otherwise read back unlabelled.
|
|
28
|
+
* Without a workspace root (a host that loaded the plugin outside the
|
|
29
|
+
* loader) the ledger is process-local and says so in its name: the
|
|
30
|
+
* read-back-next-week case then survives only as long as the daemon does.
|
|
31
|
+
*/
|
|
32
|
+
export interface TaintedWriteLedger {
|
|
33
|
+
/** The label a read of this path carries, or undefined for a path the ledger never saw. */
|
|
34
|
+
lookup(agentId: string, absolutePath: string): Promise<TrustLevel | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* Every recorded path and its label, for a result that names many files
|
|
37
|
+
* at once — a listing, a search's matches and skips — read once rather
|
|
38
|
+
* than once per file.
|
|
39
|
+
*/
|
|
40
|
+
snapshot(agentId: string): Promise<Record<string, TrustLevel>>;
|
|
41
|
+
/**
|
|
42
|
+
* Record a write at the writing session's label. A record only ever goes
|
|
43
|
+
* down and never clears — not even when a clean session later rewrites
|
|
44
|
+
* the file whole — because the path was the tainted session's choice as
|
|
45
|
+
* much as the bytes were, and because a clearing is the one record that
|
|
46
|
+
* could race a tainted write from another process into leaving tainted
|
|
47
|
+
* bytes unlabelled. Over-marking is the safe direction; an operator who
|
|
48
|
+
* wants a path back removes its lines from the ledger by hand. A clean
|
|
49
|
+
* session's write records nothing.
|
|
50
|
+
*/
|
|
51
|
+
recordWrite(agentId: string, absolutePath: string, trust: TrustLevel): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
/** The ledger's filename inside an agent's workspace. Exported so `fs.write` can refuse to write it. */
|
|
54
|
+
export declare const LEDGER_FILENAME = "fs-provenance.jsonl";
|
|
55
|
+
/** In-process only. See `TaintedWriteLedger` for when this is what a host gets. */
|
|
56
|
+
export declare const createProcessLocalLedger: () => TaintedWriteLedger;
|
|
57
|
+
/**
|
|
58
|
+
* The label the ledger's own contents carry: the lowest label recorded in
|
|
59
|
+
* it. Every path in it was chosen by a session at `unknown` or `external`,
|
|
60
|
+
* and the ledger's own path has no record, so an agent whose roots cover
|
|
61
|
+
* its workspace could otherwise `fs.read` the ledger and get a list of
|
|
62
|
+
* attacker-chosen filenames back at `agent`. Undefined for no file or an
|
|
63
|
+
* empty one; `unknown` for a file that holds lines nothing here can read
|
|
64
|
+
* — nobody vouches for those.
|
|
65
|
+
*/
|
|
66
|
+
export declare const ledgerContentTrust: (ledgerFilePath: string,
|
|
67
|
+
/**
|
|
68
|
+
* The inode the caller read the ledger at. Given, the bytes judged are
|
|
69
|
+
* read from that inode or not at all: a name swapped for an empty file
|
|
70
|
+
* after the caller's read would otherwise be read here as "no label",
|
|
71
|
+
* while the result still shows the ledger's contents. A swap reads
|
|
72
|
+
* `unknown` — nobody can vouch for bytes they can no longer see.
|
|
73
|
+
*/
|
|
74
|
+
identity?: FileIdentity) => Promise<TrustLevel | undefined>;
|
|
75
|
+
/**
|
|
76
|
+
* The label a ledger's bytes carry: the lowest recorded label, `unknown`
|
|
77
|
+
* for lines nothing here can read, nothing for an empty file. Pure over the
|
|
78
|
+
* bytes, so a caller that already holds what it showed the model — an
|
|
79
|
+
* `fs.read` result, a search's file contents — judges exactly that, and a
|
|
80
|
+
* peer rewriting the ledger in place after the read (same inode, new
|
|
81
|
+
* bytes) cannot make the shown lines read as unlabelled.
|
|
82
|
+
*/
|
|
83
|
+
export declare const ledgerTrustOfContent: (raw: string) => TrustLevel | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* The durable ledger, one append-only JSONL file per agent at
|
|
86
|
+
* `<workspaceRoot>/<agentId>/fs-provenance.jsonl`, owner-only. Each record
|
|
87
|
+
* is one `O_APPEND` write, so processes that share an agent — the daemon and
|
|
88
|
+
* a `stratus run` — interleave records rather than overwrite each other.
|
|
89
|
+
*/
|
|
90
|
+
export declare const createFileLedger: (workspaceRoot: string) => TaintedWriteLedger;
|
|
91
|
+
/**
|
|
92
|
+
* Whether `absolutePath` is an agent's ledger — exactly
|
|
93
|
+
* `<workspaceRoot>/<agentId>/fs-provenance.json`, any agent's — which is the
|
|
94
|
+
* one path `fs.write` refuses. Exactly that depth, not any descendant with
|
|
95
|
+
* the name: a project an agent keeps under its workspace may legitimately
|
|
96
|
+
* have a file called `fs-provenance.json` of its own.
|
|
97
|
+
*
|
|
98
|
+
* `workspaceRoots` are every spelling of the workspace root worth checking
|
|
99
|
+
* — the configured path and its canonical form — because the path being
|
|
100
|
+
* judged arrives canonical from the root resolver, and a workspace an
|
|
101
|
+
* operator moved behind a symlink would otherwise compare as outside.
|
|
102
|
+
*/
|
|
103
|
+
export declare const isLedgerPath: (workspaceRoots: readonly string[], absolutePath: string) => boolean;
|
|
104
|
+
/**
|
|
105
|
+
* The predicate `fs.write` refuses on and `fs.read` labels by, built once
|
|
106
|
+
* per tool call from what the workspace holds right now. A path is an
|
|
107
|
+
* agent's ledger two ways: lexically —
|
|
108
|
+
* `<workspaceRoot>/<agent>/fs-provenance.jsonl` under the configured or the
|
|
109
|
+
* canonical root — or through a link at the agent directory. An operator
|
|
110
|
+
* who relocated one agent's workspace with `<workspaceRoot>/ava -> /data/ava`
|
|
111
|
+
* has the ledger at `/data/ava/fs-provenance.jsonl`, which is how the root
|
|
112
|
+
* resolver spells every path under it and which no spelling of the root
|
|
113
|
+
* reaches, so every entry of the workspace is canonicalized and its ledger
|
|
114
|
+
* path listed — the ledger file's own canonical path too, for a link at
|
|
115
|
+
* the file rather than the directory, and its device and inode, for a hard
|
|
116
|
+
* link to it from inside a root. Built per call and never cached: a
|
|
117
|
+
* link repointed under a running daemon is judged where it points now, and
|
|
118
|
+
* one `readdir` plus two `realpath`s per agent is nothing next to the write.
|
|
119
|
+
*/
|
|
120
|
+
export interface FileIdentity {
|
|
121
|
+
dev: number;
|
|
122
|
+
ino: number;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Whether a path is an agent's ledger. `identity` is the inode the caller
|
|
126
|
+
* already holds for it — captured by the root resolver and verified by the
|
|
127
|
+
* open, so it is the file whose bytes were read or are about to be
|
|
128
|
+
* written. Given, it is what is judged; without it the path is stat'd,
|
|
129
|
+
* which names whatever is there *now*, and a peer can swap the name
|
|
130
|
+
* between a read and this check.
|
|
131
|
+
*/
|
|
132
|
+
export type LedgerGuard = (absolutePath: string, identity?: FileIdentity) => Promise<boolean>;
|
|
133
|
+
export declare const ledgerGuard: (workspaceRoot: string | undefined) => Promise<LedgerGuard>;
|
|
134
|
+
/**
|
|
135
|
+
* Whether `absolutePath`, spelled exactly so and through no link, names the
|
|
136
|
+
* file `handle` holds open — right now. Both halves matter: the canonical
|
|
137
|
+
* spelling must be the name itself, or a directory on the way is a link and
|
|
138
|
+
* the file lives somewhere else; and the inode at that name must be the
|
|
139
|
+
* handle's, or the name has since been given to a decoy while the handle
|
|
140
|
+
* still refers to wherever the link sent the create. A pathname comparison
|
|
141
|
+
* alone passes the second case.
|
|
142
|
+
*/
|
|
143
|
+
export declare const nameIdentifiesHandle: (absolutePath: string, handle: FileHandle) => Promise<boolean>;
|
|
144
|
+
//# sourceMappingURL=provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,EAA8B,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,kBAAkB;IACjC,2FAA2F;IAC3F,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAC/E;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC/D;;;;;;;;;OASG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtF;AAED,wGAAwG;AACxG,eAAO,MAAM,eAAe,wBAAwB,CAAC;AAiCrD,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,QAAO,kBAa3C,CAAC;AAiCF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC7B,gBAAgB,MAAM;AACtB;;;;;;GAMG;AACH,WAAW,YAAY,KACtB,OAAO,CAAC,UAAU,GAAG,SAAS,CA0BhC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,KAAG,UAAU,GAAG,SAW/D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,eAAe,MAAM,KAAG,kBAmExD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,GAAI,gBAAgB,SAAS,MAAM,EAAE,EAAE,cAAc,MAAM,KAAG,OAQnF,CAAC;AAEL;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAI9F,eAAO,MAAM,WAAW,GAAU,eAAe,MAAM,GAAG,SAAS,KAAG,OAAO,CAAC,WAAW,CAgExF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EACpB,QAAQ,UAAU,KACjB,OAAO,CAAC,OAAO,CAUjB,CAAC"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { constants } from 'node:fs';
|
|
2
|
+
import { lstat, mkdir, open, readdir, readFile, realpath, stat } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { isTrustLevel, leastTrusted } from '@stratusagent/core';
|
|
5
|
+
/** The ledger's filename inside an agent's workspace. Exported so `fs.write` can refuse to write it. */
|
|
6
|
+
export const LEDGER_FILENAME = 'fs-provenance.jsonl';
|
|
7
|
+
const tainted = (trust) => trust === 'external' || trust === 'unknown';
|
|
8
|
+
/** The ledger after a write at `trust`: the same object when nothing changed. */
|
|
9
|
+
const nextPaths = (paths, absolutePath, trust) => {
|
|
10
|
+
if (!tainted(trust)) {
|
|
11
|
+
return paths;
|
|
12
|
+
}
|
|
13
|
+
const recorded = paths[absolutePath];
|
|
14
|
+
const combined = recorded !== undefined ? leastTrusted(recorded, trust) : trust;
|
|
15
|
+
return combined === recorded ? paths : { ...paths, [absolutePath]: combined };
|
|
16
|
+
};
|
|
17
|
+
/** In-process only. See `TaintedWriteLedger` for when this is what a host gets. */
|
|
18
|
+
export const createProcessLocalLedger = () => {
|
|
19
|
+
const byAgent = new Map();
|
|
20
|
+
return {
|
|
21
|
+
async lookup(agentId, absolutePath) {
|
|
22
|
+
return byAgent.get(agentId)?.[absolutePath];
|
|
23
|
+
},
|
|
24
|
+
async snapshot(agentId) {
|
|
25
|
+
return { ...(byAgent.get(agentId) ?? {}) };
|
|
26
|
+
},
|
|
27
|
+
async recordWrite(agentId, absolutePath, trust) {
|
|
28
|
+
byAgent.set(agentId, nextPaths(byAgent.get(agentId) ?? {}, absolutePath, trust));
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const parseLedger = (raw, filePath) => {
|
|
33
|
+
const paths = {};
|
|
34
|
+
for (const line of raw.split('\n')) {
|
|
35
|
+
if (line.trim().length === 0) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(line);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new Error(`The filesystem provenance ledger has an invalid line: ${filePath}. Delete it to start over; the cost is that files written by earlier tainted sessions read as the agent's own.`);
|
|
44
|
+
}
|
|
45
|
+
if (typeof parsed !== 'object' || parsed === null || typeof parsed.path !== 'string') {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const record = parsed;
|
|
49
|
+
// Order-independent on purpose: labels only ever go down, so the lowest
|
|
50
|
+
// record for a path stands whichever process appended first. A path is
|
|
51
|
+
// in the ledger because a tainted write put it there, so a record whose
|
|
52
|
+
// label is missing or one nobody recognises — a hand edit, a label from
|
|
53
|
+
// a newer build — still marks the path: at `unknown`, never dropped,
|
|
54
|
+
// which would read the file back as the agent's own.
|
|
55
|
+
const label = isTrustLevel(record.trust) ? record.trust : 'unknown';
|
|
56
|
+
if (tainted(label)) {
|
|
57
|
+
const recorded = paths[record.path];
|
|
58
|
+
paths[record.path] = recorded !== undefined ? leastTrusted(recorded, label) : label;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return paths;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The label the ledger's own contents carry: the lowest label recorded in
|
|
65
|
+
* it. Every path in it was chosen by a session at `unknown` or `external`,
|
|
66
|
+
* and the ledger's own path has no record, so an agent whose roots cover
|
|
67
|
+
* its workspace could otherwise `fs.read` the ledger and get a list of
|
|
68
|
+
* attacker-chosen filenames back at `agent`. Undefined for no file or an
|
|
69
|
+
* empty one; `unknown` for a file that holds lines nothing here can read
|
|
70
|
+
* — nobody vouches for those.
|
|
71
|
+
*/
|
|
72
|
+
export const ledgerContentTrust = async (ledgerFilePath,
|
|
73
|
+
/**
|
|
74
|
+
* The inode the caller read the ledger at. Given, the bytes judged are
|
|
75
|
+
* read from that inode or not at all: a name swapped for an empty file
|
|
76
|
+
* after the caller's read would otherwise be read here as "no label",
|
|
77
|
+
* while the result still shows the ledger's contents. A swap reads
|
|
78
|
+
* `unknown` — nobody can vouch for bytes they can no longer see.
|
|
79
|
+
*/
|
|
80
|
+
identity) => {
|
|
81
|
+
let raw;
|
|
82
|
+
try {
|
|
83
|
+
if (identity !== undefined) {
|
|
84
|
+
const handle = await open(ledgerFilePath, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
85
|
+
try {
|
|
86
|
+
const info = await handle.stat();
|
|
87
|
+
if (info.dev !== identity.dev || info.ino !== identity.ino) {
|
|
88
|
+
return 'unknown';
|
|
89
|
+
}
|
|
90
|
+
raw = (await handle.readFile()).toString('utf8');
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
await handle.close();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
raw = await readFile(ledgerFilePath, 'utf8');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const code = error.code;
|
|
102
|
+
if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EISDIR' || code === 'ELOOP') {
|
|
103
|
+
// With an inode in hand, a name that no longer opens is a swap too.
|
|
104
|
+
return identity !== undefined ? 'unknown' : undefined;
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
return ledgerTrustOfContent(raw);
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* The label a ledger's bytes carry: the lowest recorded label, `unknown`
|
|
112
|
+
* for lines nothing here can read, nothing for an empty file. Pure over the
|
|
113
|
+
* bytes, so a caller that already holds what it showed the model — an
|
|
114
|
+
* `fs.read` result, a search's file contents — judges exactly that, and a
|
|
115
|
+
* peer rewriting the ledger in place after the read (same inode, new
|
|
116
|
+
* bytes) cannot make the shown lines read as unlabelled.
|
|
117
|
+
*/
|
|
118
|
+
export const ledgerTrustOfContent = (raw) => {
|
|
119
|
+
if (raw.trim().length === 0) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
let labels;
|
|
123
|
+
try {
|
|
124
|
+
labels = Object.values(parseLedger(raw, 'the ledger as read'));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return 'unknown';
|
|
128
|
+
}
|
|
129
|
+
return labels.length > 0 ? leastTrusted(...labels) : 'unknown';
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* The durable ledger, one append-only JSONL file per agent at
|
|
133
|
+
* `<workspaceRoot>/<agentId>/fs-provenance.jsonl`, owner-only. Each record
|
|
134
|
+
* is one `O_APPEND` write, so processes that share an agent — the daemon and
|
|
135
|
+
* a `stratus run` — interleave records rather than overwrite each other.
|
|
136
|
+
*/
|
|
137
|
+
export const createFileLedger = (workspaceRoot) => {
|
|
138
|
+
const ledgerPath = (agentId) => path.join(workspaceRoot, agentId, LEDGER_FILENAME);
|
|
139
|
+
const read = async (agentId) => {
|
|
140
|
+
const filePath = ledgerPath(agentId);
|
|
141
|
+
try {
|
|
142
|
+
return parseLedger(await readFile(filePath, 'utf8'), filePath);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
// No ledger yet, or a workspace root that is not a directory: either
|
|
146
|
+
// way nothing has been recorded. A tainted write still fails when it
|
|
147
|
+
// tries to record, which is the failure direction that leaves no
|
|
148
|
+
// unlabelled file behind.
|
|
149
|
+
const code = error.code;
|
|
150
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
151
|
+
return {};
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
// Serialized per process: two writes in one turn are rare, but a
|
|
157
|
+
// read-modify-write with no lock would let the second drop the first.
|
|
158
|
+
let chain = Promise.resolve();
|
|
159
|
+
return {
|
|
160
|
+
lookup: (agentId, absolutePath) => read(agentId).then((paths) => paths[absolutePath]),
|
|
161
|
+
snapshot: (agentId) => read(agentId),
|
|
162
|
+
recordWrite(agentId, absolutePath, trust) {
|
|
163
|
+
const work = chain.then(async () => {
|
|
164
|
+
if (!tainted(trust)) {
|
|
165
|
+
// A clean write records nothing — see `TaintedWriteLedger`.
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const filePath = ledgerPath(agentId);
|
|
169
|
+
await mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
170
|
+
// Read and appended through ONE descriptor. A ledger reached through
|
|
171
|
+
// a link (a relocated workspace, a relocated file — both supported)
|
|
172
|
+
// is resolved once, at this open; a second open for the append
|
|
173
|
+
// would resolve the name afresh, and a link repointed in between
|
|
174
|
+
// would put the record in some other file while the bytes it
|
|
175
|
+
// describes land where the resolver already decided. O_APPEND keeps
|
|
176
|
+
// the write itself atomic against a peer's.
|
|
177
|
+
const handle = await open(filePath, constants.O_RDWR | constants.O_CREAT | constants.O_APPEND, 0o600);
|
|
178
|
+
try {
|
|
179
|
+
// Tightened before the append, like the memory file: the mode on
|
|
180
|
+
// open applies only when it creates the file.
|
|
181
|
+
await handle.chmod(0o600);
|
|
182
|
+
const before = parseLedger((await handle.readFile()).toString('utf8'), filePath);
|
|
183
|
+
const paths = nextPaths(before, absolutePath, trust);
|
|
184
|
+
// A path already at this label or lower needs no second line.
|
|
185
|
+
if (paths === before) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const record = {
|
|
189
|
+
path: absolutePath,
|
|
190
|
+
trust: paths[absolutePath],
|
|
191
|
+
at: new Date().toISOString(),
|
|
192
|
+
};
|
|
193
|
+
await handle.appendFile(`${JSON.stringify(record)}\n`);
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
await handle.close();
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
chain = work.then(() => undefined, () => undefined);
|
|
200
|
+
return work;
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Whether `absolutePath` is an agent's ledger — exactly
|
|
206
|
+
* `<workspaceRoot>/<agentId>/fs-provenance.json`, any agent's — which is the
|
|
207
|
+
* one path `fs.write` refuses. Exactly that depth, not any descendant with
|
|
208
|
+
* the name: a project an agent keeps under its workspace may legitimately
|
|
209
|
+
* have a file called `fs-provenance.json` of its own.
|
|
210
|
+
*
|
|
211
|
+
* `workspaceRoots` are every spelling of the workspace root worth checking
|
|
212
|
+
* — the configured path and its canonical form — because the path being
|
|
213
|
+
* judged arrives canonical from the root resolver, and a workspace an
|
|
214
|
+
* operator moved behind a symlink would otherwise compare as outside.
|
|
215
|
+
*/
|
|
216
|
+
export const isLedgerPath = (workspaceRoots, absolutePath) => workspaceRoots.some((workspaceRoot) => {
|
|
217
|
+
const relative = path.relative(workspaceRoot, absolutePath);
|
|
218
|
+
if (relative.length === 0 || relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
const segments = relative.split(path.sep);
|
|
222
|
+
return segments.length === 2 && segments[1] === LEDGER_FILENAME;
|
|
223
|
+
});
|
|
224
|
+
const identityKey = (identity) => `${identity.dev}:${identity.ino}`;
|
|
225
|
+
export const ledgerGuard = async (workspaceRoot) => {
|
|
226
|
+
if (workspaceRoot === undefined) {
|
|
227
|
+
return async () => false;
|
|
228
|
+
}
|
|
229
|
+
const roots = [workspaceRoot];
|
|
230
|
+
try {
|
|
231
|
+
const canonical = await realpath(workspaceRoot);
|
|
232
|
+
if (canonical !== workspaceRoot) {
|
|
233
|
+
roots.push(canonical);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// Not there yet: only the configured spelling, and no agents to list.
|
|
238
|
+
}
|
|
239
|
+
const ledgers = new Set();
|
|
240
|
+
// And the files themselves, by identity: a hard link to a ledger from
|
|
241
|
+
// inside a root has a path no spelling reaches and `realpath` leaves
|
|
242
|
+
// alone, and is the same bytes. Plain numbers, like the resolver's
|
|
243
|
+
// `identity`, so the two sides of a comparison round the same way.
|
|
244
|
+
const identities = new Set();
|
|
245
|
+
let entries = [];
|
|
246
|
+
try {
|
|
247
|
+
entries = await readdir(workspaceRoot, { withFileTypes: true });
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
entries = [];
|
|
251
|
+
}
|
|
252
|
+
for (const entry of entries) {
|
|
253
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const lexical = path.join(workspaceRoot, entry.name, LEDGER_FILENAME);
|
|
257
|
+
// Two canonical spellings, because either component can be a link: the
|
|
258
|
+
// agent directory (`ava -> /data/ava`, judged even before the ledger
|
|
259
|
+
// exists there) and the ledger file itself (`fs-provenance.jsonl ->
|
|
260
|
+
// /data/ava-ledger.jsonl`, whose target is what the resolver returns
|
|
261
|
+
// and what a truncating write would empty).
|
|
262
|
+
try {
|
|
263
|
+
ledgers.add(path.join(await realpath(path.dirname(lexical)), LEDGER_FILENAME));
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// A dangling link holds no ledger.
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
ledgers.add(await realpath(lexical));
|
|
270
|
+
identities.add(identityKey(await stat(lexical)));
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// No ledger there yet, or a dangling link: nothing to protect.
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return async (absolutePath, identity) => {
|
|
277
|
+
if (isLedgerPath(roots, absolutePath) || ledgers.has(absolutePath)) {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
if (identities.size === 0) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
if (identity !== undefined) {
|
|
284
|
+
return identities.has(identityKey(identity));
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
return identities.has(identityKey(await stat(absolutePath)));
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Nothing there: a write about to create a file is not the ledger.
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Whether `absolutePath`, spelled exactly so and through no link, names the
|
|
297
|
+
* file `handle` holds open — right now. Both halves matter: the canonical
|
|
298
|
+
* spelling must be the name itself, or a directory on the way is a link and
|
|
299
|
+
* the file lives somewhere else; and the inode at that name must be the
|
|
300
|
+
* handle's, or the name has since been given to a decoy while the handle
|
|
301
|
+
* still refers to wherever the link sent the create. A pathname comparison
|
|
302
|
+
* alone passes the second case.
|
|
303
|
+
*/
|
|
304
|
+
export const nameIdentifiesHandle = async (absolutePath, handle) => {
|
|
305
|
+
try {
|
|
306
|
+
if ((await realpath(absolutePath)) !== absolutePath) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
const [atName, held] = await Promise.all([lstat(absolutePath), handle.stat()]);
|
|
310
|
+
return atName.dev === held.dev && atName.ino === held.ino;
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
//# sourceMappingURL=provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.js","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGzF,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAmB,MAAM,oBAAoB,CAAC;AAqDjF,wGAAwG;AACxG,MAAM,CAAC,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAiBrD,MAAM,OAAO,GAAG,CAAC,KAAiB,EAAW,EAAE,CAAC,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,SAAS,CAAC;AAE5F,iFAAiF;AACjF,MAAM,SAAS,GAAG,CAChB,KAAiC,EACjC,YAAoB,EACpB,KAAiB,EACW,EAAE;IAC9B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAChF,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC;AAChF,CAAC,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAuB,EAAE;IAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsC,CAAC;IAC9D,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY;YAChC,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;QAC9C,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,OAAO;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QAC7C,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK;YAC5C,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;QACnF,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,GAAW,EAAE,QAAgB,EAA8B,EAAE;IAChF,MAAM,KAAK,GAA+B,EAAE,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,SAAS;QACX,CAAC;QACD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,yDAAyD,QAAQ,gHAAgH,CAAC,CAAC;QACrM,CAAC;QACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,OAAQ,MAAuB,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvG,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,MAA+B,CAAC;QAC/C,wEAAwE;QACxE,uEAAuE;QACvE,wEAAwE;QACxE,wEAAwE;QACxE,qEAAqE;QACrE,qDAAqD;QACrD,MAAM,KAAK,GAAe,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QAChF,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACnB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,IAAK,CAAC,CAAC;YACrC,KAAK,CAAC,MAAM,CAAC,IAAK,CAAC,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACvF,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,cAAsB;AACtB;;;;;;GAMG;AACH,QAAuB,EACU,EAAE;IACnC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,QAAQ,GAAG,CAAC,SAAS,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5F,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjC,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC;oBAC3D,OAAO,SAAS,CAAC;gBACnB,CAAC;gBACD,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACnD,CAAC;oBAAS,CAAC;gBACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,MAAM,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;QACnD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrF,oEAAoE;YACpE,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACxD,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,OAAO,oBAAoB,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAW,EAA0B,EAAE;IAC1E,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,MAAoB,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,aAAqB,EAAsB,EAAE;IAC5E,MAAM,UAAU,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;IAEnG,MAAM,IAAI,GAAG,KAAK,EAAE,OAAe,EAAuC,EAAE;QAC1E,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC;YACH,OAAO,WAAW,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,qEAAqE;YACrE,qEAAqE;YACrE,iEAAiE;YACjE,0BAA0B;YAC1B,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;YACnD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC5C,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IAEF,iEAAiE;IACjE,sEAAsE;IACtE,IAAI,KAAK,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAE7C,OAAO;QACL,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACrF,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;QACpC,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBACjC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACpB,4DAA4D;oBAC5D,OAAO;gBACT,CAAC;gBACD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;gBACrC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;gBACtE,qEAAqE;gBACrE,oEAAoE;gBACpE,+DAA+D;gBAC/D,iEAAiE;gBACjE,6DAA6D;gBAC7D,oEAAoE;gBACpE,4CAA4C;gBAC5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACtG,IAAI,CAAC;oBACH,iEAAiE;oBACjE,8CAA8C;oBAC9C,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBAC1B,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;oBACjF,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;oBACrD,8DAA8D;oBAC9D,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;wBACrB,OAAO;oBACT,CAAC;oBACD,MAAM,MAAM,GAAiB;wBAC3B,IAAI,EAAE,YAAY;wBAClB,KAAK,EAAE,KAAK,CAAC,YAAY,CAAE;wBAC3B,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;qBAC7B,CAAC;oBACF,MAAM,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACzD,CAAC;wBAAS,CAAC;oBACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;gBACvB,CAAC;YACH,CAAC,CAAC,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,cAAiC,EAAE,YAAoB,EAAW,EAAE,CAC/F,cAAc,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,EAAE;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC5D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,eAAe,CAAC;AAClE,CAAC,CAAC,CAAC;AAiCL,MAAM,WAAW,GAAG,CAAC,QAAsB,EAAU,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;AAE1F,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,aAAiC,EAAwB,EAAE;IAC3F,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC;IAC3B,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,aAAa,CAAC,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC;QAChD,IAAI,SAAS,KAAK,aAAa,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;IACxE,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,sEAAsE;IACtE,qEAAqE;IACrE,mEAAmE;IACnE,mEAAmE;IACnE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,EAAE,CAAC;IACf,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YACpD,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACtE,uEAAuE;QACvE,qEAAqE;QACrE,oEAAoE;QACpE,qEAAqE;QACrE,4CAA4C;QAC5C,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC;QACjF,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;QACD,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YACrC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,+DAA+D;QACjE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE;QACtC,IAAI,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;YACnE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,CAAC;YACH,OAAO,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;YACnE,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,EACvC,YAAoB,EACpB,MAAkB,EACA,EAAE;IACpB,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC;YACpD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/E,OAAO,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stratusagent/plugins",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"node": ">=22.13 <23 || >=23.4"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stratusagent/agents": "0.11.
|
|
22
|
-
"@stratusagent/core": "0.11.
|
|
21
|
+
"@stratusagent/agents": "0.11.2",
|
|
22
|
+
"@stratusagent/core": "0.11.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^24.6.0",
|