@ai-coders/installer 0.3.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 (127) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +18 -0
  3. package/README.md +64 -0
  4. package/dist/callback-page.d.ts +11 -0
  5. package/dist/callback-page.js +230 -0
  6. package/dist/callback-page.js.map +1 -0
  7. package/dist/clerk-auth.d.ts +18 -0
  8. package/dist/clerk-auth.js +296 -0
  9. package/dist/clerk-auth.js.map +1 -0
  10. package/dist/cli-command.d.ts +10 -0
  11. package/dist/cli-command.js +17 -0
  12. package/dist/cli-command.js.map +1 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +84 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/i18n.d.ts +52 -0
  17. package/dist/i18n.js +163 -0
  18. package/dist/i18n.js.map +1 -0
  19. package/dist/product-manifest.d.ts +43 -0
  20. package/dist/product-manifest.js +140 -0
  21. package/dist/product-manifest.js.map +1 -0
  22. package/dist/products/second-brain/apply.d.ts +86 -0
  23. package/dist/products/second-brain/apply.js +277 -0
  24. package/dist/products/second-brain/apply.js.map +1 -0
  25. package/dist/products/second-brain/bearer.d.ts +29 -0
  26. package/dist/products/second-brain/bearer.js +78 -0
  27. package/dist/products/second-brain/bearer.js.map +1 -0
  28. package/dist/products/second-brain/bootstrap.d.ts +33 -0
  29. package/dist/products/second-brain/bootstrap.js +39 -0
  30. package/dist/products/second-brain/bootstrap.js.map +1 -0
  31. package/dist/products/second-brain/envfile.d.ts +35 -0
  32. package/dist/products/second-brain/envfile.js +136 -0
  33. package/dist/products/second-brain/envfile.js.map +1 -0
  34. package/dist/products/second-brain/github.d.ts +77 -0
  35. package/dist/products/second-brain/github.js +195 -0
  36. package/dist/products/second-brain/github.js.map +1 -0
  37. package/dist/products/second-brain/hostinger.d.ts +140 -0
  38. package/dist/products/second-brain/hostinger.js +607 -0
  39. package/dist/products/second-brain/hostinger.js.map +1 -0
  40. package/dist/products/second-brain/i18n.d.ts +887 -0
  41. package/dist/products/second-brain/i18n.js +587 -0
  42. package/dist/products/second-brain/i18n.js.map +1 -0
  43. package/dist/products/second-brain/keys.d.ts +5 -0
  44. package/dist/products/second-brain/keys.js +16 -0
  45. package/dist/products/second-brain/keys.js.map +1 -0
  46. package/dist/products/second-brain/main.d.ts +3 -0
  47. package/dist/products/second-brain/main.js +78 -0
  48. package/dist/products/second-brain/main.js.map +1 -0
  49. package/dist/products/second-brain/ports.d.ts +34 -0
  50. package/dist/products/second-brain/ports.js +77 -0
  51. package/dist/products/second-brain/ports.js.map +1 -0
  52. package/dist/products/second-brain/recovery.d.ts +50 -0
  53. package/dist/products/second-brain/recovery.js +96 -0
  54. package/dist/products/second-brain/recovery.js.map +1 -0
  55. package/dist/products/second-brain/sealed-box.d.ts +1 -0
  56. package/dist/products/second-brain/sealed-box.js +11 -0
  57. package/dist/products/second-brain/sealed-box.js.map +1 -0
  58. package/dist/products/second-brain/server.d.ts +143 -0
  59. package/dist/products/second-brain/server.js +280 -0
  60. package/dist/products/second-brain/server.js.map +1 -0
  61. package/dist/products/second-brain/service.d.ts +26 -0
  62. package/dist/products/second-brain/service.js +806 -0
  63. package/dist/products/second-brain/service.js.map +1 -0
  64. package/dist/products/second-brain/ssh.d.ts +35 -0
  65. package/dist/products/second-brain/ssh.js +140 -0
  66. package/dist/products/second-brain/ssh.js.map +1 -0
  67. package/dist/products/second-brain/state.d.ts +27 -0
  68. package/dist/products/second-brain/state.js +60 -0
  69. package/dist/products/second-brain/state.js.map +1 -0
  70. package/dist/products/second-brain/ui/assets/index-BoXpakUt.css +2 -0
  71. package/dist/products/second-brain/ui/assets/index-MgfOpW_R.js +66 -0
  72. package/dist/products/second-brain/ui/index.html +15 -0
  73. package/dist/products/second-brain/verify.d.ts +23 -0
  74. package/dist/products/second-brain/verify.js +59 -0
  75. package/dist/products/second-brain/verify.js.map +1 -0
  76. package/dist/products/severino/apply.d.ts +87 -0
  77. package/dist/products/severino/apply.js +250 -0
  78. package/dist/products/severino/apply.js.map +1 -0
  79. package/dist/products/severino/domain.d.ts +6 -0
  80. package/dist/products/severino/domain.js +20 -0
  81. package/dist/products/severino/domain.js.map +1 -0
  82. package/dist/products/severino/envfile.d.ts +20 -0
  83. package/dist/products/severino/envfile.js +89 -0
  84. package/dist/products/severino/envfile.js.map +1 -0
  85. package/dist/products/severino/github.d.ts +77 -0
  86. package/dist/products/severino/github.js +195 -0
  87. package/dist/products/severino/github.js.map +1 -0
  88. package/dist/products/severino/hostinger.d.ts +140 -0
  89. package/dist/products/severino/hostinger.js +607 -0
  90. package/dist/products/severino/hostinger.js.map +1 -0
  91. package/dist/products/severino/i18n.d.ts +887 -0
  92. package/dist/products/severino/i18n.js +587 -0
  93. package/dist/products/severino/i18n.js.map +1 -0
  94. package/dist/products/severino/keys.d.ts +5 -0
  95. package/dist/products/severino/keys.js +16 -0
  96. package/dist/products/severino/keys.js.map +1 -0
  97. package/dist/products/severino/main.d.ts +3 -0
  98. package/dist/products/severino/main.js +78 -0
  99. package/dist/products/severino/main.js.map +1 -0
  100. package/dist/products/severino/recovery.d.ts +50 -0
  101. package/dist/products/severino/recovery.js +96 -0
  102. package/dist/products/severino/recovery.js.map +1 -0
  103. package/dist/products/severino/sealed-box.d.ts +1 -0
  104. package/dist/products/severino/sealed-box.js +11 -0
  105. package/dist/products/severino/sealed-box.js.map +1 -0
  106. package/dist/products/severino/server.d.ts +123 -0
  107. package/dist/products/severino/server.js +274 -0
  108. package/dist/products/severino/server.js.map +1 -0
  109. package/dist/products/severino/service.d.ts +28 -0
  110. package/dist/products/severino/service.js +752 -0
  111. package/dist/products/severino/service.js.map +1 -0
  112. package/dist/products/severino/slack-manifest.d.ts +3 -0
  113. package/dist/products/severino/slack-manifest.js +45 -0
  114. package/dist/products/severino/slack-manifest.js.map +1 -0
  115. package/dist/products/severino/ssh.d.ts +35 -0
  116. package/dist/products/severino/ssh.js +140 -0
  117. package/dist/products/severino/ssh.js.map +1 -0
  118. package/dist/products/severino/state.d.ts +26 -0
  119. package/dist/products/severino/state.js +61 -0
  120. package/dist/products/severino/state.js.map +1 -0
  121. package/dist/products/severino/ui/assets/index-7S8Hl4jq.js +96 -0
  122. package/dist/products/severino/ui/assets/index-D5kql1Bh.css +2 -0
  123. package/dist/products/severino/ui/index.html +15 -0
  124. package/dist/products/severino/verify.d.ts +22 -0
  125. package/dist/products/severino/verify.js +149 -0
  126. package/dist/products/severino/verify.js.map +1 -0
  127. package/package.json +53 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The install pipelines. The first install runs entirely from the local
