ninedeploy 0.10.6 → 0.16.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 (149) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1 -0
  3. package/dist/client.d.ts +34 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +81 -22
  6. package/dist/client.js.map +1 -1
  7. package/dist/commands/backupPolicy.d.ts +29 -0
  8. package/dist/commands/backupPolicy.d.ts.map +1 -0
  9. package/dist/commands/backupPolicy.js +111 -0
  10. package/dist/commands/backupPolicy.js.map +1 -0
  11. package/dist/commands/certificates.d.ts +18 -7
  12. package/dist/commands/certificates.d.ts.map +1 -1
  13. package/dist/commands/certificates.js +121 -1
  14. package/dist/commands/certificates.js.map +1 -1
  15. package/dist/commands/databaseAccess.d.ts +37 -0
  16. package/dist/commands/databaseAccess.d.ts.map +1 -0
  17. package/dist/commands/databaseAccess.js +306 -0
  18. package/dist/commands/databaseAccess.js.map +1 -0
  19. package/dist/commands/deploy.d.ts.map +1 -1
  20. package/dist/commands/deploy.js +28 -7
  21. package/dist/commands/deploy.js.map +1 -1
  22. package/dist/commands/githubApps.d.ts +40 -0
  23. package/dist/commands/githubApps.d.ts.map +1 -0
  24. package/dist/commands/githubApps.js +278 -0
  25. package/dist/commands/githubApps.js.map +1 -0
  26. package/dist/commands/housekeeping.d.ts +7 -0
  27. package/dist/commands/housekeeping.d.ts.map +1 -1
  28. package/dist/commands/housekeeping.js +16 -0
  29. package/dist/commands/housekeeping.js.map +1 -1
  30. package/dist/commands/logs.d.ts.map +1 -1
  31. package/dist/commands/logs.js +17 -2
  32. package/dist/commands/logs.js.map +1 -1
  33. package/dist/commands/manage.d.ts +26 -10
  34. package/dist/commands/manage.d.ts.map +1 -1
  35. package/dist/commands/manage.js +219 -65
  36. package/dist/commands/manage.js.map +1 -1
  37. package/dist/commands/manifest.d.ts.map +1 -1
  38. package/dist/commands/manifest.js +9 -1
  39. package/dist/commands/manifest.js.map +1 -1
  40. package/dist/commands/misc.d.ts +4 -2
  41. package/dist/commands/misc.d.ts.map +1 -1
  42. package/dist/commands/misc.js +25 -11
  43. package/dist/commands/misc.js.map +1 -1
  44. package/dist/commands/multiNode.d.ts +55 -0
  45. package/dist/commands/multiNode.d.ts.map +1 -0
  46. package/dist/commands/multiNode.js +438 -0
  47. package/dist/commands/multiNode.js.map +1 -0
  48. package/dist/commands/notifications.d.ts +1 -1
  49. package/dist/commands/operations.d.ts +89 -0
  50. package/dist/commands/operations.d.ts.map +1 -0
  51. package/dist/commands/operations.js +468 -0
  52. package/dist/commands/operations.js.map +1 -0
  53. package/dist/commands/panelBackup.d.ts +28 -0
  54. package/dist/commands/panelBackup.d.ts.map +1 -0
  55. package/dist/commands/panelBackup.js +153 -0
  56. package/dist/commands/panelBackup.js.map +1 -0
  57. package/dist/commands/plugins.d.ts +1 -1
  58. package/dist/commands/plugins.d.ts.map +1 -1
  59. package/dist/commands/plugins.js +7 -3
  60. package/dist/commands/plugins.js.map +1 -1
  61. package/dist/commands/proxyConfig.d.ts +16 -0
  62. package/dist/commands/proxyConfig.d.ts.map +1 -0
  63. package/dist/commands/proxyConfig.js +122 -0
  64. package/dist/commands/proxyConfig.js.map +1 -0
  65. package/dist/commands/secretProviders.d.ts +59 -0
  66. package/dist/commands/secretProviders.d.ts.map +1 -0
  67. package/dist/commands/secretProviders.js +212 -0
  68. package/dist/commands/secretProviders.js.map +1 -0
  69. package/dist/commands/services.d.ts.map +1 -1
  70. package/dist/commands/services.js +3 -5
  71. package/dist/commands/services.js.map +1 -1
  72. package/dist/commands/setup.d.ts +3 -1
  73. package/dist/commands/setup.d.ts.map +1 -1
  74. package/dist/commands/setup.js +19 -3
  75. package/dist/commands/setup.js.map +1 -1
  76. package/dist/commands/sources.d.ts +10 -3
  77. package/dist/commands/sources.d.ts.map +1 -1
  78. package/dist/commands/sources.js +116 -18
  79. package/dist/commands/sources.js.map +1 -1
  80. package/dist/commands/templates.d.ts.map +1 -1
  81. package/dist/commands/templates.js +8 -1
  82. package/dist/commands/templates.js.map +1 -1
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js +2 -1
  85. package/dist/config.js.map +1 -1
  86. package/dist/index.js +427 -23
  87. package/dist/index.js.map +1 -1
  88. package/dist/lib/format.d.ts.map +1 -1
  89. package/dist/lib/format.js +5 -4
  90. package/dist/lib/format.js.map +1 -1
  91. package/dist/lib/panelBackupFile.d.ts +3 -0
  92. package/dist/lib/panelBackupFile.d.ts.map +1 -0
  93. package/dist/lib/panelBackupFile.js +71 -0
  94. package/dist/lib/panelBackupFile.js.map +1 -0
  95. package/dist/lib/serverRunner.d.ts +6 -0
  96. package/dist/lib/serverRunner.d.ts.map +1 -1
  97. package/dist/lib/serverRunner.js +18 -3
  98. package/dist/lib/serverRunner.js.map +1 -1
  99. package/dist/prompts.d.ts.map +1 -1
  100. package/dist/prompts.js +9 -2
  101. package/dist/prompts.js.map +1 -1
  102. package/package.json +6 -5
  103. package/src/client.ts +137 -0
  104. package/src/commands/backupPolicy.ts +123 -0
  105. package/src/commands/branding.ts +83 -0
  106. package/src/commands/buildCache.ts +72 -0
  107. package/src/commands/certificates.ts +208 -0
  108. package/src/commands/communityTemplates.ts +97 -0
  109. package/src/commands/configCenter.ts +71 -0
  110. package/src/commands/configPresets.ts +215 -0
  111. package/src/commands/databaseAccess.ts +333 -0
  112. package/src/commands/demo.ts +22 -0
  113. package/src/commands/deploy.ts +284 -0
  114. package/src/commands/doctor.ts +170 -0
  115. package/src/commands/domains.ts +170 -0
  116. package/src/commands/egress.ts +135 -0
  117. package/src/commands/emailTemplates.ts +112 -0
  118. package/src/commands/firewall.ts +82 -0
  119. package/src/commands/githubApps.ts +292 -0
  120. package/src/commands/housekeeping.ts +106 -0
  121. package/src/commands/login.ts +58 -0
  122. package/src/commands/logs.ts +91 -0
  123. package/src/commands/manage.ts +714 -0
  124. package/src/commands/manifest.ts +314 -0
  125. package/src/commands/metrics.ts +69 -0
  126. package/src/commands/misc.ts +307 -0
  127. package/src/commands/multiNode.ts +442 -0
  128. package/src/commands/notifications.ts +174 -0
  129. package/src/commands/operations.ts +519 -0
  130. package/src/commands/panelBackup.ts +167 -0
  131. package/src/commands/pgbouncer.ts +65 -0
  132. package/src/commands/plugins.ts +123 -0
  133. package/src/commands/proxyConfig.ts +117 -0
  134. package/src/commands/secretProviders.ts +228 -0
  135. package/src/commands/server.ts +126 -0
  136. package/src/commands/services.ts +249 -0
  137. package/src/commands/setup.ts +94 -0
  138. package/src/commands/sources.ts +317 -0
  139. package/src/commands/sso.ts +106 -0
  140. package/src/commands/templates.ts +158 -0
  141. package/src/commands/token.ts +66 -0
  142. package/src/commands/webhooks.ts +93 -0
  143. package/src/commands/workspaces.ts +63 -0
  144. package/src/config.ts +53 -0
  145. package/src/index.ts +1273 -0
  146. package/src/lib/format.ts +182 -0
  147. package/src/lib/panelBackupFile.ts +67 -0
  148. package/src/lib/serverRunner.ts +223 -0
  149. package/src/prompts.ts +238 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * `ninedeploy templates community {list,import,remove}` —
