@graphorin/cli 0.6.1 → 0.7.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 (87) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +3 -3
  3. package/dist/bin/graphorin.js +51 -13
  4. package/dist/bin/graphorin.js.map +1 -1
  5. package/dist/commands/audit.d.ts.map +1 -1
  6. package/dist/commands/audit.js +2 -1
  7. package/dist/commands/audit.js.map +1 -1
  8. package/dist/commands/consolidator.d.ts +56 -1
  9. package/dist/commands/consolidator.d.ts.map +1 -1
  10. package/dist/commands/consolidator.js +88 -2
  11. package/dist/commands/consolidator.js.map +1 -1
  12. package/dist/commands/doctor.d.ts.map +1 -1
  13. package/dist/commands/index.d.ts +4 -4
  14. package/dist/commands/index.js +4 -4
  15. package/dist/commands/init.d.ts +11 -4
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +15 -11
  18. package/dist/commands/init.js.map +1 -1
  19. package/dist/commands/memory.d.ts +26 -1
  20. package/dist/commands/memory.d.ts.map +1 -1
  21. package/dist/commands/memory.js +56 -3
  22. package/dist/commands/memory.js.map +1 -1
  23. package/dist/commands/pricing.d.ts +6 -0
  24. package/dist/commands/pricing.d.ts.map +1 -1
  25. package/dist/commands/pricing.js +5 -2
  26. package/dist/commands/pricing.js.map +1 -1
  27. package/dist/commands/secrets.d.ts.map +1 -1
  28. package/dist/commands/secrets.js +2 -2
  29. package/dist/commands/secrets.js.map +1 -1
  30. package/dist/commands/skills.d.ts.map +1 -1
  31. package/dist/commands/skills.js +1 -1
  32. package/dist/commands/skills.js.map +1 -1
  33. package/dist/commands/storage.d.ts +41 -1
  34. package/dist/commands/storage.d.ts.map +1 -1
  35. package/dist/commands/storage.js +75 -1
  36. package/dist/commands/storage.js.map +1 -1
  37. package/dist/commands/token.d.ts.map +1 -1
  38. package/dist/commands/token.js +2 -2
  39. package/dist/commands/token.js.map +1 -1
  40. package/dist/commands/tools-lint.js +1 -1
  41. package/dist/commands/tools-lint.js.map +1 -1
  42. package/dist/commands/traces.d.ts +14 -2
  43. package/dist/commands/traces.d.ts.map +1 -1
  44. package/dist/commands/traces.js +39 -22
  45. package/dist/commands/traces.js.map +1 -1
  46. package/dist/commands/triggers.d.ts.map +1 -1
  47. package/dist/commands/triggers.js +5 -2
  48. package/dist/commands/triggers.js.map +1 -1
  49. package/dist/index.d.ts +4 -4
  50. package/dist/index.js +4 -5
  51. package/dist/index.js.map +1 -1
  52. package/dist/internal/output.js +6 -0
  53. package/dist/internal/output.js.map +1 -1
  54. package/dist/internal/store-context.js +13 -2
  55. package/dist/internal/store-context.js.map +1 -1
  56. package/dist/package.js +1 -1
  57. package/dist/package.js.map +1 -1
  58. package/package.json +18 -14
  59. package/src/bin/graphorin.ts +1387 -0
  60. package/src/commands/audit.ts +256 -0
  61. package/src/commands/auth.ts +238 -0
  62. package/src/commands/consolidator.ts +382 -0
  63. package/src/commands/doctor.ts +253 -0
  64. package/src/commands/guard.ts +144 -0
  65. package/src/commands/index.ts +223 -0
  66. package/src/commands/init.ts +194 -0
  67. package/src/commands/memory.ts +1052 -0
  68. package/src/commands/migrate-config.ts +77 -0
  69. package/src/commands/migrate-export.ts +117 -0
  70. package/src/commands/migrate.ts +83 -0
  71. package/src/commands/pricing.ts +244 -0
  72. package/src/commands/secrets.ts +309 -0
  73. package/src/commands/skills.ts +272 -0
  74. package/src/commands/start.ts +180 -0
  75. package/src/commands/storage.ts +659 -0
  76. package/src/commands/telemetry.ts +91 -0
  77. package/src/commands/token.ts +361 -0
  78. package/src/commands/tools-lint.ts +430 -0
  79. package/src/commands/traces.ts +188 -0
  80. package/src/commands/triggers.ts +237 -0
  81. package/src/index.ts +30 -0
  82. package/src/internal/exit.ts +62 -0
  83. package/src/internal/load-config.ts +107 -0
  84. package/src/internal/offline.ts +81 -0
  85. package/src/internal/output.ts +146 -0
  86. package/src/internal/prompts.ts +58 -0
  87. package/src/internal/store-context.ts +165 -0
