@sekyuriti/attest 0.1.1 → 0.2.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/bin/attest.js +555 -0
- package/package.json +6 -2
package/bin/attest.js
ADDED
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require("child_process");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const https = require("https");
|
|
7
|
+
const http = require("http");
|
|
8
|
+
const crypto = require("crypto");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const readline = require("readline");
|
|
11
|
+
|
|
12
|
+
// ANSI colors
|
|
13
|
+
const c = {
|
|
14
|
+
reset: "\x1b[0m",
|
|
15
|
+
bold: "\x1b[1m",
|
|
16
|
+
dim: "\x1b[2m",
|
|
17
|
+
black: "\x1b[30m",
|
|
18
|
+
white: "\x1b[37m",
|
|
19
|
+
gray: "\x1b[90m",
|
|
20
|
+
inverse: "\x1b[7m",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const CONFIG_DIR = path.join(os.homedir(), ".attest");
|
|
24
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
25
|
+
const API_BASE = "https://sekyuriti.build";
|
|
26
|
+
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
28
|
+
// UTILITIES
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
function log(msg) {
|
|
32
|
+
console.log(msg);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function logBold(msg) {
|
|
36
|
+
console.log(`${c.bold}${msg}${c.reset}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function logDim(msg) {
|
|
40
|
+
console.log(`${c.dim}${msg}${c.reset}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function logBox(lines) {
|
|
44
|
+
const maxLen = Math.max(...lines.map((l) => l.length));
|
|
45
|
+
const top = "┌" + "─".repeat(maxLen + 2) + "┐";
|
|
46
|
+
const bot = "└" + "─".repeat(maxLen + 2) + "┘";
|
|
47
|
+
log(top);
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
log("│ " + line.padEnd(maxLen) + " │");
|
|
50
|
+
}
|
|
51
|
+
log(bot);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function sleep(ms) {
|
|
55
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function fetch(url, options = {}) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const isHttps = url.startsWith("https");
|
|
61
|
+
const lib = isHttps ? https : http;
|
|
62
|
+
const urlObj = new URL(url);
|
|
63
|
+
|
|
64
|
+
const req = lib.request(
|
|
65
|
+
{
|
|
66
|
+
hostname: urlObj.hostname,
|
|
67
|
+
port: urlObj.port || (isHttps ? 443 : 80),
|
|
68
|
+
path: urlObj.pathname + urlObj.search,
|
|
69
|
+
method: options.method || "GET",
|
|
70
|
+
headers: options.headers || {},
|
|
71
|
+
},
|
|
72
|
+
(res) => {
|
|
73
|
+
let data = "";
|
|
74
|
+
res.on("data", (chunk) => (data += chunk));
|
|
75
|
+
res.on("end", () => {
|
|
76
|
+
resolve({
|
|
77
|
+
ok: res.statusCode >= 200 && res.statusCode < 300,
|
|
78
|
+
status: res.statusCode,
|
|
79
|
+
json: () => Promise.resolve(JSON.parse(data)),
|
|
80
|
+
text: () => Promise.resolve(data),
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
req.on("error", reject);
|
|
87
|
+
|
|
88
|
+
if (options.body) {
|
|
89
|
+
req.write(options.body);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
req.end();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function openBrowser(url) {
|
|
97
|
+
const platform = process.platform;
|
|
98
|
+
let cmd;
|
|
99
|
+
|
|
100
|
+
if (platform === "darwin") {
|
|
101
|
+
cmd = "open";
|
|
102
|
+
} else if (platform === "win32") {
|
|
103
|
+
cmd = "start";
|
|
104
|
+
} else {
|
|
105
|
+
cmd = "xdg-open";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
spawn(cmd, [url], { detached: true, stdio: "ignore" }).unref();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function loadConfig() {
|
|
112
|
+
try {
|
|
113
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
114
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
|
|
115
|
+
}
|
|
116
|
+
} catch (e) {}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function saveConfig(config) {
|
|
121
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
122
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
123
|
+
}
|
|
124
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function clearConfig() {
|
|
128
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
129
|
+
fs.unlinkSync(CONFIG_FILE);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
134
|
+
// HEADER
|
|
135
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
136
|
+
|
|
137
|
+
function printHeader() {
|
|
138
|
+
log("");
|
|
139
|
+
logBold(" █▀▀ █▀▀ █▄▀ █▄█ █ █ █▀█ █ ▀█▀ █");
|
|
140
|
+
logBold(" ▄▄█ ██▄ █ █ █ █▄█ █▀▄ █ █ █");
|
|
141
|
+
log("");
|
|
142
|
+
logDim(" ATTEST CLI v0.2.0");
|
|
143
|
+
log("");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
147
|
+
// LOGIN COMMAND
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
149
|
+
|
|
150
|
+
async function cmdLogin() {
|
|
151
|
+
printHeader();
|
|
152
|
+
|
|
153
|
+
// Check if already logged in
|
|
154
|
+
const existing = loadConfig();
|
|
155
|
+
if (existing && existing.token) {
|
|
156
|
+
log(" You are already logged in as:");
|
|
157
|
+
logBold(` ${existing.email}`);
|
|
158
|
+
log("");
|
|
159
|
+
log(` Project: ${existing.projectName || "Unknown"}`);
|
|
160
|
+
logDim(` Key: ${existing.publicKey}`);
|
|
161
|
+
log("");
|
|
162
|
+
log(" Run 'attest logout' to sign out.");
|
|
163
|
+
log("");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Generate session token
|
|
168
|
+
const sessionId = crypto.randomBytes(16).toString("hex");
|
|
169
|
+
const authUrl = `${API_BASE}/cli/auth?session=${sessionId}`;
|
|
170
|
+
|
|
171
|
+
log(" Opening browser to authenticate...");
|
|
172
|
+
log("");
|
|
173
|
+
logBox([
|
|
174
|
+
"Waiting for browser confirmation...",
|
|
175
|
+
"",
|
|
176
|
+
`${authUrl.slice(0, 45)}...`,
|
|
177
|
+
]);
|
|
178
|
+
log("");
|
|
179
|
+
|
|
180
|
+
// Open browser
|
|
181
|
+
openBrowser(authUrl);
|
|
182
|
+
|
|
183
|
+
// Poll for completion
|
|
184
|
+
let attempts = 0;
|
|
185
|
+
const maxAttempts = 120; // 2 minutes
|
|
186
|
+
|
|
187
|
+
process.stdout.write(" Waiting");
|
|
188
|
+
|
|
189
|
+
while (attempts < maxAttempts) {
|
|
190
|
+
try {
|
|
191
|
+
const res = await fetch(
|
|
192
|
+
`${API_BASE}/api/v2/attest/cli/poll?session=${sessionId}`
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (res.ok) {
|
|
196
|
+
const data = await res.json();
|
|
197
|
+
|
|
198
|
+
if (data.status === "completed") {
|
|
199
|
+
process.stdout.write("\n\n");
|
|
200
|
+
log(` ${c.bold}✓${c.reset} Authenticated as ${c.bold}${data.email}${c.reset}`);
|
|
201
|
+
log(` ${c.bold}✓${c.reset} Project: ${c.bold}${data.projectName}${c.reset}`);
|
|
202
|
+
log(` ${c.bold}✓${c.reset} Credentials saved to ~/.attest/config.json`);
|
|
203
|
+
log("");
|
|
204
|
+
|
|
205
|
+
saveConfig({
|
|
206
|
+
token: data.token,
|
|
207
|
+
email: data.email,
|
|
208
|
+
projectId: data.projectId,
|
|
209
|
+
projectName: data.projectName,
|
|
210
|
+
publicKey: data.publicKey,
|
|
211
|
+
apiKey: data.apiKey,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} catch (e) {}
|
|
218
|
+
|
|
219
|
+
process.stdout.write(".");
|
|
220
|
+
await sleep(1000);
|
|
221
|
+
attempts++;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
process.stdout.write("\n\n");
|
|
225
|
+
log(" ✗ Authentication timed out. Please try again.");
|
|
226
|
+
log("");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
230
|
+
// LOGOUT COMMAND
|
|
231
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
232
|
+
|
|
233
|
+
async function cmdLogout() {
|
|
234
|
+
printHeader();
|
|
235
|
+
|
|
236
|
+
const config = loadConfig();
|
|
237
|
+
|
|
238
|
+
if (!config || !config.token) {
|
|
239
|
+
log(" You are not logged in.");
|
|
240
|
+
log("");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
clearConfig();
|
|
245
|
+
|
|
246
|
+
log(` ${c.bold}✓${c.reset} Logged out successfully`);
|
|
247
|
+
log(` ${c.dim}Credentials removed from ~/.attest/config.json${c.reset}`);
|
|
248
|
+
log("");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
252
|
+
// STATUS COMMAND
|
|
253
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
254
|
+
|
|
255
|
+
async function cmdStatus() {
|
|
256
|
+
printHeader();
|
|
257
|
+
|
|
258
|
+
const config = loadConfig();
|
|
259
|
+
|
|
260
|
+
if (!config || !config.token) {
|
|
261
|
+
log(" You are not logged in.");
|
|
262
|
+
log(" Run 'attest login' to authenticate.");
|
|
263
|
+
log("");
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
log(` Account: ${c.bold}${config.email}${c.reset}`);
|
|
268
|
+
log(` Project: ${c.bold}${config.projectName}${c.reset}`);
|
|
269
|
+
log(` Key: ${c.dim}${config.publicKey}${c.reset}`);
|
|
270
|
+
log("");
|
|
271
|
+
|
|
272
|
+
// Fetch usage stats
|
|
273
|
+
process.stdout.write(" Fetching usage stats...");
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
const res = await fetch(
|
|
277
|
+
`${API_BASE}/api/v2/attest/cli/status?key=${config.publicKey}`,
|
|
278
|
+
{
|
|
279
|
+
headers: {
|
|
280
|
+
Authorization: `Bearer ${config.token}`,
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (res.ok) {
|
|
286
|
+
const data = await res.json();
|
|
287
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
288
|
+
|
|
289
|
+
log(" ┌─────────────────────────────────────────┐");
|
|
290
|
+
log(` │ MONTHLY USAGE │`);
|
|
291
|
+
log(" ├─────────────────────────────────────────┤");
|
|
292
|
+
|
|
293
|
+
const used = data.used || 0;
|
|
294
|
+
const limit = data.limit || 10000;
|
|
295
|
+
const percent = Math.round((used / limit) * 100);
|
|
296
|
+
const barLen = 20;
|
|
297
|
+
const filled = Math.round((percent / 100) * barLen);
|
|
298
|
+
const bar = "█".repeat(filled) + "░".repeat(barLen - filled);
|
|
299
|
+
|
|
300
|
+
log(` │ ${bar} ${percent}% │`);
|
|
301
|
+
log(` │ ${used.toLocaleString()} / ${limit.toLocaleString()} requests │`);
|
|
302
|
+
log(" └─────────────────────────────────────────┘");
|
|
303
|
+
log("");
|
|
304
|
+
|
|
305
|
+
if (data.tier) {
|
|
306
|
+
logDim(` Tier: ${data.tier.toUpperCase()}`);
|
|
307
|
+
log("");
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
process.stdout.write("\n");
|
|
311
|
+
log(" Could not fetch usage stats.");
|
|
312
|
+
log("");
|
|
313
|
+
}
|
|
314
|
+
} catch (e) {
|
|
315
|
+
process.stdout.write("\n");
|
|
316
|
+
log(" Could not fetch usage stats.");
|
|
317
|
+
log("");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
322
|
+
// INIT COMMAND
|
|
323
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
324
|
+
|
|
325
|
+
async function cmdInit() {
|
|
326
|
+
printHeader();
|
|
327
|
+
|
|
328
|
+
const config = loadConfig();
|
|
329
|
+
|
|
330
|
+
if (!config || !config.token) {
|
|
331
|
+
log(" You are not logged in.");
|
|
332
|
+
log(" Run 'attest login' first.");
|
|
333
|
+
log("");
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
let steps = [];
|
|
338
|
+
|
|
339
|
+
// Step 1: Add .env variables
|
|
340
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
341
|
+
const envLocalPath = path.join(process.cwd(), ".env.local");
|
|
342
|
+
let targetEnvPath = envLocalPath;
|
|
343
|
+
|
|
344
|
+
if (fs.existsSync(envLocalPath)) {
|
|
345
|
+
targetEnvPath = envLocalPath;
|
|
346
|
+
} else if (fs.existsSync(envPath)) {
|
|
347
|
+
targetEnvPath = envPath;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const envVars = `
|
|
351
|
+
# ATTEST Configuration
|
|
352
|
+
NEXT_PUBLIC_ATTEST_KEY=${config.publicKey}
|
|
353
|
+
ATTEST_SECRET_KEY=${config.apiKey}
|
|
354
|
+
`.trim();
|
|
355
|
+
|
|
356
|
+
if (fs.existsSync(targetEnvPath)) {
|
|
357
|
+
let content = fs.readFileSync(targetEnvPath, "utf-8");
|
|
358
|
+
if (!content.includes("ATTEST_KEY")) {
|
|
359
|
+
content += "\n\n" + envVars + "\n";
|
|
360
|
+
fs.writeFileSync(targetEnvPath, content);
|
|
361
|
+
steps.push(`Added ATTEST config to ${path.basename(targetEnvPath)}`);
|
|
362
|
+
} else {
|
|
363
|
+
steps.push(`ATTEST config already in ${path.basename(targetEnvPath)}`);
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
fs.writeFileSync(envLocalPath, envVars + "\n");
|
|
367
|
+
steps.push("Created .env.local with ATTEST config");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Step 2: Find and update layout.tsx
|
|
371
|
+
const layoutPaths = [
|
|
372
|
+
path.join(process.cwd(), "src/app/layout.tsx"),
|
|
373
|
+
path.join(process.cwd(), "app/layout.tsx"),
|
|
374
|
+
path.join(process.cwd(), "src/app/layout.js"),
|
|
375
|
+
path.join(process.cwd(), "app/layout.js"),
|
|
376
|
+
];
|
|
377
|
+
|
|
378
|
+
let layoutPath = null;
|
|
379
|
+
for (const p of layoutPaths) {
|
|
380
|
+
if (fs.existsSync(p)) {
|
|
381
|
+
layoutPath = p;
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const scriptTag = `<Script
|
|
387
|
+
src="https://sekyuriti.build/api/v2/attest/script/${config.publicKey}"
|
|
388
|
+
strategy="beforeInteractive"
|
|
389
|
+
/>`;
|
|
390
|
+
|
|
391
|
+
if (layoutPath) {
|
|
392
|
+
let layoutContent = fs.readFileSync(layoutPath, "utf-8");
|
|
393
|
+
|
|
394
|
+
if (layoutContent.includes("sekyuriti.build/api/v2/attest/script")) {
|
|
395
|
+
steps.push("ATTEST script already in layout");
|
|
396
|
+
} else {
|
|
397
|
+
// Check if Script is already imported
|
|
398
|
+
const hasScriptImport = layoutContent.includes("from 'next/script'") || layoutContent.includes('from "next/script"');
|
|
399
|
+
|
|
400
|
+
if (!hasScriptImport) {
|
|
401
|
+
// Add Script import after the first import line
|
|
402
|
+
layoutContent = layoutContent.replace(
|
|
403
|
+
/(import .+ from ['"][^'"]+['"];?\n)/,
|
|
404
|
+
`$1import Script from "next/script";\n`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Add script tag after <body> or <body className=...>
|
|
409
|
+
if (layoutContent.includes("<body")) {
|
|
410
|
+
layoutContent = layoutContent.replace(
|
|
411
|
+
/(<body[^>]*>)/,
|
|
412
|
+
`$1\n ${scriptTag}`
|
|
413
|
+
);
|
|
414
|
+
fs.writeFileSync(layoutPath, layoutContent);
|
|
415
|
+
steps.push(`Added ATTEST script to ${path.basename(layoutPath)}`);
|
|
416
|
+
} else {
|
|
417
|
+
steps.push("Could not find <body> tag in layout");
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
steps.push("No layout.tsx found - add script manually");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Step 3: Check for middleware.ts and offer to add verification
|
|
425
|
+
const middlewarePaths = [
|
|
426
|
+
path.join(process.cwd(), "middleware.ts"),
|
|
427
|
+
path.join(process.cwd(), "src/middleware.ts"),
|
|
428
|
+
path.join(process.cwd(), "middleware.js"),
|
|
429
|
+
path.join(process.cwd(), "src/middleware.js"),
|
|
430
|
+
];
|
|
431
|
+
|
|
432
|
+
let hasMiddleware = false;
|
|
433
|
+
for (const p of middlewarePaths) {
|
|
434
|
+
if (fs.existsSync(p)) {
|
|
435
|
+
hasMiddleware = true;
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Print results
|
|
441
|
+
log(" SETUP COMPLETE");
|
|
442
|
+
log("");
|
|
443
|
+
|
|
444
|
+
for (const step of steps) {
|
|
445
|
+
log(` ${c.bold}✓${c.reset} ${step}`);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
log("");
|
|
449
|
+
|
|
450
|
+
if (!hasMiddleware) {
|
|
451
|
+
log(" Optional: Add middleware for server-side verification:");
|
|
452
|
+
log("");
|
|
453
|
+
log(` ${c.dim}// middleware.ts${c.reset}`);
|
|
454
|
+
log(` ${c.dim}import { createAttestMiddleware } from '@sekyuriti/attest/middleware';${c.reset}`);
|
|
455
|
+
log(` ${c.dim}export default createAttestMiddleware({${c.reset}`);
|
|
456
|
+
log(` ${c.dim} protectedPaths: ['/api/'],${c.reset}`);
|
|
457
|
+
log(` ${c.dim}});${c.reset}`);
|
|
458
|
+
log("");
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
log(" Documentation: https://sekyuriti.build/docs/attest");
|
|
462
|
+
log("");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
466
|
+
// WHOAMI COMMAND
|
|
467
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
468
|
+
|
|
469
|
+
async function cmdWhoami() {
|
|
470
|
+
const config = loadConfig();
|
|
471
|
+
|
|
472
|
+
if (!config || !config.token) {
|
|
473
|
+
console.log("Not logged in");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
console.log(config.email);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
481
|
+
// HELP
|
|
482
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
483
|
+
|
|
484
|
+
function printHelp() {
|
|
485
|
+
printHeader();
|
|
486
|
+
log(" USAGE");
|
|
487
|
+
log("");
|
|
488
|
+
log(` ${c.bold}attest${c.reset} <command>`);
|
|
489
|
+
log("");
|
|
490
|
+
log(" COMMANDS");
|
|
491
|
+
log("");
|
|
492
|
+
log(` ${c.bold}login${c.reset} Authenticate with SEKYURITI`);
|
|
493
|
+
log(` ${c.bold}logout${c.reset} Sign out and clear credentials`);
|
|
494
|
+
log(` ${c.bold}status${c.reset} Show account and usage info`);
|
|
495
|
+
log(` ${c.bold}init${c.reset} Setup ATTEST in current project`);
|
|
496
|
+
log(` ${c.bold}whoami${c.reset} Print current user email`);
|
|
497
|
+
log(` ${c.bold}help${c.reset} Show this help message`);
|
|
498
|
+
log("");
|
|
499
|
+
log(" EXAMPLES");
|
|
500
|
+
log("");
|
|
501
|
+
logDim(" $ attest login");
|
|
502
|
+
logDim(" $ attest init");
|
|
503
|
+
logDim(" $ attest status");
|
|
504
|
+
log("");
|
|
505
|
+
log(" DOCUMENTATION");
|
|
506
|
+
log("");
|
|
507
|
+
log(" https://sekyuriti.build/docs/attest");
|
|
508
|
+
log("");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
512
|
+
// MAIN
|
|
513
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
514
|
+
|
|
515
|
+
async function main() {
|
|
516
|
+
const args = process.argv.slice(2);
|
|
517
|
+
const cmd = args[0];
|
|
518
|
+
|
|
519
|
+
switch (cmd) {
|
|
520
|
+
case "login":
|
|
521
|
+
await cmdLogin();
|
|
522
|
+
break;
|
|
523
|
+
case "logout":
|
|
524
|
+
await cmdLogout();
|
|
525
|
+
break;
|
|
526
|
+
case "status":
|
|
527
|
+
await cmdStatus();
|
|
528
|
+
break;
|
|
529
|
+
case "init":
|
|
530
|
+
await cmdInit();
|
|
531
|
+
break;
|
|
532
|
+
case "whoami":
|
|
533
|
+
await cmdWhoami();
|
|
534
|
+
break;
|
|
535
|
+
case "help":
|
|
536
|
+
case "--help":
|
|
537
|
+
case "-h":
|
|
538
|
+
printHelp();
|
|
539
|
+
break;
|
|
540
|
+
case undefined:
|
|
541
|
+
printHelp();
|
|
542
|
+
break;
|
|
543
|
+
default:
|
|
544
|
+
log("");
|
|
545
|
+
log(` Unknown command: ${cmd}`);
|
|
546
|
+
log(" Run 'attest help' for usage.");
|
|
547
|
+
log("");
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
main().catch((e) => {
|
|
553
|
+
console.error("Error:", e.message);
|
|
554
|
+
process.exit(1);
|
|
555
|
+
});
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sekyuriti/attest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "API protection middleware for Next.js - verify requests with ATTEST",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"attest": "./bin/attest.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
@@ -18,7 +21,8 @@
|
|
|
18
21
|
}
|
|
19
22
|
},
|
|
20
23
|
"files": [
|
|
21
|
-
"dist"
|
|
24
|
+
"dist",
|
|
25
|
+
"bin"
|
|
22
26
|
],
|
|
23
27
|
"scripts": {
|
|
24
28
|
"build": "tsup",
|