@graphorin/cli 0.5.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +130 -0
  4. package/dist/bin/graphorin.d.ts +1 -0
  5. package/dist/bin/graphorin.js +606 -0
  6. package/dist/bin/graphorin.js.map +1 -0
  7. package/dist/commands/audit.d.ts +59 -0
  8. package/dist/commands/audit.d.ts.map +1 -0
  9. package/dist/commands/audit.js +156 -0
  10. package/dist/commands/audit.js.map +1 -0
  11. package/dist/commands/auth.d.ts +48 -0
  12. package/dist/commands/auth.d.ts.map +1 -0
  13. package/dist/commands/auth.js +148 -0
  14. package/dist/commands/auth.js.map +1 -0
  15. package/dist/commands/consolidator.d.ts +49 -0
  16. package/dist/commands/consolidator.d.ts.map +1 -0
  17. package/dist/commands/consolidator.js +97 -0
  18. package/dist/commands/consolidator.js.map +1 -0
  19. package/dist/commands/doctor.d.ts +64 -0
  20. package/dist/commands/doctor.d.ts.map +1 -0
  21. package/dist/commands/doctor.js +150 -0
  22. package/dist/commands/doctor.js.map +1 -0
  23. package/dist/commands/guard.d.ts +35 -0
  24. package/dist/commands/guard.d.ts.map +1 -0
  25. package/dist/commands/guard.js +86 -0
  26. package/dist/commands/guard.js.map +1 -0
  27. package/dist/commands/index.d.ts +21 -0
  28. package/dist/commands/index.js +22 -0
  29. package/dist/commands/init.d.ts +46 -0
  30. package/dist/commands/init.d.ts.map +1 -0
  31. package/dist/commands/init.js +131 -0
  32. package/dist/commands/init.js.map +1 -0
  33. package/dist/commands/memory.d.ts +243 -0
  34. package/dist/commands/memory.d.ts.map +1 -0
  35. package/dist/commands/memory.js +471 -0
  36. package/dist/commands/memory.js.map +1 -0
  37. package/dist/commands/migrate-config.d.ts +20 -0
  38. package/dist/commands/migrate-config.d.ts.map +1 -0
  39. package/dist/commands/migrate-config.js +48 -0
  40. package/dist/commands/migrate-config.js.map +1 -0
  41. package/dist/commands/migrate-export.d.ts +26 -0
  42. package/dist/commands/migrate-export.d.ts.map +1 -0
  43. package/dist/commands/migrate-export.js +67 -0
  44. package/dist/commands/migrate-export.js.map +1 -0
  45. package/dist/commands/migrate.d.ts +31 -0
  46. package/dist/commands/migrate.d.ts.map +1 -0
  47. package/dist/commands/migrate.js +59 -0
  48. package/dist/commands/migrate.js.map +1 -0
  49. package/dist/commands/pricing.d.ts +66 -0
  50. package/dist/commands/pricing.d.ts.map +1 -0
  51. package/dist/commands/pricing.js +128 -0
  52. package/dist/commands/pricing.js.map +1 -0
  53. package/dist/commands/secrets.d.ts +96 -0
  54. package/dist/commands/secrets.d.ts.map +1 -0
  55. package/dist/commands/secrets.js +182 -0
  56. package/dist/commands/secrets.js.map +1 -0
  57. package/dist/commands/skills.d.ts +58 -0
  58. package/dist/commands/skills.d.ts.map +1 -0
  59. package/dist/commands/skills.js +181 -0
  60. package/dist/commands/skills.js.map +1 -0
  61. package/dist/commands/start.d.ts +51 -0
  62. package/dist/commands/start.d.ts.map +1 -0
  63. package/dist/commands/start.js +122 -0
  64. package/dist/commands/start.js.map +1 -0
  65. package/dist/commands/storage.d.ts +110 -0
  66. package/dist/commands/storage.d.ts.map +1 -0
  67. package/dist/commands/storage.js +274 -0
  68. package/dist/commands/storage.js.map +1 -0
  69. package/dist/commands/telemetry.d.ts +21 -0
  70. package/dist/commands/telemetry.d.ts.map +1 -0
  71. package/dist/commands/telemetry.js +75 -0
  72. package/dist/commands/telemetry.js.map +1 -0
  73. package/dist/commands/token.d.ts +109 -0
  74. package/dist/commands/token.d.ts.map +1 -0
  75. package/dist/commands/token.js +246 -0
  76. package/dist/commands/token.js.map +1 -0
  77. package/dist/commands/tools-lint.d.ts +103 -0
  78. package/dist/commands/tools-lint.d.ts.map +1 -0
  79. package/dist/commands/tools-lint.js +240 -0
  80. package/dist/commands/tools-lint.js.map +1 -0
  81. package/dist/commands/traces.d.ts +33 -0
  82. package/dist/commands/traces.d.ts.map +1 -0
  83. package/dist/commands/traces.js +103 -0
  84. package/dist/commands/traces.js.map +1 -0
  85. package/dist/commands/triggers.d.ts +49 -0
  86. package/dist/commands/triggers.d.ts.map +1 -0
  87. package/dist/commands/triggers.js +129 -0
  88. package/dist/commands/triggers.js.map +1 -0
  89. package/dist/index.d.ts +46 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +47 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/internal/exit.js +17 -0
  94. package/dist/internal/exit.js.map +1 -0
  95. package/dist/internal/load-config.js +84 -0
  96. package/dist/internal/load-config.js.map +1 -0
  97. package/dist/internal/offline.d.ts +58 -0
  98. package/dist/internal/offline.d.ts.map +1 -0
  99. package/dist/internal/offline.js +72 -0
  100. package/dist/internal/offline.js.map +1 -0
  101. package/dist/internal/output.d.ts +56 -0
  102. package/dist/internal/output.d.ts.map +1 -0
  103. package/dist/internal/output.js +86 -0
  104. package/dist/internal/output.js.map +1 -0
  105. package/dist/internal/prompts.js +49 -0
  106. package/dist/internal/prompts.js.map +1 -0
  107. package/dist/internal/store-context.js +69 -0
  108. package/dist/internal/store-context.js.map +1 -0
  109. package/package.json +79 -0