3
+ * G-13 community template contribution.
4
+ *
5
+ * The CLI is a thin renderer around
6
+ * `client.templates.community.{list,import,remove}`. The
7
+ * import command accepts `-` to read from stdin so a
8
+ * `curl -s https://.../template.json | ninedeploy
9
+ * templates community import -` pipeline lands the
10
+ * upstream contribution without writing it to disk
11
+ * first.
12
+ */
13
+ import type { NineDeployClient } from '../client.js';
14
+ import { c, error, header, info, spinner, success, table } from '../lib/format.js';
15
+ import { readFile } from 'node:fs/promises';
16
+
17
+ export async function communityTemplatesList(client: NineDeployClient): Promise<void> {
18
+ const res = await spinner('Reading community catalog', () => client.templates.community.list());
19
+ header('Community templates');
20
+ info(`Entries: ${res.entries.length}`);
21
+ info(`Total size: ${res.totalBytes} bytes`);
22
+ if (res.entries.length === 0) {
23
+ info('(no community templates)');
24
+ } else {
25
+ console.log();
26
+ table(
27
+ res.entries.map((e) => ({
28
+ id: e.id,
29
+ name: e.template.name,
30
+ category: e.template.category,
31
+ bytes: e.bytes,
32
+ })),
33
+ ['id', 'name', 'category', 'bytes'],
34
+ );
35
+ }
36
+ if (res.errors.length > 0) {
37
+ console.log();
38
+ info(c.yellow(`Parse errors: ${res.errors.length}`));
39
+ for (const err of res.errors) {
40
+ console.log(` ${c.red('✗')} ${err.file}: ${err.error}`);
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * `ninedeploy templates community import <file>` — the
47
+ * path can be `-` for stdin, in which case the CLI
48
+ * reads until EOF.
49
+ */
50
+ export async function communityTemplatesImport(
51
+ client: NineDeployClient,
52
+ path: string,
53
+ opts: { replace?: boolean } = {},
54
+ ): Promise<void> {
55
+ if (!path) {
56
+ error('Usage: ninedeploy templates community import <file> [- | path.json]');
57
+ return;
58
+ }
59
+ let content: string;
60
+ if (path === '-') {
61
+ const chunks: Buffer[] = [];
62
+ for await (const chunk of process.stdin) chunks.push(chunk as Buffer);
63
+ content = Buffer.concat(chunks).toString('utf8');
64
+ } else {
65
+ try {
66
+ content = await readFile(path, 'utf8');
67
+ } catch (err) {
68
+ error(`Failed to read ${path}: ${err instanceof Error ? err.message : String(err)}`);
69
+ return;
70
+ }
71
+ }
72
+ try {
73
+ const res = await spinner('Validating + importing', () =>
74
+ client.templates.community.import(content, { replace: opts.replace }),
75
+ );
76
+ success(`Imported ${res.id} (${res.bytes} bytes, file: ${res.file}).`);
77
+ } catch (err) {
78
+ error(err instanceof Error ? err.message : String(err));
79
+ }
80
+ }
81
+
82
+ export async function communityTemplatesRemove(client: NineDeployClient, idStr: string): Promise<void> {
83
+ if (!idStr) {
84
+ error('Usage: ninedeploy templates community remove <id>');
85
+ return;
86
+ }
87
+ try {
88
+ const res = await spinner('Removing', () => client.templates.community.remove(idStr));
89
+ if (!res.removed) {
90
+ error(`Community template "${idStr}" not found.`);
91
+ return;
92
+ }
93
+ success(`Removed ${idStr}.`);
94
+ } catch (err) {
95
+ error(err instanceof Error ? err.message : String(err));
96
+ }
97
+ }
@@ -0,0 +1,71 @@
1
+ import type { NineDeployClient } from '@ninedeploy/sdk';
2
+ import { table } from '../lib/format.js';
3
+
4
+ export async function configCenterList(
5
+ client: NineDeployClient,
6
+ opts: { category?: string; plugin?: string; reveal?: boolean },
7
+ ): Promise<void> {
8
+ const { entries } = await client.config.list({
9
+ category: opts.category,
10
+ pluginId: opts.plugin,
11
+ reveal: opts.reveal,
12
+ });
13
+
14
+ if (entries.length === 0) {
15
+ console.log(' No configuration entries found.');
16
+ return;
17
+ }
18
+
19
+ table(
20
+ entries.map((e) => ({
21
+ key: e.key,
22
+ category: e.category,
23
+ type: e.type,
24
+ secret: e.isSecret ? 'yes' : 'no',
25
+ value: String(e.value ?? ''),
26
+ status: e.isConfigured ? 'configured' : 'default',
27
+ })),
28
+ ['key', 'category', 'type', 'secret', 'value', 'status'],
29
+ );
30
+ }
31
+
32
+ export async function configCenterGet(client: NineDeployClient, key: string): Promise<void> {
33
+ const item = await client.config.get(key);
34
+ console.log(` Key: ${item.key}`);
35
+ console.log(` Label: ${item.label}`);
36
+ console.log(` Category: ${item.category}`);
37
+ console.log(` Type: ${item.type}`);
38
+ console.log(` Secret: ${item.isSecret ? 'yes' : 'no'}`);
39
+ console.log(` Value: ${String(item.value ?? '')}`);
40
+ console.log(` Status: ${item.isConfigured ? 'configured' : 'default'}`);
41
+ if (item.description) console.log(` Description: ${item.description}`);
42
+ if (item.tags.length > 0) console.log(` Tags: ${item.tags.join(', ')}`);
43
+ }
44
+
45
+ export async function configCenterSet(
46
+ client: NineDeployClient,
47
+ key: string,
48
+ value: string,
49
+ opts: { secret?: boolean; desc?: string; tags?: string },
50
+ ): Promise<void> {
51
+ let parsedValue: unknown = value;
52
+ if (value === 'true') parsedValue = true;
53
+ else if (value === 'false') parsedValue = false;
54
+ else if (!Number.isNaN(Number(value)) && value.trim() !== '') parsedValue = Number(value);
55
+
56
+ const tags = opts.tags ? opts.tags.split(',').map((t) => t.trim()).filter(Boolean) : undefined;
57
+
58
+ const res = await client.config.set(key, {
59
+ value: parsedValue,
60
+ isSecret: opts.secret,
61
+ description: opts.desc,
62
+ tags,
63
+ });
64
+
65
+ console.log(` ✓ Configuration key "${res.key}" saved.`);
66
+ }
67
+
68
+ export async function configCenterDelete(client: NineDeployClient, key: string): Promise<void> {
69
+ const res = await client.config.delete(key);
70
+ console.log(` ✓ Configuration key "${res.key}" deleted.`);
71
+ }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * `ninedeploy config-preset {list,get,register,apply,remove}` — Sprint 3, Gap G-23, PR-A.
3
+ *
4
+ * A preset is a named bundle of `configCenter` writes an operator can
5
+ * register once and apply to a fresh instance with one call. The CLI
6
+ * is the operator-side counterpart of the kernel plugin (which owns
7
+ * the schema) and the HTTP surface (which is the actual apply engine).
8
+ *
9
+ * Split:
10
+ * - `list` and `get` are read-only — they hit `GET /v1/config-presets`
11
+ * and `GET /v1/config-presets/:id` and pretty-print the result.
12
+ * - `register <id>` reads a JSON values object from `--file` (or
13
+ * stdin) and POSTs it. A future PR will let the panel also
14
+ * register via the UI; today the CLI is the only registration
15
+ * path because the schema is intentionally a thin wrapper.
16
+ * - `apply <id>` is the one-shot "I just want it to work" command.
17
+ * The body is empty unless the operator wants to override a
18
+ * single value for this call only (handy for smoke tests).
19
+ * - `remove <id>` unregisters. It does NOT undo the apply — the
20
+ * values are still in configCenter. Re-running the preset
21
+ * later would write the same values back; that is the entire
22
+ * point of a registry.
23
+ */
24
+ import { readFileSync } from 'node:fs';
25
+ import { resolve } from 'node:path';
26
+ import type { NineDeployClient } from '../client.js';
27
+ import { error, header, info, success } from '../lib/format.js';
28
+
29
+ export interface ConfigPresetRegisterOptions {
30
+ /** Path to a JSON file holding the preset's values. */
31
+ file?: string;
32
+ /** Inline description (free text, max 500 chars). */
33
+ description?: string;
34
+ }
35
+
36
+ // ── `ninedeploy config-preset list` ────────────────────────────────────────
37
+
38
+ /** Pure entry point — same shape used by the CLI action and the unit test. */
39
+ export async function configPresetList(client: NineDeployClient): Promise<{ presets: string[] }> {
40
+ return await client.configPresets.list();
41
+ }
42
+
43
+ export async function configPresetListAction(client: NineDeployClient): Promise<void> {
44
+ header('Config presets');
45
+ let result: { presets: string[] };
46
+ try {
47
+ result = await configPresetList(client);
48
+ } catch (err) {
49
+ error(err instanceof Error ? err.message : String(err));
50
+ process.exitCode = 1;
51
+ return;
52
+ }
53
+ if (result.presets.length === 0) {
54
+ info('No presets are registered. Run `ninedeploy config-preset register <id> --file <path>`.');
55
+ return;
56
+ }
57
+ for (const name of result.presets) info(`• ${name}`);
58
+ }
59
+
60
+ // ── `ninedeploy config-preset get <id>` ────────────────────────────────────
61
+
62
+ /** Pure entry point — same shape used by the CLI action and the unit test. */
63
+ export async function configPresetGet(
64
+ client: NineDeployClient,
65
+ id: string,
66
+ ): Promise<{
67
+ id: string;
68
+ description: string | null;
69
+ values: Record<string, unknown>;
70
+ createdAt: string;
71
+ }> {
72
+ return await client.configPresets.get(id);
73
+ }
74
+
75
+ export async function configPresetGetAction(client: NineDeployClient, id: string): Promise<void> {
76
+ header(`Config preset: ${id}`);
77
+ let result: Awaited<ReturnType<typeof configPresetGet>>;
78
+ try {
79
+ result = await configPresetGet(client, id);
80
+ } catch (err) {
81
+ error(err instanceof Error ? err.message : String(err));
82
+ process.exitCode = 1;
83
+ return;
84
+ }
85
+ if (result.description) info(result.description);
86
+ info(`${Object.keys(result.values).length} key(s) in this preset:`);
87
+ for (const [key, value] of Object.entries(result.values)) {
88
+ info(` ${key} = ${JSON.stringify(value)}`);
89
+ }
90
+ }
91
+
92
+ // ── `ninedeploy config-preset register <id>` ──────────────────────────────
93
+
94
+ /** Pure entry point — reads the file, parses JSON, posts to the server. */
95
+ export async function configPresetRegister(
96
+ client: NineDeployClient,
97
+ id: string,
98
+ opts: ConfigPresetRegisterOptions = {},
99
+ ): Promise<{ ok: boolean; id: string; keyCount: number }> {
100
+ if (!opts.file) {
101
+ throw new Error('--file <path> is required (a JSON object of key → value pairs)');
102
+ }
103
+ const filePath = resolve(opts.file);
104
+ const raw = readFileSync(filePath, 'utf8');
105
+ let values: unknown;
106
+ try {
107
+ values = JSON.parse(raw);
108
+ } catch (err) {
109
+ throw new Error(`Invalid JSON in ${opts.file}: ${err instanceof Error ? err.message : String(err)}`);
110
+ }
111
+ if (!values || typeof values !== 'object' || Array.isArray(values)) {
112
+ throw new Error(`${opts.file} must contain a JSON object (key → value)`);
113
+ }
114
+ return await client.configPresets.register({
115
+ id,
116
+ description: opts.description,
117
+ values: values as Record<string, unknown>,
118
+ });
119
+ }
120
+
121
+ export async function configPresetRegisterAction(
122
+ client: NineDeployClient,
123
+ id: string,
124
+ opts: ConfigPresetRegisterOptions = {},
125
+ ): Promise<void> {
126
+ header('Config preset register');
127
+ if (!id) {
128
+ error('Usage: ninedeploy config-preset register <id> --file <path> [--description <text>]');
129
+ process.exitCode = 1;
130
+ return;
131
+ }
132
+ let result: Awaited<ReturnType<typeof configPresetRegister>>;
133
+ try {
134
+ result = await configPresetRegister(client, id, opts);
135
+ } catch (err) {
136
+ error(err instanceof Error ? err.message : String(err));
137
+ process.exitCode = 1;
138
+ return;
139
+ }
140
+ success(`Registered preset "${result.id}" with ${result.keyCount} key(s)`);
141
+ }
142
+
143
+ // ── `ninedeploy config-preset apply <id>` ──────────────────────────────────
144
+
145
+ /** Pure entry point — same shape used by the CLI action and the unit test. */
146
+ export async function configPresetApply(
147
+ client: NineDeployClient,
148
+ id: string,
149
+ opts: { override?: Record<string, unknown> } = {},
150
+ ): Promise<{
151
+ ok: boolean;
152
+ id: string;
153
+ keyCount: number;
154
+ failureCount?: number;
155
+ failures?: Array<{ key: string; status: 'failed'; reason?: string }>;
156
+ }> {
157
+ return await client.configPresets.apply(id, opts);
158
+ }
159
+
160
+ export async function configPresetApplyAction(
161
+ client: NineDeployClient,
162
+ id: string,
163
+ opts: { override?: Record<string, unknown> } = {},
164
+ ): Promise<void> {
165
+ header('Config preset apply');
166
+ if (!id) {
167
+ error('Usage: ninedeploy config-preset apply <id> [--override <json>]');
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+ let result: Awaited<ReturnType<typeof configPresetApply>>;
172
+ try {
173
+ result = await configPresetApply(client, id, opts);
174
+ } catch (err) {
175
+ error(err instanceof Error ? err.message : String(err));
176
+ process.exitCode = 1;
177
+ return;
178
+ }
179
+ if (!result.ok) {
180
+ error(
181
+ `Preset "${result.id}" applied with ${result.failureCount ?? 0} failure(s) of ${result.keyCount} key(s)`,
182
+ );
183
+ process.exitCode = 1;
184
+ return;
185
+ }
186
+ success(`Applied preset "${result.id}" — ${result.keyCount} key(s) written`);
187
+ }
188
+
189
+ // ── `ninedeploy config-preset remove <id>` ─────────────────────────────────
190
+
191
+ /** Pure entry point — same shape used by the CLI action and the unit test. */
192
+ export async function configPresetRemove(
193
+ client: NineDeployClient,
194
+ id: string,
195
+ ): Promise<{ ok: boolean; id: string }> {
196
+ return await client.configPresets.remove(id);
197
+ }
198
+
199
+ export async function configPresetRemoveAction(client: NineDeployClient, id: string): Promise<void> {
200
+ header('Config preset remove');
201
+ if (!id) {
202
+ error('Usage: ninedeploy config-preset remove <id>');
203
+ process.exitCode = 1;
204
+ return;
205
+ }
206
+ let result: Awaited<ReturnType<typeof configPresetRemove>>;
207
+ try {
208
+ result = await configPresetRemove(client, id);
209
+ } catch (err) {
210
+ error(err instanceof Error ? err.message : String(err));
211
+ process.exitCode = 1;
212
+ return;
213
+ }
214
+ success(`Removed preset "${result.id}" (live configCenter values are unchanged)`);
215
+ }