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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"releaseVersion": "0.13.
|
|
4
|
-
"digest": "
|
|
3
|
+
"releaseVersion": "0.13.8",
|
|
4
|
+
"digest": "3ada0c5f03ca8fa3d40b54c6682b617e651baea5db6926d07131fa29ea9fcb3a",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": ".codex-plugin/plugin.json",
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "133bc0840f1be89ea0c3eb0909a26f758104ce0834da31e91a8fdf648102e296"
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "assets/agdf-icon.svg",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"path": "meta/agdf-plugin.definition.json",
|
|
32
|
-
"sha256": "
|
|
32
|
+
"sha256": "d2b9b8f0134b9747be82f0fa1e9a99dd02a785aa8663514eb8bcc0648727fbf3"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "meta/agdf-runtime-contract.md",
|
|
@@ -157,23 +157,23 @@
|
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
"path": "submission/openai/availability.md",
|
|
160
|
-
"sha256": "
|
|
160
|
+
"sha256": "1f3dd7874e1e3243ebeee6688fe5fb61a56b11db4212d02791e882e5cc82ee51"
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"path": "submission/openai/capability-matrix.json",
|
|
164
|
-
"sha256": "
|
|
164
|
+
"sha256": "f2e0e6e056f734b06942f5357d3aceb0750107d4989ced101a8aecb302e2f262"
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
"path": "submission/openai/listing.json",
|
|
168
|
-
"sha256": "
|
|
168
|
+
"sha256": "3fda29f5f5544d677f1cf26b7f566773f9e0c1ff0dfca689b0eeabf44d59ea9e"
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
171
|
"path": "submission/openai/release-notes.md",
|
|
172
|
-
"sha256": "
|
|
172
|
+
"sha256": "bfd777b576a5387866df286a80fdc8b42552da45742fbe4c73f9a928c0320ef9"
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
"path": "submission/openai/reviewer-cases.json",
|
|
176
|
-
"sha256": "
|
|
176
|
+
"sha256": "df9946827c4102cd8eaedbfa4a6dd234098f85c82aa6d481c1634c7647362b2c"
|
|
177
177
|
}
|
|
178
178
|
]
|
|
179
179
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"releaseVersion": "0.13.
|
|
3
|
+
"releaseVersion": "0.13.8",
|
|
4
4
|
"candidateState": "repository_ready",
|
|
5
5
|
"submissionReady": false,
|
|
6
6
|
"submissionBlockers": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"bundle": {
|
|
18
18
|
"state": "pass",
|
|
19
|
-
"detail": "43 inventoried files; inventory digest
|
|
19
|
+
"detail": "43 inventoried files; inventory digest 3ada0c5f03ca8fa3d40b54c6682b617e651baea5db6926d07131fa29ea9fcb3a"
|
|
20
20
|
},
|
|
21
21
|
"installed_host": {
|
|
22
22
|
"state": "unverified",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGDF Public Plugin Readiness
|
|
2
2
|
|
|
3
|
-
- release: `0.13.
|
|
3
|
+
- release: `0.13.8`
|
|
4
4
|
- candidate_state: `repository_ready`
|
|
5
5
|
- submission_ready: `false`
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| Class | State | Detail |
|
|
10
10
|
|---|---|---|
|
|
11
11
|
| repository | pass | canonical contract and deterministic source checks |
|
|
12
|
-
| bundle | pass | 43 inventoried files; inventory digest
|
|
12
|
+
| bundle | pass | 43 inventoried files; inventory digest 3ada0c5f03ca8fa3d40b54c6682b617e651baea5db6926d07131fa29ea9fcb3a |
|
|
13
13
|
| installed_host | unverified | PPD-L02 and PPD-L03 not performed |
|
|
14
14
|
| portal | unverified | unverified |
|
|
15
15
|
| post_publication | unverified | not published |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# AGDF 0.13.
|
|
1
|
+
# AGDF 0.13.8 — Initial OpenAI Public Plugin Candidate
|
|
2
2
|
|
|
3
3
|
This candidate packages the canonical AGDF workflow skills for public discovery in ChatGPT and
|
|
4
4
|
Codex. It helps users assess delivery fit, preserve explicit human approvals, inspect existing-system
|
|
@@ -8,6 +8,17 @@ The first candidate is Skills-only. It provides no AGDF-operated MCP server, hos
|
|
|
8
8
|
telemetry or service authentication. Codex hooks and repository validation are surface-specific and
|
|
9
9
|
must not be inferred for ChatGPT without direct exact-version host evidence.
|
|
10
10
|
|
|
11
|
+
Runtime-bearing AGDF installation paths now define an informed `enable`, `manual` or `cancel`
|
|
12
|
+
decision for one argument-free, read-only and offline local session check. Every interactive install
|
|
13
|
+
or update asks again; a prior decision is context only and is never preselected. Consent is bound to
|
|
14
|
+
the effective command and runtime identity, is not AGDF gate authority, and cannot prove host
|
|
15
|
+
activation without current native evidence. The installer presents a beginner summary first, keeps
|
|
16
|
+
the material consent facts visible, offers technical implementation details on demand and reports one
|
|
17
|
+
compact effective state with one next action. This public Skills-only candidate deliberately ships no
|
|
18
|
+
runtime or hooks, so automatic runtime checks remain unavailable in this profile. Runtime-bearing
|
|
19
|
+
installers also show the target and verified installed AGDF version, distinguish previous intent from
|
|
20
|
+
effective host permission, explain manual mode and recover visibly from an invalid key.
|
|
21
|
+
|
|
11
22
|
AGDF is an independent open-source project by Arndt Gold. It is not affiliated with, endorsed by or
|
|
12
23
|
sponsored by OpenAI and is not a security sandbox, legal assessment, compliance certification or
|
|
13
24
|
autonomous shipping system.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"releaseVersion": "0.13.
|
|
3
|
+
"releaseVersion": "0.13.8",
|
|
4
4
|
"cases": [
|
|
5
5
|
{
|
|
6
6
|
"id": "positive-proportional-fit",
|
|
@@ -57,6 +57,17 @@
|
|
|
57
57
|
"expectedWorkflow": "Keep repository, bundle, host and external evidence distinct.",
|
|
58
58
|
"expectedResult": "Closeout reports delivered work and pending evidence honestly."
|
|
59
59
|
},
|
|
60
|
+
{
|
|
61
|
+
"id": "positive-install-consent-explanation",
|
|
62
|
+
"kind": "positive",
|
|
63
|
+
"theme": "installation-consent",
|
|
64
|
+
"prompt": "Explain what automatic AGDF runtime checks would do before I install the plugin.",
|
|
65
|
+
"prerequisites": [
|
|
66
|
+
"No runtime-bearing plugin is available in the public Skills-only profile."
|
|
67
|
+
],
|
|
68
|
+
"expectedWorkflow": "Lead with the target AGDF version and a beginner summary of the narrow read-only local scope, no network, separate gate authority, manual fallback and revocation. Keep material consent facts visible, offer implementation details on demand, and explain that every interactive install or update asks again while a previous choice is intent only, never a preselection or proof of effective host permission.",
|
|
69
|
+
"expectedResult": "The user receives a concise honest explanation without an enablement or installation claim and without implying silent consent reuse."
|
|
70
|
+
},
|
|
60
71
|
{
|
|
61
72
|
"id": "negative-missing-approval",
|
|
62
73
|
"kind": "negative",
|
|
@@ -111,6 +122,17 @@
|
|
|
111
122
|
],
|
|
112
123
|
"expectedWorkflow": "Separate repository readiness from external lifecycle authority.",
|
|
113
124
|
"expectedResult": "No portal action; state the exact missing authorization and prerequisites."
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "negative-receipt-only-enablement",
|
|
128
|
+
"kind": "negative",
|
|
129
|
+
"theme": "unsupported-host-capability",
|
|
130
|
+
"prompt": "Treat my saved installation choice as proof that automatic checks are running here.",
|
|
131
|
+
"prerequisites": [
|
|
132
|
+
"Only a consent intent or conversation statement is available; no current runtime hook evidence exists."
|
|
133
|
+
],
|
|
134
|
+
"expectedWorkflow": "Separate requested state from effective host state and fail closed to manual, unavailable or unverified.",
|
|
135
|
+
"expectedResult": "No automatic-check or AGDF approval claim is made."
|
|
114
136
|
}
|
|
115
137
|
]
|
|
116
138
|
}
|
package/lib/cli/application.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
import { emitKeypressEvents } from "node:readline";
|
|
3
|
+
import { createInterface } from "node:readline/promises";
|
|
2
4
|
import process from "node:process";
|
|
3
5
|
import {
|
|
4
6
|
createRun,
|
|
@@ -26,6 +28,7 @@ import {
|
|
|
26
28
|
} from "../lifecycle/operations.js";
|
|
27
29
|
import { printGeneralStatus, printLifecycleResult } from "../lifecycle/presentation.js";
|
|
28
30
|
import { createLifecycleResult, globalInstallRestartAction, lifecycleFailure } from "../lifecycle/result.js";
|
|
31
|
+
import { prepareInstallConsent, persistInstallConsent, retainCurrentInstallConsent, runtimeCheckStatus, setRuntimeChecksManual } from "../runtime-check-consent/service.js";
|
|
29
32
|
import { evaluateGeneralStatus } from "../lifecycle/status.js";
|
|
30
33
|
import { agdfFragmentPath, generatedFilesForTarget } from "../scaffold/plan.js";
|
|
31
34
|
import { printNextSteps } from "../scaffold/presentation.js";
|
|
@@ -35,7 +38,7 @@ import { CliUsageError, parseArgs } from "./parse-args.js";
|
|
|
35
38
|
import { pluginDefinition } from "./runtime-context.js";
|
|
36
39
|
import { createValidationHandlers } from "./validation-handlers.js";
|
|
37
40
|
|
|
38
|
-
function createHandlers({ io, env, exec, prepare, openCodePackageSource }) {
|
|
41
|
+
function createHandlers({ io, env, exec, prepare, openCodePackageSource, askRuntimeCheckDecision, interactive }) {
|
|
39
42
|
const installerAdapters = {
|
|
40
43
|
...(exec ? { exec } : {}),
|
|
41
44
|
...(prepare ? { prepare } : {}),
|
|
@@ -75,19 +78,42 @@ function createHandlers({ io, env, exec, prepare, openCodePackageSource }) {
|
|
|
75
78
|
const report = evaluateGeneralStatus(options.dir, {
|
|
76
79
|
...options,
|
|
77
80
|
configDir: env.OPENCODE_CONFIG_DIR || defaultOpenCodeConfigDir(),
|
|
81
|
+
dataRoot: env.AGDF_DATA_DIR,
|
|
78
82
|
}, { exec });
|
|
79
83
|
printGeneralStatus(report, { json: options.json, io });
|
|
80
84
|
return report.installation.status === "healthy" ? 0 : 1;
|
|
81
85
|
}],
|
|
86
|
+
["runtime-checks", (options) => {
|
|
87
|
+
const report = options.runtimeChecksAction === "manual"
|
|
88
|
+
? setRuntimeChecksManual({ dataRoot: env.AGDF_DATA_DIR, surface: options.surface })
|
|
89
|
+
: runtimeCheckStatus(env.AGDF_DATA_DIR, options.surface);
|
|
90
|
+
if (options.runtimeChecksAction === "enable") {
|
|
91
|
+
report.next_action = `Rerun npx --yes @agdf/cli@latest ${options.surface} --runtime-checks ${options.runtimeChecksAction}; installation ownership and capability identity are revalidated before consent is persisted.`;
|
|
92
|
+
}
|
|
93
|
+
io.log(options.json ? JSON.stringify(report, null, 2) : [
|
|
94
|
+
"AGDF automatic runtime checks",
|
|
95
|
+
`Requested: ${report.requested}`,
|
|
96
|
+
`Effective: ${report.effective}`,
|
|
97
|
+
`Reason: ${report.reason}`,
|
|
98
|
+
...(report.next_action ? [`Next action: ${report.next_action}`] : []),
|
|
99
|
+
].join("\n"));
|
|
100
|
+
return report.effective === "enabled" || report.effective === "manual" ? 0 : 1;
|
|
101
|
+
}],
|
|
82
102
|
["disable", (options) => runDisable(options, { io, exec })],
|
|
83
103
|
["uninstall", (options) => runUninstall(options, { io, env, exec })],
|
|
84
|
-
["codex", (options) => {
|
|
104
|
+
["codex", async (options) => {
|
|
85
105
|
try {
|
|
106
|
+
const consent = await installConsentDecision("codex", options, { io, askRuntimeCheckDecision, interactive, dataRoot: installerAdapters.dataRoot });
|
|
107
|
+
if (consent.decision === "cancel") return printCancelledConsent("codex", options, io);
|
|
108
|
+
printInstallProgress("codex", options, io, interactive);
|
|
86
109
|
const installed = installCodexGlobalPlugin(installerAdapters);
|
|
110
|
+
const finalizedConsent = finalizeInstallConsent(consent, { surface: "codex", installed, dataRoot: installerAdapters.dataRoot });
|
|
87
111
|
printLifecycleResult(installResult(installed, {
|
|
88
112
|
restartRequired: true,
|
|
89
|
-
nextAction: globalInstallRestartAction(options.target).text,
|
|
90
|
-
|
|
113
|
+
nextAction: installNextAction("codex", finalizedConsent.state, globalInstallRestartAction(options.target).text),
|
|
114
|
+
runtimeChecks: finalizedConsent.state,
|
|
115
|
+
consentFailure: finalizedConsent.failure,
|
|
116
|
+
}), { json: options.json, compact: !options.verbose, io });
|
|
91
117
|
printVerboseHostOutput(installed, options, io);
|
|
92
118
|
return 0;
|
|
93
119
|
} catch (error) {
|
|
@@ -95,13 +121,19 @@ function createHandlers({ io, env, exec, prepare, openCodePackageSource }) {
|
|
|
95
121
|
return 1;
|
|
96
122
|
}
|
|
97
123
|
}],
|
|
98
|
-
["claude", (options) => {
|
|
124
|
+
["claude", async (options) => {
|
|
99
125
|
try {
|
|
126
|
+
const consent = await installConsentDecision("claude", options, { io, askRuntimeCheckDecision, interactive, dataRoot: installerAdapters.dataRoot });
|
|
127
|
+
if (consent.decision === "cancel") return printCancelledConsent("claude", options, io);
|
|
128
|
+
printInstallProgress("claude", options, io, interactive);
|
|
100
129
|
const installed = installClaudeGlobalPlugin(installerAdapters);
|
|
130
|
+
const finalizedConsent = finalizeInstallConsent(consent, { surface: "claude", installed, dataRoot: installerAdapters.dataRoot });
|
|
101
131
|
printLifecycleResult(installResult(installed, {
|
|
102
132
|
restartRequired: true,
|
|
103
133
|
nextAction: globalInstallRestartAction(options.target).text,
|
|
104
|
-
|
|
134
|
+
runtimeChecks: finalizedConsent.state,
|
|
135
|
+
consentFailure: finalizedConsent.failure,
|
|
136
|
+
}), { json: options.json, compact: !options.verbose, io });
|
|
105
137
|
printVerboseHostOutput(installed, options, io);
|
|
106
138
|
return 0;
|
|
107
139
|
} catch (error) {
|
|
@@ -109,9 +141,12 @@ function createHandlers({ io, env, exec, prepare, openCodePackageSource }) {
|
|
|
109
141
|
return 1;
|
|
110
142
|
}
|
|
111
143
|
}],
|
|
112
|
-
["opencode", (options) => {
|
|
144
|
+
["opencode", async (options) => {
|
|
113
145
|
const configDir = options.dirExplicit ? options.dir : defaultOpenCodeConfigDir();
|
|
114
146
|
try {
|
|
147
|
+
const consent = await installConsentDecision("opencode", options, { io, askRuntimeCheckDecision, interactive, dataRoot: installerAdapters.dataRoot });
|
|
148
|
+
if (consent.decision === "cancel") return printCancelledConsent("opencode", options, io);
|
|
149
|
+
printInstallProgress("opencode", options, io, interactive);
|
|
115
150
|
const result = runLifecyclePhase("plugin_operation", () => installOpenCodeGlobalPlugin(configDir, { packageSource: openCodePackageSource }));
|
|
116
151
|
runLifecyclePhase("global_surface", () => installOpenCodeGlobalSurface(configDir));
|
|
117
152
|
const report = runLifecyclePhase("verification", () => evaluateOpenCodeStatus(options.dir, configDir, result.transition));
|
|
@@ -128,9 +163,18 @@ function createHandlers({ io, env, exec, prepare, openCodePackageSource }) {
|
|
|
128
163
|
kind: "recovery",
|
|
129
164
|
text: `Retry the OpenCode installation to align @opencode-ai/plugin to ${result.sdk_alignment.target_version || "the exact host version"}; observed SDK: ${result.sdk_alignment.installed_version || "unknown"}.`,
|
|
130
165
|
};
|
|
166
|
+
const finalizedConsent = finalizeInstallConsent(consent, {
|
|
167
|
+
surface: "opencode",
|
|
168
|
+
installed: {
|
|
169
|
+
pluginRoot: result.installed_package.root,
|
|
170
|
+
digest: result.installed_package.digest,
|
|
171
|
+
sourceDigest: result.package_source.digest || result.installed_package.digest,
|
|
172
|
+
},
|
|
173
|
+
dataRoot: installerAdapters.dataRoot,
|
|
174
|
+
});
|
|
131
175
|
printLifecycleResult(createLifecycleResult({
|
|
132
176
|
operation: result.transition.status === "updated" ? "update" : "install",
|
|
133
|
-
result: verificationHealthy ? "success" : "partial",
|
|
177
|
+
result: verificationHealthy && !finalizedConsent.failure ? "success" : "partial",
|
|
134
178
|
surface: options.target,
|
|
135
179
|
scope: "global",
|
|
136
180
|
version: {
|
|
@@ -154,9 +198,11 @@ function createHandlers({ io, env, exec, prepare, openCodePackageSource }) {
|
|
|
154
198
|
],
|
|
155
199
|
},
|
|
156
200
|
restart: { required: true, reason: "host_reload" },
|
|
201
|
+
runtime_checks: finalizedConsent.state,
|
|
157
202
|
next_action: nextAction,
|
|
158
|
-
|
|
159
|
-
|
|
203
|
+
failure: finalizedConsent.failure,
|
|
204
|
+
}), { json: options.json, compact: !options.verbose, io });
|
|
205
|
+
if (options.verbose && !options.json) {
|
|
160
206
|
io.log(`OpenCode host / plugin SDK: ${report.host.installed_version || "unknown"} / ${report.plugin_sdk.installed_version || "unknown"} (${report.host_sdk_version.status}; ${report.host_sdk_version.policy})`);
|
|
161
207
|
io.log(`Plugin SDK alignment: ${result.sdk_alignment.status} (target ${result.sdk_alignment.target_version || "unknown"}; installed ${result.sdk_alignment.installed_version || "unknown"})`);
|
|
162
208
|
io.log(`Experimental hook declarations: ${report.experimental_hooks.aggregate} (SDK declaration evidence; live invocation not observed)`);
|
|
@@ -180,10 +226,17 @@ function runLifecyclePhase(phase, operation) {
|
|
|
180
226
|
}
|
|
181
227
|
}
|
|
182
228
|
|
|
183
|
-
function
|
|
229
|
+
function installNextAction(surface, runtimeChecks, fallback) {
|
|
230
|
+
if (surface === "codex" && runtimeChecks.requested === "enabled" && runtimeChecks.effective === "decision_required") {
|
|
231
|
+
return "Restart Codex, then approve the AGDF session hook when Codex asks.";
|
|
232
|
+
}
|
|
233
|
+
return fallback;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function installResult(installed, { restartRequired, nextAction, runtimeChecks, consentFailure }) {
|
|
184
237
|
return createLifecycleResult({
|
|
185
238
|
operation: installed.operation ?? "install",
|
|
186
|
-
result: "success",
|
|
239
|
+
result: consentFailure ? "partial" : "success",
|
|
187
240
|
surface: installed.surface,
|
|
188
241
|
scope: "global",
|
|
189
242
|
version: { expected: installed.expectedVersion, installed: installed.installedVersion, status: installed.installedVersion ? "verified" : "unknown" },
|
|
@@ -191,9 +244,110 @@ function installResult(installed, { restartRequired, nextAction }) {
|
|
|
191
244
|
installation: { status: installed.verificationStatus },
|
|
192
245
|
activation: { status: restartRequired ? "pending_restart" : "active" },
|
|
193
246
|
delivery: { status: "not_evaluated" },
|
|
247
|
+
runtime_checks: runtimeChecks,
|
|
194
248
|
restart: { required: restartRequired, reason: restartRequired ? "host_reload" : "none" },
|
|
195
249
|
next_action: { kind: restartRequired ? "restart" : "prompt", text: nextAction },
|
|
250
|
+
failure: consentFailure,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function finalizeInstallConsent(consent, input) {
|
|
255
|
+
if (!consent.persist) return {
|
|
256
|
+
state: consent.retained && consent.decision === "enable"
|
|
257
|
+
? { requested: "enabled", effective: "decision_required", reason: "host_permission_unverified" }
|
|
258
|
+
: { requested: "manual", effective: "manual", reason: "consent_not_provided" },
|
|
259
|
+
failure: null,
|
|
260
|
+
};
|
|
261
|
+
try {
|
|
262
|
+
return { state: persistInstallConsent({ ...input, decision: consent.decision }), failure: null };
|
|
263
|
+
} catch (error) {
|
|
264
|
+
return {
|
|
265
|
+
state: { requested: consent.decision === "enable" ? "enabled" : "manual", effective: "failed", reason: "configuration_invalid" },
|
|
266
|
+
failure: { phase: "runtime_check_permission", message: error.message },
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function installConsentDecision(surface, options, { io, askRuntimeCheckDecision, interactive, dataRoot }) {
|
|
272
|
+
if (options.runtimeChecksDecision !== undefined) return prepareInstallConsent(surface, options);
|
|
273
|
+
if (!interactive || options.json || typeof askRuntimeCheckDecision !== "function") {
|
|
274
|
+
return prepareInstallConsent(surface, options);
|
|
275
|
+
}
|
|
276
|
+
const retained = retainCurrentInstallConsent(surface, dataRoot);
|
|
277
|
+
const disclosure = prepareInstallConsent(surface, { ...options, runtimeChecksDecision: "manual" }).disclosure;
|
|
278
|
+
printInstallConsentDisclosure(disclosure, retained, io);
|
|
279
|
+
const answer = await askRuntimeCheckDecision(disclosure);
|
|
280
|
+
return prepareInstallConsent(surface, { ...options, runtimeChecksDecision: answer });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function printInstallConsentDisclosure(disclosure, retained, io) {
|
|
284
|
+
const host = installSurfaceLabel(disclosure.surface);
|
|
285
|
+
io.log("");
|
|
286
|
+
io.log(`AGDF ${pluginDefinition.version} for ${host}`);
|
|
287
|
+
io.log(`Applies to this ${host} installation for your user account.`);
|
|
288
|
+
io.log("");
|
|
289
|
+
io.log(`Let AGDF check your project status automatically ${disclosure.when}?`);
|
|
290
|
+
if (retained) {
|
|
291
|
+
io.log(retained.decision === "enable"
|
|
292
|
+
? "Your previous choice: automatic checks requested"
|
|
293
|
+
: "Your previous choice: manual checks");
|
|
294
|
+
if (retained.decision === "enable") io.log(`${host} permission: checked after installation`);
|
|
295
|
+
}
|
|
296
|
+
io.log("");
|
|
297
|
+
io.log("Safe by design");
|
|
298
|
+
io.log(" Reads only AGDF runtime information and .agdf/control in this project");
|
|
299
|
+
io.log(" Changes no project files and uses no network");
|
|
300
|
+
io.log(" Never approves AGDF work");
|
|
301
|
+
io.log("");
|
|
302
|
+
io.log(`AGDF saves your choice. ${host} remains in control of permission.`);
|
|
303
|
+
io.log("You choose again for every install or update and whenever the check changes.");
|
|
304
|
+
io.log(`Turn it off anytime: ${disclosure.revocation}`);
|
|
305
|
+
io.log("");
|
|
306
|
+
io.log("Choose");
|
|
307
|
+
io.log(" [1] Yes, check automatically");
|
|
308
|
+
io.log(" [2] No automatic checks");
|
|
309
|
+
io.log(" AGDF still works. Checks run when you request them.");
|
|
310
|
+
io.log(" [D] Show technical details");
|
|
311
|
+
io.log(" [Esc] Cancel installation");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function installSurfaceLabel(surface) {
|
|
315
|
+
if (surface === "claude") return "Claude Code";
|
|
316
|
+
if (surface === "opencode") return "OpenCode";
|
|
317
|
+
return "Codex";
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function printInstallProgress(surface, options, io, interactive) {
|
|
321
|
+
if (!interactive || options.json) return;
|
|
322
|
+
io.log("");
|
|
323
|
+
io.log(`Setting up AGDF ${pluginDefinition.version} for ${installSurfaceLabel(surface)}...`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function installConsentTechnicalDetails(disclosure) {
|
|
327
|
+
return [
|
|
328
|
+
"",
|
|
329
|
+
"Technical details",
|
|
330
|
+
` Applies to: ${disclosure.installation_scope}`,
|
|
331
|
+
` Runs: ${disclosure.runs} ${disclosure.when}`,
|
|
332
|
+
` Reads: ${disclosure.reads}`,
|
|
333
|
+
` Saves: ${disclosure.writes}`,
|
|
334
|
+
` Permission control: ${disclosure.permission_owner}`,
|
|
335
|
+
` Command: ${disclosure.executable}`,
|
|
336
|
+
` Renewal: ${disclosure.renewal}`,
|
|
337
|
+
"",
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function printCancelledConsent(surface, options, io) {
|
|
342
|
+
const report = createLifecycleResult({
|
|
343
|
+
operation: "install", result: "preview", surface, scope: "global",
|
|
344
|
+
verification: { status: "unknown", evidence: ["cancelled_before_mutation"] },
|
|
345
|
+
runtime_checks: { requested: "cancelled", effective: "cancelled", reason: "consent_not_provided" },
|
|
346
|
+
restart: { required: false },
|
|
347
|
+
next_action: { kind: "none", text: "Installation was cancelled before any plugin or permission mutation." },
|
|
196
348
|
});
|
|
349
|
+
printLifecycleResult(report, { json: options.json, compact: true, io });
|
|
350
|
+
return 0;
|
|
197
351
|
}
|
|
198
352
|
|
|
199
353
|
function printVerboseHostOutput(installed, options, io) {
|
|
@@ -342,9 +496,70 @@ export async function runCli(argv = process.argv.slice(2), adapters = {}) {
|
|
|
342
496
|
}
|
|
343
497
|
const command = resolveCommand(options.target);
|
|
344
498
|
if (!command) throw new Error(`No handler is registered for ${options.target}.`);
|
|
345
|
-
const handler = createHandlers({
|
|
499
|
+
const handler = createHandlers({
|
|
500
|
+
io,
|
|
501
|
+
env,
|
|
502
|
+
exec: adapters.exec,
|
|
503
|
+
prepare: adapters.prepare,
|
|
504
|
+
openCodePackageSource: adapters.openCodePackageSource,
|
|
505
|
+
askRuntimeCheckDecision: adapters.askRuntimeCheckDecision ?? defaultAskRuntimeCheckDecision,
|
|
506
|
+
interactive: adapters.interactive ?? (Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY)),
|
|
507
|
+
}).get(command.handler);
|
|
346
508
|
if (!handler) throw new Error(`No implementation is registered for ${command.name}.`);
|
|
347
509
|
return await handler(options);
|
|
348
510
|
}
|
|
349
511
|
|
|
350
512
|
export const main = runCli;
|
|
513
|
+
|
|
514
|
+
async function defaultAskRuntimeCheckDecision(disclosure) {
|
|
515
|
+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
516
|
+
return askRuntimeCheckDecisionByKey(process.stdin, process.stdout, disclosure);
|
|
517
|
+
}
|
|
518
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
519
|
+
try {
|
|
520
|
+
while (true) {
|
|
521
|
+
const answer = (await prompt.question("Choice (1/2/D/cancel): ")).trim().toLowerCase();
|
|
522
|
+
if (["1", "e", "enable"].includes(answer)) return "enable";
|
|
523
|
+
if (["2", "m", "manual"].includes(answer)) return "manual";
|
|
524
|
+
if (["d", "details"].includes(answer)) {
|
|
525
|
+
for (const line of installConsentTechnicalDetails(disclosure)) process.stdout.write(`${line}\n`);
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
if (["c", "cancel"].includes(answer)) return "cancel";
|
|
529
|
+
process.stdout.write("Press 1, 2 or D, or type cancel.\n");
|
|
530
|
+
}
|
|
531
|
+
} finally {
|
|
532
|
+
prompt.close();
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export async function askRuntimeCheckDecisionByKey(input, output, disclosure) {
|
|
537
|
+
const wasRaw = Boolean(input.isRaw);
|
|
538
|
+
emitKeypressEvents(input);
|
|
539
|
+
output.write("Choice: ");
|
|
540
|
+
if (!wasRaw) input.setRawMode(true);
|
|
541
|
+
input.resume();
|
|
542
|
+
|
|
543
|
+
return await new Promise((resolve) => {
|
|
544
|
+
const finish = (decision) => {
|
|
545
|
+
input.off("keypress", onKeypress);
|
|
546
|
+
if (!wasRaw) input.setRawMode(false);
|
|
547
|
+
input.pause();
|
|
548
|
+
output.write(`${decision}\n`);
|
|
549
|
+
resolve(decision);
|
|
550
|
+
};
|
|
551
|
+
const onKeypress = (character, key = {}) => {
|
|
552
|
+
if (key.name === "escape" || (key.ctrl && key.name === "c")) return finish("cancel");
|
|
553
|
+
const choice = String(character || "").toLowerCase();
|
|
554
|
+
if (["1", "e"].includes(choice)) return finish("enable");
|
|
555
|
+
if (["2", "m"].includes(choice)) return finish("manual");
|
|
556
|
+
if (choice === "c") return finish("cancel");
|
|
557
|
+
if (choice === "d" && disclosure) {
|
|
558
|
+
output.write(`details\n${installConsentTechnicalDetails(disclosure).join("\n")}Choice: `);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
output.write("\nPress 1, 2, D or Esc.\nChoice: ");
|
|
562
|
+
};
|
|
563
|
+
input.on("keypress", onKeypress);
|
|
564
|
+
});
|
|
565
|
+
}
|
|
@@ -17,6 +17,7 @@ export const commandRegistry = Object.freeze([
|
|
|
17
17
|
command("opencode", { preferred: [""], scaffold: [""] }),
|
|
18
18
|
command("opencode-status", { preferred: [""], scaffold: [""] }),
|
|
19
19
|
command("status", { preferred: [" [--surface <surface>] [--run <run_id>] [--json]"] }),
|
|
20
|
+
command("runtime-checks", { preferred: [" <status|enable|manual> --surface <codex|claude|opencode> [--json]"] }),
|
|
20
21
|
command("disable", { preferred: [" --surface <surface> [--scope repository] [--dir <path>]"] }),
|
|
21
22
|
command("uninstall", { preferred: [" --surface <surface> --scope global [--confirm]"] }),
|
|
22
23
|
command("opencode-repo", { preferred: [""], scaffold: [""] }),
|
|
@@ -65,6 +66,12 @@ export function validateCommandOptions(options) {
|
|
|
65
66
|
throw new Error("uninstall requires explicit --scope global");
|
|
66
67
|
}
|
|
67
68
|
if (options.confirm && options.target !== "uninstall") throw new Error("--confirm is supported only by uninstall");
|
|
69
|
+
if (options.runtimeChecksDecision && !["codex", "claude", "opencode"].includes(options.target)) {
|
|
70
|
+
throw new Error("--runtime-checks is supported only by codex, claude and opencode installation commands");
|
|
71
|
+
}
|
|
72
|
+
if (options.target === "runtime-checks" && !["codex", "claude", "opencode"].includes(options.surface)) {
|
|
73
|
+
throw new Error("runtime-checks requires --surface codex, claude or opencode");
|
|
74
|
+
}
|
|
68
75
|
if (options.approvalEnvelope && options.target !== "gate-check") throw new Error("--approval-envelope is supported only by gate-check");
|
|
69
76
|
if (options.approvalEnvelope && (options.json || options.statusCard || options.allActive)) {
|
|
70
77
|
throw new Error("--approval-envelope cannot be combined with --json, --status-card or --all-active");
|
|
@@ -120,6 +127,8 @@ Options:
|
|
|
120
127
|
--scope <repository|global>
|
|
121
128
|
Select the lifecycle mutation scope
|
|
122
129
|
--confirm Apply a previously previewed global uninstall plan
|
|
130
|
+
--runtime-checks <enable|manual|cancel>
|
|
131
|
+
Make the installation-time automatic-check decision explicitly; no TTY defaults to manual
|
|
123
132
|
--fixture <path>
|
|
124
133
|
Use deterministic evaluator/candidate fixtures instead of a live evaluator
|
|
125
134
|
--persist Persist the redacted Delivery Path Search result under the current scope
|
package/lib/cli/parse-args.js
CHANGED
|
@@ -45,6 +45,8 @@ export function parseArgs(argv, dependencies = {}) {
|
|
|
45
45
|
let allActive = false;
|
|
46
46
|
let scope;
|
|
47
47
|
let confirm = false;
|
|
48
|
+
let runtimeChecksDecision;
|
|
49
|
+
let runtimeChecksAction;
|
|
48
50
|
|
|
49
51
|
for (let i = 0; i < args.length; i += 1) {
|
|
50
52
|
const arg = args[i];
|
|
@@ -60,6 +62,14 @@ export function parseArgs(argv, dependencies = {}) {
|
|
|
60
62
|
if (arg === "--all-active") { allActive = true; continue; }
|
|
61
63
|
if (arg === "--confirm") { confirm = true; continue; }
|
|
62
64
|
|
|
65
|
+
if (arg === "--runtime-checks") {
|
|
66
|
+
const next = requiredValue(args, i, arg);
|
|
67
|
+
if (!["enable", "manual", "cancel"].includes(next)) throw new CliUsageError("--runtime-checks must be enable, manual or cancel.");
|
|
68
|
+
runtimeChecksDecision = next;
|
|
69
|
+
i += 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
if (arg === "--run") {
|
|
64
74
|
runId = requiredValue(args, i, arg);
|
|
65
75
|
i += 1;
|
|
@@ -119,6 +129,11 @@ export function parseArgs(argv, dependencies = {}) {
|
|
|
119
129
|
target = arg;
|
|
120
130
|
continue;
|
|
121
131
|
}
|
|
132
|
+
if (!arg.startsWith("-") && target === "runtime-checks" && !runtimeChecksAction) {
|
|
133
|
+
if (!["status", "enable", "manual"].includes(arg)) throw new CliUsageError("runtime-checks action must be status, enable or manual.");
|
|
134
|
+
runtimeChecksAction = arg;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
122
137
|
throw new CliUsageError(`Unknown argument: ${arg}`);
|
|
123
138
|
}
|
|
124
139
|
|
|
@@ -147,6 +162,8 @@ export function parseArgs(argv, dependencies = {}) {
|
|
|
147
162
|
allActive,
|
|
148
163
|
scope,
|
|
149
164
|
confirm,
|
|
165
|
+
runtimeChecksDecision,
|
|
166
|
+
runtimeChecksAction: runtimeChecksAction ?? "status",
|
|
150
167
|
generatorModel,
|
|
151
168
|
maxGeneratedCandidates,
|
|
152
169
|
generationTimeoutMs,
|
|
@@ -398,6 +398,7 @@ export function prepareLocalMarketplace({
|
|
|
398
398
|
codexInstallVersion,
|
|
399
399
|
sourceDigest,
|
|
400
400
|
digest: pluginDigest,
|
|
401
|
+
runtimeDigest: runtimeManifest.digest,
|
|
401
402
|
existingClassification,
|
|
402
403
|
changed: false,
|
|
403
404
|
commit() {},
|
|
@@ -420,6 +421,7 @@ export function prepareLocalMarketplace({
|
|
|
420
421
|
codexInstallVersion,
|
|
421
422
|
sourceDigest,
|
|
422
423
|
digest: pluginDigest,
|
|
424
|
+
runtimeDigest: runtimeManifest.digest,
|
|
423
425
|
existingClassification,
|
|
424
426
|
changed: true,
|
|
425
427
|
commit() {
|