@@ -0,0 +1,180 @@
1
+ /**
2
+ * `graphorin start [--config <path>]`. Boots the standalone server.
3
+ *
4
+ * Exits 1 on every recoverable failure (missing pepper, unresolvable
5
+ * SecretRef, missing encryption peer, migration error). The CLI never
6
+ * prints raw secret values; failure messages reference the offending
7
+ * config path + the suggested `graphorin doctor` follow-up (Phase 15).
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+
12
+ import process from 'node:process';
13
+
14
+ import { applyProcessHardening, RefuseToRunAsRootError } from '@graphorin/security';
15
+ import { ConfigInvalidError, createServer, GraphorinServerError } from '@graphorin/server';
16
+
17
+ import { loadConfig } from '../internal/load-config.js';
18
+
19
+ /**
20
+ * Selector for which `SecretsStore` flavour the server activates at
21
+ * startup. Mirrors `--secrets-source` from DEC-136.
22
+ *
23
+ * @stable
24
+ */
25
+ export type SecretsSourceFlag = 'auto' | 'keyring' | 'encrypted-file' | 'env';
26
+
27
+ /**
28
+ * @stable
29
+ */
30
+ export interface StartCommandOptions {
31
+ readonly config?: string;
32
+ readonly host?: string;
33
+ readonly port?: number;
34
+ readonly logResolved?: boolean;
35
+ /**
36
+ * Override the `secrets.source` field of the loaded config. Mirrors
37
+ * the `--secrets-source <kind>` flag from DEC-136.
38
+ */
39
+ readonly secretsSource?: SecretsSourceFlag;
40
+ /**
41
+ * Refuse to fall back when the requested primary store is
42
+ * unavailable. Mirrors `--strict-secrets` from DEC-136.
43
+ */
44
+ readonly strictSecrets?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Programmatic entry - used both by the CLI binary and by tests so
49
+ * the spawn cost of running the binary is paid only when an operator
50
+ * actually invokes `graphorin start` from a shell.
51
+ *
52
+ * @stable
53
+ */
54
+ export async function runStart(
55
+ options: StartCommandOptions = {},
56
+ ): Promise<{ readonly host: string; readonly port: number }> {
57
+ applyHardeningEarly();
58
+ const loaded = await loadConfig(options.config);
59
+ if (options.logResolved !== false) {
60
+ process.stderr.write(`[graphorin/cli] resolved config: ${loaded.path}\n`);
61
+ }
62
+ const overrides = applyCliOverrides(loaded.config, options);
63
+
64
+ let server: Awaited<ReturnType<typeof createServer>> | undefined;
65
+ try {
66
+ server = await createServer({ config: overrides });
67
+ } catch (err) {
68
+ fatal(err);
69
+ }
70
+ if (server === undefined) process.exit(1);
71
+
72
+ const listening = await safeStart(server);
73
+ process.stderr.write(
74
+ `[graphorin/cli] @graphorin/server v${server.version} listening on http://${listening.host}:${listening.port}${server.config.server.basePath}\n`,
75
+ );
76
+ installSignalHandlers(server);
77
+ return listening;
78
+ }
79
+
80
+ /**
81
+ * Merge CLI flag overrides into the loaded `graphorin.config` payload.
82
+ * Exported for unit tests so callers can assert the precedence rules
83
+ * without spinning up a real server.
84
+ *
85
+ * @internal
86
+ */
87
+ export function applyCliOverrides(
88
+ input: { readonly server?: unknown; readonly secrets?: unknown } | undefined,
89
+ options: StartCommandOptions,
90
+ ): Record<string, unknown> {
91
+ const next = { ...(input ?? {}) } as Record<string, unknown>;
92
+ const serverInput = (next.server as Record<string, unknown> | undefined) ?? {};
93
+ if (options.host !== undefined || options.port !== undefined) {
94
+ next.server = {
95
+ ...serverInput,
96
+ ...(options.host !== undefined ? { host: options.host } : {}),
97
+ ...(options.port !== undefined ? { port: options.port } : {}),
98
+ };
99
+ }
100
+ const secretsInput = (next.secrets as Record<string, unknown> | undefined) ?? {};
101
+ if (options.secretsSource !== undefined || options.strictSecrets !== undefined) {
102
+ next.secrets = {
103
+ ...secretsInput,
104
+ ...(options.secretsSource !== undefined ? { source: options.secretsSource } : {}),
105
+ ...(options.strictSecrets !== undefined ? { strict: options.strictSecrets } : {}),
106
+ };
107
+ }
108
+ return next;
109
+ }
110
+
111
+ async function safeStart(server: Awaited<ReturnType<typeof createServer>>) {
112
+ try {
113
+ return await server.start();
114
+ } catch (err) {
115
+ fatal(err);
116
+ }
117
+ process.exit(1);
118
+ }
119
+
120
+ function fatal(err: unknown): never {
121
+ if (err instanceof ConfigInvalidError) {
122
+ process.stderr.write('[graphorin/cli] graphorin.config invalid:\n');
123
+ for (const issue of err.issues) {
124
+ process.stderr.write(` - ${issue.path.join('.') || '<root>'}: ${issue.message}\n`);
125
+ }
126
+ } else if (err instanceof GraphorinServerError) {
127
+ process.stderr.write(`[graphorin/cli] ${err.kind}: ${err.message}\n`);
128
+ if (err.hint !== undefined) {
129
+ process.stderr.write(` hint: ${err.hint}\n`);
130
+ }
131
+ } else if (err instanceof Error) {
132
+ process.stderr.write(`[graphorin/cli] ${err.message}\n`);
133
+ } else {
134
+ process.stderr.write(`[graphorin/cli] unknown error: ${String(err)}\n`);
135
+ }
136
+ process.exit(1);
137
+ }
138
+
139
+ /**
140
+ * Apply the standard hardening hooks before the server bootstraps.
141
+ * Failures (e.g. running as root on Linux/macOS) abort the start with
142
+ * a documented exit code.
143
+ *
144
+ * @internal
145
+ */
146
+ export function applyHardeningEarly(): void {
147
+ try {
148
+ applyProcessHardening({});
149
+ } catch (err) {
150
+ if (err instanceof RefuseToRunAsRootError) {
151
+ process.stderr.write(`[graphorin/cli] ${err.message}\n`);
152
+ process.stderr.write(
153
+ '[graphorin/cli] hint: drop privileges (systemd User=, k8s runAsNonRoot, docker --user) and retry.\n',
154
+ );
155
+ process.exit(1);
156
+ }
157
+ throw err;
158
+ }
159
+ }
160
+
161
+ function installSignalHandlers(server: Awaited<ReturnType<typeof createServer>>): void {
162
+ let shuttingDown = false;
163
+ const onSignal = (signal: NodeJS.Signals) => {
164
+ if (shuttingDown) return;
165
+ shuttingDown = true;
166
+ process.stderr.write(`[graphorin/cli] received ${signal}; draining...\n`);
167
+ server
168
+ .stop()
169
+ .then(() => {
170
+ process.stderr.write('[graphorin/cli] graceful shutdown complete\n');
171
+ process.exit(0);
172
+ })
173
+ .catch((err) => {
174
+ process.stderr.write(`[graphorin/cli] shutdown error: ${(err as Error).message}\n`);
175
+ process.exit(1);
176
+ });
177
+ };
178
+ process.once('SIGTERM', onSignal);
179
+ process.once('SIGINT', onSignal);
180
+ }