@promptbook/cli 0.112.0-96 → 0.112.0-98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/agents-server/playwright.config.ts +2 -1
- package/apps/agents-server/src/app/admin/cli-access/CliAccessClient.tsx +99 -0
- package/apps/agents-server/src/app/admin/cli-access/page.tsx +14 -0
- package/apps/agents-server/src/app/admin/code-runners/CodeRunnersClient.tsx +124 -34
- package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +46 -505
- package/apps/agents-server/src/app/admin/servers/ServersClient.tsx +23 -11
- package/apps/agents-server/src/app/admin/servers/ServersRegistryApi.ts +5 -0
- package/apps/agents-server/src/app/admin/servers/ServersRegistryDnsTypes.ts +87 -0
- package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +258 -128
- package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +46 -334
- package/apps/agents-server/src/app/admin/servers/useServersRegistryState.ts +26 -2
- package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +435 -0
- package/apps/agents-server/src/app/admin/update/page.tsx +14 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +24 -0
- package/apps/agents-server/src/app/api/admin/cli-access/route.ts +137 -0
- package/apps/agents-server/src/app/api/admin/code-runners/authentication/route.ts +140 -0
- package/apps/agents-server/src/app/api/admin/code-runners/route.ts +4 -35
- package/apps/agents-server/src/app/api/admin/servers/[serverId]/route.ts +7 -2
- package/apps/agents-server/src/app/api/admin/servers/route.ts +95 -4
- package/apps/agents-server/src/app/api/admin/update/route.ts +52 -0
- package/apps/agents-server/src/app/api/auth/login/route.ts +8 -0
- package/apps/agents-server/src/app/api/auth/logout/route.ts +10 -2
- package/apps/agents-server/src/app/api/chat/export/pdf/route.ts +63 -0
- package/apps/agents-server/src/app/page.tsx +10 -0
- package/apps/agents-server/src/components/AdminTerminal/AdminTerminalCard.tsx +279 -0
- package/apps/agents-server/src/components/AdminTerminal/useAdminTerminalSession.ts +336 -0
- package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +10 -0
- package/apps/agents-server/src/languages/ServerTranslationKeys.ts +2 -0
- package/apps/agents-server/src/languages/translations/czech.yaml +2 -0
- package/apps/agents-server/src/languages/translations/english.yaml +2 -0
- package/apps/agents-server/src/middleware.ts +32 -0
- package/apps/agents-server/src/tools/BrowserConnectionProvider.ts +1 -1
- package/apps/agents-server/src/utils/chatExport/downloadChatPdfFromServer.ts +59 -0
- package/apps/agents-server/src/utils/chatExport/renderHtmlToPdfOnServer.ts +37 -0
- package/apps/agents-server/src/utils/codeRunnerAuthentication.ts +234 -0
- package/apps/agents-server/src/utils/codeRunnerConfiguration.ts +67 -0
- package/apps/agents-server/src/utils/createInteractiveTerminalEventStream.ts +84 -0
- package/apps/agents-server/src/utils/interactiveTerminalSession.ts +442 -0
- package/apps/agents-server/src/utils/serverCliAccess.ts +221 -0
- package/apps/agents-server/src/utils/serverManagement/standaloneVpsServerMetadata.ts +145 -0
- package/apps/agents-server/src/utils/serverRegistry.ts +3 -2
- package/apps/agents-server/src/utils/session.ts +37 -9
- package/apps/agents-server/src/utils/shibboleth/createShibbolethAuthenticationLogPayload.ts +173 -0
- package/apps/agents-server/src/utils/shibboleth/writeShibbolethAuthenticationLog.ts +27 -0
- package/apps/agents-server/src/utils/standaloneVpsDnsDiagnostics.ts +258 -0
- package/apps/agents-server/src/utils/standaloneVpsRawIpBootstrap.ts +87 -0
- package/apps/agents-server/src/utils/vpsConfiguration.ts +87 -13
- package/apps/agents-server/src/utils/vpsSelfUpdate.ts +664 -0
- package/esm/apps/agents-server/src/utils/serverRegistry.d.ts +1 -1
- package/esm/index.es.js +7 -5
- package/esm/index.es.js.map +1 -1
- package/esm/src/book-components/Chat/Chat/ChatActionsBar.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
- package/esm/src/book-components/Chat/save/_common/ChatSaveFormatHandler.d.ts +35 -0
- package/esm/src/book-components/Chat/save/_common/createChatExportFilename.d.ts +11 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/book-components/Chat/Chat/Chat.tsx +2 -0
- package/src/book-components/Chat/Chat/ChatActionsBar.tsx +17 -9
- package/src/book-components/Chat/Chat/ChatProps.tsx +7 -0
- package/src/book-components/Chat/save/_common/ChatSaveFormatHandler.ts +40 -0
- package/src/book-components/Chat/save/_common/createChatExportFilename.ts +20 -0
- package/src/book-components/Chat/utils/renderMarkdown.ts +1 -3
- package/src/other/templates/getTemplatesPipelineCollection.ts +718 -790
- package/src/scrapers/document/DocumentScraper.ts +1 -1
- package/src/scrapers/document-legacy/LegacyDocumentScraper.ts +1 -1
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -0
- package/umd/apps/agents-server/src/utils/serverRegistry.d.ts +1 -1
- package/umd/index.umd.js +7 -5
- package/umd/index.umd.js.map +1 -1
- package/umd/src/book-components/Chat/Chat/ChatActionsBar.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
- package/umd/src/book-components/Chat/save/_common/ChatSaveFormatHandler.d.ts +35 -0
- package/umd/src/book-components/Chat/save/_common/createChatExportFilename.d.ts +11 -0
- package/umd/src/version.d.ts +1 -1
- package/src/conversion/validation/_importPipeline.ts +0 -88
- /package/esm/src/conversion/validation/{_importPipeline.d.ts → _importPipeline.test.d.ts} +0 -0
- /package/umd/src/conversion/validation/{_importPipeline.d.ts → _importPipeline.test.d.ts} +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { lookup } from 'dns/promises';
|
|
2
|
+
import type {
|
|
3
|
+
ManagedServerDnsDiagnostic,
|
|
4
|
+
ManagedServerDnsExpectedRecord,
|
|
5
|
+
ManagedServerDnsProviderGuide,
|
|
6
|
+
ManagedServerDnsStatus,
|
|
7
|
+
} from '../app/admin/servers/ServersRegistryDnsTypes';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolver signature used for DNS lookups.
|
|
11
|
+
*/
|
|
12
|
+
type ResolveDnsAddresses = (domain: string) => Promise<ReadonlyArray<string>>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Input used to create one standalone VPS DNS diagnostic.
|
|
16
|
+
*/
|
|
17
|
+
type CreateStandaloneVpsDomainDnsDiagnosticOptions = {
|
|
18
|
+
/**
|
|
19
|
+
* Domain currently configured in the standalone VPS server registry.
|
|
20
|
+
*/
|
|
21
|
+
readonly domain: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Public IP address detected/stored for the VPS runtime.
|
|
25
|
+
*/
|
|
26
|
+
readonly publicIpAddress: string | null | undefined;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Optional already-working hostname that subdomains may target via CNAME.
|
|
30
|
+
*/
|
|
31
|
+
readonly fallbackCnameTargetDomain?: string | null | undefined;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Optional resolver override used by unit tests.
|
|
35
|
+
*/
|
|
36
|
+
readonly resolveDnsAddresses?: ResolveDnsAddresses;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Provider guides shown together with DNS record instructions.
|
|
41
|
+
*/
|
|
42
|
+
const DNS_PROVIDER_GUIDES: ReadonlyArray<ManagedServerDnsProviderGuide> = [
|
|
43
|
+
{
|
|
44
|
+
label: 'Cloudflare DNS records',
|
|
45
|
+
href: 'https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: 'GoDaddy DNS records',
|
|
49
|
+
href: 'https://www.godaddy.com/help/manage-dns-records-680',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'Namecheap DNS records',
|
|
53
|
+
href: 'https://www.namecheap.com/support/knowledgebase/article.aspx/319/2237/how-can-i-set-up-an-a-address-record-for-my-domain/',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'Squarespace DNS records',
|
|
57
|
+
href: 'https://support.squarespace.com/hc/en-us/articles/360002101888-Adding-custom-DNS-records-to-your-Squarespace-managed-domain',
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* DNS error codes treated as "not resolving yet" during propagation.
|
|
63
|
+
*/
|
|
64
|
+
const DNS_PENDING_ERROR_CODES = new Set(['ENOTFOUND', 'ENODATA', 'EAI_AGAIN', 'ESERVFAIL']);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates the browser-safe DNS status for one standalone VPS domain.
|
|
68
|
+
*
|
|
69
|
+
* @param options - Domain, VPS IP, and optional resolver override.
|
|
70
|
+
* @returns DNS diagnostic rendered on `/admin/servers`.
|
|
71
|
+
*/
|
|
72
|
+
export async function createStandaloneVpsDomainDnsDiagnostic(
|
|
73
|
+
options: CreateStandaloneVpsDomainDnsDiagnosticOptions,
|
|
74
|
+
): Promise<ManagedServerDnsDiagnostic> {
|
|
75
|
+
const publicIpAddress = normalizePublicIpAddress(options.publicIpAddress);
|
|
76
|
+
const expectedRecords = createExpectedDnsRecords({
|
|
77
|
+
domain: options.domain,
|
|
78
|
+
publicIpAddress,
|
|
79
|
+
fallbackCnameTargetDomain: options.fallbackCnameTargetDomain,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (!publicIpAddress) {
|
|
83
|
+
return createDnsDiagnostic({
|
|
84
|
+
expectedRecords,
|
|
85
|
+
publicIpAddress: null,
|
|
86
|
+
resolvedAddresses: [],
|
|
87
|
+
status: 'unavailable',
|
|
88
|
+
summary: 'The VPS public IP address is not available yet, so DNS cannot be verified automatically.',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const resolvedAddresses = uniqueStrings(
|
|
94
|
+
await (options.resolveDnsAddresses || resolveDnsAddresses)(options.domain),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (resolvedAddresses.includes(publicIpAddress)) {
|
|
98
|
+
return createDnsDiagnostic({
|
|
99
|
+
expectedRecords,
|
|
100
|
+
publicIpAddress,
|
|
101
|
+
resolvedAddresses,
|
|
102
|
+
status: 'verified',
|
|
103
|
+
summary: `DNS is ready. \`${options.domain}\` resolves to this VPS.`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (resolvedAddresses.length === 0) {
|
|
108
|
+
return createDnsDiagnostic({
|
|
109
|
+
expectedRecords,
|
|
110
|
+
publicIpAddress,
|
|
111
|
+
resolvedAddresses,
|
|
112
|
+
status: 'pending',
|
|
113
|
+
summary: `\`${options.domain}\` does not resolve yet. Add the record below and wait for DNS propagation.`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return createDnsDiagnostic({
|
|
118
|
+
expectedRecords,
|
|
119
|
+
publicIpAddress,
|
|
120
|
+
resolvedAddresses,
|
|
121
|
+
status: 'misconfigured',
|
|
122
|
+
summary: `\`${options.domain}\` currently resolves to ${resolvedAddresses.join(', ')}, not to this VPS IP \`${publicIpAddress}\`.`,
|
|
123
|
+
});
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return createDnsDiagnostic({
|
|
126
|
+
expectedRecords,
|
|
127
|
+
publicIpAddress,
|
|
128
|
+
resolvedAddresses: [],
|
|
129
|
+
status: 'unavailable',
|
|
130
|
+
summary: `DNS verification failed: ${error instanceof Error ? error.message : 'Unknown DNS lookup error.'}`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Resolves one hostname to all currently known IP addresses.
|
|
137
|
+
*
|
|
138
|
+
* @param domain - Domain to resolve.
|
|
139
|
+
* @returns Unique list of resolved IP addresses.
|
|
140
|
+
*/
|
|
141
|
+
async function resolveDnsAddresses(domain: string): Promise<ReadonlyArray<string>> {
|
|
142
|
+
try {
|
|
143
|
+
return (await lookup(domain, { all: true, verbatim: true })).map((record) => record.address);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const code = typeof error === 'object' && error !== null && 'code' in error ? String(error.code) : null;
|
|
146
|
+
|
|
147
|
+
if (code && DNS_PENDING_ERROR_CODES.has(code)) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Creates the expected DNS records shown in the admin UI.
|
|
157
|
+
*
|
|
158
|
+
* @param options - Record inputs.
|
|
159
|
+
* @returns DNS records the admin can copy into their provider.
|
|
160
|
+
*/
|
|
161
|
+
function createExpectedDnsRecords(options: {
|
|
162
|
+
readonly domain: string;
|
|
163
|
+
readonly publicIpAddress: string | null;
|
|
164
|
+
readonly fallbackCnameTargetDomain?: string | null | undefined;
|
|
165
|
+
}): ReadonlyArray<ManagedServerDnsExpectedRecord> {
|
|
166
|
+
const expectedRecords: Array<ManagedServerDnsExpectedRecord> = [];
|
|
167
|
+
|
|
168
|
+
if (options.publicIpAddress) {
|
|
169
|
+
expectedRecords.push({
|
|
170
|
+
type: isIpv6Address(options.publicIpAddress) ? 'AAAA' : 'A',
|
|
171
|
+
name: options.domain,
|
|
172
|
+
value: options.publicIpAddress,
|
|
173
|
+
note: 'Recommended. Point this hostname directly to the VPS public IP address.',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (options.fallbackCnameTargetDomain && options.fallbackCnameTargetDomain !== options.domain) {
|
|
178
|
+
expectedRecords.push({
|
|
179
|
+
type: 'CNAME',
|
|
180
|
+
name: options.domain,
|
|
181
|
+
value: options.fallbackCnameTargetDomain,
|
|
182
|
+
note: `Optional alternative for subdomains only. Use this only when \`${options.fallbackCnameTargetDomain}\` already works on this VPS.`,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return expectedRecords;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Normalizes the stored VPS public IP address.
|
|
191
|
+
*
|
|
192
|
+
* @param value - Raw environment/config value.
|
|
193
|
+
* @returns Normalized IP address or `null` when unavailable.
|
|
194
|
+
*/
|
|
195
|
+
function normalizePublicIpAddress(value: string | null | undefined): string | null {
|
|
196
|
+
const normalizedValue = (value || '').trim();
|
|
197
|
+
|
|
198
|
+
if (!normalizedValue || normalizedValue === 'localhost') {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return normalizedValue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Creates one consistent browser payload for the DNS diagnostic.
|
|
207
|
+
*
|
|
208
|
+
* @param options - Normalized DNS state.
|
|
209
|
+
* @returns Browser-safe DNS payload.
|
|
210
|
+
*/
|
|
211
|
+
function createDnsDiagnostic(options: {
|
|
212
|
+
readonly expectedRecords: ReadonlyArray<ManagedServerDnsExpectedRecord>;
|
|
213
|
+
readonly publicIpAddress: string | null;
|
|
214
|
+
readonly resolvedAddresses: ReadonlyArray<string>;
|
|
215
|
+
readonly status: ManagedServerDnsStatus;
|
|
216
|
+
readonly summary: string;
|
|
217
|
+
}): ManagedServerDnsDiagnostic {
|
|
218
|
+
return {
|
|
219
|
+
status: options.status,
|
|
220
|
+
summary: options.summary,
|
|
221
|
+
publicIpAddress: options.publicIpAddress,
|
|
222
|
+
resolvedAddresses: options.resolvedAddresses,
|
|
223
|
+
expectedRecords: options.expectedRecords,
|
|
224
|
+
providerGuides: DNS_PROVIDER_GUIDES,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Detects whether one resolved address is IPv6.
|
|
230
|
+
*
|
|
231
|
+
* @param value - Candidate IP address.
|
|
232
|
+
* @returns `true` when the address looks like IPv6.
|
|
233
|
+
*/
|
|
234
|
+
function isIpv6Address(value: string): boolean {
|
|
235
|
+
return value.includes(':');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Removes empty values and duplicates while preserving order.
|
|
240
|
+
*
|
|
241
|
+
* @param values - Candidate values.
|
|
242
|
+
* @returns Stable unique list.
|
|
243
|
+
*/
|
|
244
|
+
function uniqueStrings(values: ReadonlyArray<string>): Array<string> {
|
|
245
|
+
const uniqueValues: Array<string> = [];
|
|
246
|
+
|
|
247
|
+
for (const value of values) {
|
|
248
|
+
const normalizedValue = value.trim();
|
|
249
|
+
|
|
250
|
+
if (!normalizedValue || uniqueValues.includes(normalizedValue)) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
uniqueValues.push(normalizedValue);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return uniqueValues;
|
|
258
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-independent context used to decide whether standalone VPS setup should
|
|
3
|
+
* continue allowing raw-IP bootstrap access.
|
|
4
|
+
*/
|
|
5
|
+
export type StandaloneVpsRawIpBootstrapContext = {
|
|
6
|
+
/**
|
|
7
|
+
* Current canonical public site URL stored in the environment.
|
|
8
|
+
*/
|
|
9
|
+
readonly nextPublicSiteUrl: string | null | undefined;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Known public IPv4/IPv6 address of the standalone VPS.
|
|
13
|
+
*/
|
|
14
|
+
readonly publicIpAddress: string | null | undefined;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns whether the standalone VPS should keep allowing raw-IP bootstrap access.
|
|
19
|
+
*
|
|
20
|
+
* This stays enabled while the canonical public site URL still points to the VPS
|
|
21
|
+
* raw IP over plain HTTP, which means domain activation has not completed yet.
|
|
22
|
+
*/
|
|
23
|
+
export function isStandaloneVpsRawIpBootstrapActive(context: StandaloneVpsRawIpBootstrapContext): boolean {
|
|
24
|
+
const parsedPublicSiteUrl = parseAbsoluteHttpUrl(context.nextPublicSiteUrl);
|
|
25
|
+
if (!parsedPublicSiteUrl || parsedPublicSiteUrl.protocol !== 'http:') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const normalizedSiteHost = normalizeHost(parsedPublicSiteUrl.host);
|
|
30
|
+
if (!normalizedSiteHost || !isIpAddressHost(normalizedSiteHost)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const normalizedConfiguredPublicIpAddress = normalizeHost(context.publicIpAddress || '');
|
|
35
|
+
if (normalizedConfiguredPublicIpAddress && normalizedSiteHost !== normalizedConfiguredPublicIpAddress) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Parses one absolute HTTP(S) URL from environment configuration.
|
|
44
|
+
*
|
|
45
|
+
* @param value - Raw environment value.
|
|
46
|
+
* @returns Parsed URL or `null` when missing/invalid.
|
|
47
|
+
*/
|
|
48
|
+
function parseAbsoluteHttpUrl(value: string | null | undefined): URL | null {
|
|
49
|
+
if (!value) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const parsedUrl = new URL(value);
|
|
55
|
+
|
|
56
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return parsedUrl;
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Removes ports and IPv6 brackets from host-like strings.
|
|
68
|
+
*
|
|
69
|
+
* @param host - Raw host header value.
|
|
70
|
+
* @returns Normalized bare hostname or IP address.
|
|
71
|
+
*/
|
|
72
|
+
function normalizeHost(host: string | null | undefined): string {
|
|
73
|
+
return (host || '')
|
|
74
|
+
.trim()
|
|
75
|
+
.replace(/^\[(.+)\](?::\d+)?$/u, '$1')
|
|
76
|
+
.replace(/:\d+$/u, '');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Checks whether a host string points to a raw IPv4 or IPv6 address.
|
|
81
|
+
*
|
|
82
|
+
* @param host - Host header or hostname.
|
|
83
|
+
* @returns `true` when the host is a raw IP address.
|
|
84
|
+
*/
|
|
85
|
+
function isIpAddressHost(host: string): boolean {
|
|
86
|
+
return /^\d{1,3}(?:\.\d{1,3}){3}$/u.test(host) || host.includes(':');
|
|
87
|
+
}
|
|
@@ -6,6 +6,7 @@ import { promisify } from 'util';
|
|
|
6
6
|
import { spaceTrim } from 'spacetrim';
|
|
7
7
|
import { NotAllowed } from '../../../../src/errors/NotAllowed';
|
|
8
8
|
import { normalizeServerDomain } from './serverRegistry';
|
|
9
|
+
import { isStandaloneVpsRawIpBootstrapActive } from './standaloneVpsRawIpBootstrap';
|
|
9
10
|
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
11
12
|
|
|
@@ -87,6 +88,21 @@ export type VpsCommandResult = {
|
|
|
87
88
|
readonly output: string;
|
|
88
89
|
};
|
|
89
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Runtime-installer execution options used by VPS admin actions.
|
|
93
|
+
*/
|
|
94
|
+
type RunVpsInstallerCommandOptions = {
|
|
95
|
+
/**
|
|
96
|
+
* Whether the installer should disable interactive prompts.
|
|
97
|
+
*/
|
|
98
|
+
readonly isNonInteractiveModeEnabled?: boolean;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Whether the installer may restart the running Agents Server process.
|
|
102
|
+
*/
|
|
103
|
+
readonly isProcessRestartEnabled?: boolean;
|
|
104
|
+
};
|
|
105
|
+
|
|
90
106
|
/**
|
|
91
107
|
* Parsed `.env` line representation used to preserve comments and ordering.
|
|
92
108
|
*/
|
|
@@ -191,30 +207,51 @@ export async function listConfiguredVpsDomains(): Promise<Array<string>> {
|
|
|
191
207
|
return parseDomainsCsv(rawServers);
|
|
192
208
|
}
|
|
193
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Options used when updating the standalone VPS domain list.
|
|
212
|
+
*/
|
|
213
|
+
type UpdateConfiguredVpsDomainsOptions = {
|
|
214
|
+
/**
|
|
215
|
+
* Optional server-level table prefix to persist together with the domain list.
|
|
216
|
+
*/
|
|
217
|
+
readonly tablePrefix?: string | null;
|
|
218
|
+
};
|
|
219
|
+
|
|
194
220
|
/**
|
|
195
221
|
* Replaces the standalone VPS domain list in `.env`.
|
|
196
222
|
*
|
|
197
223
|
* @param domains - Domains to store in `SERVERS`.
|
|
224
|
+
* @param options - Optional server-level settings to persist.
|
|
198
225
|
* @returns Safe environment snapshot after writing.
|
|
199
226
|
*/
|
|
200
227
|
export async function updateConfiguredVpsDomains(
|
|
201
228
|
domains: ReadonlyArray<string>,
|
|
229
|
+
options?: UpdateConfiguredVpsDomainsOptions,
|
|
202
230
|
): Promise<Awaited<ReturnType<typeof listVpsEnvironmentVariables>>> {
|
|
203
231
|
const normalizedDomains = normalizeDomains(domains);
|
|
204
232
|
const primaryDomain = normalizedDomains[0] ?? '';
|
|
233
|
+
const envValues = await readVpsEnvironmentMap(resolveVpsEnvironmentFilePath());
|
|
234
|
+
const currentPublicSiteUrl = envValues.get('NEXT_PUBLIC_SITE_URL') ?? process.env.NEXT_PUBLIC_SITE_URL ?? '';
|
|
235
|
+
const publicIpAddress = envValues.get('PTBK_PUBLIC_IP_ADDRESS') ?? process.env.PTBK_PUBLIC_IP_ADDRESS ?? '';
|
|
205
236
|
const updates: Record<string, string> = {
|
|
206
237
|
SERVERS: normalizedDomains.join(','),
|
|
207
238
|
};
|
|
208
239
|
|
|
209
240
|
if (primaryDomain) {
|
|
210
|
-
updates.NEXT_PUBLIC_SITE_URL =
|
|
211
|
-
|
|
241
|
+
updates.NEXT_PUBLIC_SITE_URL = isStandaloneVpsRawIpBootstrapActive({
|
|
242
|
+
nextPublicSiteUrl: currentPublicSiteUrl,
|
|
243
|
+
publicIpAddress,
|
|
244
|
+
})
|
|
245
|
+
? currentPublicSiteUrl
|
|
246
|
+
: `https://${primaryDomain}`;
|
|
212
247
|
} else {
|
|
213
|
-
const publicIpAddress = process.env.PTBK_PUBLIC_IP_ADDRESS?.trim();
|
|
214
248
|
updates.NEXT_PUBLIC_SITE_URL = publicIpAddress
|
|
215
249
|
? `http://${publicIpAddress}`
|
|
216
250
|
: '';
|
|
217
|
-
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'tablePrefix')) {
|
|
254
|
+
updates.SUPABASE_TABLE_PREFIX = options.tablePrefix?.trim() || '';
|
|
218
255
|
}
|
|
219
256
|
|
|
220
257
|
return updateVpsEnvironmentVariables(updates);
|
|
@@ -225,8 +262,14 @@ export async function updateConfiguredVpsDomains(
|
|
|
225
262
|
*
|
|
226
263
|
* @returns Command output or a skipped reason when not running on a Linux VPS.
|
|
227
264
|
*/
|
|
228
|
-
export async function applyVpsRuntimeConfiguration(
|
|
229
|
-
|
|
265
|
+
export async function applyVpsRuntimeConfiguration(
|
|
266
|
+
options?: RunVpsInstallerCommandOptions,
|
|
267
|
+
): Promise<VpsCommandResult> {
|
|
268
|
+
return runVpsInstallerCommand(
|
|
269
|
+
'apply-domains',
|
|
270
|
+
'VPS runtime configuration can only be applied on Linux.',
|
|
271
|
+
options,
|
|
272
|
+
);
|
|
230
273
|
}
|
|
231
274
|
|
|
232
275
|
/**
|
|
@@ -270,7 +313,11 @@ export async function readVpsPm2Logs(lineCount = 200): Promise<VpsCommandResult>
|
|
|
270
313
|
* @param unavailableOutput - Message returned on non-Linux platforms.
|
|
271
314
|
* @returns Command output or unavailable reason.
|
|
272
315
|
*/
|
|
273
|
-
async function runVpsInstallerCommand(
|
|
316
|
+
async function runVpsInstallerCommand(
|
|
317
|
+
command: string,
|
|
318
|
+
unavailableOutput: string,
|
|
319
|
+
options?: RunVpsInstallerCommandOptions,
|
|
320
|
+
): Promise<VpsCommandResult> {
|
|
274
321
|
if (process.platform !== 'linux') {
|
|
275
322
|
return {
|
|
276
323
|
isAvailable: false,
|
|
@@ -288,10 +335,7 @@ async function runVpsInstallerCommand(command: string, unavailableOutput: string
|
|
|
288
335
|
|
|
289
336
|
try {
|
|
290
337
|
const { stdout, stderr } = await execFileAsync('bash', [scriptPath, command], {
|
|
291
|
-
env:
|
|
292
|
-
...process.env,
|
|
293
|
-
PTBK_NON_INTERACTIVE: '1',
|
|
294
|
-
},
|
|
338
|
+
env: createVpsInstallerCommandEnvironment(options),
|
|
295
339
|
maxBuffer: 1024 * 1024,
|
|
296
340
|
});
|
|
297
341
|
|
|
@@ -316,6 +360,31 @@ async function runVpsInstallerCommand(command: string, unavailableOutput: string
|
|
|
316
360
|
}
|
|
317
361
|
}
|
|
318
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Creates the environment passed to the shared VPS installer script.
|
|
365
|
+
*
|
|
366
|
+
* @param options - Installer execution options.
|
|
367
|
+
* @returns Child-process environment.
|
|
368
|
+
*
|
|
369
|
+
* @private internal helper of `vpsConfiguration`
|
|
370
|
+
*/
|
|
371
|
+
export function createVpsInstallerCommandEnvironment(
|
|
372
|
+
options?: RunVpsInstallerCommandOptions,
|
|
373
|
+
): NodeJS.ProcessEnv {
|
|
374
|
+
const environment: NodeJS.ProcessEnv = {
|
|
375
|
+
...process.env,
|
|
376
|
+
...(options?.isProcessRestartEnabled === false ? { PTBK_SKIP_PM2_RESTART: '1' } : {}),
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
if (options?.isNonInteractiveModeEnabled === false) {
|
|
380
|
+
delete environment.PTBK_NON_INTERACTIVE;
|
|
381
|
+
} else {
|
|
382
|
+
environment.PTBK_NON_INTERACTIVE = '1';
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return environment;
|
|
386
|
+
}
|
|
387
|
+
|
|
319
388
|
/**
|
|
320
389
|
* Returns whether an environment variable must be masked in the UI.
|
|
321
390
|
*
|
|
@@ -477,7 +546,12 @@ function parseEnvLines(content: string): Array<ParsedEnvLine> {
|
|
|
477
546
|
return { type: 'raw', raw };
|
|
478
547
|
}
|
|
479
548
|
|
|
480
|
-
const
|
|
549
|
+
const key = match[1];
|
|
550
|
+
const rawValue = match[2] ?? '';
|
|
551
|
+
if (!key) {
|
|
552
|
+
return { type: 'raw', raw };
|
|
553
|
+
}
|
|
554
|
+
|
|
481
555
|
return {
|
|
482
556
|
type: 'entry',
|
|
483
557
|
raw,
|
|
@@ -528,7 +602,7 @@ async function readOptionalTextFile(filePath: string): Promise<string> {
|
|
|
528
602
|
*
|
|
529
603
|
* @returns Script path or `null` when unavailable.
|
|
530
604
|
*/
|
|
531
|
-
async function resolveVpsInstallerScriptPath(): Promise<string | null> {
|
|
605
|
+
export async function resolveVpsInstallerScriptPath(): Promise<string | null> {
|
|
532
606
|
const candidates = [
|
|
533
607
|
process.env.PTBK_VPS_INSTALL_SCRIPT?.trim(),
|
|
534
608
|
process.env.PTBK_REPOSITORY_DIR ? join(process.env.PTBK_REPOSITORY_DIR, 'other/vps/install.sh') : '',
|