@ninedeploy/sdk 0.10.5 → 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 (129) hide show
  1. package/LICENSE +21 -21
  2. package/dist/apiPath.d.ts +5 -0
  3. package/dist/apiPath.d.ts.map +1 -0
  4. package/dist/apiPath.js +18 -0
  5. package/dist/apiPath.js.map +1 -0
  6. package/dist/client.d.ts +29 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +51 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/databaseImport.d.ts +138 -0
  11. package/dist/databaseImport.d.ts.map +1 -0
  12. package/dist/databaseImport.js +126 -0
  13. package/dist/databaseImport.js.map +1 -0
  14. package/dist/errors.js +1 -1
  15. package/dist/errors.js.map +1 -1
  16. package/dist/index.d.ts +12 -1973
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +9 -641
  19. package/dist/index.js.map +1 -1
  20. package/dist/manifest.d.ts.map +1 -1
  21. package/dist/manifest.js +25 -21
  22. package/dist/manifest.js.map +1 -1
  23. package/dist/resources/auth.d.ts +282 -0
  24. package/dist/resources/auth.d.ts.map +1 -0
  25. package/dist/resources/auth.js +111 -0
  26. package/dist/resources/auth.js.map +1 -0
  27. package/dist/resources/databases.d.ts +178 -0
  28. package/dist/resources/databases.d.ts.map +1 -0
  29. package/dist/resources/databases.js +82 -0
  30. package/dist/resources/databases.js.map +1 -0
  31. package/dist/resources/deploys.d.ts +125 -0
  32. package/dist/resources/deploys.d.ts.map +1 -0
  33. package/dist/resources/deploys.js +46 -0
  34. package/dist/resources/deploys.js.map +1 -0
  35. package/dist/resources/domains.d.ts +101 -0
  36. package/dist/resources/domains.d.ts.map +1 -0
  37. package/dist/resources/domains.js +28 -0
  38. package/dist/resources/domains.js.map +1 -0
  39. package/dist/resources/env.d.ts +55 -0
  40. package/dist/resources/env.d.ts.map +1 -0
  41. package/dist/resources/env.js +33 -0
  42. package/dist/resources/env.js.map +1 -0
  43. package/dist/resources/logs.d.ts +27 -0
  44. package/dist/resources/logs.d.ts.map +1 -0
  45. package/dist/resources/logs.js +18 -0
  46. package/dist/resources/logs.js.map +1 -0
  47. package/dist/resources/network.d.ts +134 -0
  48. package/dist/resources/network.d.ts.map +1 -0
  49. package/dist/resources/network.js +33 -0
  50. package/dist/resources/network.js.map +1 -0
  51. package/dist/resources/plugins.d.ts +42 -0
  52. package/dist/resources/plugins.d.ts.map +1 -0
  53. package/dist/resources/plugins.js +22 -0
  54. package/dist/resources/plugins.js.map +1 -0
  55. package/dist/resources/servers.d.ts +130 -0
  56. package/dist/resources/servers.d.ts.map +1 -0
  57. package/dist/resources/servers.js +37 -0
  58. package/dist/resources/servers.js.map +1 -0
  59. package/dist/resources/services.d.ts +185 -0
  60. package/dist/resources/services.d.ts.map +1 -0
  61. package/dist/resources/services.js +83 -0
  62. package/dist/resources/services.js.map +1 -0
  63. package/dist/resources/settings.d.ts +312 -0
  64. package/dist/resources/settings.d.ts.map +1 -0
  65. package/dist/resources/settings.js +85 -0
  66. package/dist/resources/settings.js.map +1 -0
  67. package/dist/resources/sources.d.ts +87 -0
  68. package/dist/resources/sources.d.ts.map +1 -0
  69. package/dist/resources/sources.js +39 -0
  70. package/dist/resources/sources.js.map +1 -0
  71. package/dist/resources/system.d.ts +240 -0
  72. package/dist/resources/system.d.ts.map +1 -0
  73. package/dist/resources/system.js +86 -0
  74. package/dist/resources/system.js.map +1 -0
  75. package/dist/resources/templates.d.ts +36 -0
  76. package/dist/resources/templates.d.ts.map +1 -0
  77. package/dist/resources/templates.js +17 -0
  78. package/dist/resources/templates.js.map +1 -0
  79. package/dist/resources/terminals.d.ts +60 -0
  80. package/dist/resources/terminals.d.ts.map +1 -0
  81. package/dist/resources/terminals.js +35 -0
  82. package/dist/resources/terminals.js.map +1 -0
  83. package/dist/resources/traefik.d.ts +84 -0
  84. package/dist/resources/traefik.d.ts.map +1 -0
  85. package/dist/resources/traefik.js +35 -0
  86. package/dist/resources/traefik.js.map +1 -0
  87. package/dist/resources/volumes.d.ts +114 -0
  88. package/dist/resources/volumes.d.ts.map +1 -0
  89. package/dist/resources/volumes.js +47 -0
  90. package/dist/resources/volumes.js.map +1 -0
  91. package/dist/terminal.d.ts +98 -0
  92. package/dist/terminal.d.ts.map +1 -0
  93. package/dist/terminal.js +180 -0
  94. package/dist/terminal.js.map +1 -0
  95. package/dist/transport.d.ts +61 -0
  96. package/dist/transport.d.ts.map +1 -0
  97. package/dist/transport.js +90 -0
  98. package/dist/transport.js.map +1 -0
  99. package/dist/types.d.ts +539 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +5 -0
  102. package/dist/types.js.map +1 -0
  103. package/package.json +5 -4
  104. package/src/apiPath.ts +23 -0
  105. package/src/client.ts +70 -0
  106. package/src/databaseImport.ts +174 -0
  107. package/src/errors.ts +27 -0
  108. package/src/index.ts +118 -0
  109. package/src/manifest.ts +414 -0
  110. package/src/resources/auth.ts +357 -0
  111. package/src/resources/databases.ts +227 -0
  112. package/src/resources/deploys.ts +161 -0
  113. package/src/resources/domains.ts +142 -0
  114. package/src/resources/env.ts +84 -0
  115. package/src/resources/logs.ts +48 -0
  116. package/src/resources/network.ts +126 -0
  117. package/src/resources/plugins.ts +52 -0
  118. package/src/resources/servers.ts +141 -0
  119. package/src/resources/services.ts +250 -0
  120. package/src/resources/settings.ts +275 -0
  121. package/src/resources/sources.ts +131 -0
  122. package/src/resources/system.ts +281 -0
  123. package/src/resources/templates.ts +55 -0
  124. package/src/resources/terminals.ts +112 -0
  125. package/src/resources/traefik.ts +116 -0
  126. package/src/resources/volumes.ts +138 -0
  127. package/src/terminal.ts +254 -0
  128. package/src/transport.ts +154 -0
  129. package/src/types.ts +588 -0
