fraim 2.0.360-next.0 → 2.0.360-next.1
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/registry/domains.json +159 -0
- package/dist/registry/scripts/domains/catalog.js +112 -0
- package/dist/registry/scripts/learning/entry-authoring.js +63 -25
- package/dist/registry/scripts/learning/run-application.js +12 -2
- package/dist/registry/scripts/learning/run-publication.js +169 -16
- package/dist/registry/scripts/learning-usage/local-runtime.js +1 -3
- package/dist/src/cli/learning/runtime.js +2 -0
- package/dist/src/cli/utils/remote-sync.js +7 -0
- package/dist/src/cli/utils/script-sync-utils.js +4 -0
- package/dist/src/config/learning-domains.js +24 -108
- package/dist/src/core/ai-mentor.js +7 -0
- package/dist/src/core/utils/domain-rule-assembler.js +206 -0
- package/dist/src/core/utils/local-registry-resolver.js +135 -32
- package/dist/src/local-mcp-server/agent-token-prices.js +25 -1
- package/dist/src/local-mcp-server/learning-context-builder.js +94 -36
- package/dist/src/local-mcp-server/stdio-server.js +122 -39
- package/dist/src/mcp/tool-schemas.js +2 -2
- package/dist/src/services/mcp-service.js +35 -16
- package/dist/src/services/registry-service.js +4 -0
- package/package.json +3 -2
- package/dist/registry/scripts/learning/learning-domains.json +0 -80
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"mapVersion": 3,
|
|
4
|
+
"domains": {
|
|
5
|
+
"global": {
|
|
6
|
+
"purpose": "Shared integrity, authority, communication and evidence mandates",
|
|
7
|
+
"rules": [
|
|
8
|
+
"rules/constitution.md",
|
|
9
|
+
"rules/evidence-cardinality.md"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"ai-engineering": {
|
|
13
|
+
"purpose": "Build and validate AI systems",
|
|
14
|
+
"rules": [
|
|
15
|
+
"rules/ai-engineering/ai-engineering.md"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"bank-compliance": {
|
|
19
|
+
"purpose": "Review banking compliance evidence",
|
|
20
|
+
"rules": [
|
|
21
|
+
"rules/bank-compliance/bank-compliance.md"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"bank-operations": {
|
|
25
|
+
"purpose": "Prepare banking and KYC operations",
|
|
26
|
+
"rules": [
|
|
27
|
+
"rules/bank-operations/bank-operations.md"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"career": {
|
|
31
|
+
"purpose": "Support professional development",
|
|
32
|
+
"rules": []
|
|
33
|
+
},
|
|
34
|
+
"customer": {
|
|
35
|
+
"purpose": "Support customer relationships and systems",
|
|
36
|
+
"rules": [
|
|
37
|
+
"rules/customer/customer.md"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"design": {
|
|
41
|
+
"purpose": "Create and evaluate user experiences and brand systems",
|
|
42
|
+
"rules": [
|
|
43
|
+
"rules/design/design.md"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"dev-ops": {
|
|
47
|
+
"purpose": "Operate infrastructure and production services",
|
|
48
|
+
"rules": [
|
|
49
|
+
"rules/dev-ops/dev-ops.md"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"engineering": {
|
|
53
|
+
"purpose": "Implement and maintain software",
|
|
54
|
+
"rules": [
|
|
55
|
+
"rules/engineering/engineering.md"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"executive-assistance": {
|
|
59
|
+
"purpose": "Support executive coordination",
|
|
60
|
+
"rules": []
|
|
61
|
+
},
|
|
62
|
+
"finance": {
|
|
63
|
+
"purpose": "Analyze finances and prepare funding",
|
|
64
|
+
"rules": [
|
|
65
|
+
"rules/finance/finance.md"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"fraim": {
|
|
69
|
+
"purpose": "Author and maintain FRAIM capabilities",
|
|
70
|
+
"rules": [
|
|
71
|
+
"rules/fraim/artifact-boundaries.md"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"go-to-market": {
|
|
75
|
+
"purpose": "Plan and execute market positioning, demand and launches",
|
|
76
|
+
"rules": [
|
|
77
|
+
"rules/go-to-market/go-to-market.md"
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"inclusion": {
|
|
81
|
+
"purpose": "Evaluate inclusion policy and workforce evidence",
|
|
82
|
+
"rules": [
|
|
83
|
+
"rules/inclusion/inclusion.md"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"legal": {
|
|
87
|
+
"purpose": "Prepare legal analysis and filings for qualified review",
|
|
88
|
+
"rules": [
|
|
89
|
+
"rules/legal/legal.md"
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"management": {
|
|
93
|
+
"purpose": "Coordinate people and work",
|
|
94
|
+
"rules": []
|
|
95
|
+
},
|
|
96
|
+
"people-ops": {
|
|
97
|
+
"purpose": "Support workforce operations",
|
|
98
|
+
"rules": [
|
|
99
|
+
"rules/people-ops/people-ops.md"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"procurement": {
|
|
103
|
+
"purpose": "Evaluate suppliers and prepare purchasing decisions",
|
|
104
|
+
"rules": [
|
|
105
|
+
"rules/procurement/procurement.md"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"product": {
|
|
109
|
+
"purpose": "Define and validate customer outcomes",
|
|
110
|
+
"rules": [
|
|
111
|
+
"rules/product/product.md"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"project-management": {
|
|
115
|
+
"purpose": "Plan and govern delivery",
|
|
116
|
+
"rules": [
|
|
117
|
+
"rules/project-management/project-management.md"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"quality-assurance": {
|
|
121
|
+
"purpose": "Validate product behavior and quality",
|
|
122
|
+
"rules": [
|
|
123
|
+
"rules/quality-assurance/quality-assurance.md"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"recruiting": {
|
|
127
|
+
"purpose": "Source and assess candidates",
|
|
128
|
+
"rules": [
|
|
129
|
+
"rules/recruiting/recruiting.md"
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
"sales": {
|
|
133
|
+
"purpose": "Advance named customer opportunities and commercial deals",
|
|
134
|
+
"rules": [
|
|
135
|
+
"rules/sales/sales.md"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
"salesforce": {
|
|
139
|
+
"purpose": "Configure Salesforce workflows and data",
|
|
140
|
+
"rules": [
|
|
141
|
+
"rules/salesforce/salesforce.md"
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
"security": {
|
|
145
|
+
"purpose": "Assess security and compliance controls",
|
|
146
|
+
"rules": [
|
|
147
|
+
"rules/security/security.md"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"strategy": {
|
|
151
|
+
"purpose": "Develop organizational strategy",
|
|
152
|
+
"rules": []
|
|
153
|
+
},
|
|
154
|
+
"travel": {
|
|
155
|
+
"purpose": "Plan travel",
|
|
156
|
+
"rules": []
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isSafeRegistryPath = isSafeRegistryPath;
|
|
4
|
+
exports.jobDomainFromRegistryPath = jobDomainFromRegistryPath;
|
|
5
|
+
exports.validateDomainCatalog = validateDomainCatalog;
|
|
6
|
+
exports.mergeDomainCatalog = mergeDomainCatalog;
|
|
7
|
+
exports.selectCatalogDomain = selectCatalogDomain;
|
|
8
|
+
function hasOwn(value, key) {
|
|
9
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
10
|
+
}
|
|
11
|
+
function isSafeDomainId(domain) {
|
|
12
|
+
return /^[a-z][a-z-]*$/.test(domain) && !['__proto__', 'constructor', 'prototype'].includes(domain);
|
|
13
|
+
}
|
|
14
|
+
function isSafeRegistryPath(value, kind) {
|
|
15
|
+
return typeof value === 'string' && new RegExp(`^${kind}/[a-zA-Z0-9_/-]+\\.md$`).test(value)
|
|
16
|
+
&& !value.includes('//') && !value.split('/').some(part => part === '.' || part === '..');
|
|
17
|
+
}
|
|
18
|
+
/** Extract a folder, never a frontmatter declaration, leaf-name alias or employee. */
|
|
19
|
+
function jobDomainFromRegistryPath(path) {
|
|
20
|
+
if (typeof path !== 'string')
|
|
21
|
+
return null;
|
|
22
|
+
const clean = path.replace(/\\/g, '/').replace(/^registry\//, '');
|
|
23
|
+
if (!isSafeRegistryPath(clean, 'jobs'))
|
|
24
|
+
return null;
|
|
25
|
+
const rolePath = /^jobs\/(?:ai-employee|ai-manager|personalized-employee)\//.test(clean);
|
|
26
|
+
const match = clean.match(rolePath
|
|
27
|
+
? /^jobs\/(?:ai-employee|ai-manager|personalized-employee)\/([a-z][a-z-]*)\/[^/]+\.md$/
|
|
28
|
+
: /^jobs\/([a-z][a-z-]*)\/[^/]+\.md$/);
|
|
29
|
+
return match && isSafeDomainId(match[1]) ? match[1] : null;
|
|
30
|
+
}
|
|
31
|
+
function validateDomainCatalog(value) {
|
|
32
|
+
const errors = [];
|
|
33
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
34
|
+
return ['Catalog must be an object'];
|
|
35
|
+
const catalog = value;
|
|
36
|
+
if (catalog.schemaVersion !== 2)
|
|
37
|
+
errors.push('Unsupported catalog schemaVersion');
|
|
38
|
+
if (!Number.isInteger(catalog.mapVersion) || catalog.mapVersion < 3)
|
|
39
|
+
errors.push('Invalid catalog mapVersion');
|
|
40
|
+
if (hasOwn(catalog, 'unmigratedJobs'))
|
|
41
|
+
errors.push('Job rollout lists are obsolete; folders own domains');
|
|
42
|
+
if (!hasOwn(catalog, 'domains') || !catalog.domains || typeof catalog.domains !== 'object' || Array.isArray(catalog.domains))
|
|
43
|
+
return [...errors, 'Missing domains'];
|
|
44
|
+
if (!hasOwn(catalog.domains, 'global'))
|
|
45
|
+
errors.push('Missing global domain');
|
|
46
|
+
for (const [domain, definition] of Object.entries(catalog.domains)) {
|
|
47
|
+
if (!isSafeDomainId(domain))
|
|
48
|
+
errors.push(`Invalid domain: ${domain}`);
|
|
49
|
+
if (!definition || typeof definition !== 'object' || Array.isArray(definition)) {
|
|
50
|
+
errors.push(`Invalid definition: ${domain}`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (hasOwn(definition, 'jobs'))
|
|
54
|
+
errors.push(`Job membership is obsolete: ${domain}`);
|
|
55
|
+
if (!hasOwn(definition, 'purpose') || typeof definition.purpose !== 'string' || !definition.purpose.trim())
|
|
56
|
+
errors.push(`Missing purpose: ${domain}`);
|
|
57
|
+
if (!hasOwn(definition, 'rules') || !Array.isArray(definition.rules))
|
|
58
|
+
errors.push(`Missing rules: ${domain}`);
|
|
59
|
+
else {
|
|
60
|
+
const paths = new Set();
|
|
61
|
+
for (const rule of definition.rules) {
|
|
62
|
+
if (!isSafeRegistryPath(rule, 'rules'))
|
|
63
|
+
errors.push(`Invalid rule path: ${rule}`);
|
|
64
|
+
if (paths.has(rule))
|
|
65
|
+
errors.push(`Duplicate rule: ${rule}`);
|
|
66
|
+
paths.add(rule);
|
|
67
|
+
}
|
|
68
|
+
if (domain === 'global' && !paths.has('rules/constitution.md'))
|
|
69
|
+
errors.push('Global rules must include the constitution');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return errors;
|
|
73
|
+
}
|
|
74
|
+
/** Authorized overlays configure rules; they cannot reassign a job or employee. */
|
|
75
|
+
function mergeDomainCatalog(base, extension) {
|
|
76
|
+
const baseErrors = validateDomainCatalog(base);
|
|
77
|
+
if (baseErrors.length)
|
|
78
|
+
throw new Error(baseErrors.join('; '));
|
|
79
|
+
if (!extension || typeof extension !== 'object' || Array.isArray(extension)
|
|
80
|
+
|| !hasOwn(extension, 'domains') || !extension.domains || typeof extension.domains !== 'object' || Array.isArray(extension.domains)) {
|
|
81
|
+
throw new Error('Catalog extension must define domains');
|
|
82
|
+
}
|
|
83
|
+
if (extension.schemaVersion !== undefined && extension.schemaVersion !== 2)
|
|
84
|
+
throw new Error('Unsupported catalog extension schemaVersion');
|
|
85
|
+
if (hasOwn(extension, 'unmigratedJobs'))
|
|
86
|
+
throw new Error('Job rollout lists are obsolete; folders own domains');
|
|
87
|
+
const result = JSON.parse(JSON.stringify(base));
|
|
88
|
+
for (const [domain, definition] of Object.entries(extension.domains)) {
|
|
89
|
+
if (!isSafeDomainId(domain))
|
|
90
|
+
throw new Error(`Invalid domain: ${domain}`);
|
|
91
|
+
if (!definition || typeof definition !== 'object' || Array.isArray(definition))
|
|
92
|
+
throw new Error(`Invalid definition: ${domain}`);
|
|
93
|
+
result.domains[domain] = { ...(hasOwn(result.domains, domain) ? result.domains[domain] : {}), ...definition };
|
|
94
|
+
}
|
|
95
|
+
if (extension.mapVersion !== undefined)
|
|
96
|
+
result.mapVersion = extension.mapVersion;
|
|
97
|
+
const errors = validateDomainCatalog(result);
|
|
98
|
+
if (errors.length)
|
|
99
|
+
throw new Error(errors.join('; '));
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
function selectCatalogDomain(catalog, jobPath) {
|
|
103
|
+
// A broken sibling rule list must not change this job's folder identity.
|
|
104
|
+
if (!catalog || catalog.schemaVersion !== 2 || !catalog.domains || typeof catalog.domains !== 'object'
|
|
105
|
+
|| Array.isArray(catalog.domains))
|
|
106
|
+
return { domain: null, selection: 'unknown' };
|
|
107
|
+
if (!jobPath)
|
|
108
|
+
return { domain: 'global', selection: 'connect' };
|
|
109
|
+
const domain = jobDomainFromRegistryPath(jobPath);
|
|
110
|
+
return domain && hasOwn(catalog.domains, domain)
|
|
111
|
+
? { domain, selection: 'mapped' } : { domain: null, selection: 'unknown' };
|
|
112
|
+
}
|
|
@@ -4,49 +4,87 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.deriveDomain = deriveDomain;
|
|
7
|
+
exports.storedEntryDomain = storedEntryDomain;
|
|
8
|
+
exports.selectStoredLearningEntries = selectStoredLearningEntries;
|
|
7
9
|
exports.readerWordCount = readerWordCount;
|
|
8
10
|
exports.validateEntry = validateEntry;
|
|
9
11
|
exports.renderEntry = renderEntry;
|
|
10
12
|
exports.isLearningScaffolding = isLearningScaffolding;
|
|
11
13
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const catalog_1 = require("../domains/catalog");
|
|
13
16
|
// Relative to the shipped helper, never to the job's working directory.
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
let catalog = { schemaVersion: 2, mapVersion: 0, domains: {} };
|
|
18
|
+
try {
|
|
19
|
+
const candidate = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(__dirname, '..', '..', 'domains.json'), 'utf8'));
|
|
20
|
+
if (candidate?.schemaVersion !== 2 || !candidate.domains || typeof candidate.domains !== 'object' || Array.isArray(candidate.domains))
|
|
21
|
+
throw new Error('Invalid domain catalog');
|
|
22
|
+
catalog = candidate;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
console.warn('Learning domain catalog unavailable: restore or sync domains.json beside the installed scripts, then retry domain resolution. Existing source files are preserved.');
|
|
26
|
+
}
|
|
27
|
+
const domainData = (effectiveCatalog) => ({ mapVersion: effectiveCatalog?.mapVersion ?? 0, domains: effectiveCatalog?.schemaVersion === 2 && effectiveCatalog.domains && typeof effectiveCatalog.domains === 'object' && !Array.isArray(effectiveCatalog.domains) ? Object.keys(effectiveCatalog.domains).filter(domain => domain !== 'global' && /^[a-z][a-z-]*$/.test(domain)) : [] });
|
|
28
|
+
function deriveDomain(origins, effectiveCatalog = catalog) {
|
|
29
|
+
const map = domainData(effectiveCatalog);
|
|
16
30
|
const unknown = (reason) => ({ status: 'needs-domain', value: null, reason, mapVersion: map.mapVersion });
|
|
17
31
|
if (!origins.length)
|
|
18
32
|
return unknown('No originating job recorded');
|
|
19
33
|
const decisions = [];
|
|
20
34
|
for (const origin of origins) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
declaration = frontmatter[1].match(/^domain:\s*["']?([\w-]+)/m)?.[1];
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (declaration !== undefined) {
|
|
32
|
-
if (declaration !== 'global' && !map.domains.includes(String(declaration)))
|
|
33
|
-
return unknown(`Invalid domain declaration for ${origin.name}`);
|
|
34
|
-
decisions.push({ value: String(declaration), basis: 'job-declaration' });
|
|
35
|
+
// Current folder identity wins; job declarations are no longer domain authority.
|
|
36
|
+
// Missing provenance is not a connect request and must remain unresolved.
|
|
37
|
+
const selected = origin.registryPath ? (0, catalog_1.selectCatalogDomain)(effectiveCatalog, origin.registryPath) : null;
|
|
38
|
+
if (selected?.selection === 'mapped') {
|
|
39
|
+
decisions.push({ value: selected.domain, basis: 'catalog' });
|
|
35
40
|
continue;
|
|
36
41
|
}
|
|
37
|
-
|
|
38
|
-
const category = clean?.match(/^jobs\/(?:ai-employee\/|ai-manager\/|personalized-employee\/)?([^/]+)\/[^/]+\.md$/)?.[1];
|
|
39
|
-
if (!category)
|
|
40
|
-
return unknown(`No mapped job path for ${origin.name}`);
|
|
41
|
-
const value = map.jobDomainMap[category] || (map.globalJobCategories.includes(category) ? 'global' : null);
|
|
42
|
-
if (!value)
|
|
43
|
-
return unknown(`Unmapped category for ${origin.name}`);
|
|
44
|
-
decisions.push({ value, basis: 'category' });
|
|
42
|
+
return unknown(`No current domain folder for ${origin.name}; retain original provenance for review`);
|
|
45
43
|
}
|
|
46
44
|
if (new Set(decisions.map(d => d.value)).size !== 1)
|
|
47
45
|
return unknown('Originating jobs span multiple domains');
|
|
48
46
|
return { status: 'resolved', ...decisions[0], mapVersion: map.mapVersion };
|
|
49
47
|
}
|
|
48
|
+
/** Resolve an existing entry without rewriting its source, ID, dates or approval. */
|
|
49
|
+
function storedEntryDomain(content, storageDomain, effectiveCatalog = catalog) {
|
|
50
|
+
const map = domainData(effectiveCatalog);
|
|
51
|
+
const unknown = (reason) => ({ status: 'needs-domain', value: null, reason, mapVersion: map.mapVersion });
|
|
52
|
+
const fields = (label) => [...content.matchAll(new RegExp(`^\\*\\*${label}\\*\\*:\\s*([^\\r\\n]+)\\r?$`, 'gm'))].map(match => match[1].trim());
|
|
53
|
+
const domains = fields('Domain');
|
|
54
|
+
const bases = fields('Domain basis');
|
|
55
|
+
if (domains.length > 1 || bases.length > 1)
|
|
56
|
+
return unknown('Conflicting domain metadata');
|
|
57
|
+
const basis = bases[0]?.match(/^(manager|catalog|category|job-declaration); map (\d+)$/);
|
|
58
|
+
const declared = domains[0];
|
|
59
|
+
if (basis && (basis[1] === 'manager' || Number(basis[2]) >= 3)) {
|
|
60
|
+
if (!declared || !['global', ...map.domains].includes(declared))
|
|
61
|
+
return unknown('Retired or invalid explicit domain');
|
|
62
|
+
return { status: 'resolved', value: declared, basis: basis[1], mapVersion: Number(basis[2]) };
|
|
63
|
+
}
|
|
64
|
+
const origins = fields('Originating job').flatMap(value => value.split(';').map(value => value.trim()).filter(Boolean));
|
|
65
|
+
if (origins.length)
|
|
66
|
+
return deriveDomain(origins.map(registryPath => ({ name: registryPath, registryPath })), effectiveCatalog);
|
|
67
|
+
// Undecorated files contain intentionally global preferences and patterns.
|
|
68
|
+
// An old umbrella filename alone never proves a current domain assignment.
|
|
69
|
+
if (storageDomain === 'global' && (!declared || declared === 'global'))
|
|
70
|
+
return { status: 'resolved', value: 'global', basis: 'category', mapVersion: map.mapVersion };
|
|
71
|
+
return unknown('Historical entry needs original job provenance or an explicit manager domain decision');
|
|
72
|
+
}
|
|
73
|
+
/** Select entry blocks for one job while retaining provenance in the original text. */
|
|
74
|
+
function selectStoredLearningEntries(content, storageDomain, targetDomain, includeGlobal = true, effectiveCatalog = catalog) {
|
|
75
|
+
const headings = [...content.matchAll(/^#{2,6}\s+\[(?:P-)?(?:CRITICAL|HIGH|MED|MEDIUM|LOW|IMPORTANT|MONITOR)\].*$/gm)];
|
|
76
|
+
const blocks = headings.length ? headings.map((heading, index) => content.slice(heading.index, headings[index + 1]?.index ?? content.length).trim()) : [content.trim()].filter(Boolean);
|
|
77
|
+
const entries = [];
|
|
78
|
+
let unresolved = 0;
|
|
79
|
+
for (const block of blocks) {
|
|
80
|
+
const decision = storedEntryDomain(block, storageDomain, effectiveCatalog);
|
|
81
|
+
if (decision.status === 'needs-domain')
|
|
82
|
+
unresolved++;
|
|
83
|
+
else if (decision.value === targetDomain || (includeGlobal && decision.value === 'global'))
|
|
84
|
+
entries.push(block);
|
|
85
|
+
}
|
|
86
|
+
return { entries, unresolved };
|
|
87
|
+
}
|
|
50
88
|
function readerText(entry) {
|
|
51
89
|
return [entry.title, entry.takeaway, entry.why, entry.example, entry.next].join(' ');
|
|
52
90
|
}
|
|
@@ -68,7 +106,7 @@ function validateEntry(entry) {
|
|
|
68
106
|
if (!entry.domain || !Number.isInteger(entry.domain.mapVersion))
|
|
69
107
|
throw new Error('invalid-entry: domain');
|
|
70
108
|
if (entry.domain.status === 'resolved') {
|
|
71
|
-
if (
|
|
109
|
+
if (entry.domain.mapVersion < 1 || !/^[a-z][a-z-]*$/.test(entry.domain.value) || ['constructor', 'prototype'].includes(entry.domain.value) || !['catalog', 'category', 'job-declaration', 'manager'].includes(entry.domain.basis))
|
|
72
110
|
throw new Error('invalid-entry: domain');
|
|
73
111
|
}
|
|
74
112
|
else if (entry.domain.status !== 'needs-domain' || entry.domain.value !== null || !entry.domain.reason)
|
|
@@ -47,7 +47,7 @@ function capabilityGuards(action, proof, options) {
|
|
|
47
47
|
const required = [action.target, ...(action.delivery?.callers || [])].map(ref => resolved(ref, options).file);
|
|
48
48
|
if (required.some(file => {
|
|
49
49
|
const snapshot = snapshots.get(node_path_1.default.resolve(file));
|
|
50
|
-
return !snapshot || typeof snapshot.realPath !== 'string' || !snapshot.realPath
|
|
50
|
+
return !snapshot || snapshot.kind !== undefined || typeof snapshot.realPath !== 'string' || !snapshot.realPath
|
|
51
51
|
|| typeof snapshot.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(snapshot.sha256);
|
|
52
52
|
}))
|
|
53
53
|
throw new Error('capability-local-proof-required');
|
|
@@ -57,8 +57,13 @@ function capabilityGuards(action, proof, options) {
|
|
|
57
57
|
throw new Error('capability-local-proof-conflict');
|
|
58
58
|
}
|
|
59
59
|
return [...snapshots.values()].map(item => {
|
|
60
|
+
if (item.kind !== undefined && item.kind !== 'directory')
|
|
61
|
+
throw new Error('capability-local-proof-conflict');
|
|
60
62
|
const absent = item.realPath === null && item.sha256 === null;
|
|
61
|
-
|
|
63
|
+
const directory = item.kind === 'directory';
|
|
64
|
+
if (directory && (item.sha256 !== null || (!absent && (typeof item.realPath !== 'string' || !item.realPath))))
|
|
65
|
+
throw new Error('capability-local-proof-conflict');
|
|
66
|
+
if (!directory && !absent && (typeof item.realPath !== 'string' || typeof item.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(item.sha256)))
|
|
62
67
|
throw new Error('capability-local-proof-conflict');
|
|
63
68
|
return { file: item.file, verify: () => {
|
|
64
69
|
if (absent) {
|
|
@@ -72,6 +77,11 @@ function capabilityGuards(action, proof, options) {
|
|
|
72
77
|
}
|
|
73
78
|
throw new Error('capability-local-proof-changed');
|
|
74
79
|
}
|
|
80
|
+
if (directory) {
|
|
81
|
+
if (!node_fs_1.default.existsSync(item.file) || !node_fs_1.default.statSync(item.file).isDirectory() || node_fs_1.default.realpathSync(item.file) !== item.realPath)
|
|
82
|
+
throw new Error('capability-local-proof-changed');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
75
85
|
if (!node_fs_1.default.existsSync(item.file) || node_fs_1.default.realpathSync(item.file) !== item.realPath || (0, proposal_queue_1.hashContent)(node_fs_1.default.readFileSync(item.file, 'utf8')) !== item.sha256)
|
|
76
86
|
throw new Error('capability-local-proof-changed');
|
|
77
87
|
} };
|