@geraldmaron/construct 1.4.0 → 1.4.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/bin/construct +2 -1
- package/bin/construct-postinstall.mjs +27 -2
- package/config/tag-vocabulary.json +264 -0
- package/lib/config/schema.mjs +1 -1
- package/lib/doctor/diagnosis.mjs +20 -0
- package/lib/doctor/index.mjs +5 -0
- package/lib/embed/worker.mjs +5 -0
- package/lib/env-config.mjs +10 -2
- package/lib/export-validate.mjs +34 -2
- package/lib/hooks/guard-bash.mjs +3 -1
- package/lib/host/readiness.mjs +109 -0
- package/lib/host-disposition.mjs +10 -1
- package/lib/install/stage-project.mjs +41 -4
- package/lib/intake/prepare.mjs +2 -0
- package/lib/mcp/destructive-approval.mjs +57 -0
- package/lib/mcp/server.mjs +209 -35
- package/lib/mcp/tool-rate-limit.mjs +47 -0
- package/lib/mcp/tool-safety.mjs +94 -0
- package/lib/mcp/tool-surface-parity.mjs +60 -0
- package/lib/mcp/tools/orchestration-run.mjs +45 -7
- package/lib/mcp/tools/project.mjs +25 -8
- package/lib/mcp/tools/storage.mjs +9 -1
- package/lib/mcp/tools/web-search-governance.mjs +96 -0
- package/lib/mcp/tools/web-search.mjs +6 -44
- package/lib/mcp-platform-config.mjs +27 -18
- package/lib/oracle/daemon-entry.mjs +6 -0
- package/lib/orchestration/runtime.mjs +81 -42
- package/lib/orchestration/web-capability.mjs +59 -0
- package/lib/orchestration/worker.mjs +263 -19
- package/lib/orchestration-policy.mjs +36 -0
- package/lib/output-quality.mjs +61 -2
- package/lib/path-policy.mjs +56 -0
- package/lib/providers/secret-audit-wiring.mjs +35 -18
- package/lib/providers/secret-resolver.mjs +28 -13
- package/lib/registry/catalog.mjs +6 -0
- package/lib/registry/loader.mjs +7 -1
- package/lib/registry/validate.mjs +3 -3
- package/lib/sandbox.mjs +1 -1
- package/lib/service-manager.mjs +59 -9
- package/lib/storage/admin.mjs +7 -2
- package/package.json +6 -1
- package/registry/agent-manifest.json +117 -0
- package/registry/capabilities.json +1880 -0
- package/schemas/brand-voice.schema.json +24 -0
- package/schemas/capability-registry.schema.json +72 -0
- package/schemas/certification-run.schema.json +130 -0
- package/schemas/demo-recording.schema.json +46 -0
- package/schemas/eval-dataset.schema.json +79 -0
- package/schemas/execution-capability-profile.schema.json +46 -0
- package/schemas/execution-policy.schema.json +114 -0
- package/schemas/improvement-proposal.schema.json +65 -0
- package/schemas/mcp-tool-output.schema.json +61 -0
- package/schemas/platform-capabilities.schema.json +83 -0
- package/schemas/project-config.schema.json +227 -0
- package/schemas/project-demo.schema.json +60 -0
- package/schemas/provider-behavior-matrix.schema.json +91 -0
- package/schemas/scope.schema.json +197 -0
- package/schemas/specialist-trace.schema.json +107 -0
- package/schemas/team.schema.json +99 -0
- package/schemas/unified-registry.schema.json +548 -0
- package/scripts/sync-specialists.mjs +135 -12
- package/specialists/org/specialists/cx-researcher.json +1 -0
- package/specialists/prompts/cx-researcher.md +9 -8
- package/vendor/pandoc-ext/README.md +3 -0
- package/vendor/pandoc-ext/diagram.lua +687 -0
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
|
-
import { existsSync, copyFileSync, writeFileSync, mkdirSync, chmodSync } from 'node:fs';
|
|
14
|
+
import { existsSync, readFileSync, copyFileSync, writeFileSync, mkdirSync, chmodSync } from 'node:fs';
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
|
|
17
17
|
export function stageProjectAdapters({ projectRoot, packageRoot, pkgVersion, log, hosts = null }) {
|
|
@@ -27,7 +27,7 @@ export function stageProjectAdapters({ projectRoot, packageRoot, pkgVersion, log
|
|
|
27
27
|
|
|
28
28
|
if (!existsSync(syncScript)) {
|
|
29
29
|
emit(`sync-specialists.mjs not found at ${syncScript}; skipping adapter sync`);
|
|
30
|
-
return {
|
|
30
|
+
return finishStage({ projectRoot, pkgVersion, synced: false, reason: 'sync-script-missing' });
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// A `hosts` array restricts which adapter sets sync writes (construct-4xy6);
|
|
@@ -45,9 +45,46 @@ export function stageProjectAdapters({ projectRoot, packageRoot, pkgVersion, log
|
|
|
45
45
|
|
|
46
46
|
if (result.status !== 0) {
|
|
47
47
|
emit(`sync failed (exit ${result.status}); project left in a clean state`);
|
|
48
|
-
return {
|
|
48
|
+
return finishStage({ projectRoot, pkgVersion, synced: false, reason: `sync-exit-${result.status}` });
|
|
49
49
|
}
|
|
50
|
-
return {
|
|
50
|
+
return finishStage({ projectRoot, pkgVersion, synced: true, reason: null });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const STAGE_STATE_FILE = 'stage-state.json';
|
|
54
|
+
|
|
55
|
+
// Record the staging outcome under .construct/ so a half-stage (launcher present,
|
|
56
|
+
// adapters not synced) is detectable and repairable instead of re-derived from
|
|
57
|
+
// filesystem heuristics. The marker write is best-effort — a state-write failure must
|
|
58
|
+
// never turn a successful stage into a reported failure.
|
|
59
|
+
|
|
60
|
+
function finishStage({ projectRoot, pkgVersion, synced, reason }) {
|
|
61
|
+
const dotConstruct = path.join(projectRoot, '.construct');
|
|
62
|
+
try {
|
|
63
|
+
mkdirSync(dotConstruct, { recursive: true });
|
|
64
|
+
const state = { staged: true, synced, reason, pkgVersion: pkgVersion ?? null, ts: new Date().toISOString() };
|
|
65
|
+
writeFileSync(path.join(dotConstruct, STAGE_STATE_FILE), JSON.stringify(state, null, 2) + '\n');
|
|
66
|
+
} catch { /* marker is advisory */ }
|
|
67
|
+
return { staged: true, synced };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function readStageState(projectRoot) {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(readFileSync(path.join(projectRoot, '.construct', STAGE_STATE_FILE), 'utf8'));
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Re-drive a half-staged project to a synced state. stageProjectAdapters is idempotent,
|
|
79
|
+
// so re-running the sync is the repair; an already-synced project is a no-op.
|
|
80
|
+
|
|
81
|
+
export function repairStagedProject({ projectRoot, packageRoot, pkgVersion, log, hosts = null }) {
|
|
82
|
+
const state = readStageState(projectRoot);
|
|
83
|
+
if (state && state.synced === true) {
|
|
84
|
+
return { staged: true, synced: true, repaired: false };
|
|
85
|
+
}
|
|
86
|
+
const result = stageProjectAdapters({ projectRoot, packageRoot, pkgVersion, log, hosts });
|
|
87
|
+
return { ...result, repaired: result.synced === true };
|
|
51
88
|
}
|
|
52
89
|
|
|
53
90
|
function ensureProjectLauncher({ projectRoot, templateDir, pkgVersion }) {
|
package/lib/intake/prepare.mjs
CHANGED
|
@@ -123,6 +123,8 @@ export async function prepareIntakeForIngestedFile({
|
|
|
123
123
|
const tagSuggestions = suggestTags(triage, related, vocab);
|
|
124
124
|
|
|
125
125
|
const baseEntry = {
|
|
126
|
+
// OWASP LLM01: externally-ingested document bodies are untrusted data, not instructions
|
|
127
|
+
trust: 'untrusted',
|
|
126
128
|
intake: {
|
|
127
129
|
sourcePath: ingestedFile.sourcePath,
|
|
128
130
|
outputPath: ingestedFile.outputPath,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/mcp/destructive-approval.mjs — out-of-band approval tokens for destructive MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* storage_reset and delete_ingested_artifacts are reachable only through the model's
|
|
5
|
+
* argument channel, so a confirm flag the model itself supplies cannot authorize
|
|
6
|
+
* irreversible deletion. Tokens are issued out-of-band by an operator action (never the
|
|
7
|
+
* model), persisted with 0600 under the user state dir — outside any project root, so a
|
|
8
|
+
* root-contained file tool cannot read them — and consumed one-time. consumeApprovalToken
|
|
9
|
+
* returns false unless a live, unexpired token for the scope matches, and never writes
|
|
10
|
+
* when no token is supplied.
|
|
11
|
+
*/
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { doctorRoot } from '../config/xdg.mjs';
|
|
16
|
+
|
|
17
|
+
const TOKEN_TTL_MS = 5 * 60 * 1000;
|
|
18
|
+
|
|
19
|
+
function storePath(env) {
|
|
20
|
+
return path.join(doctorRoot(undefined, env), 'destructive-approvals.json');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readStore(file) {
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
26
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function writeStore(file, tokens) {
|
|
33
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
34
|
+
fs.writeFileSync(file, JSON.stringify(tokens), { mode: 0o600 });
|
|
35
|
+
try { fs.chmodSync(file, 0o600); } catch { /* non-posix filesystems */ }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function issueApprovalToken(scope, { env = process.env, now = Date.now() } = {}) {
|
|
39
|
+
if (typeof scope !== 'string' || !scope) throw new Error('scope required');
|
|
40
|
+
const file = storePath(env);
|
|
41
|
+
const token = randomBytes(24).toString('hex');
|
|
42
|
+
const live = readStore(file).filter((t) => t.expiresAt > now);
|
|
43
|
+
live.push({ token, scope, expiresAt: now + TOKEN_TTL_MS });
|
|
44
|
+
writeStore(file, live);
|
|
45
|
+
return token;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function consumeApprovalToken(scope, token, { env = process.env, now = Date.now() } = {}) {
|
|
49
|
+
if (typeof token !== 'string' || !token) return false;
|
|
50
|
+
const file = storePath(env);
|
|
51
|
+
const tokens = readStore(file);
|
|
52
|
+
const idx = tokens.findIndex((t) => t.scope === scope && t.token === token && t.expiresAt > now);
|
|
53
|
+
if (idx === -1) return false;
|
|
54
|
+
tokens.splice(idx, 1);
|
|
55
|
+
writeStore(file, tokens.filter((t) => t.expiresAt > now));
|
|
56
|
+
return true;
|
|
57
|
+
}
|