@@ -0,0 +1,109 @@
1
+ import { CommonOutputOptions } from "../internal/output.js";
2
+ import { TokenMetadata } from "@graphorin/security";
3
+
4
+ //#region src/commands/token.d.ts
5
+
6
+ /** @stable */
7
+ interface TokenCommonOptions extends CommonOutputOptions {
8
+ readonly config?: string;
9
+ }
10
+ /** @stable */
11
+ interface TokenCreateOptions extends TokenCommonOptions {
12
+ readonly label?: string;
13
+ /** Comma-separated scope list. */
14
+ readonly scopes: ReadonlyArray<string>;
15
+ /** Duration string: `30d`, `12h`, `90m`, `45s`. */
16
+ readonly expiresIn?: string;
17
+ readonly env?: 'live' | 'test';
18
+ }
19
+ /** @stable */
20
+ interface TokenCreateResult {
21
+ readonly id: string;
22
+ readonly label?: string;
23
+ readonly scopes: ReadonlyArray<string>;
24
+ readonly raw: string;
25
+ readonly expiresAt?: string;
26
+ }
27
+ /**
28
+ * Create a token. Returns the raw value once + the persisted record.
29
+ *
30
+ * @stable
31
+ */
32
+ declare function runTokenCreate(options: TokenCreateOptions): Promise<TokenCreateResult>;
33
+ /** @stable */
34
+ interface TokenListOptions extends TokenCommonOptions {
35
+ readonly includeRevoked?: boolean;
36
+ }
37
+ /**
38
+ * List token metadata.
39
+ *
40
+ * @stable
41
+ */
42
+ declare function runTokenList(options?: TokenListOptions): Promise<ReadonlyArray<TokenMetadata>>;
43
+ /** @stable */
44
+ interface TokenRevokeOptions extends TokenCommonOptions {
45
+ readonly id: string;
46
+ }
47
+ /**
48
+ * Revoke a single token.
49
+ *
50
+ * @stable
51
+ */
52
+ declare function runTokenRevoke(options: TokenRevokeOptions): Promise<TokenMetadata | undefined>;
53
+ /** @stable */
54
+ interface TokenRotateOptions extends TokenCommonOptions {
55
+ readonly id: string;
56
+ readonly env?: 'live' | 'test';
57
+ }
58
+ /**
59
+ * Revoke an existing token and immediately mint a fresh one with the
60
+ * same scopes. Returns the new raw token bytes once.
61
+ *
62
+ * @stable
63
+ */
64
+ declare function runTokenRotate(options: TokenRotateOptions): Promise<TokenCreateResult>;
65
+ /** @stable */
66
+ interface TokenRekeyOptions extends TokenCommonOptions {
67
+ readonly env?: 'live' | 'test';
68
+ }
69
+ /**
70
+ * Re-issue every active token. Used after a known compromise.
71
+ *
72
+ * @stable
73
+ */
74
+ declare function runTokenRekey(options?: TokenRekeyOptions): Promise<ReadonlyArray<{
75
+ readonly oldId: string;
76
+ readonly newId: string;
77
+ readonly raw: string;
78
+ }>>;
79
+ /** @stable */
80
+ interface TokenVerifyOptions extends CommonOutputOptions {
81
+ readonly token: string;
82
+ /** Optional override of the expected token prefix. */
83
+ readonly prefix?: string;
84
+ }
85
+ /** @stable */
86
+ interface TokenVerifyResult {
87
+ readonly ok: boolean;
88
+ readonly reason?: string;
89
+ }
90
+ /**
91
+ * Offline checksum verification. Confirms the structural shape, the
92
+ * environment marker, and the CRC checksum but does NOT consult the
93
+ * token store — it only proves the token was minted by a Graphorin
94
+ * helper.
95
+ *
96
+ * @stable
97
+ */
98
+ declare function runTokenVerify(options: TokenVerifyOptions): TokenVerifyResult;
99
+ /**
100
+ * Tiny duration parser. Accepts `Ns`, `Nm`, `Nh`, `Nd`. Returns
101
+ * milliseconds. Throws on invalid input — surfaced as a fail-fast at
102
+ * the CLI boundary.
103
+ *
104
+ * @internal
105
+ */
106
+ declare function parseDuration(input: string): number;
107
+ //#endregion
108
+ export { TokenCommonOptions, TokenCreateOptions, TokenCreateResult, TokenListOptions, TokenRekeyOptions, TokenRevokeOptions, TokenRotateOptions, TokenVerifyOptions, TokenVerifyResult, parseDuration, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify };
109
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","names":[],"sources":["../../src/commands/token.ts"],"sourcesContent":[],"mappings":";;;;;AAgMA;AAWsB,UA5JL,kBAAA,SAA2B,mBA4JR,CAAA;EAAU,SAAA,MAAA,CAAA,EAAA,MAAA;;;AAA4B,UAvJzD,kBAAA,SAA2B,kBAuJ8B,CAAA;EAuCzD,SAAA,KAAA,CAAA,EAAA,MAAkB;EASb;EACX,SAAA,MAAA,EArMQ,aAqMR,CAAA,MAAA,CAAA;EAET;EADC,SAAA,SAAA,CAAA,EAAA,MAAA;EAAO,SAAA,GAAA,CAAA,EAAA,MAAA,GAAA,MAAA;AAqCV;AAOA;AAagB,UAxPC,iBAAA,CAwPuB;EA2BxB,SAAA,EAAA,EAAA,MAAa;;mBAhRV;;;;;;;;;iBAUG,cAAA,UAAwB,qBAAqB,QAAQ;;UA8C1D,gBAAA,SAAyB;;;;;;;;iBASpB,YAAA,WACX,mBACR,QAAQ,cAAc;;UA2BR,kBAAA,SAA2B;;;;;;;;iBAStB,cAAA,UACX,qBACR,QAAQ;;UAsBM,kBAAA,SAA2B;;;;;;;;;;iBAWtB,cAAA,UAAwB,qBAAqB,QAAQ;;UAuC1D,iBAAA,SAA0B;;;;;;;;iBASrB,aAAA,WACX,oBACR,QACD;;;;;;UAoCe,kBAAA,SAA2B;;;;;;UAO3B,iBAAA;;;;;;;;;;;;iBAaD,cAAA,UAAwB,qBAAqB;;;;;;;;iBA2B7C,aAAA"}
@@ -0,0 +1,246 @@
1
+ import { brand, defaultPrintSink, emitReport, statusMarker } from "../internal/output.js";
2
+ import { EXIT_CODES } from "../internal/exit.js";
3
+ import { openStoreContext } from "../internal/store-context.js";
4
+ import { createToken, listTokens, rekeyTokens, revokeToken, rotateToken, verifyOffline } from "@graphorin/security";
5
+
6
+ //#region src/commands/token.ts
7
+ /**
8
+ * `graphorin token` — manage server auth tokens.
9
+ *
10
+ * The subcommand thin-wraps the library functions in
11
+ * `@graphorin/security/auth` (`createToken`, `listTokens`,
12
+ * `revokeToken`, `rotateToken`, `rekeyTokens`, `verifyOffline`). Per
13
+ * Phase 15 § Tokens the surface ships:
14
+ *
15
+ * - `graphorin token create --label <name> --scopes <list> [--expires-in <duration>]`
16
+ * - `graphorin token list`
17
+ * - `graphorin token revoke <id>`
18
+ * - `graphorin token rotate <id>`
19
+ * - `graphorin token rekey`
20
+ * - `graphorin token verify <token>`
21
+ *
22
+ * The raw token bytes are shown to the operator at most once — at the
23
+ * call site of `create` / `rotate` / `rekey`. They are wrapped in a
24
+ * {@link SecretValue} on the way back to keep accidental logging out
25
+ * of the codepath; the CLI prints them via `value.use((s) => ...)` so
26
+ * the bytes never live as a plain string variable longer than needed.
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+ const ONE_DAY_MS = 1440 * 60 * 1e3;
31
+ /**
32
+ * Create a token. Returns the raw value once + the persisted record.
33
+ *
34
+ * @stable
35
+ */
36
+ async function runTokenCreate(options) {
37
+ const ctx = await openStoreContext({
38
+ ...options.config !== void 0 ? { config: options.config } : {},
39
+ requirePepper: true
40
+ });
41
+ try {
42
+ if (ctx.pepper === void 0) throw new Error("[graphorin/cli] internal: pepper resolution should have populated ctx.pepper");
43
+ const env = options.env ?? "live";
44
+ const expiresInMs = options.expiresIn !== void 0 ? parseDuration(options.expiresIn) : void 0;
45
+ const created = await createToken({
46
+ tokenStore: ctx.store.authTokens,
47
+ pepper: ctx.pepper,
48
+ env,
49
+ scopes: options.scopes,
50
+ ...options.label !== void 0 ? { label: options.label } : {},
51
+ ...expiresInMs !== void 0 ? { expiresInMs } : {}
52
+ });
53
+ const raw = await created.raw.use((s) => String(s));
54
+ const out = Object.freeze({
55
+ id: created.record.id,
56
+ ...created.record.label !== void 0 ? { label: created.record.label } : {},
57
+ scopes: Object.freeze([...created.record.scopes]),
58
+ raw,
59
+ ...created.record.expiresAt !== void 0 ? { expiresAt: created.record.expiresAt } : {}
60
+ });
61
+ emitReport(options, out, () => {
62
+ const print = options.print ?? defaultPrintSink;
63
+ print(brand(`token created (id=${out.id}, scopes=${out.scopes.join(",")})`));
64
+ print(brand(`raw token (shown ONCE):`));
65
+ print(` ${out.raw}`);
66
+ if (out.expiresAt !== void 0) print(brand(`expires at: ${out.expiresAt}`));
67
+ });
68
+ return out;
69
+ } finally {
70
+ await ctx.close();
71
+ }
72
+ }
73
+ /**
74
+ * List token metadata.
75
+ *
76
+ * @stable
77
+ */
78
+ async function runTokenList(options = {}) {
79
+ const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
80
+ try {
81
+ const list = await listTokens(ctx.store.authTokens, { includeRevoked: options.includeRevoked === true });
82
+ emitReport(options, list, () => {
83
+ const print = options.print ?? defaultPrintSink;
84
+ if (list.length === 0) {
85
+ print(brand("no tokens registered."));
86
+ return;
87
+ }
88
+ print(brand(`${list.length} token(s):`));
89
+ for (const t of list) print(` ${t.revokedAt !== void 0 ? statusMarker("warn") : statusMarker("ok")} ${t.id} (label=${t.label ?? "-"}, scopes=${t.scopes.join(",")})`);
90
+ });
91
+ return list;
92
+ } finally {
93
+ await ctx.close();
94
+ }
95
+ }
96
+ /**
97
+ * Revoke a single token.
98
+ *
99
+ * @stable
100
+ */
101
+ async function runTokenRevoke(options) {
102
+ const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
103
+ try {
104
+ const result = await revokeToken(ctx.store.authTokens, options.id);
105
+ emitReport(options, result ?? null, () => {
106
+ const print = options.print ?? defaultPrintSink;
107
+ if (result === void 0) {
108
+ print(brand(`token '${options.id}' not found.`));
109
+ process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
110
+ return;
111
+ }
112
+ print(brand(`token '${result.id}' revoked at ${result.revokedAt ?? "<now>"}`));
113
+ });
114
+ return result;
115
+ } finally {
116
+ await ctx.close();
117
+ }
118
+ }
119
+ /**
120
+ * Revoke an existing token and immediately mint a fresh one with the
121
+ * same scopes. Returns the new raw token bytes once.
122
+ *
123
+ * @stable
124
+ */
125
+ async function runTokenRotate(options) {
126
+ const ctx = await openStoreContext({
127
+ ...options.config !== void 0 ? { config: options.config } : {},
128
+ requirePepper: true
129
+ });
130
+ try {
131
+ if (ctx.pepper === void 0) throw new Error("[graphorin/cli] internal: pepper missing.");
132
+ const env = options.env ?? "live";
133
+ const result = await rotateToken({
134
+ tokenStore: ctx.store.authTokens,
135
+ pepper: ctx.pepper,
136
+ id: options.id,
137
+ env
138
+ });
139
+ const raw = await result.next.raw.use((s) => String(s));
140
+ const out = Object.freeze({
141
+ id: result.next.record.id,
142
+ ...result.next.record.label !== void 0 ? { label: result.next.record.label } : {},
143
+ scopes: Object.freeze([...result.next.record.scopes]),
144
+ raw,
145
+ ...result.next.record.expiresAt !== void 0 ? { expiresAt: result.next.record.expiresAt } : {}
146
+ });
147
+ emitReport(options, out, () => {
148
+ const print = options.print ?? defaultPrintSink;
149
+ print(brand(`token '${result.old.id}' revoked + replaced by '${out.id}'`));
150
+ print(brand("raw token (shown ONCE):"));
151
+ print(` ${raw}`);
152
+ });
153
+ return out;
154
+ } finally {
155
+ await ctx.close();
156
+ }
157
+ }
158
+ /**
159
+ * Re-issue every active token. Used after a known compromise.
160
+ *
161
+ * @stable
162
+ */
163
+ async function runTokenRekey(options = {}) {
164
+ const ctx = await openStoreContext({
165
+ ...options.config !== void 0 ? { config: options.config } : {},
166
+ requirePepper: true
167
+ });
168
+ try {
169
+ if (ctx.pepper === void 0) throw new Error("[graphorin/cli] internal: pepper missing.");
170
+ const result = await rekeyTokens({
171
+ tokenStore: ctx.store.authTokens,
172
+ pepper: ctx.pepper,
173
+ env: options.env ?? "live"
174
+ });
175
+ const out = [];
176
+ for (const [oldId, created] of result) {
177
+ const raw = await created.raw.use((s) => String(s));
178
+ out.push(Object.freeze({
179
+ oldId,
180
+ newId: created.record.id,
181
+ raw
182
+ }));
183
+ }
184
+ const frozen = Object.freeze(out);
185
+ emitReport(options, frozen, () => {
186
+ const print = options.print ?? defaultPrintSink;
187
+ print(brand(`rekeyed ${frozen.length} token(s):`));
188
+ for (const row of frozen) {
189
+ print(` - old=${row.oldId} new=${row.newId}`);
190
+ print(` raw: ${row.raw}`);
191
+ }
192
+ });
193
+ return frozen;
194
+ } finally {
195
+ await ctx.close();
196
+ }
197
+ }
198
+ /**
199
+ * Offline checksum verification. Confirms the structural shape, the
200
+ * environment marker, and the CRC checksum but does NOT consult the
201
+ * token store — it only proves the token was minted by a Graphorin
202
+ * helper.
203
+ *
204
+ * @stable
205
+ */
206
+ function runTokenVerify(options) {
207
+ const result = verifyOffline(options.token, { ...options.prefix !== void 0 ? { acceptPrefix: options.prefix } : {} });
208
+ const out = Object.freeze({
209
+ ok: result.ok,
210
+ ...result.ok ? {} : { reason: result.reason }
211
+ });
212
+ emitReport(options, out, () => {
213
+ const print = options.print ?? defaultPrintSink;
214
+ if (out.ok) {
215
+ print(brand(`token format ${statusMarker("ok")} (offline checksum verified)`));
216
+ return;
217
+ }
218
+ print(brand(`token format ${statusMarker("fail")} (${out.reason ?? "unknown reason"})`));
219
+ process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
220
+ });
221
+ return out;
222
+ }
223
+ /**
224
+ * Tiny duration parser. Accepts `Ns`, `Nm`, `Nh`, `Nd`. Returns
225
+ * milliseconds. Throws on invalid input — surfaced as a fail-fast at
226
+ * the CLI boundary.
227
+ *
228
+ * @internal
229
+ */
230
+ function parseDuration(input) {
231
+ const match = /^(\d+)\s*([smhd])$/i.exec(input.trim());
232
+ if (!match) throw new Error(`[graphorin/cli] invalid --expires-in '${input}'. Use the form '<number><s|m|h|d>' (e.g. 30d).`);
233
+ const value = Number.parseInt(match[1], 10);
234
+ const unit = match[2].toLowerCase();
235
+ switch (unit) {
236
+ case "s": return value * 1e3;
237
+ case "m": return value * 6e4;
238
+ case "h": return value * 60 * 6e4;
239
+ case "d": return value * ONE_DAY_MS;
240
+ default: throw new Error(`[graphorin/cli] invalid --expires-in unit '${unit}'.`);
241
+ }
242
+ }
243
+
244
+ //#endregion
245
+ export { parseDuration, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify };
246
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","names":["out: TokenCreateResult","out: Array<{ readonly oldId: string; readonly newId: string; readonly raw: string }>","out: TokenVerifyResult"],"sources":["../../src/commands/token.ts"],"sourcesContent":["/**\n * `graphorin token` — manage server auth tokens.\n *\n * The subcommand thin-wraps the library functions in\n * `@graphorin/security/auth` (`createToken`, `listTokens`,\n * `revokeToken`, `rotateToken`, `rekeyTokens`, `verifyOffline`). Per\n * Phase 15 § Tokens the surface ships:\n *\n * - `graphorin token create --label <name> --scopes <list> [--expires-in <duration>]`\n * - `graphorin token list`\n * - `graphorin token revoke <id>`\n * - `graphorin token rotate <id>`\n * - `graphorin token rekey`\n * - `graphorin token verify <token>`\n *\n * The raw token bytes are shown to the operator at most once — at the\n * call site of `create` / `rotate` / `rekey`. They are wrapped in a\n * {@link SecretValue} on the way back to keep accidental logging out\n * of the codepath; the CLI prints them via `value.use((s) => ...)` so\n * the bytes never live as a plain string variable longer than needed.\n *\n * @packageDocumentation\n */\n\nimport {\n createToken,\n listTokens,\n rekeyTokens,\n revokeToken,\n rotateToken,\n type TokenMetadata,\n verifyOffline,\n} from '@graphorin/security';\n\nimport { EXIT_CODES } from '../internal/exit.js';\nimport {\n brand,\n type CommonOutputOptions,\n defaultPrintSink,\n emitReport,\n statusMarker,\n} from '../internal/output.js';\nimport { openStoreContext } from '../internal/store-context.js';\n\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\n/** @stable */\nexport interface TokenCommonOptions extends CommonOutputOptions {\n readonly config?: string;\n}\n\n/** @stable */\nexport interface TokenCreateOptions extends TokenCommonOptions {\n readonly label?: string;\n /** Comma-separated scope list. */\n readonly scopes: ReadonlyArray<string>;\n /** Duration string: `30d`, `12h`, `90m`, `45s`. */\n readonly expiresIn?: string;\n readonly env?: 'live' | 'test';\n}\n\n/** @stable */\nexport interface TokenCreateResult {\n readonly id: string;\n readonly label?: string;\n readonly scopes: ReadonlyArray<string>;\n readonly raw: string;\n readonly expiresAt?: string;\n}\n\n/**\n * Create a token. Returns the raw value once + the persisted record.\n *\n * @stable\n */\nexport async function runTokenCreate(options: TokenCreateOptions): Promise<TokenCreateResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n requirePepper: true,\n });\n try {\n if (ctx.pepper === undefined) {\n throw new Error(\n '[graphorin/cli] internal: pepper resolution should have populated ctx.pepper',\n );\n }\n const env = options.env ?? 'live';\n const expiresInMs =\n options.expiresIn !== undefined ? parseDuration(options.expiresIn) : undefined;\n const created = await createToken({\n tokenStore: ctx.store.authTokens,\n pepper: ctx.pepper,\n env,\n scopes: options.scopes,\n ...(options.label !== undefined ? { label: options.label } : {}),\n ...(expiresInMs !== undefined ? { expiresInMs } : {}),\n });\n const raw = await created.raw.use((s) => String(s));\n const out: TokenCreateResult = Object.freeze({\n id: created.record.id,\n ...(created.record.label !== undefined ? { label: created.record.label } : {}),\n scopes: Object.freeze([...created.record.scopes]),\n raw,\n ...(created.record.expiresAt !== undefined ? { expiresAt: created.record.expiresAt } : {}),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`token created (id=${out.id}, scopes=${out.scopes.join(',')})`));\n print(brand(`raw token (shown ONCE):`));\n print(` ${out.raw}`);\n if (out.expiresAt !== undefined) {\n print(brand(`expires at: ${out.expiresAt}`));\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface TokenListOptions extends TokenCommonOptions {\n readonly includeRevoked?: boolean;\n}\n\n/**\n * List token metadata.\n *\n * @stable\n */\nexport async function runTokenList(\n options: TokenListOptions = {},\n): Promise<ReadonlyArray<TokenMetadata>> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const list = await listTokens(ctx.store.authTokens, {\n includeRevoked: options.includeRevoked === true,\n });\n emitReport(options, list, () => {\n const print = options.print ?? defaultPrintSink;\n if (list.length === 0) {\n print(brand('no tokens registered.'));\n return;\n }\n print(brand(`${list.length} token(s):`));\n for (const t of list) {\n const status = t.revokedAt !== undefined ? statusMarker('warn') : statusMarker('ok');\n print(` ${status} ${t.id} (label=${t.label ?? '-'}, scopes=${t.scopes.join(',')})`);\n }\n });\n return list;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface TokenRevokeOptions extends TokenCommonOptions {\n readonly id: string;\n}\n\n/**\n * Revoke a single token.\n *\n * @stable\n */\nexport async function runTokenRevoke(\n options: TokenRevokeOptions,\n): Promise<TokenMetadata | undefined> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const result = await revokeToken(ctx.store.authTokens, options.id);\n emitReport(options, result ?? null, () => {\n const print = options.print ?? defaultPrintSink;\n if (result === undefined) {\n print(brand(`token '${options.id}' not found.`));\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return;\n }\n print(brand(`token '${result.id}' revoked at ${result.revokedAt ?? '<now>'}`));\n });\n return result;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface TokenRotateOptions extends TokenCommonOptions {\n readonly id: string;\n readonly env?: 'live' | 'test';\n}\n\n/**\n * Revoke an existing token and immediately mint a fresh one with the\n * same scopes. Returns the new raw token bytes once.\n *\n * @stable\n */\nexport async function runTokenRotate(options: TokenRotateOptions): Promise<TokenCreateResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n requirePepper: true,\n });\n try {\n if (ctx.pepper === undefined) {\n throw new Error('[graphorin/cli] internal: pepper missing.');\n }\n const env = options.env ?? 'live';\n const result = await rotateToken({\n tokenStore: ctx.store.authTokens,\n pepper: ctx.pepper,\n id: options.id,\n env,\n });\n const raw = await result.next.raw.use((s) => String(s));\n const out: TokenCreateResult = Object.freeze({\n id: result.next.record.id,\n ...(result.next.record.label !== undefined ? { label: result.next.record.label } : {}),\n scopes: Object.freeze([...result.next.record.scopes]),\n raw,\n ...(result.next.record.expiresAt !== undefined\n ? { expiresAt: result.next.record.expiresAt }\n : {}),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`token '${result.old.id}' revoked + replaced by '${out.id}'`));\n print(brand('raw token (shown ONCE):'));\n print(` ${raw}`);\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface TokenRekeyOptions extends TokenCommonOptions {\n readonly env?: 'live' | 'test';\n}\n\n/**\n * Re-issue every active token. Used after a known compromise.\n *\n * @stable\n */\nexport async function runTokenRekey(\n options: TokenRekeyOptions = {},\n): Promise<\n ReadonlyArray<{ readonly oldId: string; readonly newId: string; readonly raw: string }>\n> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n requirePepper: true,\n });\n try {\n if (ctx.pepper === undefined) {\n throw new Error('[graphorin/cli] internal: pepper missing.');\n }\n const result = await rekeyTokens({\n tokenStore: ctx.store.authTokens,\n pepper: ctx.pepper,\n env: options.env ?? 'live',\n });\n const out: Array<{ readonly oldId: string; readonly newId: string; readonly raw: string }> = [];\n for (const [oldId, created] of result) {\n const raw = await created.raw.use((s) => String(s));\n out.push(Object.freeze({ oldId, newId: created.record.id, raw }));\n }\n const frozen = Object.freeze(out);\n emitReport(options, frozen, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`rekeyed ${frozen.length} token(s):`));\n for (const row of frozen) {\n print(` - old=${row.oldId} new=${row.newId}`);\n print(` raw: ${row.raw}`);\n }\n });\n return frozen;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface TokenVerifyOptions extends CommonOutputOptions {\n readonly token: string;\n /** Optional override of the expected token prefix. */\n readonly prefix?: string;\n}\n\n/** @stable */\nexport interface TokenVerifyResult {\n readonly ok: boolean;\n readonly reason?: string;\n}\n\n/**\n * Offline checksum verification. Confirms the structural shape, the\n * environment marker, and the CRC checksum but does NOT consult the\n * token store — it only proves the token was minted by a Graphorin\n * helper.\n *\n * @stable\n */\nexport function runTokenVerify(options: TokenVerifyOptions): TokenVerifyResult {\n const result = verifyOffline(options.token, {\n ...(options.prefix !== undefined ? { acceptPrefix: options.prefix } : {}),\n });\n const out: TokenVerifyResult = Object.freeze({\n ok: result.ok,\n ...(result.ok ? {} : { reason: result.reason }),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n if (out.ok) {\n print(brand(`token format ${statusMarker('ok')} (offline checksum verified)`));\n return;\n }\n print(brand(`token format ${statusMarker('fail')} (${out.reason ?? 'unknown reason'})`));\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n });\n return out;\n}\n\n/**\n * Tiny duration parser. Accepts `Ns`, `Nm`, `Nh`, `Nd`. Returns\n * milliseconds. Throws on invalid input — surfaced as a fail-fast at\n * the CLI boundary.\n *\n * @internal\n */\nexport function parseDuration(input: string): number {\n const match = /^(\\d+)\\s*([smhd])$/i.exec(input.trim());\n if (!match) {\n throw new Error(\n `[graphorin/cli] invalid --expires-in '${input}'. Use the form '<number><s|m|h|d>' (e.g. 30d).`,\n );\n }\n const value = Number.parseInt(match[1] as string, 10);\n const unit = (match[2] as string).toLowerCase();\n switch (unit) {\n case 's':\n return value * 1000;\n case 'm':\n return value * 60_000;\n case 'h':\n return value * 60 * 60_000;\n case 'd':\n return value * ONE_DAY_MS;\n default:\n throw new Error(`[graphorin/cli] invalid --expires-in unit '${unit}'.`);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import { CommonOutputOptions } from "../internal/output.js";
2
+ import { LintFinding, LintFindingKind } from "@graphorin/eslint-plugin";
3
+
4
+ //#region src/commands/tools-lint.d.ts
5
+
6
+ /**
7
+ * Counter event emitted per below-threshold tool per invocation. The
8
+ * CLI exposes a configurable sink so observability pipelines (Phase
9
+ * 04) can wire the counter into Prometheus / OTLP without touching
10
+ * the CLI runtime. Default: no-op.
11
+ *
12
+ * Mirrors the `tool.lint.threshold.violations.total{toolName,score,
13
+ * threshold}` contract documented in RB-49 § Counter emission.
14
+ *
15
+ * @stable
16
+ */
17
+ interface ToolsLintThresholdViolation {
18
+ readonly toolName: string;
19
+ readonly score: number;
20
+ readonly threshold: number;
21
+ }
22
+ /** @stable */
23
+ type ToolsLintCounterSink = (event: ToolsLintThresholdViolation) => void;
24
+ /** @stable */
25
+ interface ToolsLintOptions extends CommonOutputOptions {
26
+ /** Optional path to a `tsconfig.json` whose `include` overrides the file glob. */
27
+ readonly config?: string;
28
+ /** Minimum acceptable per-tool score. Default `60`. */
29
+ readonly threshold?: number;
30
+ /** Output format. Default `'text'`. */
31
+ readonly format?: 'text' | 'json';
32
+ /** Optional override of the file glob pattern. */
33
+ readonly source?: string;
34
+ /** Override `cwd`. Default `process.cwd()`. */
35
+ readonly cwd?: string;
36
+ /**
37
+ * Test seam — supply a list of `(file, source)` pairs directly so
38
+ * the test does not need to fish around the filesystem.
39
+ */
40
+ readonly inlineSources?: ReadonlyArray<{
41
+ readonly file: string;
42
+ readonly source: string;
43
+ }>;
44
+ /**
45
+ * Optional sink for the `tool.lint.threshold.violations.total`
46
+ * counter (RB-49). The CLI calls this once per below-threshold tool
47
+ * per invocation. Default: no-op.
48
+ */
49
+ readonly counterSink?: ToolsLintCounterSink;
50
+ }
51
+ /**
52
+ * Documented JSON shape emitted on `--format json`. The shape is
53
+ * stable + forward-compatible with the `@eslint/mcp` industry
54
+ * direction (per-finding `rule` / `severity` / `message` / `location`
55
+ * mirror the ESLint LSP convention).
56
+ *
57
+ * @stable
58
+ */
59
+ interface ToolsLintReport {
60
+ readonly summary: {
61
+ readonly totalTools: number;
62
+ readonly totalFindings: number;
63
+ readonly threshold: number;
64
+ readonly passed: number;
65
+ readonly failed: number;
66
+ };
67
+ readonly tools: ReadonlyArray<ToolsLintReportTool>;
68
+ }
69
+ /** @stable */
70
+ interface ToolsLintReportTool {
71
+ readonly name: string;
72
+ readonly source: string;
73
+ readonly score: number;
74
+ readonly axes: {
75
+ readonly description: number;
76
+ readonly examples: number;
77
+ readonly parameterNaming: number;
78
+ };
79
+ readonly findings: ReadonlyArray<ToolsLintReportFinding>;
80
+ }
81
+ /** @stable */
82
+ interface ToolsLintReportFinding {
83
+ readonly rule: LintFinding['rule'];
84
+ readonly kind: LintFindingKind;
85
+ readonly severity: LintFinding['severity'];
86
+ readonly message: string;
87
+ readonly location: {
88
+ readonly file: string;
89
+ readonly line: number;
90
+ };
91
+ readonly hint?: string;
92
+ readonly matchedPattern?: string;
93
+ }
94
+ /**
95
+ * Run the discovery + grader pipeline. Returns the structured report
96
+ * the CLI emits to stdout.
97
+ *
98
+ * @stable
99
+ */
100
+ declare function runToolsLint(options?: ToolsLintOptions): Promise<ToolsLintReport>;
101
+ //#endregion
102
+ export { ToolsLintOptions, ToolsLintReport, runToolsLint };
103
+ //# sourceMappingURL=tools-lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-lint.d.ts","names":[],"sources":["../../src/commands/tools-lint.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;UA0EiB,2BAAA;;;;;;KAOL,oBAAA,WAA+B;;UAG1B,gBAAA,SAAyB;;;;;;;;;;;;;;;2BAef;;;;;;;;;yBAMF;;;;;;;;;;UAWR,eAAA;;;;;;;;kBAQC,cAAc;;;UAIf,mBAAA;;;;;;;;;qBASI,cAAc;;;UAIlB,sBAAA;iBACA;iBACA;qBACI;;;;;;;;;;;;;;;iBAgBC,YAAA,WAAsB,mBAAwB,QAAQ"}