create-agdf 0.13.7 → 0.13.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 +26 -0
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/hooks/hooks.json +3 -2
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +24 -1
- package/generated/plugins/agdf/runtime/agdf-session-check.js +92 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +24 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +9 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/parse-args.js +17 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime-check-consent/contract.js +71 -0
- package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +19 -2
- package/generated/plugins/agdf/submission/openai/availability.md +1 -1
- package/generated/plugins/agdf/submission/openai/capability-matrix.json +8 -1
- package/generated/plugins/agdf/submission/openai/release-notes.md +12 -1
- package/generated/plugins/agdf/submission/openai/reviewer-cases.json +23 -1
- package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +24 -1
- package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +8 -1
- package/generated/submissions/openai/agdf/submission/openai/inventory.json +9 -9
- package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
- package/generated/submissions/openai/agdf/submission/openai/release-notes.md +12 -1
- package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +23 -1
- package/lib/cli/application.js +228 -13
- package/lib/cli/command-registry.js +9 -0
- package/lib/cli/parse-args.js +17 -0
- package/lib/installers/local-marketplace.js +2 -0
- package/lib/installers/opencode.js +21 -5
- package/lib/installers/plugin-installers.js +8 -0
- package/lib/lifecycle/presentation.js +43 -2
- package/lib/lifecycle/result.js +9 -0
- package/lib/lifecycle/status.js +5 -1
- package/lib/runtime-check-consent/adapters.js +42 -0
- package/lib/runtime-check-consent/claude-settings.js +71 -0
- package/lib/runtime-check-consent/contract.js +71 -0
- package/lib/runtime-check-consent/coordinator.js +37 -0
- package/lib/runtime-check-consent/service.js +181 -0
- package/lib/runtime-check-consent/state.js +90 -0
- package/opencode-plugin.js +30 -15
- package/package.json +2 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { renameSyncWithRetry } from "../fs-swap.js";
|
|
5
|
+
|
|
6
|
+
export function runtimeCheckReceiptPath(dataRoot, surface) {
|
|
7
|
+
if (!["codex", "claude", "opencode"].includes(surface)) throw new Error("AGDF_RUNTIME_CHECK_RECEIPT_SURFACE_INVALID");
|
|
8
|
+
return join(resolve(dataRoot), "runtime-checks", `${surface}.json`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createRuntimeCheckReceipt({ surface, decision, capabilityIdentity, command }) {
|
|
12
|
+
if (!["codex", "claude", "opencode"].includes(surface)) throw new Error("AGDF_RUNTIME_CHECK_RECEIPT_SURFACE_INVALID");
|
|
13
|
+
if (!["enable", "manual"].includes(decision)) throw new Error("AGDF_RUNTIME_CHECK_RECEIPT_DECISION_INVALID");
|
|
14
|
+
if (!/^[a-f0-9]{64}$/.test(capabilityIdentity)) throw new Error("AGDF_RUNTIME_CHECK_RECEIPT_IDENTITY_INVALID");
|
|
15
|
+
return Object.freeze({
|
|
16
|
+
schema_version: 1,
|
|
17
|
+
owner: "create-agdf",
|
|
18
|
+
capability_id: "automatic-runtime-checks",
|
|
19
|
+
surface,
|
|
20
|
+
requested_state: decision === "enable" ? "enabled" : "manual",
|
|
21
|
+
capability_identity: capabilityIdentity,
|
|
22
|
+
command,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function validateRuntimeCheckReceipt(receipt) {
|
|
27
|
+
if (receipt?.schema_version !== 1 || receipt.owner !== "create-agdf"
|
|
28
|
+
|| receipt.capability_id !== "automatic-runtime-checks") return { status: "receipt_unowned", receipt: null };
|
|
29
|
+
if (!["codex", "claude", "opencode"].includes(receipt.surface)
|
|
30
|
+
|| !["enabled", "manual"].includes(receipt.requested_state)
|
|
31
|
+
|| !/^[a-f0-9]{64}$/.test(receipt.capability_identity)
|
|
32
|
+
|| typeof receipt.command !== "string" || !receipt.command.trim()) {
|
|
33
|
+
return { status: "receipt_invalid", receipt: null };
|
|
34
|
+
}
|
|
35
|
+
return { status: "valid", receipt };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function readRuntimeCheckReceipt(dataRoot, surface) {
|
|
39
|
+
const path = runtimeCheckReceiptPath(dataRoot, surface);
|
|
40
|
+
if (!existsSync(path)) return { status: "receipt_missing", receipt: null, path };
|
|
41
|
+
try {
|
|
42
|
+
const file = lstatSync(path);
|
|
43
|
+
if (!file.isFile() || file.isSymbolicLink()) return { status: "receipt_unowned", receipt: null, path };
|
|
44
|
+
return { ...validateRuntimeCheckReceipt(JSON.parse(readFileSync(path, "utf8"))), path };
|
|
45
|
+
} catch {
|
|
46
|
+
return { status: "receipt_invalid", receipt: null, path };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function writeRuntimeCheckReceipt(dataRoot, receipt, adapters = {}) {
|
|
51
|
+
const validated = validateRuntimeCheckReceipt(receipt);
|
|
52
|
+
if (validated.status !== "valid") throw new Error(`AGDF_RUNTIME_CHECK_${validated.status.toUpperCase()}`);
|
|
53
|
+
const path = runtimeCheckReceiptPath(dataRoot, receipt.surface);
|
|
54
|
+
const parent = dirname(path);
|
|
55
|
+
const fs = {
|
|
56
|
+
mkdir: adapters.mkdir ?? mkdirSync,
|
|
57
|
+
write: adapters.write ?? writeFileSync,
|
|
58
|
+
rename: adapters.rename ?? renameSyncWithRetry,
|
|
59
|
+
remove: adapters.remove ?? rmSync,
|
|
60
|
+
};
|
|
61
|
+
fs.mkdir(parent, { recursive: true });
|
|
62
|
+
const parentStats = lstatSync(parent);
|
|
63
|
+
if (!parentStats.isDirectory() || parentStats.isSymbolicLink()) throw new Error("AGDF_RUNTIME_CHECK_RECEIPT_PARENT_UNOWNED");
|
|
64
|
+
const temp = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
65
|
+
const backup = `${path}.bak-${process.pid}-${randomUUID()}`;
|
|
66
|
+
const hadPrevious = existsSync(path);
|
|
67
|
+
fs.write(temp, `${JSON.stringify(receipt, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
68
|
+
try {
|
|
69
|
+
if (hadPrevious && process.platform === "win32") fs.rename(path, backup);
|
|
70
|
+
fs.rename(temp, path);
|
|
71
|
+
if (hadPrevious && process.platform === "win32") fs.remove(backup, { force: true });
|
|
72
|
+
} catch (error) {
|
|
73
|
+
try { fs.remove(temp, { force: true }); } catch {}
|
|
74
|
+
if (hadPrevious && process.platform === "win32" && existsSync(backup) && !existsSync(path)) fs.rename(backup, path);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
return path;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function deriveRuntimeCheckState({ receiptResult, surface, capabilityIdentity, hostEvidence }) {
|
|
81
|
+
if (receiptResult.status !== "valid") return { requested: "unknown", effective: receiptResult.status, reason: receiptResult.status };
|
|
82
|
+
const receipt = receiptResult.receipt;
|
|
83
|
+
if (receipt.surface !== surface) return { requested: receipt.requested_state, effective: "renewal_required", reason: "installed_identity_mismatch" };
|
|
84
|
+
if (receipt.capability_identity !== capabilityIdentity) return { requested: receipt.requested_state, effective: "renewal_required", reason: "capability_identity_changed" };
|
|
85
|
+
if (receipt.requested_state === "manual") return { requested: "manual", effective: "manual", reason: "consent_not_provided" };
|
|
86
|
+
if (hostEvidence?.status === "enabled" && hostEvidence.capability_identity === capabilityIdentity) {
|
|
87
|
+
return { requested: "enabled", effective: "enabled", reason: "none" };
|
|
88
|
+
}
|
|
89
|
+
return { requested: "enabled", effective: hostEvidence?.status || "decision_required", reason: hostEvidence?.reason || "host_permission_unverified" };
|
|
90
|
+
}
|
package/opencode-plugin.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { evaluateOpenCodeRepositoryActivation } from "./lib/installers/opencode-activation.js";
|
|
3
|
+
import { executeOpenCodeAutomaticRuntimeCheck } from "./lib/runtime-check-consent/service.js";
|
|
3
4
|
|
|
4
5
|
const packageJson = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8"));
|
|
5
6
|
|
|
@@ -15,7 +16,7 @@ const readValidatorExpectedVersion = () => {
|
|
|
15
16
|
}
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
export const AGDFPlugin = async ({ directory, client }) => {
|
|
19
|
+
export const AGDFPlugin = async ({ directory, client }, dependencies = {}) => {
|
|
19
20
|
const controlDir = `${directory}/.agdf/control`;
|
|
20
21
|
const activation = () => evaluateOpenCodeRepositoryActivation(directory);
|
|
21
22
|
const status = () => {
|
|
@@ -45,13 +46,11 @@ export const AGDFPlugin = async ({ directory, client }) => {
|
|
|
45
46
|
const appendGuidance = async (output, key) => {
|
|
46
47
|
if (!output || !Array.isArray(output[key])) {
|
|
47
48
|
try {
|
|
48
|
-
await
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
extra: { key, repositoryActivation: activation().state },
|
|
54
|
-
},
|
|
49
|
+
await safeLog({
|
|
50
|
+
service: "agdf",
|
|
51
|
+
level: "warn",
|
|
52
|
+
message: `AGDF OpenCode guidance degraded: ${key} output is unavailable`,
|
|
53
|
+
extra: { key, repositoryActivation: activation().state },
|
|
55
54
|
});
|
|
56
55
|
} catch {}
|
|
57
56
|
return;
|
|
@@ -65,17 +64,31 @@ export const AGDFPlugin = async ({ directory, client }) => {
|
|
|
65
64
|
} catch {}
|
|
66
65
|
};
|
|
67
66
|
|
|
67
|
+
const safeLog = async (body) => {
|
|
68
|
+
try {
|
|
69
|
+
const response = await client?.app?.log?.({ body });
|
|
70
|
+
if (response?.error) await client?.app?.log?.(body);
|
|
71
|
+
} catch {
|
|
72
|
+
try { await client?.app?.log?.(body); } catch {}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let automaticCheck;
|
|
77
|
+
const currentAutomaticCheck = () => {
|
|
78
|
+
automaticCheck ??= (dependencies.executeAutomaticRuntimeCheck ?? executeOpenCodeAutomaticRuntimeCheck)({ directory });
|
|
79
|
+
return automaticCheck;
|
|
80
|
+
};
|
|
81
|
+
|
|
68
82
|
return {
|
|
69
83
|
event: async ({ event }) => {
|
|
70
84
|
if (event?.type === "session.created") {
|
|
71
85
|
const currentStatus = status();
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
},
|
|
86
|
+
const runtimeCheck = currentAutomaticCheck();
|
|
87
|
+
await safeLog({
|
|
88
|
+
service: "agdf",
|
|
89
|
+
level: "info",
|
|
90
|
+
message: currentStatus.active ? "AGDF OpenCode active through durable control" : "AGDF OpenCode global hook active without durable control",
|
|
91
|
+
extra: { ...currentStatus, automatic_runtime_check: { effective: runtimeCheck.effective, reason: runtimeCheck.reason, ran: runtimeCheck.ran } },
|
|
79
92
|
});
|
|
80
93
|
if (!currentStatus.active) {
|
|
81
94
|
await safeToast(
|
|
@@ -112,6 +125,8 @@ export const AGDFPlugin = async ({ directory, client }) => {
|
|
|
112
125
|
|
|
113
126
|
"experimental.chat.system.transform": async (_input, output) => {
|
|
114
127
|
await appendGuidance(output, "system");
|
|
128
|
+
const runtimeCheck = currentAutomaticCheck();
|
|
129
|
+
if (runtimeCheck.ran && runtimeCheck.output && Array.isArray(output?.system)) output.system.push(runtimeCheck.output);
|
|
115
130
|
},
|
|
116
131
|
|
|
117
132
|
"experimental.session.compacting": async (_input, output) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-agdf",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.8",
|
|
4
4
|
"description": "Bootstrap AGDF repository-local plugin files, Copilot/OpenCode instructions, skills, control files and machine-readable checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"test:delivery-path-search-unit": "node ./scripts/delivery-path-search-unit-test.js",
|
|
33
33
|
"test:delivery-path-search-generator": "node ./scripts/delivery-path-search-generator-test.js",
|
|
34
34
|
"test:opencode-hardening": "node ./scripts/opencode-hardening-test.js",
|
|
35
|
+
"test:runtime-check-consent": "node ./scripts/runtime-check-consent-test.js",
|
|
35
36
|
"test:cli-modularization": "node ./scripts/cli-modularization-test.js",
|
|
36
37
|
"test:local-validator": "node ./scripts/local-validator-test.js",
|
|
37
38
|
"test:local-marketplace": "node ./scripts/local-marketplace-test.js",
|