3
+ * machine: it uploads the source tree over SFTP and builds on the VPS, so no
4
+ * GitHub credential is involved. Every effect is injected so the sequences
5
+ * are unit-testable; the server wires real implementations.
6
+ */
7
+ import type { DeployKeyPair } from "./keys.js";
8
+ import type { InstallerLanguage } from "./i18n.js";
9
+ import type { InstallerState } from "./state.js";
10
+ import type { SshCommandResult } from "./ssh.js";
11
+ import type { VerificationResult } from "./verify.js";
12
+ export type InstallStepId = "generate-key" | "connect-vps" | "authorize-key" | "write-env" | "upload-source" | "build-deploy" | "verify-key" | "create-owner" | "verify-mcp" | "save-state";
13
+ export declare const installStepIds: readonly InstallStepId[];
14
+ export declare const localDeployStepIds: readonly InstallStepId[];
15
+ export type InstallStepStatus = "pending" | "running" | "ok" | "failed" | "skipped";
16
+ export interface InstallStepReport {
17
+ readonly id: InstallStepId;
18
+ status: InstallStepStatus;
19
+ note?: string;
20
+ link?: string;
21
+ }
22
+ export interface InstallInput {
23
+ readonly language: InstallerLanguage;
24
+ readonly repository?: {
25
+ readonly owner: string;
26
+ readonly repo: string;
27
+ } | undefined;
28
+ readonly vps: {
29
+ readonly host: string;
30
+ readonly port: number;
31
+ readonly username: string;
32
+ };
33
+ readonly vpsAuth: {
34
+ readonly kind: "password";
35
+ readonly password: string;
36
+ } | {
37
+ readonly kind: "key";
38
+ readonly privateKey: string;
39
+ };
40
+ readonly domain: string;
41
+ readonly openRouterApiKey: string;
42
+ readonly ownerEmail: string;
43
+ readonly ownerPassword: string;
44
+ readonly enableBearerToken: boolean;
45
+ }
46
+ export interface LocalDeployInput {
47
+ readonly vps: {
48
+ readonly host: string;
49
+ readonly port: number;
50
+ readonly username: string;
51
+ };
52
+ readonly privateKey: string;
53
+ readonly domain: string;
54
+ }
55
+ export interface SshLike {
56
+ exec(command: string, input?: string): Promise<SshCommandResult>;
57
+ readFileIfExists(path: string): Promise<string | null>;
58
+ writeFile(path: string, content: string | Buffer, mode?: number): Promise<void>;
59
+ close(): void;
60
+ }
61
+ export interface InstallDependencies {
62
+ readonly generateKey: () => Promise<DeployKeyPair>;
63
+ readonly connectSsh: (credentials: {
64
+ host: string;
65
+ port: number;
66
+ username: string;
67
+ auth: InstallInput["vpsAuth"];
68
+ }) => Promise<SshLike>;
69
+ readonly archiveSource: () => Promise<Buffer>;
70
+ readonly isSourceDirty?: (() => Promise<boolean>) | undefined;
71
+ readonly verifyDeployment: (publicUrl: string) => Promise<VerificationResult>;
72
+ readonly saveState: (state: InstallerState) => Promise<void>;
73
+ readonly now: () => Date;
74
+ readonly sleep: (ms: number) => Promise<void>;
75
+ readonly freshDatabasePassword?: string;
76
+ readonly freshBearerToken?: string;
77
+ }
78
+ export interface InstallOutcome {
79
+ readonly ok: boolean;
80
+ readonly steps: readonly InstallStepReport[];
81
+ readonly verification?: VerificationResult;
82
+ /** Present only when the install provisioned a static bearer token. */
83
+ readonly bearerToken?: string;
84
+ }
85
+ export declare function runInstall(input: InstallInput, deps: InstallDependencies, onProgress?: (steps: readonly InstallStepReport[]) => void): Promise<InstallOutcome>;
86
+ export declare function runLocalDeploy(input: LocalDeployInput, deps: InstallDependencies, onProgress?: (steps: readonly InstallStepReport[]) => void): Promise<InstallOutcome>;
@@ -0,0 +1,277 @@
1
+ import { buildOwnerBootstrapInput, interpretBootstrapResult, ownerBootstrapCommand, waitEngineCommand, } from "./bootstrap.js";
2
+ import { provisionBearerToken } from "./bearer.js";
3
+ import { mergeVpsEnv, generateDatabasePassword, parseEnvContent, vpsEnvPath, } from "./envfile.js";
4
+ import { chooseHostPorts, isDefaultHostPorts, parsePortProbe, portProbeCommand, } from "./ports.js";
5
+ import { buildAuthorizedKeysCommand } from "./ssh.js";
6
+ export const installStepIds = [
7
+ "generate-key",
8
+ "connect-vps",
9
+ "authorize-key",
10
+ "write-env",
11
+ "upload-source",
12
+ "build-deploy",
13
+ "verify-key",
14
+ "create-owner",
15
+ "verify-mcp",
16
+ "save-state",
17
+ ];
18
+ export const localDeployStepIds = [
19
+ "connect-vps",
20
+ "upload-source",
21
+ "build-deploy",
22
+ "verify-mcp",
23
+ ];
24
+ const envPath = vpsEnvPath;
25
+ const tarballPath = "/opt/second-brain/source.tar.gz";
26
+ const verifyAttempts = 6;
27
+ const verifyDelayMs = 20_000;
28
+ function createStepRunner(ids, onProgress) {
29
+ const steps = ids.map((id) => ({
30
+ id,
31
+ status: "pending",
32
+ }));
33
+ const report = () => onProgress?.(steps.map((item) => ({ ...item })));
34
+ const step = (id) => {
35
+ const found = steps.find((candidate) => candidate.id === id);
36
+ if (found === undefined)
37
+ throw new Error(`Unknown step ${id}`);
38
+ return found;
39
+ };
40
+ return {
41
+ steps,
42
+ step,
43
+ report,
44
+ fail(id, message, link) {
45
+ const failed = step(id);
46
+ failed.status = "failed";
47
+ failed.note = message;
48
+ if (link !== undefined)
49
+ failed.link = link;
50
+ for (const remaining of steps) {
51
+ if (remaining.status === "pending")
52
+ remaining.status = "skipped";
53
+ }
54
+ report();
55
+ return { ok: false, steps };
56
+ },
57
+ async run(id, action) {
58
+ step(id).status = "running";
59
+ report();
60
+ try {
61
+ const value = await action();
62
+ step(id).status = "ok";
63
+ report();
64
+ return { ok: true, value };
65
+ }
66
+ catch (error) {
67
+ return {
68
+ ok: false,
69
+ message: error instanceof Error ? error.message : String(error),
70
+ link: error instanceof Error
71
+ ? error.link
72
+ : undefined,
73
+ };
74
+ }
75
+ },
76
+ };
77
+ }
78
+ async function execOrThrow(ssh, command, fallbackMessage) {
79
+ const result = await ssh.exec(command);
80
+ if (result.code !== 0) {
81
+ throw new Error(result.stderr.trim() || fallbackMessage);
82
+ }
83
+ }
84
+ async function uploadSource(ssh, tarball) {
85
+ await execOrThrow(ssh, "mkdir -p /opt/second-brain", "mkdir failed.");
86
+ await ssh.writeFile(tarballPath, tarball, 0o600);
87
+ // Remove directories the repo owns before extracting so files deleted
88
+ // from the repo cannot linger and end up inside the next image build.
89
+ // The VPS .env is not in the tarball and is never touched.
90
+ await execOrThrow(ssh, "rm -rf /opt/second-brain/src /opt/second-brain/migrations /opt/second-brain/test /opt/second-brain/docs /opt/second-brain/deploy /opt/second-brain/installer" +
91
+ ` && tar -xzf ${tarballPath} -C /opt/second-brain && rm -f ${tarballPath}`, "The source upload could not be extracted.");
92
+ }
93
+ async function buildDeploy(ssh) {
94
+ const result = await ssh.exec("SECOND_BRAIN_DEPLOY_MODE=build bash /opt/second-brain/deploy/deploy.sh");
95
+ if (result.code !== 0) {
96
+ // deploy.sh dumps compose status and service logs to stdout on failure,
97
+ // while Docker's pull/build progress floods stderr. Keep the stdout tail
98
+ // (the diagnosis) large and the stderr tail (the compose error line)
99
+ // small so the progress panel shows what actually broke.
100
+ const tail = (text, max) => text.length > max ? text.slice(-max) : text;
101
+ const output = `${tail(result.stdout, 3400)}\n${tail(result.stderr, 600)}`.trim();
102
+ throw new Error(output || "The build on the server failed.");
103
+ }
104
+ }
105
+ async function waitForEngine(ssh) {
106
+ await execOrThrow(ssh, waitEngineCommand, "The engine did not become healthy after owner bootstrap.");
107
+ }
108
+ async function verifyWithRetries(deps, domain) {
109
+ let verification;
110
+ for (let attempt = 1; attempt <= verifyAttempts; attempt += 1) {
111
+ verification = await deps.verifyDeployment(`https://${domain}`);
112
+ if (verification.ready)
113
+ return verification;
114
+ if (attempt < verifyAttempts)
115
+ await deps.sleep(verifyDelayMs);
116
+ }
117
+ throw new Error(verification?.steps.find((item) => item.outcome === "failed")?.summary ??
118
+ "The server did not pass verification.");
119
+ }
120
+ export async function runInstall(input, deps, onProgress) {
121
+ const runner = createStepRunner(installStepIds, onProgress);
122
+ const generated = await runner.run("generate-key", () => deps.generateKey());
123
+ if (!generated.ok)
124
+ return runner.fail("generate-key", generated.message);
125
+ const keys = generated.value;
126
+ const connected = await runner.run("connect-vps", () => deps.connectSsh({ ...input.vps, auth: input.vpsAuth }));
127
+ if (!connected.ok)
128
+ return runner.fail("connect-vps", connected.message);
129
+ const ssh = connected.value;
130
+ try {
131
+ const authorized = await runner.run("authorize-key", () => execOrThrow(ssh, buildAuthorizedKeysCommand(keys.publicKey), "authorized_keys update failed."));
132
+ if (!authorized.ok)
133
+ return runner.fail("authorize-key", authorized.message);
134
+ const wroteEnv = await runner.run("write-env", async () => {
135
+ const existing = await ssh.readFileIfExists(envPath);
136
+ // Another product on the same VPS may hold the default loopback ports;
137
+ // probe the server and provision free ones before compose binds them.
138
+ const probe = await ssh.exec(portProbeCommand);
139
+ const hostPorts = chooseHostPorts({
140
+ existingEnv: existing === null ? {} : parseEnvContent(existing),
141
+ occupied: parsePortProbe(probe.stdout),
142
+ });
143
+ const merged = mergeVpsEnv({
144
+ existingContent: existing ?? undefined,
145
+ domain: input.domain,
146
+ openRouterApiKey: input.openRouterApiKey,
147
+ freshDatabasePassword: deps.freshDatabasePassword ?? generateDatabasePassword(),
148
+ hostPorts,
149
+ });
150
+ await execOrThrow(ssh, "mkdir -p /opt/second-brain", "mkdir failed.");
151
+ await ssh.writeFile(envPath, merged.content, 0o600);
152
+ if (!isDefaultHostPorts(hostPorts)) {
153
+ runner.step("write-env").note =
154
+ `alternate-ports ${hostPorts.httpPort}/${hostPorts.oauthPort}`;
155
+ }
156
+ else if (merged.keptExistingDatabasePassword) {
157
+ runner.step("write-env").note = "kept-database-password";
158
+ }
159
+ });
160
+ if (!wroteEnv.ok)
161
+ return runner.fail("write-env", wroteEnv.message);
162
+ const uploaded = await runner.run("upload-source", async () => {
163
+ const tarball = await deps.archiveSource();
164
+ await uploadSource(ssh, tarball);
165
+ // The archive ships HEAD; uncommitted local edits stay behind. Warn so
166
+ // a member never wonders why a change did not reach the server.
167
+ if ((await deps.isSourceDirty?.()) === true) {
168
+ runner.step("upload-source").note = "uncommitted-changes";
169
+ }
170
+ });
171
+ if (!uploaded.ok)
172
+ return runner.fail("upload-source", uploaded.message);
173
+ const built = await runner.run("build-deploy", () => buildDeploy(ssh));
174
+ if (!built.ok)
175
+ return runner.fail("build-deploy", built.message);
176
+ const keyWorks = await runner.run("verify-key", async () => {
177
+ const session = await deps.connectSsh({
178
+ ...input.vps,
179
+ auth: { kind: "key", privateKey: keys.privateKey },
180
+ });
181
+ session.close();
182
+ });
183
+ if (!keyWorks.ok)
184
+ return runner.fail("verify-key", keyWorks.message);
185
+ let bearerToken;
186
+ const owner = await runner.run("create-owner", async () => {
187
+ const result = await ssh.exec(ownerBootstrapCommand, buildOwnerBootstrapInput(input.ownerEmail, input.ownerPassword));
188
+ const outcome = interpretBootstrapResult(result);
189
+ if (outcome.kind === "failed")
190
+ throw new Error(outcome.message);
191
+ if (outcome.kind === "already-exists") {
192
+ runner.step("create-owner").note = "already-exists";
193
+ }
194
+ if (input.enableBearerToken) {
195
+ bearerToken = await provisionBearerToken(ssh, input.ownerEmail, deps.freshBearerToken);
196
+ }
197
+ await waitForEngine(ssh);
198
+ });
199
+ if (!owner.ok)
200
+ return runner.fail("create-owner", owner.message);
201
+ let verification;
202
+ const verified = await runner.run("verify-mcp", async () => {
203
+ verification = await verifyWithRetries(deps, input.domain);
204
+ });
205
+ if (!verified.ok)
206
+ return runner.fail("verify-mcp", verified.message);
207
+ const saved = await runner.run("save-state", async () => {
208
+ const timestamp = deps.now().toISOString();
209
+ await deps.saveState({
210
+ version: 1,
211
+ language: input.language,
212
+ ...(input.repository === undefined
213
+ ? {}
214
+ : { repository: input.repository }),
215
+ vps: input.vps,
216
+ deployKey: keys,
217
+ domain: input.domain,
218
+ ownerEmail: input.ownerEmail,
219
+ installedAt: timestamp,
220
+ updatedAt: timestamp,
221
+ });
222
+ });
223
+ if (!saved.ok)
224
+ return runner.fail("save-state", saved.message);
225
+ runner.report();
226
+ return {
227
+ ok: true,
228
+ steps: runner.steps,
229
+ ...(verification === undefined ? {} : { verification }),
230
+ ...(bearerToken === undefined ? {} : { bearerToken }),
231
+ };
232
+ }
233
+ finally {
234
+ ssh.close();
235
+ }
236
+ }
237
+ export async function runLocalDeploy(input, deps, onProgress) {
238
+ const runner = createStepRunner(localDeployStepIds, onProgress);
239
+ const connected = await runner.run("connect-vps", () => deps.connectSsh({
240
+ ...input.vps,
241
+ auth: { kind: "key", privateKey: input.privateKey },
242
+ }));
243
+ if (!connected.ok)
244
+ return runner.fail("connect-vps", connected.message);
245
+ const ssh = connected.value;
246
+ try {
247
+ const uploaded = await runner.run("upload-source", async () => {
248
+ const tarball = await deps.archiveSource();
249
+ await uploadSource(ssh, tarball);
250
+ if ((await deps.isSourceDirty?.()) === true) {
251
+ runner.step("upload-source").note = "uncommitted-changes";
252
+ }
253
+ });
254
+ if (!uploaded.ok)
255
+ return runner.fail("upload-source", uploaded.message);
256
+ const built = await runner.run("build-deploy", () => buildDeploy(ssh));
257
+ if (!built.ok)
258
+ return runner.fail("build-deploy", built.message);
259
+ let verification;
260
+ const verified = await runner.run("verify-mcp", async () => {
261
+ await waitForEngine(ssh);
262
+ verification = await verifyWithRetries(deps, input.domain);
263
+ });
264
+ if (!verified.ok)
265
+ return runner.fail("verify-mcp", verified.message);
266
+ runner.report();
267
+ return {
268
+ ok: true,
269
+ steps: runner.steps,
270
+ ...(verification === undefined ? {} : { verification }),
271
+ };
272
+ }
273
+ finally {
274
+ ssh.close();
275
+ }
276
+ }
277
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../../packages/second-brain/src/apply.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { SshCommandResult } from "./ssh.js";
2
+ export declare function generateBearerToken(): string;
3
+ /**
4
+ * Runs psql inside the postgres container, which owns the credentials as
5
+ * POSTGRES_USER/POSTGRES_DB environment variables. The query arrives on
6
+ * stdin via buildAccountIdLookupInput.
7
+ */
8
+ export declare const accountIdLookupCommand: string;
9
+ export declare function buildAccountIdLookupInput(email: string): string;
10
+ export type AccountIdLookupOutcome = {
11
+ readonly kind: "found";
12
+ readonly accountId: string;
13
+ } | {
14
+ readonly kind: "failed";
15
+ readonly message: string;
16
+ };
17
+ /** The SSH surface bearer provisioning needs; SshLike satisfies it. */
18
+ export interface BearerSsh {
19
+ exec(command: string, input?: string): Promise<SshCommandResult>;
20
+ readFileIfExists(path: string): Promise<string | null>;
21
+ writeFile(path: string, content: string | Buffer, mode?: number): Promise<void>;
22
+ }
23
+ /**
24
+ * Looks up the owner's account id and stores a fresh bearer credential in
25
+ * the VPS .env. The caller must restart the engine afterwards (wait-engine
26
+ * mode does) so the stored credential becomes active.
27
+ */
28
+ export declare function provisionBearerToken(ssh: BearerSsh, ownerEmail: string, freshToken?: string): Promise<string>;
29
+ export declare function interpretAccountIdLookup(result: SshCommandResult): AccountIdLookupOutcome;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Optional static bearer credential provisioning. The engine accepts a
3
+ * static token alongside OAuth when INVOKTA_HTTP_BEARER_TOKEN and
4
+ * INVOKTA_HTTP_PRINCIPAL_ID are both present in /opt/second-brain/.env (see
5
+ * the engine's src/http-auth.ts). The principal must be the owner's account
6
+ * id, which exists only after owner bootstrap, so provisioning runs inside
7
+ * the create-owner step. The lookup SQL travels on stdin — the command line
8
+ * never carries the owner's email — and the email is escaped into a SQL
9
+ * literal so it cannot break out of the query.
10
+ */
11
+ import { randomBytes } from "node:crypto";
12
+ import { vpsEnvPath, withBearerCredentials } from "./envfile.js";
13
+ export function generateBearerToken() {
14
+ // 32 bytes → 43 base64url characters, above the engine's 32-character
15
+ // minimum and safe for parseEnv.
16
+ return randomBytes(32).toString("base64url");
17
+ }
18
+ /**
19
+ * Runs psql inside the postgres container, which owns the credentials as
20
+ * POSTGRES_USER/POSTGRES_DB environment variables. The query arrives on
21
+ * stdin via buildAccountIdLookupInput.
22
+ */
23
+ export const accountIdLookupCommand = "cd /opt/second-brain && docker compose exec -T postgres " +
24
+ 'sh -c \'exec psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAq -v ON_ERROR_STOP=1\'';
25
+ export function buildAccountIdLookupInput(email) {
26
+ if (/[\r\n\0]/u.test(email)) {
27
+ throw new Error("The owner email must not contain line breaks.");
28
+ }
29
+ // Doubling single quotes is complete escaping for a standard-conforming
30
+ // Postgres string literal. The column stores lower(btrim(email)).
31
+ const literal = email.replaceAll("'", "''");
32
+ return `select id from accounts where email = lower(btrim('${literal}'));\n`;
33
+ }
34
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
35
+ /**
36
+ * Looks up the owner's account id and stores a fresh bearer credential in
37
+ * the VPS .env. The caller must restart the engine afterwards (wait-engine
38
+ * mode does) so the stored credential becomes active.
39
+ */
40
+ export async function provisionBearerToken(ssh, ownerEmail, freshToken) {
41
+ const lookup = await ssh.exec(accountIdLookupCommand, buildAccountIdLookupInput(ownerEmail));
42
+ const outcome = interpretAccountIdLookup(lookup);
43
+ if (outcome.kind === "failed")
44
+ throw new Error(outcome.message);
45
+ const token = freshToken ?? generateBearerToken();
46
+ const existing = await ssh.readFileIfExists(vpsEnvPath);
47
+ if (existing === null) {
48
+ throw new Error("The server configuration went missing before the bearer token could be stored.");
49
+ }
50
+ await ssh.writeFile(vpsEnvPath, withBearerCredentials(existing, {
51
+ token,
52
+ principalId: outcome.accountId,
53
+ }), 0o600);
54
+ return token;
55
+ }
56
+ export function interpretAccountIdLookup(result) {
57
+ if (result.code === 0) {
58
+ const value = result.stdout.trim();
59
+ if (uuidPattern.test(value))
60
+ return { kind: "found", accountId: value };
61
+ if (value === "") {
62
+ return {
63
+ kind: "failed",
64
+ message: "The owner account was not found for the provided email.",
65
+ };
66
+ }
67
+ }
68
+ const lastLine = result.stderr
69
+ .split("\n")
70
+ .map((line) => line.trim())
71
+ .filter((line) => line !== "")
72
+ .at(-1);
73
+ return {
74
+ kind: "failed",
75
+ message: lastLine ?? "The owner account id could not be read.",
76
+ };
77
+ }
78
+ //# sourceMappingURL=bearer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearer.js","sourceRoot":"","sources":["../../../packages/second-brain/src/bearer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGjE,MAAM,UAAU,mBAAmB;IACjC,sEAAsE;IACtE,iCAAiC;IACjC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GACjC,0DAA0D;IAC1D,mFAAmF,CAAC;AAEtF,MAAM,UAAU,yBAAyB,CAAC,KAAa;IACrD,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,wEAAwE;IACxE,kEAAkE;IAClE,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,OAAO,sDAAsD,OAAO,QAAQ,CAAC;AAC/E,CAAC;AAMD,MAAM,WAAW,GACf,iEAAiE,CAAC;AAapE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,GAAc,EACd,UAAkB,EAClB,UAAmB;IAEnB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAC3B,sBAAsB,EACtB,yBAAyB,CAAC,UAAU,CAAC,CACtC,CAAC;IACF,MAAM,OAAO,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,UAAU,IAAI,mBAAmB,EAAE,CAAC;IAClD,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACxD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,gFAAgF,CACjF,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,CAAC,SAAS,CACjB,UAAU,EACV,qBAAqB,CAAC,QAAQ,EAAE;QAC9B,KAAK;QACL,WAAW,EAAE,OAAO,CAAC,SAAS;KAC/B,CAAC,EACF,KAAK,CACN,CAAC;IACF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,MAAwB;IAExB,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACxE,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACjB,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,yDAAyD;aACnE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM;SAC3B,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;SAC7B,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACV,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,QAAQ,IAAI,yCAAyC;KAC/D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Remote owner-account creation.
3
+ *
4
+ * The engine reads the credentials from standard input, so nothing secret
5
+ * reaches the command line: the password never appears in `ps`, in the
6
+ * server's shell history, or in a container's environment. `-T` keeps compose
7
+ * off a TTY, which is what lets the engine read the piped credentials.
8
+ *
9
+ * The stderr sentences below match the stable messages surfaced by the
10
+ * engine's src/oauth/bootstrap-failure.ts.
11
+ */
12
+ import type { SshCommandResult } from "./ssh.js";
13
+ export declare const ownerBootstrapCommand: string;
14
+ /**
15
+ * Engine and worker fail closed until embedding spaces exist. Call this after
16
+ * owner bootstrap so crash-looping containers pick up the new spaces.
17
+ */
18
+ export declare const waitEngineCommand: string;
19
+ /**
20
+ * Builds the two credential lines. A line break in either field would shift
21
+ * the password onto a line the engine does not read, so it is refused rather
22
+ * than escaped.
23
+ */
24
+ export declare function buildOwnerBootstrapInput(email: string, password: string): string;
25
+ export type BootstrapOutcome = {
26
+ readonly kind: "created";
27
+ } | {
28
+ readonly kind: "already-exists";
29
+ } | {
30
+ readonly kind: "failed";
31
+ readonly message: string;
32
+ };
33
+ export declare function interpretBootstrapResult(result: SshCommandResult): BootstrapOutcome;
@@ -0,0 +1,39 @@
1
+ export const ownerBootstrapCommand = "cd /opt/second-brain && docker compose run --rm -T " +
2
+ "auth node dist/oauth/bootstrap-noninteractive.js";
3
+ /**
4
+ * Engine and worker fail closed until embedding spaces exist. Call this after
5
+ * owner bootstrap so crash-looping containers pick up the new spaces.
6
+ */
7
+ export const waitEngineCommand = "cd /opt/second-brain && SECOND_BRAIN_DEPLOY_MODE=wait-engine " +
8
+ "bash /opt/second-brain/deploy/deploy.sh";
9
+ /**
10
+ * Builds the two credential lines. A line break in either field would shift
11
+ * the password onto a line the engine does not read, so it is refused rather
12
+ * than escaped.
13
+ */
14
+ export function buildOwnerBootstrapInput(email, password) {
15
+ for (const value of [email, password]) {
16
+ if (/[\r\n]/u.test(value)) {
17
+ throw new Error("Bootstrap credentials must not contain line breaks.");
18
+ }
19
+ }
20
+ return `${email}\n${password}\n`;
21
+ }
22
+ export function interpretBootstrapResult(result) {
23
+ if (result.code === 0 && result.stdout.includes("OAuth owner created:")) {
24
+ return { kind: "created" };
25
+ }
26
+ if (result.stderr.includes("The OAuth owner account already exists.")) {
27
+ return { kind: "already-exists" };
28
+ }
29
+ const lastLine = result.stderr
30
+ .split("\n")
31
+ .map((line) => line.trim())
32
+ .filter((line) => line !== "")
33
+ .at(-1);
34
+ return {
35
+ kind: "failed",
36
+ message: lastLine ?? "The owner account could not be created.",
37
+ };
38
+ }
39
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../../packages/second-brain/src/bootstrap.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,qBAAqB,GAChC,qDAAqD;IACrD,kDAAkD,CAAC;AAErD;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAC5B,+DAA+D;IAC/D,yCAAyC,CAAC;AAE5C;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAa,EACb,QAAgB;IAEhB,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;QACtC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,KAAK,KAAK,QAAQ,IAAI,CAAC;AACnC,CAAC;AAOD,MAAM,UAAU,wBAAwB,CACtC,MAAwB;IAExB,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,yCAAyC,CAAC,EAAE,CAAC;QACtE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACpC,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM;SAC3B,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;SAC7B,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACV,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,QAAQ,IAAI,yCAAyC;KAC/D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { HostPorts } from "./ports.js";
2
+ /** The placeholder written by deploy/vps-setup.sh before any real install. */
3
+ export declare const databasePasswordPlaceholder = "replace-with-a-strong-database-password";
4
+ export declare const vpsEnvPath = "/opt/second-brain/.env";
5
+ /**
6
+ * Members paste domains in every shape — with https://, trailing slashes,
7
+ * paths, uppercase. The OAuth server refuses to start unless
8
+ * SECOND_BRAIN_PUBLIC_URL is a clean HTTPS origin, so normalize here.
9
+ */
10
+ export declare function normalizeDomain(value: string): string;
11
+ export declare function generateDatabasePassword(): string;
12
+ export declare function parseEnvContent(text: string): Record<string, string>;
13
+ export interface MergeVpsEnvInput {
14
+ readonly existingContent?: string | undefined;
15
+ readonly domain: string;
16
+ readonly openRouterApiKey: string;
17
+ readonly freshDatabasePassword: string;
18
+ readonly hostPorts: HostPorts;
19
+ }
20
+ export interface MergeVpsEnvResult {
21
+ readonly content: string;
22
+ readonly keptExistingDatabasePassword: boolean;
23
+ }
24
+ export interface BearerCredentials {
25
+ readonly token: string;
26
+ readonly principalId: string;
27
+ }
28
+ /**
29
+ * Adds or replaces the static bearer credential lines in the VPS .env. This
30
+ * runs after owner bootstrap — the principal id does not exist earlier — so
31
+ * it edits the file in place instead of re-rendering: deploy.sh may have
32
+ * upserted image pins since write-env, and those must survive.
33
+ */
34
+ export declare function withBearerCredentials(content: string, credentials: BearerCredentials): string;
35
+ export declare function mergeVpsEnv(input: MergeVpsEnvInput): MergeVpsEnvResult;