@trigguard/cli 0.1.1
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 +70 -0
- package/data/execution-surfaces.json +28 -0
- package/dist/auth.js +20 -0
- package/dist/commands/authorize.d.ts +1 -0
- package/dist/commands/authorize.js +99 -0
- package/dist/commands/chaos.d.ts +1 -0
- package/dist/commands/chaos.js +35 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +27 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +50 -0
- package/dist/commands/log.d.ts +3 -0
- package/dist/commands/log.js +119 -0
- package/dist/commands/logMonitor.d.ts +1 -0
- package/dist/commands/logMonitor.js +65 -0
- package/dist/commands/login-web.d.ts +1 -0
- package/dist/commands/login-web.js +80 -0
- package/dist/commands/policy-distribution.d.ts +10 -0
- package/dist/commands/policy-distribution.js +108 -0
- package/dist/commands/policy-runtime.d.ts +4 -0
- package/dist/commands/policy-runtime.js +61 -0
- package/dist/commands/policy.d.ts +1 -0
- package/dist/commands/policy.js +123 -0
- package/dist/commands/policyLifecycle.d.ts +13 -0
- package/dist/commands/policyLifecycle.js +601 -0
- package/dist/commands/receiptFetch.d.ts +1 -0
- package/dist/commands/receiptFetch.js +44 -0
- package/dist/commands/receiptProof.d.ts +1 -0
- package/dist/commands/receiptProof.js +43 -0
- package/dist/commands/replay.d.ts +2 -0
- package/dist/commands/replay.js +130 -0
- package/dist/commands/session.d.ts +6 -0
- package/dist/commands/session.js +280 -0
- package/dist/commands/simulate.d.ts +5 -0
- package/dist/commands/simulate.js +89 -0
- package/dist/commands/tg-authorize.d.ts +12 -0
- package/dist/commands/tg-authorize.js +191 -0
- package/dist/commands/tg-init.d.ts +1 -0
- package/dist/commands/tg-init.js +149 -0
- package/dist/commands/tg-setup.d.ts +1 -0
- package/dist/commands/tg-setup.js +43 -0
- package/dist/commands/tg-surfaces.d.ts +7 -0
- package/dist/commands/tg-surfaces.js +50 -0
- package/dist/commands/tg-verify.d.ts +1 -0
- package/dist/commands/tg-verify.js +118 -0
- package/dist/commands/transparency.d.ts +2 -0
- package/dist/commands/transparency.js +65 -0
- package/dist/commands/verify.d.ts +1 -0
- package/dist/commands/verify.js +127 -0
- package/dist/commands/verifyBundle.d.ts +1 -0
- package/dist/commands/verifyBundle.js +109 -0
- package/dist/commands/verifyReceiptCmd.d.ts +1 -0
- package/dist/commands/verifyReceiptCmd.js +49 -0
- package/dist/commands/witness.d.ts +1 -0
- package/dist/commands/witness.js +22 -0
- package/dist/cp/cliDeviceAuth.d.ts +24 -0
- package/dist/cp/cliDeviceAuth.js +68 -0
- package/dist/cp/client.d.ts +19 -0
- package/dist/cp/client.js +73 -0
- package/dist/cp/config.d.ts +8 -0
- package/dist/cp/config.js +113 -0
- package/dist/cp/credentials.d.ts +9 -0
- package/dist/cp/credentials.js +31 -0
- package/dist/cp/provisionCliKey.d.ts +12 -0
- package/dist/cp/provisionCliKey.js +43 -0
- package/dist/cp/types.d.ts +37 -0
- package/dist/cp/types.js +1 -0
- package/dist/stdin.js +9 -0
- package/dist/tg/args.d.ts +3 -0
- package/dist/tg/args.js +28 -0
- package/dist/tg/authorize-format.d.ts +21 -0
- package/dist/tg/authorize-format.js +87 -0
- package/dist/tg/errors.d.ts +6 -0
- package/dist/tg/errors.js +53 -0
- package/dist/tg/gateway.d.ts +2 -0
- package/dist/tg/gateway.js +19 -0
- package/dist/tg/help.d.ts +7 -0
- package/dist/tg/help.js +164 -0
- package/dist/tg/receipt.d.ts +1 -0
- package/dist/tg/receipt.js +13 -0
- package/dist/tg/shellQuote.d.ts +1 -0
- package/dist/tg/shellQuote.js +6 -0
- package/dist/tg.js +92 -0
- package/package.json +50 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { computeReplayArtifactHash, loadAllReplayArtifacts, loadReplayArtifact, replayDecision, signReplayReceipt, verifyReplayReceipt, verifyTransparencyInclusion, } from "@trigguard/policy-engine";
|
|
2
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
function printReplayResult(artifact, actual, replayMatch) {
|
|
4
|
+
console.log(`Receipt: ${artifact.receipt_id}`);
|
|
5
|
+
console.log(`Bundle: ${artifact.policy_bundle_hash}`);
|
|
6
|
+
console.log(`Expected: ${artifact.expected_decision}`);
|
|
7
|
+
console.log(`Actual: ${actual}`);
|
|
8
|
+
console.log(`Match: ${replayMatch ? "YES" : "NO"}`);
|
|
9
|
+
}
|
|
10
|
+
function argValue(args, name) {
|
|
11
|
+
const i = args.indexOf(name);
|
|
12
|
+
if (i < 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
return args[i + 1];
|
|
15
|
+
}
|
|
16
|
+
function hasFlag(args, name) {
|
|
17
|
+
return args.includes(name);
|
|
18
|
+
}
|
|
19
|
+
function parseJsonFile(path) {
|
|
20
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
21
|
+
}
|
|
22
|
+
export async function runReplay(argv) {
|
|
23
|
+
const sub = argv[0];
|
|
24
|
+
if (sub === "sign") {
|
|
25
|
+
await runReplaySign(argv.slice(1));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (sub === "verify") {
|
|
29
|
+
await runReplayVerify(argv.slice(1));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const receiptId = argv[0];
|
|
33
|
+
if (!receiptId) {
|
|
34
|
+
console.error("Usage: trigguard replay <receipt_id>\n or: trigguard replay sign <receipt_id> [--out file] [--anchor]\n or: trigguard replay verify <receipt.json> [--artifact artifact.json] [--bundle-hash sha256:..|64-hex] [--public-key pem] [--json]");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const artifact = loadReplayArtifact(receiptId);
|
|
39
|
+
if (!artifact) {
|
|
40
|
+
console.error(`Replay artifact not found for receipt_id=${receiptId}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const out = replayDecision(artifact);
|
|
45
|
+
printReplayResult(artifact, out.decision, out.replay_match);
|
|
46
|
+
if (!out.replay_match)
|
|
47
|
+
process.exitCode = 2;
|
|
48
|
+
}
|
|
49
|
+
async function runReplaySign(argv) {
|
|
50
|
+
const receiptId = argv[0];
|
|
51
|
+
if (!receiptId) {
|
|
52
|
+
console.error("Usage: trigguard replay sign <receipt_id> [--out file] [--anchor]");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const artifact = loadReplayArtifact(receiptId);
|
|
57
|
+
if (!artifact) {
|
|
58
|
+
console.error(`Replay artifact not found for receipt_id=${receiptId}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const anchor = hasFlag(argv, "--anchor");
|
|
63
|
+
const outPath = argValue(argv, "--out");
|
|
64
|
+
const signer = argValue(argv, "--signer");
|
|
65
|
+
const signed = signReplayReceipt(artifact, { anchor, ...(signer ? { signer } : {}) });
|
|
66
|
+
if (outPath)
|
|
67
|
+
writeFileSync(outPath, `${JSON.stringify(signed, null, 2)}\n`, "utf8");
|
|
68
|
+
console.log(JSON.stringify(signed, null, 2));
|
|
69
|
+
}
|
|
70
|
+
async function runReplayVerify(argv) {
|
|
71
|
+
const receiptPath = argv[0];
|
|
72
|
+
if (!receiptPath) {
|
|
73
|
+
console.error("Usage: trigguard replay verify <receipt.json> [--artifact artifact.json] [--bundle-hash sha256:..|64-hex] [--public-key pem] [--json]");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const jsonOut = hasFlag(argv, "--json");
|
|
78
|
+
const artifactPath = argValue(argv, "--artifact");
|
|
79
|
+
const expectedBundleHash = argValue(argv, "--bundle-hash");
|
|
80
|
+
const publicKeyPem = argValue(argv, "--public-key");
|
|
81
|
+
const replayReceipt = parseJsonFile(receiptPath);
|
|
82
|
+
const artifact = artifactPath
|
|
83
|
+
? parseJsonFile(artifactPath)
|
|
84
|
+
: loadReplayArtifact(replayReceipt.receipt_id);
|
|
85
|
+
if (!artifact) {
|
|
86
|
+
console.error("Replay artifact could not be loaded (use --artifact <artifact.json>).");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const verifyResult = verifyReplayReceipt({ replayReceipt, artifact, expectedBundleHash, publicKeyPem });
|
|
91
|
+
if (!verifyResult.ok) {
|
|
92
|
+
if (jsonOut)
|
|
93
|
+
console.log(JSON.stringify(verifyResult, null, 2));
|
|
94
|
+
else
|
|
95
|
+
console.error(`INVALID ${verifyResult.failed_check}: ${verifyResult.detail}`);
|
|
96
|
+
process.exitCode = 2;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const inclusion = verifyTransparencyInclusion(computeReplayArtifactHash(artifact));
|
|
100
|
+
const out = {
|
|
101
|
+
ok: true,
|
|
102
|
+
checks: verifyResult.checks,
|
|
103
|
+
transparency: inclusion,
|
|
104
|
+
receipt_id: replayReceipt.receipt_id,
|
|
105
|
+
};
|
|
106
|
+
if (jsonOut)
|
|
107
|
+
console.log(JSON.stringify(out, null, 2));
|
|
108
|
+
else {
|
|
109
|
+
console.log("OK replay receipt signature and artifact verified");
|
|
110
|
+
console.log(`Transparency inclusion: ${inclusion.ok ? "YES" : "NO"}`);
|
|
111
|
+
console.log(`Transparency root: ${inclusion.root_hash}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export async function runReplayAudit() {
|
|
115
|
+
const artifacts = loadAllReplayArtifacts();
|
|
116
|
+
let matches = 0;
|
|
117
|
+
let mismatches = 0;
|
|
118
|
+
for (const artifact of artifacts) {
|
|
119
|
+
const out = replayDecision(artifact);
|
|
120
|
+
if (out.replay_match)
|
|
121
|
+
matches += 1;
|
|
122
|
+
else
|
|
123
|
+
mismatches += 1;
|
|
124
|
+
}
|
|
125
|
+
console.log(`Total receipts: ${artifacts.length}`);
|
|
126
|
+
console.log(`Replay matches: ${matches}`);
|
|
127
|
+
console.log(`Replay mismatches: ${mismatches}`);
|
|
128
|
+
if (mismatches > 0)
|
|
129
|
+
process.exitCode = 2;
|
|
130
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SessionSnapshot } from "../cp/types.js";
|
|
2
|
+
export declare function resolveSessionSnapshot(): Promise<SessionSnapshot>;
|
|
3
|
+
export declare function runLogin(args: string[]): Promise<void>;
|
|
4
|
+
export declare function runLogout(args: string[]): Promise<void>;
|
|
5
|
+
export declare function runWhoami(args: string[]): Promise<void>;
|
|
6
|
+
export declare function runStatus(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import readline from "node:readline/promises";
|
|
2
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { ControlPlaneError, fetchDashboardOrgs, fetchMe, loginWithPassword, resolveActiveOrg, } from "../cp/client.js";
|
|
4
|
+
import { loadConfig, saveConfig, clearSession } from "../cp/config.js";
|
|
5
|
+
import { resolveApiKeySource, storedCliApiKeyForSession } from "../cp/credentials.js";
|
|
6
|
+
import { defaultMachineLabel, ensureCliApiKey } from "../cp/provisionCliKey.js";
|
|
7
|
+
import { runLoginWeb } from "./login-web.js";
|
|
8
|
+
function flagValue(args, name) {
|
|
9
|
+
const i = args.indexOf(name);
|
|
10
|
+
if (i === -1)
|
|
11
|
+
return undefined;
|
|
12
|
+
const next = args[i + 1];
|
|
13
|
+
if (next === undefined || next.startsWith("-"))
|
|
14
|
+
return undefined;
|
|
15
|
+
return next;
|
|
16
|
+
}
|
|
17
|
+
function hasFlag(args, name) {
|
|
18
|
+
return args.includes(name);
|
|
19
|
+
}
|
|
20
|
+
async function promptLine(label, hidden = false) {
|
|
21
|
+
if (hidden && process.stdin.isTTY) {
|
|
22
|
+
output.write(label);
|
|
23
|
+
const chunks = [];
|
|
24
|
+
input.setRawMode?.(true);
|
|
25
|
+
try {
|
|
26
|
+
for await (const chunk of input) {
|
|
27
|
+
const s = chunk.toString("utf8");
|
|
28
|
+
for (const ch of s) {
|
|
29
|
+
const code = ch.charCodeAt(0);
|
|
30
|
+
if (code === 13 || code === 10) {
|
|
31
|
+
output.write("\n");
|
|
32
|
+
return chunks.join("");
|
|
33
|
+
}
|
|
34
|
+
if (code === 127 || code === 8) {
|
|
35
|
+
chunks.pop();
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (code === 3) {
|
|
39
|
+
output.write("\n");
|
|
40
|
+
throw new Error("login_cancelled");
|
|
41
|
+
}
|
|
42
|
+
chunks.push(ch);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
input.setRawMode?.(false);
|
|
48
|
+
}
|
|
49
|
+
return chunks.join("");
|
|
50
|
+
}
|
|
51
|
+
const rl = readline.createInterface({ input, output });
|
|
52
|
+
try {
|
|
53
|
+
return (await rl.question(label)).trim();
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
rl.close();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function resolveSessionSnapshot() {
|
|
60
|
+
const config = loadConfig();
|
|
61
|
+
const token = config.sessionToken;
|
|
62
|
+
if (!token) {
|
|
63
|
+
return {
|
|
64
|
+
authenticated: false,
|
|
65
|
+
user: null,
|
|
66
|
+
orgs: [],
|
|
67
|
+
activeOrg: null,
|
|
68
|
+
controlPlaneUrl: config.controlPlaneUrl,
|
|
69
|
+
environment: config.environment,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const me = await fetchMe({ ...config, sessionToken: token });
|
|
74
|
+
let orgs = [];
|
|
75
|
+
try {
|
|
76
|
+
const dash = await fetchDashboardOrgs({ ...config, sessionToken: token });
|
|
77
|
+
orgs = dash.orgs;
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
if (!(e instanceof ControlPlaneError) || e.code !== "email_verification_required") {
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
authenticated: true,
|
|
86
|
+
user: me,
|
|
87
|
+
orgs,
|
|
88
|
+
activeOrg: resolveActiveOrg(orgs, config.activeOrgId),
|
|
89
|
+
controlPlaneUrl: config.controlPlaneUrl,
|
|
90
|
+
environment: config.environment,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return {
|
|
95
|
+
authenticated: false,
|
|
96
|
+
user: null,
|
|
97
|
+
orgs: [],
|
|
98
|
+
activeOrg: null,
|
|
99
|
+
controlPlaneUrl: config.controlPlaneUrl,
|
|
100
|
+
environment: config.environment,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function runLogin(args) {
|
|
105
|
+
const usePassword = hasFlag(args, "--password") ||
|
|
106
|
+
Boolean(flagValue(args, "--email")) ||
|
|
107
|
+
Boolean(process.env.TRIGGUARD_PASSWORD?.trim());
|
|
108
|
+
if (hasFlag(args, "--web") || (!usePassword && input.isTTY)) {
|
|
109
|
+
await runLoginWeb(args);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const json = hasFlag(args, "--json");
|
|
113
|
+
const email = flagValue(args, "--email") ??
|
|
114
|
+
process.env.TRIGGUARD_EMAIL?.trim() ??
|
|
115
|
+
(await promptLine("Email: "));
|
|
116
|
+
const password = flagValue(args, "--password") ??
|
|
117
|
+
process.env.TRIGGUARD_PASSWORD ??
|
|
118
|
+
(await promptLine("Password: ", true));
|
|
119
|
+
if (!email || !password) {
|
|
120
|
+
throw new Error("email_and_password_required");
|
|
121
|
+
}
|
|
122
|
+
const config = loadConfig();
|
|
123
|
+
let token;
|
|
124
|
+
let user;
|
|
125
|
+
try {
|
|
126
|
+
({ token, user } = await loginWithPassword(config, email, password));
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
if (e instanceof ControlPlaneError && e.code === "invalid_credentials") {
|
|
130
|
+
throw new Error("Invalid email or password");
|
|
131
|
+
}
|
|
132
|
+
throw e;
|
|
133
|
+
}
|
|
134
|
+
let activeOrgId;
|
|
135
|
+
const priorOrgHint = config.user?.email === user.email ? config.activeOrgId : undefined;
|
|
136
|
+
try {
|
|
137
|
+
const dash = await fetchDashboardOrgs({ ...config, sessionToken: token });
|
|
138
|
+
activeOrgId = resolveActiveOrg(dash.orgs, priorOrgHint)?.orgId;
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
if (e instanceof ControlPlaneError && e.code === "email_verification_required") {
|
|
142
|
+
activeOrgId = undefined;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
throw e;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const sessionConfig = {
|
|
149
|
+
...config,
|
|
150
|
+
sessionToken: token,
|
|
151
|
+
activeOrgId,
|
|
152
|
+
user: { id: user.id, email: user.email },
|
|
153
|
+
};
|
|
154
|
+
let apiKey;
|
|
155
|
+
let apiKeyId;
|
|
156
|
+
let apiKeyDisplayName;
|
|
157
|
+
if (activeOrgId) {
|
|
158
|
+
const reuseKey = storedCliApiKeyForSession(config, user, activeOrgId);
|
|
159
|
+
const key = await ensureCliApiKey(sessionConfig, activeOrgId, reuseKey, defaultMachineLabel());
|
|
160
|
+
apiKey = key.rawKey;
|
|
161
|
+
apiKeyId = key.keyId;
|
|
162
|
+
apiKeyDisplayName = key.displayName;
|
|
163
|
+
}
|
|
164
|
+
saveConfig({
|
|
165
|
+
version: config.version,
|
|
166
|
+
controlPlaneUrl: config.controlPlaneUrl,
|
|
167
|
+
environment: config.environment,
|
|
168
|
+
sessionToken: token,
|
|
169
|
+
activeOrgId,
|
|
170
|
+
user: { id: user.id, email: user.email },
|
|
171
|
+
apiKey,
|
|
172
|
+
apiKeyId,
|
|
173
|
+
apiKeyDisplayName,
|
|
174
|
+
});
|
|
175
|
+
if (json) {
|
|
176
|
+
console.log(JSON.stringify({
|
|
177
|
+
ok: true,
|
|
178
|
+
user: { id: user.id, email: user.email },
|
|
179
|
+
controlPlaneUrl: config.controlPlaneUrl,
|
|
180
|
+
activeOrgId: activeOrgId ?? null,
|
|
181
|
+
apiKeyConfigured: Boolean(apiKey),
|
|
182
|
+
apiKeySource: apiKey ? "config" : "none",
|
|
183
|
+
}, null, 2));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
console.log(`Signed in as ${user.email}`);
|
|
187
|
+
if (activeOrgId)
|
|
188
|
+
console.log(`Workspace: ${activeOrgId}`);
|
|
189
|
+
else
|
|
190
|
+
console.log("Workspace: (verify email to list organizations)");
|
|
191
|
+
if (apiKey)
|
|
192
|
+
console.log("API key configured for this machine.");
|
|
193
|
+
}
|
|
194
|
+
export async function runLogout(args) {
|
|
195
|
+
const json = hasFlag(args, "--json");
|
|
196
|
+
clearSession(loadConfig());
|
|
197
|
+
if (json) {
|
|
198
|
+
console.log(JSON.stringify({ ok: true }, null, 2));
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
console.log("Signed out.");
|
|
202
|
+
}
|
|
203
|
+
export async function runWhoami(args) {
|
|
204
|
+
const json = hasFlag(args, "--json");
|
|
205
|
+
const config = loadConfig();
|
|
206
|
+
const snap = await resolveSessionSnapshot();
|
|
207
|
+
const apiKeySource = resolveApiKeySource(config);
|
|
208
|
+
const apiKeyConfigured = apiKeySource !== "none";
|
|
209
|
+
if (!snap.authenticated || !snap.user) {
|
|
210
|
+
if (json) {
|
|
211
|
+
console.log(JSON.stringify({ authenticated: false, apiKeyConfigured: false }, null, 2));
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
console.log("Not signed in. Run: tg login");
|
|
215
|
+
}
|
|
216
|
+
process.exit(snap.authenticated ? 0 : 1);
|
|
217
|
+
}
|
|
218
|
+
if (json) {
|
|
219
|
+
console.log(JSON.stringify({
|
|
220
|
+
authenticated: true,
|
|
221
|
+
user: snap.user,
|
|
222
|
+
activeOrg: snap.activeOrg,
|
|
223
|
+
controlPlaneUrl: snap.controlPlaneUrl,
|
|
224
|
+
environment: snap.environment,
|
|
225
|
+
apiKeyConfigured,
|
|
226
|
+
apiKeySource,
|
|
227
|
+
}, null, 2));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
console.log(`User: ${snap.user.email}`);
|
|
231
|
+
console.log(`User ID: ${snap.user.id}`);
|
|
232
|
+
if (snap.activeOrg) {
|
|
233
|
+
console.log(`Organization: ${snap.activeOrg.name}`);
|
|
234
|
+
console.log(`Workspace: ${snap.activeOrg.orgId}`);
|
|
235
|
+
console.log(`Plan: ${snap.activeOrg.plan}`);
|
|
236
|
+
}
|
|
237
|
+
else if (!snap.user.emailVerifiedAt) {
|
|
238
|
+
console.log("Email verification required before workspace listing.");
|
|
239
|
+
}
|
|
240
|
+
console.log(`Environment: ${snap.environment}`);
|
|
241
|
+
console.log(`API key configured: ${apiKeyConfigured ? "yes" : "no"}`);
|
|
242
|
+
console.log(`API key source: ${apiKeySource}`);
|
|
243
|
+
}
|
|
244
|
+
export async function runStatus(args) {
|
|
245
|
+
const json = hasFlag(args, "--json");
|
|
246
|
+
const config = loadConfig();
|
|
247
|
+
const snap = await resolveSessionSnapshot();
|
|
248
|
+
const apiKeySource = resolveApiKeySource(config);
|
|
249
|
+
const apiKeyConfigured = apiKeySource !== "none";
|
|
250
|
+
const payload = {
|
|
251
|
+
authentication: snap.authenticated ? "signed_in" : "signed_out",
|
|
252
|
+
organization: snap.activeOrg?.name ?? null,
|
|
253
|
+
workspace: snap.activeOrg?.orgId ?? null,
|
|
254
|
+
plan: snap.activeOrg?.plan ?? null,
|
|
255
|
+
environment: snap.environment,
|
|
256
|
+
apiEndpoint: snap.controlPlaneUrl,
|
|
257
|
+
emailVerified: Boolean(snap.user?.emailVerifiedAt),
|
|
258
|
+
user: snap.user?.email ?? null,
|
|
259
|
+
apiKeyConfigured,
|
|
260
|
+
apiKeySource,
|
|
261
|
+
authorityStatus: snap.authenticated && apiKeyConfigured ? "ready" : "needs_login",
|
|
262
|
+
verificationStatus: "available",
|
|
263
|
+
};
|
|
264
|
+
if (json) {
|
|
265
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
console.log("TrigGuard CLI status");
|
|
269
|
+
console.log(` Authentication: ${payload.authentication}`);
|
|
270
|
+
console.log(` User: ${payload.user ?? "—"}`);
|
|
271
|
+
console.log(` Organization: ${payload.organization ?? "—"}`);
|
|
272
|
+
console.log(` Workspace: ${payload.workspace ?? "—"}`);
|
|
273
|
+
console.log(` Plan: ${payload.plan ?? "—"}`);
|
|
274
|
+
console.log(` Environment: ${payload.environment}`);
|
|
275
|
+
console.log(` API endpoint: ${payload.apiEndpoint}`);
|
|
276
|
+
console.log(` Email verified: ${payload.emailVerified ? "yes" : "no"}`);
|
|
277
|
+
console.log(` API key: ${apiKeyConfigured ? `configured (${apiKeySource})` : "not configured"}`);
|
|
278
|
+
console.log(` Authority: ${payload.authorityStatus}`);
|
|
279
|
+
console.log(` Verification: ${payload.verificationStatus}`);
|
|
280
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { requestDecision, TrigGuardClientError, } from "@trigguard/decision";
|
|
3
|
+
import { DEFAULT_DEV_PORT, MOCK_AUTHORITY_PUBLIC_KEY_HEX } from "../dev/authority.js";
|
|
4
|
+
const DEFAULT_ENDPOINT = `http://127.0.0.1:${DEFAULT_DEV_PORT}`;
|
|
5
|
+
function ucaseDecision(d) {
|
|
6
|
+
if (d === "permit")
|
|
7
|
+
return "PERMIT";
|
|
8
|
+
if (d === "deny")
|
|
9
|
+
return "DENY";
|
|
10
|
+
return d.toUpperCase();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Simulates a POST /decide against a local (or custom) execution authority; verifies the
|
|
14
|
+
* Ed25519 receipt. Does not run protected actions.
|
|
15
|
+
*/
|
|
16
|
+
export async function runSimulate(argv) {
|
|
17
|
+
const { values, positionals } = parseArgs({
|
|
18
|
+
args: argv,
|
|
19
|
+
options: {
|
|
20
|
+
riskScore: { type: "string" },
|
|
21
|
+
origin: { type: "string" },
|
|
22
|
+
endpoint: { type: "string" },
|
|
23
|
+
"public-key": { type: "string" },
|
|
24
|
+
"timeout-ms": { type: "string" },
|
|
25
|
+
json: { type: "boolean", default: false },
|
|
26
|
+
},
|
|
27
|
+
allowPositionals: true,
|
|
28
|
+
strict: true,
|
|
29
|
+
});
|
|
30
|
+
const surface = String(positionals[0] ?? "").trim();
|
|
31
|
+
if (!surface) {
|
|
32
|
+
console.error("trigguard simulate: missing <surface> (e.g. deploy.production)");
|
|
33
|
+
process.exit(2);
|
|
34
|
+
}
|
|
35
|
+
const endpoint = (values.endpoint && String(values.endpoint).trim()) || DEFAULT_ENDPOINT;
|
|
36
|
+
const authorityPublicKey = (values["public-key"] && String(values["public-key"]).trim()) ||
|
|
37
|
+
MOCK_AUTHORITY_PUBLIC_KEY_HEX;
|
|
38
|
+
let riskScore;
|
|
39
|
+
if (values.riskScore != null && String(values.riskScore).trim() !== "") {
|
|
40
|
+
const n = Number(String(values.riskScore).trim());
|
|
41
|
+
if (!Number.isFinite(n)) {
|
|
42
|
+
console.error("trigguard simulate: --riskScore must be a number");
|
|
43
|
+
process.exit(2);
|
|
44
|
+
}
|
|
45
|
+
riskScore = n;
|
|
46
|
+
}
|
|
47
|
+
const origin = values.origin != null && String(values.origin).trim() !== ""
|
|
48
|
+
? String(values.origin).trim()
|
|
49
|
+
: "trigguard-cli-simulate";
|
|
50
|
+
let timeoutMs;
|
|
51
|
+
if (values["timeout-ms"] != null && String(values["timeout-ms"]).trim() !== "") {
|
|
52
|
+
const t = Number.parseInt(String(values["timeout-ms"]).trim(), 10);
|
|
53
|
+
if (!Number.isFinite(t) || t <= 0) {
|
|
54
|
+
console.error("trigguard simulate: --timeout-ms must be a positive integer");
|
|
55
|
+
process.exit(2);
|
|
56
|
+
}
|
|
57
|
+
timeoutMs = t;
|
|
58
|
+
}
|
|
59
|
+
const jsonOut = values.json === true;
|
|
60
|
+
try {
|
|
61
|
+
const result = await requestDecision({
|
|
62
|
+
endpoint,
|
|
63
|
+
authorityPublicKey,
|
|
64
|
+
surface,
|
|
65
|
+
riskScore,
|
|
66
|
+
origin,
|
|
67
|
+
timeoutMs,
|
|
68
|
+
});
|
|
69
|
+
if (jsonOut) {
|
|
70
|
+
console.log(JSON.stringify(result, null, 2));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.log(`Decision: ${ucaseDecision(String(result.decision))}`);
|
|
74
|
+
console.log(`Reason: ${result.reason}`);
|
|
75
|
+
console.log(`Policy fingerprint: ${result.policyFingerprint}`);
|
|
76
|
+
console.log(`Timestamp: ${result.timestamp}`);
|
|
77
|
+
console.log(`Endpoint: ${endpoint}`);
|
|
78
|
+
console.log("Receipt: verified (Ed25519)");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
83
|
+
console.error(`trigguard simulate: ${msg}`);
|
|
84
|
+
if (e instanceof TrigGuardClientError && e.code) {
|
|
85
|
+
process.stderr.write(`(code: ${e.code})\n`);
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface AuthorizeInput {
|
|
2
|
+
surface: string;
|
|
3
|
+
actorId: string;
|
|
4
|
+
intent?: string;
|
|
5
|
+
risk?: string | number;
|
|
6
|
+
environment?: string;
|
|
7
|
+
context?: Record<string, unknown>;
|
|
8
|
+
subjectDigest?: string;
|
|
9
|
+
}
|
|
10
|
+
export { defaultGatewayUrl } from "../tg/gateway.js";
|
|
11
|
+
export declare function resolveApiKey(): string | undefined;
|
|
12
|
+
export declare function runTgAuthorize(args: string[]): Promise<void>;
|