@@ -0,0 +1,161 @@
1
+ // Client surface: deploys, webhooks, jobs, insights, ai.
2
+ // Split out of the former single-file SDK entry (src/index.ts); the public
3
+ // surface is re-exported unchanged from there.
4
+ import type {
5
+ AnalyzeRepoInput,
6
+ WebhookCreate,
7
+ CreatedWebhook,
8
+ Deployment,
9
+ QueueResponse,
10
+ AiConfigStatus,
11
+ AiConfigUpdate,
12
+ RepoInsights,
13
+ TriggerDeploy,
14
+ Webhook,
15
+ ImageTransfer,
16
+ } from '@ninedeploy/schemas';
17
+ import type { JobKind, ScheduledJob, JobRun } from '../types.js';
18
+ import type { Transport } from '../transport.js';
19
+
20
+ export interface DeploysApi {
21
+ deploys: {
22
+ /**
23
+ * Queue a deployment of the service's configured branch head. Answers
24
+ * `alreadyInProgress: true` with the in-flight deployment's id when one
25
+ * is already building or deploying.
26
+ */
27
+ trigger: (serviceId: number, input?: TriggerDeploy) => Promise<{ deploymentId: number; alreadyInProgress?: boolean }>;
28
+ list: (serviceId: number) => Promise<Deployment[]>;
29
+ /**
30
+ * Global deploy queue view: every in-flight (queued / building /
31
+ * deploying) deployment the caller can see, with service + project
32
+ * metadata. The optional `status` filter is a comma-separated token
33
+ * list (e.g. `?status=queued,building`).
34
+ */
35
+ queue: (query?: string) => Promise<QueueResponse>;
36
+ rollback: (serviceId: number, deploymentId: number) => Promise<{ deploymentId: number }>;
37
+ /** Cancel a queued/in-flight deployment (checkpoints abort at step boundaries). */
38
+ cancel: (serviceId: number, deploymentId: number) => Promise<{ ok: boolean; status: string }>;
39
+ /**
40
+ * Remove a finished deployment from history, with its build log.
41
+ * Refused for an in-flight deployment (cancel it first) and for the one
42
+ * currently serving traffic. Requires the `admin` role on the service.
43
+ */
44
+ remove: (serviceId: number, deploymentId: number) => Promise<{ ok: boolean; id: number }>;
45
+ /** Build-config + env-key diff against the previous deployment. */
46
+ configDiff: (serviceId: number, deploymentId: number) => Promise<{ deploymentId: number; previousDeploymentId: number | null; changed: boolean; diff: string }>;
47
+ /** Build log download URL (serves the raw log file as an attachment). */
48
+ logDownloadUrl: (serviceId: number, deploymentId: number) => string;
49
+ /**
50
+ * Promote: deploy ANOTHER service at this service's exact running
51
+ * commit — the staging → production lane hop. Both services must
52
+ * track the same repository and the caller needs `member` on both.
53
+ */
54
+ promote: (serviceId: number, targetServiceId: number) => Promise<{ ok: boolean; deploymentId: number; commitSha: string; promotedFrom: string }>;
55
+ // ── 0.16 T8 surfaces ──
56
+ /** Image transfers of one deployment (`GET /v1/deployments/:id/image-transfers`, any seat on its service). */
57
+ imageTransfers: (deploymentId: number) => Promise<ImageTransfer[]>;
58
+ // ── end 0.16 T8 ──
59
+ };
60
+ webhooks: {
61
+ list: (serviceId: number) => Promise<Webhook[]>;
62
+ create: (serviceId: number, input?: WebhookCreate) => Promise<CreatedWebhook>;
63
+ remove: (serviceId: number, hookId: number) => Promise<void>;
64
+ };
65
+ /**
66
+ * r552: aligned with apps/server/src/modules/jobs.ts — `kind` includes
67
+ * `'backup'`, and create/update return the full serialized job (the PATCH
68
+ * was typed `{ ok }`, a field the server never sent).
69
+ */
70
+ jobs: {
71
+ list: (serviceId: number) => Promise<ScheduledJob[]>;
72
+ create: (serviceId: number, input: { name: string; cron: string; kind: JobKind; command?: string; enabled?: boolean }) => Promise<ScheduledJob>;
73
+ update: (serviceId: number, jobId: number, input: Partial<{ name: string; cron: string; kind: JobKind; command: string; enabled: boolean }>) => Promise<ScheduledJob>;
74
+ remove: (serviceId: number, jobId: number) => Promise<{ ok: boolean }>;
75
+ run: (serviceId: number, jobId: number) => Promise<{ ok: boolean }>;
76
+ runs: (serviceId: number, jobId: number) => Promise<JobRun[]>;
77
+ };
78
+ insights: {
79
+ /** Pre-deploy repository inspection (DeployWizard): clone + framework detection. */
80
+ analyze: (input: AnalyzeRepoInput) => Promise<RepoInsights>;
81
+ /** Latest stored analysis for a service (null before the first deploy/refresh). */
82
+ get: (serviceId: number) => Promise<RepoInsights | null>;
83
+ /** Re-analyze the service's repository now and store the result. */
84
+ refresh: (serviceId: number) => Promise<RepoInsights>;
85
+ };
86
+ /** AI failure diagnosis (BYO-key): operator-configured, OpenAI-compatible. */
87
+ ai: {
88
+ /** Never carries key material — only endpoint/model + hasApiKey. */
89
+ getConfig: () => Promise<AiConfigStatus>;
90
+ updateConfig: (input: AiConfigUpdate) => Promise<{ ok: boolean }>;
91
+ /** Ask the configured provider to diagnose a failed build log. */
92
+ diagnose: (serviceId: number, deploymentId: number) => Promise<{ diagnosis: string; model: string }>;
93
+ /** Describe the app in plain language, get a schema-valid .ninedeploy manifest. */
94
+ suggestManifest: (description: string) => Promise<{ manifest: Record<string, unknown>; model: string }>;
95
+ };
96
+ }
97
+
98
+ export function deploysApi(t: Transport): DeploysApi {
99
+ const { get, send, request } = t;
100
+ return {
101
+ deploys: {
102
+ trigger: (serviceId, input) =>
103
+ send<{ deploymentId: number; alreadyInProgress?: boolean }>('POST', `/v1/services/${serviceId}/deploys`, input ?? {}),
104
+ list: (serviceId) => get<Deployment[]>(`/v1/services/${serviceId}/deploys`),
105
+ queue: (query) =>
106
+ get<QueueResponse>(`/v1/services/queue${query ? (query.startsWith('?') ? query : `?${query}`) : ''}`),
107
+ rollback: (serviceId, deploymentId) =>
108
+ send<{ deploymentId: number }>('POST', `/v1/services/${serviceId}/deploys/${deploymentId}/rollback`),
109
+ cancel: (serviceId, deploymentId) =>
110
+ send<{ ok: boolean; status: string }>('POST', `/v1/services/${serviceId}/deploys/${deploymentId}/cancel`),
111
+ remove: (serviceId, deploymentId) =>
112
+ send<{ ok: boolean; id: number }>('DELETE', `/v1/services/${serviceId}/deploys/${deploymentId}`),
113
+ configDiff: (serviceId, deploymentId) =>
114
+ get<{ deploymentId: number; previousDeploymentId: number | null; changed: boolean; diff: string }>(
115
+ `/v1/services/${serviceId}/deploys/${deploymentId}/diff`,
116
+ ),
117
+ promote: (serviceId, targetServiceId) =>
118
+ send<{ ok: boolean; deploymentId: number; commitSha: string; promotedFrom: string }>(
119
+ 'POST',
120
+ `/v1/services/${serviceId}/promote`,
121
+ { targetServiceId },
122
+ ),
123
+ logDownloadUrl: (serviceId, deploymentId) =>
124
+ `/v1/services/${serviceId}/deploys/${deploymentId}/logs/download`,
125
+ // ── 0.16 T8 surfaces ──
126
+ imageTransfers: (deploymentId) => get<ImageTransfer[]>(`/v1/deployments/${deploymentId}/image-transfers`),
127
+ // ── end 0.16 T8 ──
128
+ },
129
+ webhooks: {
130
+ list: (serviceId) => get<Webhook[]>(`/v1/services/${serviceId}/webhooks`),
131
+ create: (serviceId, input) => send<CreatedWebhook>('POST', `/v1/services/${serviceId}/webhooks`, input ?? {}),
132
+ remove: async (serviceId, hookId) => {
133
+ await request(`/v1/services/${serviceId}/webhooks/${hookId}`, { method: 'DELETE' });
134
+ },
135
+ },
136
+ jobs: {
137
+ list: (serviceId) => get(`/v1/services/${serviceId}/jobs`),
138
+ create: (serviceId, input) => send<ScheduledJob>('POST', `/v1/services/${serviceId}/jobs`, input),
139
+ update: (serviceId, jobId, input) => send<ScheduledJob>('PATCH', `/v1/services/${serviceId}/jobs/${jobId}`, input),
140
+ remove: (serviceId, jobId) => send<{ ok: boolean }>('DELETE', `/v1/services/${serviceId}/jobs/${jobId}`),
141
+ run: (serviceId, jobId) => send<{ ok: boolean }>('POST', `/v1/services/${serviceId}/jobs/${jobId}/run`),
142
+ runs: (serviceId, jobId) => get(`/v1/services/${serviceId}/jobs/${jobId}/runs`),
143
+ },
144
+ insights: {
145
+ analyze: (input) => send<RepoInsights>('POST', '/v1/insights', input),
146
+ get: (serviceId) => get<RepoInsights | null>(`/v1/services/${serviceId}/insights`),
147
+ refresh: (serviceId) => send<RepoInsights>('POST', `/v1/services/${serviceId}/insights/refresh`),
148
+ },
149
+ ai: {
150
+ getConfig: () => get<AiConfigStatus>('/v1/ai/config'),
151
+ updateConfig: (input) => send<{ ok: boolean }>('PUT', '/v1/ai/config', input),
152
+ diagnose: (serviceId, deploymentId) =>
153
+ send<{ diagnosis: string; model: string }>(
154
+ 'POST',
155
+ `/v1/ai/services/${serviceId}/deploys/${deploymentId}/diagnose`,
156
+ ),
157
+ suggestManifest: (description) =>
158
+ send<{ manifest: Record<string, unknown>; model: string }>('POST', '/v1/ai/suggest-manifest', { description }),
159
+ },
160
+ };
161
+ }
@@ -0,0 +1,142 @@
1
+ // Client surface: domains, domainPresets.
2
+ // Split out of the former single-file SDK entry (src/index.ts); the public
3
+ // surface is re-exported unchanged from there.
4
+ import type {
5
+ CreateDomainInput,
6
+ Domain,
7
+ DomainPatch,
8
+ DomainEntry,
9
+ } from '@ninedeploy/schemas';
10
+ import type { CreatedDomain, DomainVerifyResult, StartDomainTransferResult, DomainTransferPreview, AcceptDomainTransferInput, AcceptDomainTransferResult } from '../types.js';
11
+ import type { Transport } from '../transport.js';
12
+
13
+ export interface DomainsApi {
14
+ domains: {
15
+ list: (serviceId: number) => Promise<Domain[]>;
16
+ /** Add a domain. A non-operator's hostname outside the instance zone comes back `pending` with a TXT `verification` challenge. */
17
+ create: (serviceId: number, input: CreateDomainInput) => Promise<CreatedDomain>;
18
+ /**
19
+ * r332: prove ownership of a `pending` domain and bring it live. Checks
20
+ * the TXT challenge `create` returned; idempotent (an `active` domain
21
+ * answers `verified: true`) and safe to poll while DNS propagates.
22
+ */
23
+ verify: (serviceId: number, domainId: number) => Promise<DomainVerifyResult>;
24
+ remove: (serviceId: number, domainId: number) => Promise<void>;
25
+ /**
26
+ * DNS status for a domain: does the hostname resolve to the addresses
27
+ * this panel expects? Advisory — routing is governed by ownership.
28
+ */
29
+ dnsCheck: (
30
+ serviceId: number,
31
+ domainId: number,
32
+ ) => Promise<{
33
+ hostname: string;
34
+ status: 'ok' | 'mismatch' | 'unresolved';
35
+ addresses: { a: string[]; aaaa: string[] };
36
+ expected: string[];
37
+ }>;
38
+ /** Update routing extras: ssl toggle, www→apex redirect, custom headers, basicAuth, ipAllowlist, rateLimit. */
39
+ update: (serviceId: number, domainId: number, input: DomainPatch) => Promise<Domain>;
40
+ all: () => Promise<DomainEntry[]>;
41
+ setSsl: (domainId: number, ssl: boolean) => Promise<{ id: number; ssl: boolean }>;
42
+ /** Toggle sticky-session routing for the service (G-28). */
43
+ setStickySession: (serviceId: number, enabled: boolean) => Promise<{ id: number; enabled: boolean; active: boolean }>;
44
+ /**
45
+ * Start a domain transfer (G-29). The source user (admin
46
+ * on the source service) names the target email; the
47
+ * server returns a one-time `acceptUrl` to forward. The
48
+ * target user accepts via `acceptDomainTransfer(token,
49
+ * { targetServiceId })`. The transfer expires after 7
50
+ * days; cancelling is one call.
51
+ */
52
+ transfer: (domainId: number, input: { targetEmail: string }) => Promise<StartDomainTransferResult>;
53
+ /**
54
+ * Preview a transfer by token. Does NOT require auth
55
+ * (the token is the secret) — the panel uses this to
56
+ * render the accept page to a logged-out visitor.
57
+ */
58
+ previewTransfer: (token: string) => Promise<DomainTransferPreview>;
59
+ /**
60
+ * Accept a transfer. The caller must be authenticated
61
+ * and the caller's email must equal the transfer's
62
+ * target email; the panel / CLI surfaces the preview
63
+ * first so the user knows what they're signing in to.
64
+ */
65
+ acceptTransfer: (token: string, input: AcceptDomainTransferInput) => Promise<AcceptDomainTransferResult>;
66
+ /**
67
+ * Cancel a pending transfer. Only the source user
68
+ * (or an instance operator) can cancel.
69
+ */
70
+ cancelTransfer: (token: string) => Promise<{ transferId: number; status: 'cancelled' }>;
71
+ };
72
+ domainPresets: {
73
+ /** Every `IDomainProvider` the running kernel has registered. */
74
+ list: () => Promise<{ providers: string[] }>;
75
+ /**
76
+ * Manually create the DNS record for `hostname` via the operator's
77
+ * active provider. `content` is optional: when omitted the route
78
+ * falls back to the panel's `dns_records_content` setting, then to
79
+ * `detectPublicIp()`. Returns the upstream recordId so the caller
80
+ * can correlate.
81
+ */
82
+ apply: (input: { hostname: string; content?: string }) => Promise<{
83
+ hostname: string;
84
+ provider: string;
85
+ zone: string;
86
+ recordId: string;
87
+ type: 'A' | 'CNAME';
88
+ content: string;
89
+ }>;
90
+ };
91
+ }
92
+
93
+ export function domainsApi(t: Transport): DomainsApi {
94
+ const { get, send, request } = t;
95
+ return {
96
+ domains: {
97
+ list: (serviceId) => get<Domain[]>(`/v1/services/${serviceId}/domains`),
98
+ create: (serviceId, input) => send<CreatedDomain>('POST', `/v1/services/${serviceId}/domains`, input),
99
+ verify: (serviceId, domainId) =>
100
+ send<DomainVerifyResult>('POST', `/v1/services/${serviceId}/domains/${domainId}/verify`, {}),
101
+ dnsCheck: (serviceId, domainId) =>
102
+ get<{
103
+ hostname: string;
104
+ status: 'ok' | 'mismatch' | 'unresolved';
105
+ addresses: { a: string[]; aaaa: string[] };
106
+ expected: string[];
107
+ }>(`/v1/services/${serviceId}/domains/${domainId}/dns`),
108
+ update: (serviceId, domainId, input) =>
109
+ send<Domain>('PATCH', `/v1/services/${serviceId}/domains/${domainId}`, input),
110
+ remove: async (serviceId, domainId) => {
111
+ await request(`/v1/services/${serviceId}/domains/${domainId}`, { method: 'DELETE' });
112
+ },
113
+ all: () => get<DomainEntry[]>('/v1/domains'),
114
+ setSsl: (domainId, ssl) => send<{ id: number; ssl: boolean }>('PATCH', `/v1/domains/${domainId}`, { ssl }),
115
+ /** Toggle sticky-session routing (G-28) — sits under `/domains` for legacy compatibility. */
116
+ setStickySession: (serviceId, enabled) =>
117
+ send<{ id: number; enabled: boolean; active: boolean }>(
118
+ 'POST',
119
+ `/v1/services/${serviceId}/sticky-session`,
120
+ { enabled },
121
+ ),
122
+ transfer: (domainId, input) =>
123
+ send<StartDomainTransferResult>('POST', `/v1/domains/${domainId}/transfer`, input),
124
+ previewTransfer: (token) => get<DomainTransferPreview>(`/v1/domain-transfers/${token}`),
125
+ acceptTransfer: (token, input) =>
126
+ send<AcceptDomainTransferResult>('POST', `/v1/domain-transfers/${token}/accept`, input),
127
+ cancelTransfer: (token) =>
128
+ send<{ transferId: number; status: 'cancelled' }>('POST', `/v1/domain-transfers/${token}/cancel`, {}),
129
+ },
130
+ domainPresets: {
131
+ list: () => get<{ providers: string[] }>('/v1/domain-presets'),
132
+ apply: (input) => send<{
133
+ hostname: string;
134
+ provider: string;
135
+ zone: string;
136
+ recordId: string;
137
+ type: 'A' | 'CNAME';
138
+ content: string;
139
+ }>('POST', '/v1/domain-presets/apply', input),
140
+ },
141
+ };
142
+ }
@@ -0,0 +1,84 @@
1
+ // Client surface: env, projectEnv, previewEnv.
2
+ // Split out of the former single-file SDK entry (src/index.ts); the public
3
+ // surface is re-exported unchanged from there.
4
+ import type {
5
+ EnvVar,
6
+ UpsertEnvVarInput,
7
+ } from '@ninedeploy/schemas';
8
+ import type { Transport } from '../transport.js';
9
+
10
+ export interface EnvApi {
11
+ env: {
12
+ list: (serviceId: number) => Promise<EnvVar[]>;
13
+ create: (serviceId: number, input: UpsertEnvVarInput) => Promise<EnvVar>;
14
+ update: (serviceId: number, varId: number, input: UpsertEnvVarInput) => Promise<EnvVar>;
15
+ remove: (serviceId: number, varId: number) => Promise<void>;
16
+ /** Bulk import from a .env-formatted string. Returns imported/skipped counts. */
17
+ import: (serviceId: number, content: string) => Promise<{ imported: number; skipped: number; errors: Array<{ line: number; message: string }> }>;
18
+ /** Export non-secret env vars as a .env-formatted string (admin floor). */
19
+ export: (serviceId: number) => Promise<{ content: string; count: number }>;
20
+ /** Cross-scope key search: where a given env key is defined. */
21
+ search: (q: string) => Promise<{ results: Array<{ key: string; isSecret: boolean; scope: string; serviceId: number | null; serviceName: string | null }> }>;
22
+ };
23
+ projectEnv: {
24
+ /** Shared env vars applied to every service in a project (service env wins). */
25
+ list: (projectId: number) => Promise<EnvVar[]>;
26
+ create: (projectId: number, input: UpsertEnvVarInput) => Promise<EnvVar>;
27
+ update: (projectId: number, varId: number, input: UpsertEnvVarInput) => Promise<EnvVar>;
28
+ remove: (projectId: number, varId: number) => Promise<void>;
29
+ };
30
+ /**
31
+ * 0.12 preview-only env: values a service hands ONLY to its PR previews
32
+ * (they override the parent's non-secret values there; production never
33
+ * sees them). Same floors as `env`: viewer reads, member writes. A panel
34
+ * older than 0.12 answers 404.
35
+ */
36
+ previewEnv: {
37
+ list: (serviceId: number) => Promise<EnvVar[]>;
38
+ create: (serviceId: number, input: UpsertEnvVarInput) => Promise<EnvVar>;
39
+ update: (serviceId: number, varId: number, input: UpsertEnvVarInput) => Promise<EnvVar>;
40
+ remove: (serviceId: number, varId: number) => Promise<void>;
41
+ };
42
+ }
43
+
44
+ export function envApi(t: Transport): EnvApi {
45
+ const { get, send, request } = t;
46
+ return {
47
+ env: {
48
+ list: (serviceId) => get<EnvVar[]>(`/v1/services/${serviceId}/env`),
49
+ create: (serviceId, input) => send<EnvVar>('POST', `/v1/services/${serviceId}/env`, input),
50
+ update: (serviceId, varId, input) => send<EnvVar>('PATCH', `/v1/services/${serviceId}/env/${varId}`, input),
51
+ remove: async (serviceId, varId) => {
52
+ await request(`/v1/services/${serviceId}/env/${varId}`, { method: 'DELETE' });
53
+ },
54
+ import: (serviceId, content) =>
55
+ send<{ imported: number; skipped: number; errors: Array<{ line: number; message: string }> }>(
56
+ 'POST',
57
+ `/v1/services/${serviceId}/env/import`,
58
+ { content },
59
+ ),
60
+ export: (serviceId) =>
61
+ get<{ content: string; count: number }>(`/v1/services/${serviceId}/env/export`),
62
+ search: (q) =>
63
+ get<{ results: Array<{ key: string; isSecret: boolean; scope: string; serviceId: number | null; serviceName: string | null }> }>(
64
+ `/v1/env/search?q=${encodeURIComponent(q)}`,
65
+ ),
66
+ },
67
+ projectEnv: {
68
+ list: (projectId) => get<EnvVar[]>(`/v1/projects/${projectId}/env`),
69
+ create: (projectId, input) => send<EnvVar>('POST', `/v1/projects/${projectId}/env`, input),
70
+ update: (projectId, varId, input) => send<EnvVar>('PATCH', `/v1/projects/${projectId}/env/${varId}`, input),
71
+ remove: async (projectId, varId) => {
72
+ await request(`/v1/projects/${projectId}/env/${varId}`, { method: 'DELETE' });
73
+ },
74
+ },
75
+ previewEnv: {
76
+ list: (serviceId) => get<EnvVar[]>(`/v1/services/${serviceId}/env/preview`),
77
+ create: (serviceId, input) => send<EnvVar>('POST', `/v1/services/${serviceId}/env/preview`, input),
78
+ update: (serviceId, varId, input) => send<EnvVar>('PATCH', `/v1/services/${serviceId}/env/preview/${varId}`, input),
79
+ remove: async (serviceId, varId) => {
80
+ await request(`/v1/services/${serviceId}/env/preview/${varId}`, { method: 'DELETE' });
81
+ },
82
+ },
83
+ };
84
+ }
@@ -0,0 +1,48 @@
1
+ // Client surface: logDrains.
2
+ // Split out of the former single-file SDK entry (src/index.ts); the public
3
+ // surface is re-exported unchanged from there.
4
+ import type {
5
+ LogDrain,
6
+ LogDrainCreateInput,
7
+ LogDrainUpdateInput,
8
+ LogDrainTestResult,
9
+ } from '@ninedeploy/schemas';
10
+ import type { LogSearchInput, LogSearchResult } from '../types.js';
11
+ import type { Transport } from '../transport.js';
12
+
13
+ export interface LogsApi {
14
+ logDrains: {
15
+ list: (query?: { serviceId?: number }) => Promise<LogDrain[]>;
16
+ get: (id: number) => Promise<LogDrain>;
17
+ create: (input: LogDrainCreateInput) => Promise<LogDrain>;
18
+ update: (id: number, input: LogDrainUpdateInput) => Promise<LogDrain>;
19
+ remove: (id: number) => Promise<{ ok: boolean }>;
20
+ test: (id: number) => Promise<LogDrainTestResult>;
21
+ /**
22
+ * Search the configured Loki drain for `query` over
23
+ * the last `sinceMinutes` (default 15). The route
24
+ * round-trips to the drain's `url` so the upstream
25
+ * does the heavy lifting; this is the same shape
26
+ * the operator sees in the Loki Grafana panel.
27
+ */
28
+ search: (input: LogSearchInput) => Promise<LogSearchResult>;
29
+ };
30
+ }
31
+
32
+ export function logsApi(t: Transport): LogsApi {
33
+ const { get, send } = t;
34
+ return {
35
+ logDrains: {
36
+ list: (query) => {
37
+ const qs = query?.serviceId !== undefined ? `?serviceId=${encodeURIComponent(String(query.serviceId))}` : '';
38
+ return get<LogDrain[]>(`/v1/log-drains${qs}`);
39
+ },
40
+ get: (id) => get<LogDrain>(`/v1/log-drains/${id}`),
41
+ create: (input) => send<LogDrain>('POST', '/v1/log-drains', input),
42
+ update: (id, input) => send<LogDrain>('PATCH', `/v1/log-drains/${id}`, input),
43
+ remove: (id) => send<{ ok: boolean }>('DELETE', `/v1/log-drains/${id}`),
44
+ test: (id) => send<LogDrainTestResult>('POST', `/v1/log-drains/${id}/test`),
45
+ search: (input) => send<LogSearchResult>('POST', '/v1/log-drains/search', input),
46
+ },
47
+ };
48
+ }
@@ -0,0 +1,126 @@
1
+ // Client surface: networks, tunnels, firewall, egress.
2
+ // Split out of the former single-file SDK entry (src/index.ts); the public
3
+ // surface is re-exported unchanged from there.
4
+ import type {
5
+ CreateTunnelInput,
6
+ TunnelEntry,
7
+ } from '@ninedeploy/schemas';
8
+ import type { Transport } from '../transport.js';
9
+
10
+ export interface NetworkApi {
11
+ networks: {
12
+ list: () => Promise<{
13
+ networks: Array<{
14
+ name: string;
15
+ driver: string;
16
+ members: string[];
17
+ /** True when the network is reserved by NineDeploy (cannot be removed). */
18
+ isManaged?: boolean;
19
+ }>;
20
+ remote: number | null;
21
+ }>;
22
+ create: (input: { name: string; driver?: 'bridge' | 'overlay'; serverId?: number | null }) => Promise<{ ok: boolean; name: string }>;
23
+ remove: (name: string, serverId?: number) => Promise<{ ok: boolean }>;
24
+ attach: (input: { network: string; container: string; serverId?: number | null }) => Promise<{ ok: boolean }>;
25
+ detach: (input: { network: string; container: string; serverId?: number | null }) => Promise<{ ok: boolean }>;
26
+ /**
27
+ * r557: container names attached to one network (operator-only). The
28
+ * server answers `{ members: [] }` when docker cannot inspect it.
29
+ */
30
+ members: (name: string) => Promise<{ members: string[] }>;
31
+ };
32
+ tunnels: {
33
+ list: () => Promise<TunnelEntry[]>;
34
+ create: (input: CreateTunnelInput) => Promise<TunnelEntry>;
35
+ remove: (id: number) => Promise<void>;
36
+ };
37
+ firewall: {
38
+ status: () => Promise<{
39
+ installed: boolean;
40
+ active: boolean;
41
+ supported: boolean;
42
+ rules: Array<{ id: number; to: string; action: string; from: string; comment?: string }>;
43
+ defaultIncoming: string;
44
+ defaultOutgoing: string;
45
+ }>;
46
+ toggle: (enabled: boolean) => Promise<{ ok: boolean; status: any }>;
47
+ addRule: (input: { port: number | string; proto?: 'tcp' | 'udp' | 'any'; action?: 'allow' | 'deny' | 'limit'; from?: string; comment?: string }) => Promise<{ ok: boolean; status: any }>;
48
+ deleteRule: (id: number | string) => Promise<{ ok: boolean; status: any }>;
49
+ applyRecommended: () => Promise<{ ok: boolean; status: any }>;
50
+ };
51
+ egress: {
52
+ /** Every egress IP rule across every registered driver. G-15. */
53
+ list: () => Promise<{
54
+ drivers: Array<{
55
+ name: string;
56
+ rules: Array<{
57
+ selector: { projectId: number; sourceCidr?: string };
58
+ ip: string;
59
+ createdAt: string;
60
+ }>;
61
+ }>;
62
+ }>;
63
+ /** Attach an egress IP to a project on the named (or first) driver. */
64
+ set: (input: { projectId: number; ip: string; driver?: string }) => Promise<{
65
+ ok: boolean;
66
+ driver: string;
67
+ rule: { selector: { projectId: number; sourceCidr?: string }; ip: string; createdAt: string };
68
+ /** Present (with `ok: false`) when the route refused the request. */
69
+ error?: string;
70
+ }>;
71
+ /** Detach the rule for a project (on the named or first driver). */
72
+ clear: (projectId: number, driver?: string) => Promise<{ ok: boolean; driver: string; error?: string }>;
73
+ };
74
+ }
75
+
76
+ export function networkApi(t: Transport): NetworkApi {
77
+ const { get, send, request } = t;
78
+ return {
79
+ networks: {
80
+ list: () => get<{ networks: Array<{ name: string; driver: string; members: string[] }>; remote: number | null }>('/v1/networks'),
81
+ create: (input) => send<{ ok: boolean; name: string }>('POST', '/v1/networks', { driver: 'bridge', ...input }),
82
+ remove: (name, serverId) =>
83
+ send<{ ok: boolean }>('DELETE', `/v1/networks/${encodeURIComponent(name)}${serverId != null ? `?serverId=${serverId}` : ''}`),
84
+ attach: (input) => send<{ ok: boolean }>('POST', '/v1/networks/attach', input),
85
+ detach: (input) => send<{ ok: boolean }>('POST', '/v1/networks/detach', input),
86
+ members: (name) => get<{ members: string[] }>(`/v1/networks/${encodeURIComponent(name)}/members`),
87
+ },
88
+ tunnels: {
89
+ list: () => get<TunnelEntry[]>('/v1/tunnels'),
90
+ create: (input) => send<TunnelEntry>('POST', '/v1/tunnels', input),
91
+ remove: async (id) => {
92
+ await request(`/v1/tunnels/${id}`, { method: 'DELETE' });
93
+ },
94
+ },
95
+ firewall: {
96
+ status: () => get<any>('/v1/firewall'),
97
+ toggle: (enabled) => send<any>('POST', '/v1/firewall/toggle', { enabled }),
98
+ addRule: (input) => send<any>('POST', '/v1/firewall/rules', input),
99
+ deleteRule: (id) => request(`/v1/firewall/rules/${id}`, { method: 'DELETE' }),
100
+ applyRecommended: () => send<any>('POST', '/v1/firewall/recommended'),
101
+ },
102
+ egress: {
103
+ list: () => get<{
104
+ drivers: Array<{
105
+ name: string;
106
+ rules: Array<{
107
+ selector: { projectId: number; sourceCidr?: string };
108
+ ip: string;
109
+ createdAt: string;
110
+ }>;
111
+ }>;
112
+ }>('/v1/egress'),
113
+ set: (input) => send<{
114
+ ok: boolean;
115
+ driver: string;
116
+ rule: { selector: { projectId: number; sourceCidr?: string }; ip: string; createdAt: string };
117
+ error?: string;
118
+ }>('POST', '/v1/egress', input),
119
+ clear: (projectId, driver) =>
120
+ send<{ ok: boolean; driver: string; error?: string }>(
121
+ 'DELETE',
122
+ `/v1/egress/${projectId}${driver ? `?driver=${encodeURIComponent(driver)}` : ''}`,
123
+ ),
124
+ },
125
+ };
126
+ }
@@ -0,0 +1,52 @@
1
+ // Client surface: plugins, menus.
2
+ // Split out of the former single-file SDK entry (src/index.ts); the public
3
+ // surface is re-exported unchanged from there.
4
+ import type {
5
+ PluginListResponse,
6
+ PluginInspectResponse,
7
+ InstallPluginInput,
8
+ MarketplaceCatalogResponse,
9
+ MenuListResponse,
10
+ } from '@ninedeploy/schemas';
11
+ import type { Transport } from '../transport.js';
12
+
13
+ export interface PluginsApi {
14
+ plugins: {
15
+ list: () => Promise<PluginListResponse>;
16
+ marketplace: (opts?: { refresh?: boolean }) => Promise<MarketplaceCatalogResponse>;
17
+ install: (input: InstallPluginInput) => Promise<{ ok: boolean; id: string; status: string }>;
18
+ enable: (id: string) => Promise<{ ok: boolean; id: string; status: string }>;
19
+ disable: (id: string) => Promise<{ ok: boolean; id: string; status: string }>;
20
+ reload: (id: string) => Promise<{ ok: boolean; id: string; status: string }>;
21
+ inspect: (id: string) => Promise<PluginInspectResponse>;
22
+ uninstall: (id: string) => Promise<{ ok: boolean; id: string }>;
23
+ };
24
+ menus: {
25
+ list: (query?: { slot?: string }) => Promise<MenuListResponse>;
26
+ };
27
+ }
28
+
29
+ export function pluginsApi(t: Transport): PluginsApi {
30
+ const { get, send } = t;
31
+ return {
32
+ plugins: {
33
+ list: () => get<PluginListResponse>('/v1/plugins'),
34
+ marketplace: (opts) =>
35
+ get<MarketplaceCatalogResponse>(
36
+ `/v1/plugins/marketplace${opts?.refresh ? '?refresh=true' : ''}`,
37
+ ),
38
+ install: (input) => send<{ ok: boolean; id: string; status: string }>('POST', '/v1/plugins/install', input),
39
+ enable: (id) => send<{ ok: boolean; id: string; status: string }>('POST', `/v1/plugins/${encodeURIComponent(id)}/enable`),
40
+ disable: (id) => send<{ ok: boolean; id: string; status: string }>('POST', `/v1/plugins/${encodeURIComponent(id)}/disable`),
41
+ reload: (id) => send<{ ok: boolean; id: string; status: string }>('POST', `/v1/plugins/${encodeURIComponent(id)}/reload`),
42
+ inspect: (id) => get<PluginInspectResponse>(`/v1/plugins/${encodeURIComponent(id)}/inspect`),
43
+ uninstall: (id) => send<{ ok: boolean; id: string }>('POST', `/v1/plugins/${encodeURIComponent(id)}/uninstall`),
44
+ },
45
+ menus: {
46
+ list: (query) => {
47
+ const qs = query?.slot ? `?slot=${encodeURIComponent(query.slot)}` : '';
48
+ return get<MenuListResponse>(`/v1/menus${qs}`);
49
+ },
50
+ },
51
+ };
52
+ }