@milpa/comal 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/agent.mjs +8 -2
- package/src/cli.mjs +65 -11
- package/src/finding-recorder.mjs +126 -0
- package/src/findings.mjs +63 -0
- package/src/house.mjs +16 -5
- package/src/i18n/cli-es.mjs +1 -1
- package/src/i18n/es.mjs +21 -1
- package/src/operations.mjs +10 -0
- package/src/permission-rules.mjs +3 -2
- package/src/permissions.mjs +75 -10
- package/src/plugin-candidate-child.mjs +204 -0
- package/src/plugin-candidate-runner.mjs +419 -0
- package/src/plugin-config.mjs +34 -5
- package/src/plugin-contract.mjs +309 -0
- package/src/plugin-controls.mjs +238 -32
- package/src/plugin-repair.mjs +981 -0
- package/src/plugin-trust.mjs +14 -4
- package/src/plugin-workbench.mjs +675 -0
- package/src/plugins.mjs +115 -24
- package/src/remote-permissions.mjs +7 -0
- package/src/scoped-policy.mjs +7 -1
- package/src/sessions.mjs +5 -1
- package/src/subagents.mjs +3 -3
- package/src/tools.mjs +17 -2
- package/src/tui/controller.mjs +1 -1
- package/src/tui/discovery.mjs +2 -2
- package/src/tui/screen.mjs +12 -9
- package/src/work-plan.mjs +77 -3
- package/src/workflows/chrome-page-task.mjs +8 -3
- package/src/workflows/chrome-repair.mjs +88 -0
- package/src/workflows/chrome-task.mjs +14 -5
package/src/plugin-trust.mjs
CHANGED
|
@@ -13,6 +13,7 @@ registerMessages({
|
|
|
13
13
|
'Trust record: invalid files.': 'Registro de confianza: archivos inválidos.',
|
|
14
14
|
'Trust record: invalid date.': 'Registro de confianza: fecha inválida.',
|
|
15
15
|
'Trust record: invalid package.': 'Registro de confianza: paquete inválido.',
|
|
16
|
+
'Trust record: invalid reason.': 'Registro de confianza: motivo inválido.',
|
|
16
17
|
'Unrecognized trust registry format.': 'Formato de registro de confianza no reconocido.',
|
|
17
18
|
'Trust registry ignored: {reason}': 'Registro de confianza ignorado: {reason}',
|
|
18
19
|
'No recorded trust for {id} in this project. Use /plugins enable {id}.': 'No hay confianza registrada para {id} en este proyecto. Usa /plugins enable {id}.',
|
|
@@ -27,6 +28,7 @@ const ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, SHA = /^[a-f0-9]{64}$/;
|
|
|
27
28
|
const canonical = value => Array.isArray(value) ? value.map(canonical) : value && typeof value === 'object' ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])])) : value;
|
|
28
29
|
export const configDigest = config => createHash('sha256').update(JSON.stringify(canonical(config ?? {}))).digest('hex');
|
|
29
30
|
const fail = (message, code = 'PLUGIN_TRUST_INVALID') => Object.assign(new Error(message), { code });
|
|
31
|
+
const TRUST_REASONS = ['contract_repair'];
|
|
30
32
|
|
|
31
33
|
export function pluginTrustFile(env = process.env) {
|
|
32
34
|
return path.resolve(env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'comal', 'plugin-trust.json');
|
|
@@ -34,7 +36,7 @@ export function pluginTrustFile(env = process.env) {
|
|
|
34
36
|
|
|
35
37
|
function record(value) {
|
|
36
38
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw fail(t('Invalid trust record.'));
|
|
37
|
-
const keys = ['project', 'id', 'module', 'code_fingerprint', 'config_sha256', 'files', 'trusted_at', 'package'];
|
|
39
|
+
const keys = ['project', 'id', 'module', 'code_fingerprint', 'config_sha256', 'files', 'trusted_at', 'package', 'reason'];
|
|
38
40
|
for (const key of Object.keys(value)) if (!keys.includes(key)) throw fail(t('Trust record: unknown field {key}', { key }));
|
|
39
41
|
if (typeof value.project !== 'string' || !path.isAbsolute(value.project) || typeof value.module !== 'string' || !path.isAbsolute(value.module)) throw fail(t('Trust record: absolute paths required.'));
|
|
40
42
|
if (!ID.test(value.id || '') || !SHA.test(value.code_fingerprint || '') || !SHA.test(value.config_sha256 || '')) throw fail(t('Trust record: invalid id or fingerprints.'));
|
|
@@ -42,7 +44,9 @@ function record(value) {
|
|
|
42
44
|
if (typeof value.trusted_at !== 'string') throw fail(t('Trust record: invalid date.'));
|
|
43
45
|
const pkg = value.package;
|
|
44
46
|
if (pkg !== undefined && (!pkg || typeof pkg.name !== 'string' || pkg.name.length > 214 || typeof pkg.version !== 'string' || pkg.version.length > 64 || !SHA.test(pkg.integrity || '') || Object.keys(pkg).some(key => !['name', 'version', 'integrity'].includes(key)))) throw fail(t('Trust record: invalid package.'));
|
|
45
|
-
|
|
47
|
+
// reason: why the house, not the human's card, recorded this trust (only 'contract_repair' today). Absent on human records.
|
|
48
|
+
if (value.reason !== undefined && !TRUST_REASONS.includes(value.reason)) throw fail(t('Trust record: invalid reason.'));
|
|
49
|
+
return { project: value.project, id: value.id, module: value.module, code_fingerprint: value.code_fingerprint, config_sha256: value.config_sha256, files: value.files.map(file => ({ path: file.path, sha256: file.sha256 })), trusted_at: value.trusted_at, ...(pkg ? { package: { name: pkg.name, version: pkg.version, integrity: pkg.integrity } } : {}), ...(value.reason !== undefined ? { reason: value.reason } : {}) };
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
/**
|
|
@@ -92,9 +96,9 @@ export class PluginTrustStore {
|
|
|
92
96
|
await writePrivateJson(this.file, { version: VERSION, plugins: records }, { workspace: this.workspace });
|
|
93
97
|
this.#records = records;
|
|
94
98
|
}
|
|
95
|
-
async trust({ project, entry, code }) {
|
|
99
|
+
async trust({ project, entry, code, reason }) {
|
|
96
100
|
await this.#ready();
|
|
97
|
-
const next = record({ project, id: entry.id, module: code.module, code_fingerprint: code.code_fingerprint, config_sha256: configDigest(entry.config), files: code.files, trusted_at: new Date().toISOString(), ...(code.package ? { package: code.package } : {}) });
|
|
101
|
+
const next = record({ project, id: entry.id, module: code.module, code_fingerprint: code.code_fingerprint, config_sha256: configDigest(entry.config), files: code.files, trusted_at: new Date().toISOString(), ...(code.package ? { package: code.package } : {}), ...(reason !== undefined ? { reason } : {}) });
|
|
98
102
|
await this.#save([...this.#records.filter(item => !(item.project === project && item.id === entry.id)), next]);
|
|
99
103
|
return structuredClone(next);
|
|
100
104
|
}
|
|
@@ -105,6 +109,12 @@ export class PluginTrustStore {
|
|
|
105
109
|
await this.#save(this.#records.map(item => item.project === project && item.id === id ? next : item));
|
|
106
110
|
return structuredClone(next);
|
|
107
111
|
}
|
|
112
|
+
/** Puts back exactly the record that was there before (or none): the rollback of a repair's trust. */
|
|
113
|
+
async restore(project, id, previous) {
|
|
114
|
+
await this.#ready();
|
|
115
|
+
const others = this.#records.filter(item => !(item.project === project && item.id === id));
|
|
116
|
+
await this.#save(previous ? [...others, record(previous)] : others);
|
|
117
|
+
}
|
|
108
118
|
async revoke(project, id) {
|
|
109
119
|
await this.#ready();
|
|
110
120
|
const found = this.find(project, id); if (!found) return false;
|