@yiaany/ghostapi 0.1.9 → 0.2.0
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/CHANGELOG.md +21 -0
- package/CONTRIBUTING.md +132 -119
- package/LICENSE +21 -21
- package/PROVENANCE.md +13 -0
- package/README.md +78 -84
- package/SECURITY.md +56 -53
- package/dist/actions/gateway.js +320 -72
- package/dist/actions/gateway.js.map +1 -1
- package/dist/actions/index.d.ts +2 -2
- package/dist/actions/index.js +1 -1
- package/dist/actions/index.js.map +1 -1
- package/dist/agents/agentPrompt.js +7 -5
- package/dist/agents/agentPrompt.js.map +1 -1
- package/dist/ai/aiClient.js +5 -5
- package/dist/ai/aiClient.js.map +1 -1
- package/dist/ai/aiGenerator.js +66 -26
- package/dist/ai/aiGenerator.js.map +1 -1
- package/dist/ai/genericInference.js +151 -36
- package/dist/ai/genericInference.js.map +1 -1
- package/dist/ai/prompts.js +55 -49
- package/dist/ai/prompts.js.map +1 -1
- package/dist/approvals/approvalInbox.js +649 -133
- package/dist/approvals/approvalInbox.js.map +1 -1
- package/dist/approvals/index.d.ts +2 -2
- package/dist/approvals/index.js +1 -1
- package/dist/approvals/index.js.map +1 -1
- package/dist/behavior/behaviorStore.js +34 -14
- package/dist/behavior/behaviorStore.js.map +1 -1
- package/dist/cache/index.js +14 -5
- package/dist/cache/index.js.map +1 -1
- package/dist/cli/index.js +226 -93
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/openUrl.js +17 -3
- package/dist/cli/openUrl.js.map +1 -1
- package/dist/cli/parser.js +79 -18
- package/dist/cli/parser.js.map +1 -1
- package/dist/config/dataPaths.js +4 -2
- package/dist/config/dataPaths.js.map +1 -1
- package/dist/config/localConfig.js +5 -2
- package/dist/config/localConfig.js.map +1 -1
- package/dist/config/serverConfig.js +11 -5
- package/dist/config/serverConfig.js.map +1 -1
- package/dist/contracts/contract.js +311 -83
- package/dist/contracts/contract.js.map +1 -1
- package/dist/contracts/index.d.ts +2 -2
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/credentials/credentialBroker.js +391 -79
- package/dist/credentials/credentialBroker.js.map +1 -1
- package/dist/credentials/index.d.ts +2 -2
- package/dist/credentials/index.js +1 -1
- package/dist/credentials/index.js.map +1 -1
- package/dist/dashboard/app.js +823 -685
- package/dist/dashboard/dashboard.js.map +1 -1
- package/dist/dashboard/index.html +704 -267
- package/dist/dashboard/styles.css +1040 -349
- package/dist/egress/capabilities.js +55 -25
- package/dist/egress/capabilities.js.map +1 -1
- package/dist/egress/linuxBootstrap.js +47 -11
- package/dist/egress/linuxBootstrap.js.map +1 -1
- package/dist/egress/run.js +170 -31
- package/dist/egress/run.js.map +1 -1
- package/dist/errors/errorEngine.js +15 -3
- package/dist/errors/errorEngine.js.map +1 -1
- package/dist/errors/providerErrors.js.map +1 -1
- package/dist/evals/evals.js +304 -78
- package/dist/evals/evals.js.map +1 -1
- package/dist/evals/index.d.ts +2 -2
- package/dist/evals/index.js +1 -1
- package/dist/evals/index.js.map +1 -1
- package/dist/evidence/index.d.ts +2 -2
- package/dist/evidence/index.js +1 -1
- package/dist/evidence/index.js.map +1 -1
- package/dist/evidence/report.d.ts +1 -1
- package/dist/evidence/report.js +263 -79
- package/dist/evidence/report.js.map +1 -1
- package/dist/fault/faultLab.js +24 -9
- package/dist/fault/faultLab.js.map +1 -1
- package/dist/index.d.ts +42 -42
- package/dist/index.js +21 -21
- package/dist/index.js.map +1 -1
- package/dist/inventory/analysis.d.ts +1 -1
- package/dist/inventory/analysis.js +174 -50
- package/dist/inventory/analysis.js.map +1 -1
- package/dist/inventory/index.d.ts +4 -4
- package/dist/inventory/index.js +3 -3
- package/dist/inventory/index.js.map +1 -1
- package/dist/inventory/inventoryController.js +362 -115
- package/dist/inventory/inventoryController.js.map +1 -1
- package/dist/inventory/types.js +680 -140
- package/dist/inventory/types.js.map +1 -1
- package/dist/ledger/actionLedger.js +355 -78
- package/dist/ledger/actionLedger.js.map +1 -1
- package/dist/ledger/index.d.ts +2 -2
- package/dist/ledger/index.js +1 -1
- package/dist/ledger/index.js.map +1 -1
- package/dist/mcp/server.js +26 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +1 -1
- package/dist/policy/index.js.map +1 -1
- package/dist/policy/policy.js +112 -32
- package/dist/policy/policy.js.map +1 -1
- package/dist/productTelemetry/index.js +71 -20
- package/dist/productTelemetry/index.js.map +1 -1
- package/dist/providers/conformance.js +10 -5
- package/dist/providers/conformance.js.map +1 -1
- package/dist/providers/discord.js +2 -2
- package/dist/providers/generic.js +3 -3
- package/dist/providers/github.js +2 -2
- package/dist/providers/index.d.ts +3 -3
- package/dist/providers/index.js +2 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.js +3 -3
- package/dist/providers/packs/resendPack.js +51 -25
- package/dist/providers/packs/resendPack.js.map +1 -1
- package/dist/providers/packs/stripePack.js +590 -121
- package/dist/providers/packs/stripePack.js.map +1 -1
- package/dist/providers/registry.js +4 -4
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/runtime.js +7 -6
- package/dist/providers/runtime.js.map +1 -1
- package/dist/providers/stripeWebhook.js +39 -10
- package/dist/providers/stripeWebhook.js.map +1 -1
- package/dist/providers/twilio.js +2 -2
- package/dist/proxy/cacheKey.js +9 -2
- package/dist/proxy/cacheKey.js.map +1 -1
- package/dist/proxy/providerDetector.js +22 -9
- package/dist/proxy/providerDetector.js.map +1 -1
- package/dist/proxy/proxyHandler.js +57 -22
- package/dist/proxy/proxyHandler.js.map +1 -1
- package/dist/proxy/requestNormalizer.js +4 -2
- package/dist/proxy/requestNormalizer.js.map +1 -1
- package/dist/reliability/costGovernance.js +211 -49
- package/dist/reliability/costGovernance.js.map +1 -1
- package/dist/reliability/index.d.ts +8 -8
- package/dist/reliability/index.js +4 -4
- package/dist/reliability/index.js.map +1 -1
- package/dist/reliability/reconciliation.js +274 -65
- package/dist/reliability/reconciliation.js.map +1 -1
- package/dist/reliability/runtimeHealth.js +127 -35
- package/dist/reliability/runtimeHealth.js.map +1 -1
- package/dist/reliability/slo.js +257 -50
- package/dist/reliability/slo.js.map +1 -1
- package/dist/report/safetyReport.d.ts +6 -0
- package/dist/report/safetyReport.js +56 -12
- package/dist/report/safetyReport.js.map +1 -1
- package/dist/rules/aiRules.js +5 -5
- package/dist/rules/aiRules.js.map +1 -1
- package/dist/safety/index.d.ts +2 -2
- package/dist/safety/index.js +1 -1
- package/dist/safety/index.js.map +1 -1
- package/dist/safety/safetyController.js +675 -165
- package/dist/safety/safetyController.js.map +1 -1
- package/dist/scenarios/scenarioBundle.js +204 -71
- package/dist/scenarios/scenarioBundle.js.map +1 -1
- package/dist/scenarios/scenarioStore.js +88 -29
- package/dist/scenarios/scenarioStore.js.map +1 -1
- package/dist/security/headerSanitizer.js +52 -10
- package/dist/security/headerSanitizer.js.map +1 -1
- package/dist/security/secrets.js +10 -5
- package/dist/security/secrets.js.map +1 -1
- package/dist/server/accessControl.js +37 -10
- package/dist/server/accessControl.js.map +1 -1
- package/dist/server/createServer.js +1 -1
- package/dist/server/createServer.js.map +1 -1
- package/dist/server/eventsStore.js +15 -5
- package/dist/server/eventsStore.js.map +1 -1
- package/dist/server/routes.js +61 -19
- package/dist/server/routes.js.map +1 -1
- package/dist/server/sse.js +1 -1
- package/dist/server/sse.js.map +1 -1
- package/dist/setup/sdkDetector.js +7 -2
- package/dist/setup/sdkDetector.js.map +1 -1
- package/dist/setup/setupGenerator.js +136 -48
- package/dist/setup/setupGenerator.js.map +1 -1
- package/dist/state/stateExtractor.js +3 -1
- package/dist/state/stateExtractor.js.map +1 -1
- package/dist/state/stateResolver.js +10 -7
- package/dist/state/stateResolver.js.map +1 -1
- package/dist/state/stateStore.js +13 -4
- package/dist/state/stateStore.js.map +1 -1
- package/dist/storage/fileStore.js +12 -4
- package/dist/storage/fileStore.js.map +1 -1
- package/dist/teamControl/controlPlane.js +846 -174
- package/dist/teamControl/controlPlane.js.map +1 -1
- package/dist/teamControl/deployment.js +19 -5
- package/dist/teamControl/deployment.js.map +1 -1
- package/dist/teamControl/index.d.ts +4 -4
- package/dist/teamControl/index.js +2 -2
- package/dist/teamControl/index.js.map +1 -1
- package/dist/tests/testGenerator.js +15 -7
- package/dist/tests/testGenerator.js.map +1 -1
- package/dist/trust/index.d.ts +2 -2
- package/dist/trust/index.js +1 -1
- package/dist/trust/index.js.map +1 -1
- package/dist/trust/trustLadder.js +309 -63
- package/dist/trust/trustLadder.js.map +1 -1
- package/dist/worlds/index.d.ts +2 -2
- package/dist/worlds/index.js +1 -1
- package/dist/worlds/index.js.map +1 -1
- package/dist/worlds/worlds.js +364 -65
- package/dist/worlds/worlds.js.map +1 -1
- package/docs/assets/dashboard.png +0 -0
- package/docs/ci.md +49 -49
- package/docs/github-actions.md +84 -84
- package/docs/mcp.md +58 -52
- package/docs/policy.md +63 -61
- package/docs/providers/authoring-packs.md +180 -175
- package/docs/providers/stripe-core-pack.md +84 -82
- package/docs/releases/README.md +13 -0
- package/docs/security/action-gateway-threat-model.md +34 -34
- package/docs/security/action-ledger-incident-replay-threat-model.md +42 -42
- package/docs/security/approval-inbox-threat-model.md +35 -35
- package/docs/security/credential-broker-threat-model.md +52 -52
- package/docs/security/egress-threat-model.md +126 -126
- package/docs/security/inventory-threat-model.md +104 -104
- package/docs/security/kill-switch-budgets-threat-model.md +37 -37
- package/docs/security/reliability-threat-model.md +111 -111
- package/docs/security/trust-ladder-threat-model.md +33 -33
- package/docs/telemetry.md +14 -0
- package/docs/usage.md +680 -571
- package/examples/README.md +10 -10
- package/examples/agent-instructions/README.md +39 -39
- package/examples/ci-smoke/README.md +21 -21
- package/examples/ci-smoke/ghostapi.policy.yaml +22 -22
- package/examples/ci-smoke/package.json +9 -9
- package/examples/ci-smoke/production-egress.mjs +4 -2
- package/examples/ci-smoke/safe.mjs +21 -17
- package/examples/evals/README.md +24 -24
- package/examples/evals/retry-after.eval.json +71 -52
- package/examples/generic-rest/README.md +27 -27
- package/examples/github-fetch/README.md +38 -35
- package/examples/openai-streaming/README.md +16 -16
- package/examples/openai-streaming/streaming-tool-call.mjs +44 -31
- package/examples/policy/ghostapi.policy.yaml +27 -27
- package/examples/record-replay/README.md +19 -19
- package/examples/record-replay/replay-requests.json +22 -22
- package/examples/record-replay/stripe-checkout.har.json +30 -30
- package/examples/resend-node/README.md +22 -22
- package/examples/stripe-node/README.md +35 -35
- package/examples/stripe-node/checkout-flow.mjs +87 -61
- package/examples/twilio-node/README.md +21 -21
- package/examples/worlds/README.md +19 -19
- package/examples/worlds/subscription-recovery.mjs +21 -12
- package/package.json +28 -6
- package/dist/landing/assets/index-BXR41fAD.css +0 -1
- package/dist/landing/assets/index-CxWsopzP.css +0 -1
- package/dist/landing/assets/index-D8_ggMRE.js +0 -55
- package/dist/landing/assets/index-DZlpyhqd.js +0 -55
- package/dist/landing/assets/index-Dk-Xezs3.js +0 -55
- package/dist/landing/index.html +0 -15
- package/dist/landing/landing.d.ts +0 -3
- package/dist/landing/landing.js +0 -12
- package/dist/landing/landing.js.map +0 -1
- package/docs/commercial/README.md +0 -29
- package/docs/commercial/data-inventory.md +0 -36
- package/docs/commercial/manual-invoicing.md +0 -44
- package/docs/commercial/metrics.md +0 -51
- package/docs/commercial/pricing.md +0 -103
- package/docs/commercial/privacy-policy-draft.md +0 -41
- package/docs/commercial/terms-topics-for-counsel.md +0 -51
- package/docs/design-partners/README.md +0 -52
- package/docs/design-partners/demo-narratives.md +0 -33
- package/docs/design-partners/design-partner-offer.md +0 -39
- package/docs/design-partners/discovery-questions.md +0 -28
- package/docs/design-partners/feedback-capture-template.md +0 -48
- package/docs/design-partners/icp-one-pager.md +0 -43
- package/docs/design-partners/onboarding-checklist.md +0 -33
- package/docs/design-partners/pilot-success-criteria.md +0 -27
- package/docs/design-partners/pricing-interview-script.md +0 -18
- package/docs/design-partners/telemetry-plan.md +0 -48
- package/docs/development/baseline.md +0 -114
- package/docs/development/onboarding-smoke.md +0 -37
- package/docs/development/verification-0.1.8.md +0 -25
- package/docs/enterprise-product-roadmap-ru.md +0 -1879
- package/docs/fundraising/README.md +0 -87
- package/docs/fundraising/data-room-checklist.md +0 -72
- package/docs/fundraising/demo-script.md +0 -52
- package/docs/fundraising/design-partners-50.md +0 -74
- package/docs/fundraising/launch-posts.md +0 -37
- package/docs/fundraising/metrics-and-evidence.md +0 -30
- package/docs/fundraising/roadmap-12-month.md +0 -25
- package/docs/fundraising/technical-due-diligence-index.md +0 -37
- package/docs/fundraising/yc-application.md +0 -25
- package/docs/hosted-pilot.md +0 -111
- package/docs/operations/disaster-recovery-runbook.md +0 -81
- package/docs/operations/kill-switch-runbook.md +0 -32
- package/docs/release-checklist.md +0 -83
- package/docs/release-migration-and-rollback.md +0 -63
- package/docs/release-readiness.md +0 -39
- package/docs/team-control-plane.md +0 -72
package/dist/cli/index.js
CHANGED
|
@@ -10,26 +10,26 @@ import { createServer } from "../server/createServer.js";
|
|
|
10
10
|
import { clearCache } from "../cache/index.js";
|
|
11
11
|
import { clearState } from "../state/stateStore.js";
|
|
12
12
|
import { clearEvents } from "../server/eventsStore.js";
|
|
13
|
-
import { DEFAULT_MODEL, loadServerConfig } from "../config/serverConfig.js";
|
|
14
|
-
import { initializeLocalConfig, readLocalConfig, writeLocalConfig } from "../config/localConfig.js";
|
|
13
|
+
import { DEFAULT_MODEL, loadServerConfig, } from "../config/serverConfig.js";
|
|
14
|
+
import { initializeLocalConfig, readLocalConfig, writeLocalConfig, } from "../config/localConfig.js";
|
|
15
15
|
import { getDataPaths } from "../config/dataPaths.js";
|
|
16
|
-
import { detectEgressCapabilities, formatEgressCapabilityReport } from "../egress/capabilities.js";
|
|
16
|
+
import { detectEgressCapabilities, formatEgressCapabilityReport, } from "../egress/capabilities.js";
|
|
17
17
|
import { EgressRunError, runEgressCommand } from "../egress/run.js";
|
|
18
|
-
import { PolicyValidationError, evaluatePolicy, formatPolicyDecision, loadPolicyFile } from "../policy/index.js";
|
|
18
|
+
import { PolicyValidationError, evaluatePolicy, formatPolicyDecision, loadPolicyFile, } from "../policy/index.js";
|
|
19
19
|
import { isLoopbackHost } from "../server/accessControl.js";
|
|
20
|
-
import { getProviderManifests, isRegisteredProvider, providerRegistry } from "../providers/registry.js";
|
|
20
|
+
import { getProviderManifests, isRegisteredProvider, providerRegistry, } from "../providers/registry.js";
|
|
21
21
|
import { parseCliArgs } from "./parser.js";
|
|
22
22
|
import { CliError } from "./errors.js";
|
|
23
23
|
import { startMcpServer } from "../mcp/server.js";
|
|
24
24
|
import { generateRepoSetup, writeRepoSetup } from "../setup/setupGenerator.js";
|
|
25
25
|
import { generateSafetyReport } from "../report/safetyReport.js";
|
|
26
|
-
import { compareEvidenceReports, formatEvidenceCompare, formatEvidenceReport, generateEvidenceReport, loadEvidenceReport, EvidenceReportError } from "../evidence/index.js";
|
|
27
|
-
import { createScenarioReplayer, formatScenarioSanitizationSummary, loadScenarioBundle, prepareScenarioRecordingFromFile, ScenarioBundleError, writeScenarioBundle } from "../scenarios/scenarioBundle.js";
|
|
28
|
-
import { ContractError, diffContracts, formatContractDiff, importHarContractFromFile, importOpenApiContractFromFile, loadContract, writeContract } from "../contracts/index.js";
|
|
26
|
+
import { compareEvidenceReports, formatEvidenceCompare, formatEvidenceReport, generateEvidenceReport, loadEvidenceReport, EvidenceReportError, } from "../evidence/index.js";
|
|
27
|
+
import { createScenarioReplayer, formatScenarioSanitizationSummary, loadScenarioBundle, prepareScenarioRecordingFromFile, ScenarioBundleError, writeScenarioBundle, } from "../scenarios/scenarioBundle.js";
|
|
28
|
+
import { ContractError, diffContracts, formatContractDiff, importHarContractFromFile, importOpenApiContractFromFile, loadContract, writeContract, } from "../contracts/index.js";
|
|
29
29
|
import { EvalError, formatEvalReport, runEval } from "../evals/index.js";
|
|
30
|
-
import { SyntheticWorldError, createWorld, forkWorld, formatWorld, inspectWorld, resetWorld } from "../worlds/index.js";
|
|
31
|
-
import { formatProductTelemetry, readProductTelemetry, recordProductTelemetry, setProductTelemetryEnabled } from "../productTelemetry/index.js";
|
|
32
|
-
import { ActionGatewayError, createLocalActionGateway } from "../actions/index.js";
|
|
30
|
+
import { SyntheticWorldError, createWorld, forkWorld, formatWorld, inspectWorld, resetWorld, } from "../worlds/index.js";
|
|
31
|
+
import { formatProductTelemetry, readProductTelemetry, recordProductTelemetry, setProductTelemetryEnabled, } from "../productTelemetry/index.js";
|
|
32
|
+
import { ActionGatewayError, createLocalActionGateway, } from "../actions/index.js";
|
|
33
33
|
import { openUrl } from "./openUrl.js";
|
|
34
34
|
async function main() {
|
|
35
35
|
const command = parseCliArgs(process.argv.slice(2));
|
|
@@ -161,7 +161,9 @@ async function startServer(config, open = false) {
|
|
|
161
161
|
console.warn("Warning: GhostAPI is bound beyond loopback. Use HTTPS or a secure tunnel because the dashboard token does not encrypt traffic.");
|
|
162
162
|
}
|
|
163
163
|
if (open)
|
|
164
|
-
openUrl(remote && config.authToken
|
|
164
|
+
openUrl(remote && config.authToken
|
|
165
|
+
? `${dashboardUrl}?token=${encodeURIComponent(config.authToken)}`
|
|
166
|
+
: dashboardUrl);
|
|
165
167
|
});
|
|
166
168
|
}
|
|
167
169
|
async function createDevTlsOptions(host) {
|
|
@@ -172,8 +174,14 @@ async function createDevTlsOptions(host) {
|
|
|
172
174
|
{ name: "basicConstraints", cA: false },
|
|
173
175
|
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
|
174
176
|
{ name: "extKeyUsage", serverAuth: true },
|
|
175
|
-
{
|
|
176
|
-
|
|
177
|
+
{
|
|
178
|
+
name: "subjectAltName",
|
|
179
|
+
altNames: [
|
|
180
|
+
{ type: 2, value: "localhost" },
|
|
181
|
+
{ type: 7, ip: "127.0.0.1" },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
],
|
|
177
185
|
});
|
|
178
186
|
return { key: pems.private, cert: pems.cert };
|
|
179
187
|
}
|
|
@@ -201,7 +209,7 @@ async function initProject() {
|
|
|
201
209
|
}
|
|
202
210
|
async function printRepoSetup(write) {
|
|
203
211
|
const written = write ? await writeRepoSetup() : null;
|
|
204
|
-
const setup = written?.setup ?? await generateRepoSetup();
|
|
212
|
+
const setup = written?.setup ?? (await generateRepoSetup());
|
|
205
213
|
console.log(setup.summary);
|
|
206
214
|
console.log("");
|
|
207
215
|
console.log("Commands:");
|
|
@@ -232,11 +240,13 @@ function openDashboard(options) {
|
|
|
232
240
|
}
|
|
233
241
|
async function printSafetyReport() {
|
|
234
242
|
const report = await generateSafetyReport();
|
|
235
|
-
console.log(`GhostAPI
|
|
243
|
+
console.log(`GhostAPI provider-risk heuristic scan for ${report.projectRoot}`);
|
|
236
244
|
console.log(`Detected SDKs: ${report.detected.length > 0 ? report.detected.join(", ") : "none"}`);
|
|
245
|
+
console.log(`Scanned files: ${report.limits.scannedFiles}/${report.limits.maxFiles} (files over ${report.limits.maxFileBytes} bytes are skipped)`);
|
|
237
246
|
console.log("");
|
|
238
247
|
if (report.findings.length === 0) {
|
|
239
|
-
console.log("No
|
|
248
|
+
console.log("No matches found in the bounded heuristic scan.");
|
|
249
|
+
console.log("This is not proof that the repository contains no production endpoints, credentials, or egress paths.");
|
|
240
250
|
}
|
|
241
251
|
else {
|
|
242
252
|
console.log("Findings:");
|
|
@@ -260,7 +270,9 @@ async function setModel(model) {
|
|
|
260
270
|
}
|
|
261
271
|
function listProviders() {
|
|
262
272
|
for (const manifest of getProviderManifests()) {
|
|
263
|
-
const version = manifest.packVersion === null
|
|
273
|
+
const version = manifest.packVersion === null
|
|
274
|
+
? manifest.implementation
|
|
275
|
+
: `pack ${manifest.packVersion}`;
|
|
264
276
|
console.log(`${manifest.name.padEnd(8)} ${manifest.displayName} (${version})`);
|
|
265
277
|
}
|
|
266
278
|
}
|
|
@@ -277,60 +289,123 @@ function inspectProvider(provider) {
|
|
|
277
289
|
console.log(`Mode: ${manifest.implementation}`);
|
|
278
290
|
console.log(`Pack version: ${manifest.packVersion ?? "n/a"}`);
|
|
279
291
|
console.log(`API versions: ${manifest.apiVersions?.supported.join(", ") ?? "legacy"}`);
|
|
280
|
-
console.log(`Capabilities: ${Object.entries(manifest.capabilities)
|
|
292
|
+
console.log(`Capabilities: ${Object.entries(manifest.capabilities)
|
|
293
|
+
.filter(([, enabled]) => enabled)
|
|
294
|
+
.map(([name]) => name)
|
|
295
|
+
.join(", ")}`);
|
|
281
296
|
console.log("Error formatting: supported");
|
|
282
297
|
}
|
|
283
298
|
async function runDoctor(options) {
|
|
284
299
|
if (options.egress) {
|
|
285
300
|
const report = detectEgressCapabilities();
|
|
286
|
-
console.log(options.json
|
|
301
|
+
console.log(options.json
|
|
302
|
+
? JSON.stringify(report, null, 2)
|
|
303
|
+
: formatEgressCapabilityReport(report));
|
|
287
304
|
return;
|
|
288
305
|
}
|
|
289
306
|
const checks = [];
|
|
290
307
|
let config = null;
|
|
291
308
|
try {
|
|
292
309
|
config = loadServerConfig(process.env, [], options.port ? { port: options.port } : {});
|
|
293
|
-
checks.push({
|
|
310
|
+
checks.push({
|
|
311
|
+
label: "Config",
|
|
312
|
+
status: "ok",
|
|
313
|
+
detail: "loaded",
|
|
314
|
+
docs: "docs/usage.md#configuration",
|
|
315
|
+
});
|
|
294
316
|
}
|
|
295
317
|
catch (error) {
|
|
296
|
-
checks.push({
|
|
318
|
+
checks.push({
|
|
319
|
+
label: "Config",
|
|
320
|
+
status: "fail",
|
|
321
|
+
detail: error instanceof Error ? error.message : "invalid configuration",
|
|
322
|
+
remediation: "Fix GHOSTAPI_* environment values or .ghostapi/config.json, then re-run ghostapi doctor.",
|
|
323
|
+
docs: "docs/usage.md#configuration",
|
|
324
|
+
});
|
|
297
325
|
}
|
|
298
326
|
const major = Number(process.versions.node.split(".")[0]);
|
|
299
|
-
checks.push({
|
|
327
|
+
checks.push({
|
|
328
|
+
label: "Node version",
|
|
329
|
+
status: major >= 20 ? "ok" : "fail",
|
|
330
|
+
detail: process.versions.node,
|
|
331
|
+
remediation: "Install Node.js 20 or newer.",
|
|
332
|
+
docs: "README.md#quickstart",
|
|
333
|
+
});
|
|
300
334
|
const writeAccess = await canWriteGhostApiDir();
|
|
301
|
-
checks.push({
|
|
335
|
+
checks.push({
|
|
336
|
+
label: "GhostAPI data write access",
|
|
337
|
+
status: writeAccess ? "ok" : "fail",
|
|
338
|
+
detail: getDataPaths().root,
|
|
339
|
+
remediation: "Check GHOSTAPI_DATA_DIR and directory permissions.",
|
|
340
|
+
docs: "docs/usage.md#local-data-and-retention",
|
|
341
|
+
});
|
|
302
342
|
if (config !== null) {
|
|
303
343
|
const portAvailable = await isPortAvailable(config.host, config.port);
|
|
304
|
-
checks.push({
|
|
305
|
-
|
|
344
|
+
checks.push({
|
|
345
|
+
label: "Port availability",
|
|
346
|
+
status: portAvailable ? "ok" : "fail",
|
|
347
|
+
detail: `${config.host}:${config.port}`,
|
|
348
|
+
remediation: `Run ghostapi start --port <free-port> or stop the process using ${config.port}.`,
|
|
349
|
+
docs: "README.md#quickstart",
|
|
350
|
+
});
|
|
351
|
+
checks.push({
|
|
352
|
+
label: "Model config",
|
|
353
|
+
status: config.model.trim() !== "" ? "ok" : "fail",
|
|
354
|
+
detail: config.model,
|
|
355
|
+
remediation: "Run ghostapi model set <model>.",
|
|
356
|
+
docs: "docs/usage.md#configuration",
|
|
357
|
+
});
|
|
306
358
|
const hasApiKey = Boolean(config.apiKey);
|
|
307
359
|
checks.push({
|
|
308
360
|
label: "LLM API key",
|
|
309
361
|
status: config.allowExternalLlm && !hasApiKey ? "fail" : "ok",
|
|
310
|
-
detail: config.offline
|
|
311
|
-
|
|
312
|
-
|
|
362
|
+
detail: config.offline
|
|
363
|
+
? "offline mode"
|
|
364
|
+
: config.allowExternalLlm
|
|
365
|
+
? hasApiKey
|
|
366
|
+
? "explicitly enabled"
|
|
367
|
+
: "enabled without GHOSTAPI_LLM_API_KEY"
|
|
368
|
+
: "disabled; deterministic provider mocks enabled",
|
|
369
|
+
remediation: config.allowExternalLlm && !hasApiKey
|
|
370
|
+
? "Set GHOSTAPI_LLM_API_KEY or remove the external LLM opt-in."
|
|
371
|
+
: undefined,
|
|
372
|
+
docs: "README.md#security-boundaries",
|
|
313
373
|
});
|
|
314
374
|
const remote = !isLoopbackHost(config.host);
|
|
315
375
|
checks.push({
|
|
316
376
|
label: "Remote bind auth",
|
|
317
|
-
status: !remote ||
|
|
377
|
+
status: !remote ||
|
|
378
|
+
(config.authToken !== undefined && config.authToken.length >= 24)
|
|
379
|
+
? "ok"
|
|
380
|
+
: "fail",
|
|
318
381
|
detail: remote ? "non-loopback host configured" : "loopback only",
|
|
319
|
-
remediation: remote
|
|
320
|
-
|
|
382
|
+
remediation: remote
|
|
383
|
+
? "Set a strong GHOSTAPI_AUTH_TOKEN and use HTTPS or a secure tunnel."
|
|
384
|
+
: undefined,
|
|
385
|
+
docs: "README.md#security-boundaries",
|
|
321
386
|
});
|
|
322
387
|
}
|
|
323
388
|
const tlsBypass = process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0";
|
|
324
|
-
checks.push({
|
|
389
|
+
checks.push({
|
|
390
|
+
label: "TLS safety",
|
|
391
|
+
status: !tlsBypass ? "ok" : "fail",
|
|
392
|
+
detail: tlsBypass ? "NODE_TLS_REJECT_UNAUTHORIZED=0" : "safe",
|
|
393
|
+
remediation: "Unset NODE_TLS_REJECT_UNAUTHORIZED.",
|
|
394
|
+
docs: "SECURITY.md",
|
|
395
|
+
});
|
|
325
396
|
const egress = detectEgressCapabilities();
|
|
326
397
|
const linuxNamespace = egress.capabilities.find((capability) => capability.id === "linux-network-namespace");
|
|
327
398
|
const enforcementAvailable = process.platform === "linux" && linuxNamespace !== undefined;
|
|
328
399
|
checks.push({
|
|
329
400
|
label: "Enforcement capability",
|
|
330
401
|
status: enforcementAvailable ? "warn" : "warn",
|
|
331
|
-
detail: enforcementAvailable
|
|
332
|
-
|
|
333
|
-
|
|
402
|
+
detail: enforcementAvailable
|
|
403
|
+
? linuxNamespace.detail
|
|
404
|
+
: `ghostapi run process isolation is unsupported/experimental on ${process.platform}`,
|
|
405
|
+
remediation: enforcementAvailable
|
|
406
|
+
? "Run ghostapi run to execute the real preflight; doctor does not launch the target."
|
|
407
|
+
: "Use ghostapi start --open for local provider simulation, or run enforced tests on Linux.",
|
|
408
|
+
docs: "docs/security/egress-threat-model.md",
|
|
334
409
|
});
|
|
335
410
|
if (options.json) {
|
|
336
411
|
console.log(JSON.stringify({ schemaVersion: 1, checks, egress }, null, 2));
|
|
@@ -363,7 +438,13 @@ async function explainPolicy(file, event) {
|
|
|
363
438
|
console.log(formatPolicyDecision(evaluatePolicy(loaded.policy, event)));
|
|
364
439
|
}
|
|
365
440
|
async function generateEvidence(options) {
|
|
366
|
-
const { report, path } = await generateEvidenceReport({
|
|
441
|
+
const { report, path } = await generateEvidenceReport({
|
|
442
|
+
policyPath: options.policyPath,
|
|
443
|
+
runPath: options.runPath,
|
|
444
|
+
outPath: options.outPath,
|
|
445
|
+
contractBaselinePath: options.contractBaselinePath,
|
|
446
|
+
contractCandidatePath: options.contractCandidatePath,
|
|
447
|
+
});
|
|
367
448
|
if (options.json)
|
|
368
449
|
console.log(JSON.stringify({ path, report }, null, 2));
|
|
369
450
|
else {
|
|
@@ -376,13 +457,17 @@ async function generateEvidence(options) {
|
|
|
376
457
|
}
|
|
377
458
|
async function viewEvidence(options) {
|
|
378
459
|
const report = await loadEvidenceReport(options.path);
|
|
379
|
-
console.log(options.json
|
|
460
|
+
console.log(options.json
|
|
461
|
+
? JSON.stringify(report, null, 2)
|
|
462
|
+
: formatEvidenceReport(report));
|
|
380
463
|
}
|
|
381
464
|
async function compareEvidence(options) {
|
|
382
465
|
const left = await loadEvidenceReport(options.leftPath);
|
|
383
466
|
const right = await loadEvidenceReport(options.rightPath);
|
|
384
467
|
const result = compareEvidenceReports(left, right);
|
|
385
|
-
console.log(options.json
|
|
468
|
+
console.log(options.json
|
|
469
|
+
? JSON.stringify(result, null, 2)
|
|
470
|
+
: formatEvidenceCompare(result));
|
|
386
471
|
if (!result.equal)
|
|
387
472
|
process.exitCode = 1;
|
|
388
473
|
}
|
|
@@ -390,7 +475,7 @@ async function recordScenario(options) {
|
|
|
390
475
|
const bundle = await prepareScenarioRecordingFromFile(options.inputPath, {
|
|
391
476
|
title: options.title,
|
|
392
477
|
allowedSandboxHosts: options.allowedSandboxHosts,
|
|
393
|
-
pii: parsePiiRules(options.pii)
|
|
478
|
+
pii: parsePiiRules(options.pii),
|
|
394
479
|
});
|
|
395
480
|
console.log(formatScenarioSanitizationSummary(bundle.sanitization));
|
|
396
481
|
if (bundle.sanitization.requiresApproval && !options.approve) {
|
|
@@ -415,7 +500,9 @@ async function replayBundle(options) {
|
|
|
415
500
|
}
|
|
416
501
|
}
|
|
417
502
|
async function importOpenApi(options) {
|
|
418
|
-
const contract = await importOpenApiContractFromFile(options.inputPath, {
|
|
503
|
+
const contract = await importOpenApiContractFromFile(options.inputPath, {
|
|
504
|
+
title: options.title,
|
|
505
|
+
});
|
|
419
506
|
const path = await writeContract(contract, options.outPath);
|
|
420
507
|
console.log(`Contract: ${path}`);
|
|
421
508
|
console.log(`Operations: ${contract.operations.length}`);
|
|
@@ -424,7 +511,7 @@ async function importHar(options) {
|
|
|
424
511
|
const { bundle, contract } = await importHarContractFromFile(options.inputPath, {
|
|
425
512
|
title: options.title,
|
|
426
513
|
allowedSandboxHosts: options.allowedSandboxHosts,
|
|
427
|
-
pii: parsePiiRules(options.pii)
|
|
514
|
+
pii: parsePiiRules(options.pii),
|
|
428
515
|
});
|
|
429
516
|
console.log(formatScenarioSanitizationSummary(bundle.sanitization));
|
|
430
517
|
if (bundle.sanitization.requiresApproval && !options.approve) {
|
|
@@ -439,10 +526,19 @@ async function diffContractFiles(options) {
|
|
|
439
526
|
const [baseline, candidate, loadedPolicy] = await Promise.all([
|
|
440
527
|
loadContract(options.baselinePath),
|
|
441
528
|
loadContract(options.candidatePath),
|
|
442
|
-
options.policyPath === undefined
|
|
529
|
+
options.policyPath === undefined
|
|
530
|
+
? Promise.resolve(null)
|
|
531
|
+
: loadPolicyFile(options.policyPath, process.cwd(), true),
|
|
443
532
|
]);
|
|
444
533
|
const diff = diffContracts(baseline, candidate);
|
|
445
|
-
const decision = loadedPolicy === null
|
|
534
|
+
const decision = loadedPolicy === null
|
|
535
|
+
? null
|
|
536
|
+
: evaluatePolicy(loadedPolicy.policy, {
|
|
537
|
+
type: "report",
|
|
538
|
+
productionEgressAttempts: 0,
|
|
539
|
+
forbiddenCredentialMatches: 0,
|
|
540
|
+
breakingContractChanges: diff.summary.breaking,
|
|
541
|
+
});
|
|
446
542
|
if (options.json)
|
|
447
543
|
console.log(JSON.stringify({ diff, policy: decision }, null, 2));
|
|
448
544
|
else {
|
|
@@ -450,11 +546,17 @@ async function diffContractFiles(options) {
|
|
|
450
546
|
if (decision !== null)
|
|
451
547
|
console.log(`Policy: ${decision.allowed ? "PASS" : "FAIL"} - ${decision.reason}`);
|
|
452
548
|
}
|
|
453
|
-
if (options.ci &&
|
|
549
|
+
if (options.ci &&
|
|
550
|
+
(decision === null ? diff.summary.breaking > 0 : !decision.allowed))
|
|
454
551
|
process.exitCode = 2;
|
|
455
552
|
}
|
|
456
553
|
async function runEvalCommand(options) {
|
|
457
|
-
const { report, path } = await runEval({
|
|
554
|
+
const { report, path } = await runEval({
|
|
555
|
+
specPath: options.specPath,
|
|
556
|
+
template: options.template,
|
|
557
|
+
evidencePath: options.evidencePath,
|
|
558
|
+
outPath: options.outPath,
|
|
559
|
+
});
|
|
458
560
|
if (options.json)
|
|
459
561
|
console.log(JSON.stringify({ path, report }, null, 2));
|
|
460
562
|
else {
|
|
@@ -467,7 +569,9 @@ async function runEvalCommand(options) {
|
|
|
467
569
|
}
|
|
468
570
|
async function createWorldCommand(options) {
|
|
469
571
|
const world = await createWorld(options);
|
|
470
|
-
console.log(options.json
|
|
572
|
+
console.log(options.json
|
|
573
|
+
? JSON.stringify(world, null, 2)
|
|
574
|
+
: `${formatWorld(world)}\nPath: ${getDataPaths().worlds}`);
|
|
471
575
|
}
|
|
472
576
|
async function inspectWorldCommand(id, json) {
|
|
473
577
|
const world = await inspectWorld(id);
|
|
@@ -475,15 +579,21 @@ async function inspectWorldCommand(id, json) {
|
|
|
475
579
|
}
|
|
476
580
|
async function resetWorldCommand(id, json) {
|
|
477
581
|
const world = await resetWorld(id);
|
|
478
|
-
console.log(json
|
|
582
|
+
console.log(json
|
|
583
|
+
? JSON.stringify(world, null, 2)
|
|
584
|
+
: `${formatWorld(world)}\nReset: restored baseline state.`);
|
|
479
585
|
}
|
|
480
586
|
async function forkWorldCommand(sourceId, options) {
|
|
481
587
|
const world = await forkWorld(sourceId, options);
|
|
482
|
-
console.log(options.json
|
|
588
|
+
console.log(options.json
|
|
589
|
+
? JSON.stringify(world, null, 2)
|
|
590
|
+
: `${formatWorld(world)}\nForked from: ${sourceId}`);
|
|
483
591
|
}
|
|
484
592
|
async function inspectActionCommand(actionId, json) {
|
|
485
593
|
const record = await createLocalActionGateway().inspect(actionId);
|
|
486
|
-
console.log(json
|
|
594
|
+
console.log(json
|
|
595
|
+
? JSON.stringify(record, null, 2)
|
|
596
|
+
: `Action: ${record.envelope.actionId}\nHash: ${record.actionHash}\nReceipts: ${record.receipts.map((receipt) => receipt.status).join(", ")}`);
|
|
487
597
|
}
|
|
488
598
|
async function runTelemetryCommand(action, json) {
|
|
489
599
|
const snapshot = action === "enable"
|
|
@@ -513,16 +623,22 @@ function parsePiiRules(value) {
|
|
|
513
623
|
if (value === undefined)
|
|
514
624
|
return undefined;
|
|
515
625
|
const selected = new Set(value === "none" ? [] : value.split(","));
|
|
516
|
-
return {
|
|
626
|
+
return {
|
|
627
|
+
emails: selected.has("emails"),
|
|
628
|
+
phones: selected.has("phones"),
|
|
629
|
+
addresses: selected.has("addresses"),
|
|
630
|
+
};
|
|
517
631
|
}
|
|
518
632
|
async function loadReplayRequests(path) {
|
|
519
|
-
const target = isAbsolute(path)
|
|
633
|
+
const target = isAbsolute(path)
|
|
634
|
+
? resolve(path)
|
|
635
|
+
: resolve(process.cwd(), path);
|
|
520
636
|
if (!isInsideAllowedRoots(target))
|
|
521
637
|
throw new CliError("Replay requests path traversal outside the project root or GHOSTAPI_DATA_DIR is not allowed.");
|
|
522
638
|
const info = await lstat(target);
|
|
523
639
|
if (!info.isFile() || info.isSymbolicLink())
|
|
524
640
|
throw new CliError("Replay requests input must be a regular non-symlink file.");
|
|
525
|
-
if (!await isRealPathInsideAllowedRoots(target))
|
|
641
|
+
if (!(await isRealPathInsideAllowedRoots(target)))
|
|
526
642
|
throw new CliError("Replay requests input resolves outside the project root or GHOSTAPI_DATA_DIR through a symlink.");
|
|
527
643
|
const source = await readFile(target, "utf8");
|
|
528
644
|
if (Buffer.byteLength(source, "utf8") > 1024 * 1024)
|
|
@@ -534,30 +650,47 @@ async function loadReplayRequests(path) {
|
|
|
534
650
|
catch {
|
|
535
651
|
throw new CliError("Replay requests input is not valid JSON.");
|
|
536
652
|
}
|
|
537
|
-
const requests = Array.isArray(parsed)
|
|
653
|
+
const requests = Array.isArray(parsed)
|
|
654
|
+
? parsed
|
|
655
|
+
: parsed !== null &&
|
|
656
|
+
typeof parsed === "object" &&
|
|
657
|
+
Array.isArray(parsed.requests)
|
|
658
|
+
? parsed.requests
|
|
659
|
+
: null;
|
|
538
660
|
if (requests === null || requests.length === 0 || requests.length > 100)
|
|
539
661
|
throw new CliError("Replay requests input must contain 1-100 requests.");
|
|
540
662
|
return requests.map((request) => {
|
|
541
|
-
if (request === null ||
|
|
663
|
+
if (request === null ||
|
|
664
|
+
typeof request !== "object" ||
|
|
665
|
+
Array.isArray(request))
|
|
542
666
|
throw new CliError("Replay request must be an object.");
|
|
543
667
|
const candidate = request;
|
|
544
|
-
if (typeof candidate.method !== "string" ||
|
|
668
|
+
if (typeof candidate.method !== "string" ||
|
|
669
|
+
typeof candidate.path !== "string")
|
|
545
670
|
throw new CliError("Replay request requires string method and path.");
|
|
546
|
-
return {
|
|
671
|
+
return {
|
|
672
|
+
method: candidate.method,
|
|
673
|
+
path: candidate.path,
|
|
674
|
+
headers: candidate.headers,
|
|
675
|
+
body: candidate.body,
|
|
676
|
+
};
|
|
547
677
|
});
|
|
548
678
|
}
|
|
549
679
|
function isInsideAllowedRoots(target) {
|
|
550
|
-
return isInside(process.cwd(), target) || isInside(getDataPaths().root, target);
|
|
680
|
+
return (isInside(process.cwd(), target) || isInside(getDataPaths().root, target));
|
|
551
681
|
}
|
|
552
682
|
function isInside(root, target) {
|
|
553
683
|
const relativePath = relative(resolve(root), target);
|
|
554
|
-
return relativePath !== ".." &&
|
|
684
|
+
return (relativePath !== ".." &&
|
|
685
|
+
!relativePath.startsWith(`..${sep}`) &&
|
|
686
|
+
!isAbsolute(relativePath));
|
|
555
687
|
}
|
|
556
688
|
async function isRealPathInsideAllowedRoots(target) {
|
|
557
689
|
const realTarget = await realpath(target);
|
|
558
690
|
const realProjectRoot = await realpath(process.cwd());
|
|
559
691
|
const dataRoot = await realpath(getDataPaths().root).catch(() => null);
|
|
560
|
-
return isInside(realProjectRoot, realTarget) ||
|
|
692
|
+
return (isInside(realProjectRoot, realTarget) ||
|
|
693
|
+
(dataRoot !== null && isInside(dataRoot, realTarget)));
|
|
561
694
|
}
|
|
562
695
|
async function canWriteGhostApiDir() {
|
|
563
696
|
const dataDir = getDataPaths().root;
|
|
@@ -579,39 +712,39 @@ function isPortAvailable(host, port) {
|
|
|
579
712
|
});
|
|
580
713
|
}
|
|
581
714
|
function printHelp() {
|
|
582
|
-
console.log(`GhostAPI CLI
|
|
583
|
-
|
|
584
|
-
Usage:
|
|
585
|
-
ghostapi start [--host 127.0.0.1] [--port 8080] [--model gpt-4o-mini] [--offline] [--https] [--allow-external-llm] [--open]
|
|
586
|
-
ghostapi open [--host 127.0.0.1] [--port 8080] [--https]
|
|
587
|
-
ghostapi clear cache|state|events|all
|
|
588
|
-
ghostapi model get
|
|
589
|
-
ghostapi model set <model>
|
|
590
|
-
ghostapi providers list
|
|
591
|
-
ghostapi providers inspect <provider>
|
|
592
|
-
ghostapi setup [--write]
|
|
593
|
-
ghostapi report
|
|
594
|
-
ghostapi mcp
|
|
595
|
-
ghostapi doctor [--port 8080] [--json]
|
|
596
|
-
ghostapi doctor --egress [--json]
|
|
597
|
-
ghostapi run [--port 8080] [--allow-host localhost] [--policy ghostapi.policy.yaml] -- <command> [args...]
|
|
598
|
-
ghostapi policy validate [--file ghostapi.policy.yaml]
|
|
599
|
-
ghostapi policy explain <scenario-id>|network <host>|credential <value>|enforcement <mode>|report <production-attempts> <credential-matches> [--file ghostapi.policy.yaml]
|
|
600
|
-
ghostapi evidence generate [--policy ghostapi.policy.yaml] [--run .ghostapi/runs/<id>/run.json] [--out .ghostapi/reports/report.json] [--contract-baseline base.contract.json --contract-candidate head.contract.json] [--ci] [--json]
|
|
601
|
-
ghostapi evidence view <report.json> [--json]
|
|
602
|
-
ghostapi evidence compare <left.json> <right.json> [--json]
|
|
603
|
-
ghostapi record --input <capture.json|har.json> --allow-sandbox-host <host> [--out bundle.json] [--title title] [--pii emails,phones,addresses] [--approve]
|
|
604
|
-
ghostapi replay <bundle.json> --requests <requests.json> [--json]
|
|
605
|
-
ghostapi contract import-openapi --input <openapi.json> [--out contract.json] [--title title]
|
|
606
|
-
ghostapi contract import-har --input <capture.har> --allow-sandbox-host <host> [--out bundle.json] [--contract-out contract.json] [--title title] [--pii emails,phones,addresses] [--approve]
|
|
607
|
-
ghostapi contract diff --baseline <contract.json> --candidate <contract.json> [--policy ghostapi.policy.yaml] [--ci] [--json]
|
|
608
|
-
ghostapi eval --spec <eval.json>|--template <name> [--evidence report.json] [--out report.eval.json] [--ci] [--json]
|
|
609
|
-
ghostapi world create --id <world-id> --seed <seed> [--title title] [--json]
|
|
610
|
-
ghostapi world inspect <world-id> [--json]
|
|
611
|
-
ghostapi world reset <world-id> [--json]
|
|
612
|
-
ghostapi world fork <source-world-id> --id <fork-world-id> [--title title] [--json]
|
|
613
|
-
ghostapi action inspect <action-id> [--json]
|
|
614
|
-
ghostapi telemetry status|enable|disable|export [--json]
|
|
715
|
+
console.log(`GhostAPI CLI
|
|
716
|
+
|
|
717
|
+
Usage:
|
|
718
|
+
ghostapi start [--host 127.0.0.1] [--port 8080] [--model gpt-4o-mini] [--offline] [--https] [--allow-external-llm] [--open]
|
|
719
|
+
ghostapi open [--host 127.0.0.1] [--port 8080] [--https]
|
|
720
|
+
ghostapi clear cache|state|events|all
|
|
721
|
+
ghostapi model get
|
|
722
|
+
ghostapi model set <model>
|
|
723
|
+
ghostapi providers list
|
|
724
|
+
ghostapi providers inspect <provider>
|
|
725
|
+
ghostapi setup [--write]
|
|
726
|
+
ghostapi report
|
|
727
|
+
ghostapi mcp
|
|
728
|
+
ghostapi doctor [--port 8080] [--json]
|
|
729
|
+
ghostapi doctor --egress [--json]
|
|
730
|
+
ghostapi run [--port 8080] [--allow-host localhost] [--policy ghostapi.policy.yaml] -- <command> [args...]
|
|
731
|
+
ghostapi policy validate [--file ghostapi.policy.yaml]
|
|
732
|
+
ghostapi policy explain <scenario-id>|network <host>|credential <value>|enforcement <mode>|report <production-attempts> <credential-matches> [--file ghostapi.policy.yaml]
|
|
733
|
+
ghostapi evidence generate [--policy ghostapi.policy.yaml] [--run .ghostapi/runs/<id>/run.json] [--out .ghostapi/reports/report.json] [--contract-baseline base.contract.json --contract-candidate head.contract.json] [--ci] [--json]
|
|
734
|
+
ghostapi evidence view <report.json> [--json]
|
|
735
|
+
ghostapi evidence compare <left.json> <right.json> [--json]
|
|
736
|
+
ghostapi record --input <capture.json|har.json> --allow-sandbox-host <host> [--out bundle.json] [--title title] [--pii emails,phones,addresses] [--approve]
|
|
737
|
+
ghostapi replay <bundle.json> --requests <requests.json> [--json]
|
|
738
|
+
ghostapi contract import-openapi --input <openapi.json> [--out contract.json] [--title title]
|
|
739
|
+
ghostapi contract import-har --input <capture.har> --allow-sandbox-host <host> [--out bundle.json] [--contract-out contract.json] [--title title] [--pii emails,phones,addresses] [--approve]
|
|
740
|
+
ghostapi contract diff --baseline <contract.json> --candidate <contract.json> [--policy ghostapi.policy.yaml] [--ci] [--json]
|
|
741
|
+
ghostapi eval --spec <eval.json>|--template <name> [--evidence report.json] [--out report.eval.json] [--ci] [--json]
|
|
742
|
+
ghostapi world create --id <world-id> --seed <seed> [--title title] [--json]
|
|
743
|
+
ghostapi world inspect <world-id> [--json]
|
|
744
|
+
ghostapi world reset <world-id> [--json]
|
|
745
|
+
ghostapi world fork <source-world-id> --id <fork-world-id> [--title title] [--json]
|
|
746
|
+
ghostapi action inspect <action-id> [--json]
|
|
747
|
+
ghostapi telemetry status|enable|disable|export [--json]
|
|
615
748
|
ghostapi init`);
|
|
616
749
|
}
|
|
617
750
|
main().catch((error) => {
|