@rizom/ops 0.2.0-alpha.3 → 0.2.0-alpha.30
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/README.md +6 -2
- package/dist/age-key-bootstrap.d.ts +17 -0
- package/dist/brains-ops.js +336 -146
- package/dist/cert-bootstrap.d.ts +22 -0
- package/dist/content-repo.d.ts +12 -0
- package/dist/default-user-runner.d.ts +1 -1
- package/dist/deploy.js +46 -46
- package/dist/index.d.ts +4 -0
- package/dist/index.js +336 -146
- package/dist/load-registry.d.ts +19 -3
- package/dist/observed-status.d.ts +12 -0
- package/dist/onboard-user.d.ts +2 -2
- package/dist/origin-ca.d.ts +1 -0
- package/dist/parse-args.d.ts +1 -0
- package/dist/push-secrets.d.ts +9 -0
- package/dist/push-target.d.ts +2 -0
- package/dist/reconcile-all.d.ts +2 -2
- package/dist/reconcile-cohort.d.ts +2 -2
- package/dist/reconcile-lib.d.ts +4 -2
- package/dist/run-command.d.ts +8 -2
- package/dist/run-subprocess.d.ts +6 -0
- package/dist/schema.d.ts +103 -6
- package/dist/secrets-encrypt.d.ts +32 -0
- package/dist/secrets-push.d.ts +2 -5
- package/dist/ssh-key-bootstrap.d.ts +26 -0
- package/dist/user-runner.d.ts +5 -0
- package/package.json +5 -3
- package/templates/rover-pilot/.env.schema +5 -0
- package/templates/rover-pilot/.github/workflows/build.yml +1 -0
- package/templates/rover-pilot/.github/workflows/deploy.yml +67 -15
- package/templates/rover-pilot/.github/workflows/reconcile.yml +16 -2
- package/templates/rover-pilot/README.md +5 -2
- package/templates/rover-pilot/deploy/Dockerfile +22 -7
- package/templates/rover-pilot/deploy/kamal/deploy.yml +3 -2
- package/templates/rover-pilot/deploy/scripts/decrypt-user-secrets.ts +83 -0
- package/templates/rover-pilot/deploy/scripts/provision-server.ts +1 -1
- package/templates/rover-pilot/deploy/scripts/resolve-deploy-handles.ts +3 -1
- package/templates/rover-pilot/deploy/scripts/resolve-user-config.ts +12 -12
- package/templates/rover-pilot/deploy/scripts/sync-content-repo.ts +179 -0
- package/templates/rover-pilot/docs/onboarding-checklist.md +28 -8
- package/templates/rover-pilot/docs/operator-playbook.md +59 -0
- package/templates/rover-pilot/docs/user-onboarding.md +505 -0
- package/templates/rover-pilot/package.json +3 -0
- package/templates/rover-pilot/pilot.yaml +3 -0
- package/templates/rover-pilot/users/alice.yaml +5 -1
- package/dist/user-secret-names.d.ts +0 -6
package/dist/load-registry.d.ts
CHANGED
|
@@ -13,6 +13,21 @@ export interface ResolvedCohort {
|
|
|
13
13
|
brainVersionOverride?: string;
|
|
14
14
|
presetOverride?: PilotPreset;
|
|
15
15
|
aiApiKeyOverride?: string;
|
|
16
|
+
gitSyncTokenOverride?: string;
|
|
17
|
+
mcpAuthTokenOverride?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedAnchorProfileSocialLink {
|
|
20
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
21
|
+
url: string;
|
|
22
|
+
label?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolvedAnchorProfile {
|
|
25
|
+
name: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
website?: string;
|
|
28
|
+
email?: string;
|
|
29
|
+
story?: string;
|
|
30
|
+
socialLinks?: ResolvedAnchorProfileSocialLink[];
|
|
16
31
|
}
|
|
17
32
|
export interface ResolvedUserIdentity {
|
|
18
33
|
handle: string;
|
|
@@ -23,7 +38,11 @@ export interface ResolvedUserIdentity {
|
|
|
23
38
|
domain: string;
|
|
24
39
|
contentRepo: string;
|
|
25
40
|
discordEnabled: boolean;
|
|
41
|
+
discordAnchorUserId?: string;
|
|
26
42
|
effectiveAiApiKey: string;
|
|
43
|
+
effectiveGitSyncToken: string;
|
|
44
|
+
effectiveMcpAuthToken: string;
|
|
45
|
+
anchorProfile: ResolvedAnchorProfile;
|
|
27
46
|
snapshotStatus: SnapshotStatus;
|
|
28
47
|
}
|
|
29
48
|
export interface ResolvedUser extends ResolvedUserIdentity {
|
|
@@ -40,7 +59,4 @@ export interface PilotRegistry {
|
|
|
40
59
|
cohorts: ResolvedCohort[];
|
|
41
60
|
users: ResolvedUser[];
|
|
42
61
|
}
|
|
43
|
-
declare class PilotRegistryError extends Error {
|
|
44
|
-
}
|
|
45
62
|
export declare function loadPilotRegistry(rootDir: string, options?: LoadPilotRegistryOptions): Promise<PilotRegistry>;
|
|
46
|
-
export { PilotRegistryError };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FetchLike } from "@brains/utils/origin-ca";
|
|
2
|
+
import type { ObservedUserStatus, ResolvedUserIdentity } from "./load-registry";
|
|
3
|
+
export interface LookupResult {
|
|
4
|
+
address: string;
|
|
5
|
+
family: number;
|
|
6
|
+
}
|
|
7
|
+
export type LookupHost = (hostname: string) => Promise<LookupResult>;
|
|
8
|
+
export interface CreateObservedStatusResolverOptions {
|
|
9
|
+
fetchImpl?: FetchLike;
|
|
10
|
+
lookupHost?: LookupHost;
|
|
11
|
+
}
|
|
12
|
+
export declare function createObservedStatusResolver(options?: CreateObservedStatusResolverOptions): (user: ResolvedUserIdentity) => Promise<ObservedUserStatus>;
|
package/dist/onboard-user.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { type UserRunner } from "./reconcile-lib";
|
|
2
|
-
export declare function onboardUser(rootDir: string, handle: string, runner?: UserRunner): Promise<void>;
|
|
1
|
+
import { type UserRunner, type ContentRepoSyncOptions } from "./reconcile-lib";
|
|
2
|
+
export declare function onboardUser(rootDir: string, handle: string, runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createOriginCertificateRequest, generateOriginKeyPair, issueCloudflareOriginCertificate, setCloudflareZoneSslStrict, type CloudflareOriginCaResult, type FetchLike, type OriginCertificateRequest, type OriginKeyPair, } from "@brains/utils/origin-ca";
|
package/dist/parse-args.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { normalizePushTarget, type PushTarget } from "./push-target";
|
|
2
|
+
import { type RunCommand } from "./run-subprocess";
|
|
3
|
+
export type SecretPair = readonly [name: string, value: string];
|
|
4
|
+
export interface PushSecretsOptions {
|
|
5
|
+
runCommand?: RunCommand | undefined;
|
|
6
|
+
logger?: ((message: string) => void) | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function pushSecretsToBackend(_target: PushTarget, secrets: readonly SecretPair[], options?: PushSecretsOptions): Promise<void>;
|
|
9
|
+
export { normalizePushTarget };
|
package/dist/reconcile-all.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { type UserRunner } from "./reconcile-lib";
|
|
2
|
-
export declare function reconcileAll(rootDir: string, runner?: UserRunner): Promise<void>;
|
|
1
|
+
import { type UserRunner, type ContentRepoSyncOptions } from "./reconcile-lib";
|
|
2
|
+
export declare function reconcileAll(rootDir: string, runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { type UserRunner } from "./reconcile-lib";
|
|
2
|
-
export declare function reconcileCohort(rootDir: string, cohortId: string, runner?: UserRunner): Promise<void>;
|
|
1
|
+
import { type UserRunner, type ContentRepoSyncOptions } from "./reconcile-lib";
|
|
2
|
+
export declare function reconcileCohort(rootDir: string, cohortId: string, runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
|
package/dist/reconcile-lib.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type ContentRepoSyncOptions } from "./content-repo";
|
|
1
2
|
import { type PilotRegistry, type ResolvedUser } from "./load-registry";
|
|
2
3
|
import type { UserRunner } from "./user-runner";
|
|
3
|
-
export type {
|
|
4
|
-
export
|
|
4
|
+
export type { ContentRepoSyncOptions } from "./content-repo";
|
|
5
|
+
export type { ContentRepoFile, UserRunResult, UserRunner } from "./user-runner";
|
|
6
|
+
export declare function runUsers(rootDir: string, registry: PilotRegistry, users: ResolvedUser[], runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
|
|
5
7
|
export declare function findUser(rootDir: string, handle: string): Promise<{
|
|
6
8
|
registry: PilotRegistry;
|
|
7
9
|
user: ResolvedUser;
|
package/dist/run-command.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { FetchLike } from "@brains/utils/origin-ca";
|
|
1
2
|
import type { LoadPilotRegistryOptions } from "./load-registry";
|
|
3
|
+
import { type LookupHost } from "./observed-status";
|
|
2
4
|
import type { ParsedArgs } from "./parse-args";
|
|
3
|
-
import { type RunCommand as
|
|
5
|
+
import { type RunCommand as OpsRunCommand } from "./run-subprocess";
|
|
6
|
+
import { type SshKeygen } from "./ssh-key-bootstrap";
|
|
4
7
|
import type { UserRunner } from "./user-runner";
|
|
5
8
|
export interface CommandResult {
|
|
6
9
|
success: boolean;
|
|
@@ -10,6 +13,9 @@ export interface CommandDependencies extends LoadPilotRegistryOptions {
|
|
|
10
13
|
runner?: UserRunner;
|
|
11
14
|
env?: NodeJS.ProcessEnv | undefined;
|
|
12
15
|
logger?: ((message: string) => void) | undefined;
|
|
13
|
-
|
|
16
|
+
fetchImpl?: FetchLike | undefined;
|
|
17
|
+
lookupHost?: LookupHost | undefined;
|
|
18
|
+
bootstrapRunCommand?: OpsRunCommand | undefined;
|
|
19
|
+
sshKeygen?: SshKeygen | undefined;
|
|
14
20
|
}
|
|
15
21
|
export declare function runCommand(parsed: ParsedArgs, dependencies?: CommandDependencies): Promise<CommandResult>;
|
package/dist/schema.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare const presetSchema: z.ZodEnum<["core", "default", "pro"]>;
|
|
|
3
3
|
export declare const exactVersionSchema: z.ZodString;
|
|
4
4
|
export declare const handleSchema: z.ZodString;
|
|
5
5
|
export declare const secretNameSchema: z.ZodString;
|
|
6
|
+
export declare const agePublicKeySchema: z.ZodString;
|
|
6
7
|
export declare const pilotSchema: z.ZodObject<{
|
|
7
8
|
schemaVersion: z.ZodLiteral<1>;
|
|
8
9
|
brainVersion: z.ZodString;
|
|
@@ -12,73 +13,169 @@ export declare const pilotSchema: z.ZodObject<{
|
|
|
12
13
|
domainSuffix: z.ZodString;
|
|
13
14
|
preset: z.ZodEnum<["core", "default", "pro"]>;
|
|
14
15
|
aiApiKey: z.ZodString;
|
|
16
|
+
gitSyncToken: z.ZodString;
|
|
17
|
+
mcpAuthToken: z.ZodString;
|
|
18
|
+
agePublicKey: z.ZodString;
|
|
15
19
|
}, "strict", z.ZodTypeAny, {
|
|
20
|
+
agePublicKey: string;
|
|
16
21
|
schemaVersion: 1;
|
|
17
22
|
brainVersion: string;
|
|
18
23
|
model: "rover";
|
|
19
24
|
githubOrg: string;
|
|
20
25
|
contentRepoPrefix: string;
|
|
21
26
|
domainSuffix: string;
|
|
22
|
-
preset: "
|
|
27
|
+
preset: "default" | "core" | "pro";
|
|
23
28
|
aiApiKey: string;
|
|
29
|
+
gitSyncToken: string;
|
|
30
|
+
mcpAuthToken: string;
|
|
24
31
|
}, {
|
|
32
|
+
agePublicKey: string;
|
|
25
33
|
schemaVersion: 1;
|
|
26
34
|
brainVersion: string;
|
|
27
35
|
model: "rover";
|
|
28
36
|
githubOrg: string;
|
|
29
37
|
contentRepoPrefix: string;
|
|
30
38
|
domainSuffix: string;
|
|
31
|
-
preset: "
|
|
39
|
+
preset: "default" | "core" | "pro";
|
|
32
40
|
aiApiKey: string;
|
|
41
|
+
gitSyncToken: string;
|
|
42
|
+
mcpAuthToken: string;
|
|
33
43
|
}>;
|
|
34
44
|
export declare const userSchema: z.ZodObject<{
|
|
35
45
|
handle: z.ZodString;
|
|
36
46
|
discord: z.ZodObject<{
|
|
37
47
|
enabled: z.ZodBoolean;
|
|
48
|
+
anchorUserId: z.ZodOptional<z.ZodString>;
|
|
38
49
|
}, "strict", z.ZodTypeAny, {
|
|
39
50
|
enabled: boolean;
|
|
51
|
+
anchorUserId?: string | undefined;
|
|
40
52
|
}, {
|
|
41
53
|
enabled: boolean;
|
|
54
|
+
anchorUserId?: string | undefined;
|
|
42
55
|
}>;
|
|
43
56
|
aiApiKeyOverride: z.ZodOptional<z.ZodString>;
|
|
57
|
+
gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
|
|
58
|
+
mcpAuthTokenOverride: z.ZodOptional<z.ZodString>;
|
|
59
|
+
anchorProfile: z.ZodOptional<z.ZodObject<{
|
|
60
|
+
name: z.ZodOptional<z.ZodString>;
|
|
61
|
+
description: z.ZodOptional<z.ZodString>;
|
|
62
|
+
website: z.ZodOptional<z.ZodString>;
|
|
63
|
+
email: z.ZodOptional<z.ZodString>;
|
|
64
|
+
story: z.ZodOptional<z.ZodString>;
|
|
65
|
+
socialLinks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
66
|
+
platform: z.ZodEnum<["github", "instagram", "linkedin", "email", "website"]>;
|
|
67
|
+
url: z.ZodString;
|
|
68
|
+
label: z.ZodOptional<z.ZodString>;
|
|
69
|
+
}, "strict", z.ZodTypeAny, {
|
|
70
|
+
url: string;
|
|
71
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
72
|
+
label?: string | undefined;
|
|
73
|
+
}, {
|
|
74
|
+
url: string;
|
|
75
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
76
|
+
label?: string | undefined;
|
|
77
|
+
}>, "many">>;
|
|
78
|
+
}, "strict", z.ZodTypeAny, {
|
|
79
|
+
name?: string | undefined;
|
|
80
|
+
email?: string | undefined;
|
|
81
|
+
website?: string | undefined;
|
|
82
|
+
description?: string | undefined;
|
|
83
|
+
story?: string | undefined;
|
|
84
|
+
socialLinks?: {
|
|
85
|
+
url: string;
|
|
86
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
87
|
+
label?: string | undefined;
|
|
88
|
+
}[] | undefined;
|
|
89
|
+
}, {
|
|
90
|
+
name?: string | undefined;
|
|
91
|
+
email?: string | undefined;
|
|
92
|
+
website?: string | undefined;
|
|
93
|
+
description?: string | undefined;
|
|
94
|
+
story?: string | undefined;
|
|
95
|
+
socialLinks?: {
|
|
96
|
+
url: string;
|
|
97
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
98
|
+
label?: string | undefined;
|
|
99
|
+
}[] | undefined;
|
|
100
|
+
}>>;
|
|
44
101
|
}, "strict", z.ZodTypeAny, {
|
|
45
102
|
handle: string;
|
|
46
103
|
discord: {
|
|
47
104
|
enabled: boolean;
|
|
105
|
+
anchorUserId?: string | undefined;
|
|
48
106
|
};
|
|
49
107
|
aiApiKeyOverride?: string | undefined;
|
|
108
|
+
gitSyncTokenOverride?: string | undefined;
|
|
109
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
110
|
+
anchorProfile?: {
|
|
111
|
+
name?: string | undefined;
|
|
112
|
+
email?: string | undefined;
|
|
113
|
+
website?: string | undefined;
|
|
114
|
+
description?: string | undefined;
|
|
115
|
+
story?: string | undefined;
|
|
116
|
+
socialLinks?: {
|
|
117
|
+
url: string;
|
|
118
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
119
|
+
label?: string | undefined;
|
|
120
|
+
}[] | undefined;
|
|
121
|
+
} | undefined;
|
|
50
122
|
}, {
|
|
51
123
|
handle: string;
|
|
52
124
|
discord: {
|
|
53
125
|
enabled: boolean;
|
|
126
|
+
anchorUserId?: string | undefined;
|
|
54
127
|
};
|
|
55
128
|
aiApiKeyOverride?: string | undefined;
|
|
129
|
+
gitSyncTokenOverride?: string | undefined;
|
|
130
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
131
|
+
anchorProfile?: {
|
|
132
|
+
name?: string | undefined;
|
|
133
|
+
email?: string | undefined;
|
|
134
|
+
website?: string | undefined;
|
|
135
|
+
description?: string | undefined;
|
|
136
|
+
story?: string | undefined;
|
|
137
|
+
socialLinks?: {
|
|
138
|
+
url: string;
|
|
139
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
140
|
+
label?: string | undefined;
|
|
141
|
+
}[] | undefined;
|
|
142
|
+
} | undefined;
|
|
56
143
|
}>;
|
|
57
144
|
export declare const cohortSchema: z.ZodEffects<z.ZodObject<{
|
|
58
145
|
members: z.ZodArray<z.ZodString, "many">;
|
|
59
146
|
brainVersionOverride: z.ZodOptional<z.ZodString>;
|
|
60
147
|
presetOverride: z.ZodOptional<z.ZodEnum<["core", "default", "pro"]>>;
|
|
61
148
|
aiApiKeyOverride: z.ZodOptional<z.ZodString>;
|
|
149
|
+
gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
|
|
150
|
+
mcpAuthTokenOverride: z.ZodOptional<z.ZodString>;
|
|
62
151
|
}, "strict", z.ZodTypeAny, {
|
|
63
152
|
members: string[];
|
|
64
153
|
aiApiKeyOverride?: string | undefined;
|
|
154
|
+
gitSyncTokenOverride?: string | undefined;
|
|
155
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
65
156
|
brainVersionOverride?: string | undefined;
|
|
66
|
-
presetOverride?: "
|
|
157
|
+
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
67
158
|
}, {
|
|
68
159
|
members: string[];
|
|
69
160
|
aiApiKeyOverride?: string | undefined;
|
|
161
|
+
gitSyncTokenOverride?: string | undefined;
|
|
162
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
70
163
|
brainVersionOverride?: string | undefined;
|
|
71
|
-
presetOverride?: "
|
|
164
|
+
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
72
165
|
}>, {
|
|
73
166
|
members: string[];
|
|
74
167
|
aiApiKeyOverride?: string | undefined;
|
|
168
|
+
gitSyncTokenOverride?: string | undefined;
|
|
169
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
75
170
|
brainVersionOverride?: string | undefined;
|
|
76
|
-
presetOverride?: "
|
|
171
|
+
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
77
172
|
}, {
|
|
78
173
|
members: string[];
|
|
79
174
|
aiApiKeyOverride?: string | undefined;
|
|
175
|
+
gitSyncTokenOverride?: string | undefined;
|
|
176
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
80
177
|
brainVersionOverride?: string | undefined;
|
|
81
|
-
presetOverride?: "
|
|
178
|
+
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
82
179
|
}>;
|
|
83
180
|
export type PilotConfig = z.infer<typeof pilotSchema>;
|
|
84
181
|
export type UserConfig = z.infer<typeof userSchema>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "@brains/utils";
|
|
2
|
+
declare const encryptedUserSecretsSchema: z.ZodObject<{
|
|
3
|
+
gitSyncToken: z.ZodOptional<z.ZodString>;
|
|
4
|
+
mcpAuthToken: z.ZodOptional<z.ZodString>;
|
|
5
|
+
discordBotToken: z.ZodOptional<z.ZodString>;
|
|
6
|
+
aiApiKey: z.ZodOptional<z.ZodString>;
|
|
7
|
+
}, "strict", z.ZodTypeAny, {
|
|
8
|
+
aiApiKey?: string | undefined;
|
|
9
|
+
gitSyncToken?: string | undefined;
|
|
10
|
+
mcpAuthToken?: string | undefined;
|
|
11
|
+
discordBotToken?: string | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
aiApiKey?: string | undefined;
|
|
14
|
+
gitSyncToken?: string | undefined;
|
|
15
|
+
mcpAuthToken?: string | undefined;
|
|
16
|
+
discordBotToken?: string | undefined;
|
|
17
|
+
}>;
|
|
18
|
+
export type EncryptedUserSecrets = z.infer<typeof encryptedUserSecretsSchema>;
|
|
19
|
+
export interface SecretsEncryptOptions {
|
|
20
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
21
|
+
logger?: ((message: string) => void) | undefined;
|
|
22
|
+
dryRun?: boolean | undefined;
|
|
23
|
+
}
|
|
24
|
+
export interface SecretsEncryptResult {
|
|
25
|
+
encryptedPath: string;
|
|
26
|
+
plaintextPath: string;
|
|
27
|
+
deletedPlaintext: boolean;
|
|
28
|
+
encryptedKeys: Array<keyof EncryptedUserSecrets>;
|
|
29
|
+
dryRun?: boolean | undefined;
|
|
30
|
+
}
|
|
31
|
+
export declare function encryptPilotSecrets(rootDir: string, handle: string, options?: SecretsEncryptOptions): Promise<SecretsEncryptResult>;
|
|
32
|
+
export {};
|
package/dist/secrets-push.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RunCommand } from "./run-subprocess";
|
|
1
2
|
export interface SecretsPushOptions {
|
|
2
3
|
env?: NodeJS.ProcessEnv | undefined;
|
|
3
4
|
logger?: ((message: string) => void) | undefined;
|
|
@@ -9,8 +10,4 @@ export interface SecretsPushResult {
|
|
|
9
10
|
skippedKeys: string[];
|
|
10
11
|
dryRun?: boolean | undefined;
|
|
11
12
|
}
|
|
12
|
-
export
|
|
13
|
-
stdin?: string;
|
|
14
|
-
env?: NodeJS.ProcessEnv;
|
|
15
|
-
}) => Promise<void>;
|
|
16
|
-
export declare function pushPilotSecrets(rootDir: string, handle: string, options?: SecretsPushOptions): Promise<SecretsPushResult>;
|
|
13
|
+
export declare function pushPilotSecrets(rootDir: string, options?: SecretsPushOptions): Promise<SecretsPushResult>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type FetchLike } from "@brains/utils/origin-ca";
|
|
2
|
+
import { type RunCommand } from "./run-subprocess";
|
|
3
|
+
export interface SshKeyBootstrapOptions {
|
|
4
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
5
|
+
fetchImpl?: FetchLike | undefined;
|
|
6
|
+
logger?: (message: string) => void;
|
|
7
|
+
pushTo?: string | undefined;
|
|
8
|
+
runCommand?: RunCommand | undefined;
|
|
9
|
+
sshKeygen?: SshKeygen | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface SshKeyBootstrapResult {
|
|
12
|
+
createdHetznerKey: boolean;
|
|
13
|
+
createdLocalKey: boolean;
|
|
14
|
+
privateKeyPath: string;
|
|
15
|
+
publicKeyPath: string;
|
|
16
|
+
sshKeyName: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SshKeygen {
|
|
19
|
+
createEd25519KeyPair: (privateKeyPath: string, comment: string) => void;
|
|
20
|
+
derivePublicKey: (privateKeyPath: string) => string;
|
|
21
|
+
}
|
|
22
|
+
export declare function runPilotSshKeyBootstrap(rootDir: string, options?: SshKeyBootstrapOptions): Promise<{
|
|
23
|
+
success: boolean;
|
|
24
|
+
message?: string;
|
|
25
|
+
}>;
|
|
26
|
+
export declare function bootstrapPilotSshKey(rootDir: string, options?: SshKeyBootstrapOptions): Promise<SshKeyBootstrapResult>;
|
package/dist/user-runner.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { ResolvedUser } from "./load-registry";
|
|
2
|
+
export interface ContentRepoFile {
|
|
3
|
+
path: string;
|
|
4
|
+
content: string;
|
|
5
|
+
}
|
|
2
6
|
export interface UserRunResult {
|
|
3
7
|
brainYaml?: string;
|
|
4
8
|
envFile?: string;
|
|
9
|
+
contentRepoFiles?: ContentRepoFile[];
|
|
5
10
|
}
|
|
6
11
|
export type UserRunner = (user: ResolvedUser) => Promise<UserRunResult | void>;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.2.0-alpha.
|
|
7
|
+
"version": "0.2.0-alpha.30",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
@@ -32,9 +32,11 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"lint": "eslint . --ext .ts",
|
|
34
34
|
"lint:fix": "eslint . --ext .ts --fix",
|
|
35
|
-
"test": "bun test"
|
|
35
|
+
"test": "bun test --timeout 20000"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"age-encryption": "^0.3.0"
|
|
36
39
|
},
|
|
37
|
-
"dependencies": {},
|
|
38
40
|
"devDependencies": {
|
|
39
41
|
"@brains/eslint-config": "workspace:*",
|
|
40
42
|
"@brains/typescript-config": "workspace:*",
|
|
@@ -4,18 +4,23 @@
|
|
|
4
4
|
# ----------
|
|
5
5
|
|
|
6
6
|
# AI provider
|
|
7
|
+
# Shared GitHub secret by default; a per-user override may come from the decrypted
|
|
8
|
+
# users/<handle>.secrets.yaml.age file at deploy time.
|
|
7
9
|
# @required @sensitive
|
|
8
10
|
AI_API_KEY=
|
|
9
11
|
|
|
10
12
|
# Git sync
|
|
13
|
+
# Comes from the decrypted users/<handle>.secrets.yaml.age file.
|
|
11
14
|
# @required @sensitive
|
|
12
15
|
GIT_SYNC_TOKEN=
|
|
13
16
|
|
|
14
17
|
# MCP interface
|
|
18
|
+
# Comes from the decrypted users/<handle>.secrets.yaml.age file.
|
|
15
19
|
# @required @sensitive
|
|
16
20
|
MCP_AUTH_TOKEN=
|
|
17
21
|
|
|
18
22
|
# Discord (optional, per-user)
|
|
23
|
+
# Comes from the decrypted users/<handle>.secrets.yaml.age file when enabled.
|
|
19
24
|
# @sensitive
|
|
20
25
|
DISCORD_BOT_TOKEN=
|
|
21
26
|
|
|
@@ -12,6 +12,8 @@ on:
|
|
|
12
12
|
paths:
|
|
13
13
|
- users/*/.env
|
|
14
14
|
- users/*/brain.yaml
|
|
15
|
+
- users/*/content/**
|
|
16
|
+
- users/*.secrets.yaml.age
|
|
15
17
|
- deploy/**
|
|
16
18
|
- .github/workflows/deploy.yml
|
|
17
19
|
|
|
@@ -73,8 +75,18 @@ jobs:
|
|
|
73
75
|
- name: Install operator tooling
|
|
74
76
|
run: bun install
|
|
75
77
|
|
|
78
|
+
- name: Decrypt user secrets
|
|
79
|
+
id: user_secrets
|
|
80
|
+
env:
|
|
81
|
+
AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }}
|
|
82
|
+
run: bun deploy/scripts/decrypt-user-secrets.ts "$HANDLE"
|
|
83
|
+
|
|
76
84
|
- name: Reconcile selected user config
|
|
77
|
-
|
|
85
|
+
env:
|
|
86
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
87
|
+
run: |
|
|
88
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
89
|
+
bunx brains-ops onboard "$GITHUB_WORKSPACE" "$HANDLE"
|
|
78
90
|
|
|
79
91
|
- name: Resolve generated user config
|
|
80
92
|
id: user_config
|
|
@@ -82,10 +94,9 @@ jobs:
|
|
|
82
94
|
|
|
83
95
|
- name: Validate selected secrets
|
|
84
96
|
env:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
DISCORD_BOT_TOKEN: ${{ steps.user_config.outputs.discord_bot_token_secret_name != '' && secrets[steps.user_config.outputs.discord_bot_token_secret_name] || '' }}
|
|
97
|
+
SHARED_AI_API_KEY: ${{ secrets[steps.user_secrets.outputs.shared_ai_api_key_secret_name] }}
|
|
98
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
99
|
+
SHARED_MCP_AUTH_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_mcp_auth_token_secret_name] }}
|
|
89
100
|
HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }}
|
|
90
101
|
HCLOUD_SSH_KEY_NAME: ${{ secrets.HCLOUD_SSH_KEY_NAME }}
|
|
91
102
|
HCLOUD_SERVER_TYPE: ${{ secrets.HCLOUD_SERVER_TYPE }}
|
|
@@ -96,7 +107,19 @@ jobs:
|
|
|
96
107
|
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
|
97
108
|
CERTIFICATE_PEM: ${{ secrets.CERTIFICATE_PEM }}
|
|
98
109
|
PRIVATE_KEY_PEM: ${{ secrets.PRIVATE_KEY_PEM }}
|
|
99
|
-
run:
|
|
110
|
+
run: |
|
|
111
|
+
export AI_API_KEY="${AI_API_KEY:-$SHARED_AI_API_KEY}"
|
|
112
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
113
|
+
export MCP_AUTH_TOKEN="${MCP_AUTH_TOKEN:-$SHARED_MCP_AUTH_TOKEN}"
|
|
114
|
+
bun deploy/scripts/validate-secrets.ts
|
|
115
|
+
|
|
116
|
+
- name: Seed content repo
|
|
117
|
+
env:
|
|
118
|
+
CONTENT_REPO: ${{ steps.user_config.outputs.content_repo }}
|
|
119
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
120
|
+
run: |
|
|
121
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
122
|
+
bun deploy/scripts/sync-content-repo.ts
|
|
100
123
|
|
|
101
124
|
- name: Log in to GHCR
|
|
102
125
|
uses: docker/login-action@v3
|
|
@@ -143,14 +166,17 @@ jobs:
|
|
|
143
166
|
|
|
144
167
|
- name: Write .kamal/secrets
|
|
145
168
|
env:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
DISCORD_BOT_TOKEN: ${{ steps.user_config.outputs.discord_bot_token_secret_name != '' && secrets[steps.user_config.outputs.discord_bot_token_secret_name] || '' }}
|
|
169
|
+
SHARED_AI_API_KEY: ${{ secrets[steps.user_secrets.outputs.shared_ai_api_key_secret_name] }}
|
|
170
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
171
|
+
SHARED_MCP_AUTH_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_mcp_auth_token_secret_name] }}
|
|
150
172
|
KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
|
|
151
173
|
CERTIFICATE_PEM: ${{ secrets.CERTIFICATE_PEM }}
|
|
152
174
|
PRIVATE_KEY_PEM: ${{ secrets.PRIVATE_KEY_PEM }}
|
|
153
|
-
run:
|
|
175
|
+
run: |
|
|
176
|
+
export AI_API_KEY="${AI_API_KEY:-$SHARED_AI_API_KEY}"
|
|
177
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
178
|
+
export MCP_AUTH_TOKEN="${MCP_AUTH_TOKEN:-$SHARED_MCP_AUTH_TOKEN}"
|
|
179
|
+
bun deploy/scripts/write-kamal-secrets.ts
|
|
154
180
|
|
|
155
181
|
- name: Provision server
|
|
156
182
|
id: provision
|
|
@@ -167,8 +193,11 @@ jobs:
|
|
|
167
193
|
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
|
168
194
|
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
|
169
195
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
196
|
+
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
170
197
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
171
|
-
run:
|
|
198
|
+
run: |
|
|
199
|
+
bun deploy/scripts/update-dns.ts
|
|
200
|
+
BRAIN_DOMAIN="$PREVIEW_DOMAIN" bun deploy/scripts/update-dns.ts
|
|
172
201
|
|
|
173
202
|
- name: Validate SSH key
|
|
174
203
|
run: ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null
|
|
@@ -192,13 +221,21 @@ jobs:
|
|
|
192
221
|
env:
|
|
193
222
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
194
223
|
VERSION: brain-${{ steps.user_config.outputs.brain_version }}
|
|
224
|
+
IMAGE_REPOSITORY: ${{ steps.user_config.outputs.image_repository }}
|
|
225
|
+
REGISTRY_USERNAME: ${{ steps.user_config.outputs.registry_username }}
|
|
226
|
+
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
227
|
+
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
228
|
+
BRAIN_YAML_PATH: ${{ steps.user_config.outputs.brain_yaml_path }}
|
|
195
229
|
run: kamal setup --skip-push -c deploy/kamal/deploy.yml
|
|
196
230
|
|
|
197
231
|
- name: Verify origin TLS
|
|
198
232
|
env:
|
|
199
233
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
200
234
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
201
|
-
|
|
235
|
+
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
236
|
+
run: |
|
|
237
|
+
curl -I -k --max-time 20 --resolve "$BRAIN_DOMAIN:443:$SERVER_IP" "https://$BRAIN_DOMAIN/health"
|
|
238
|
+
curl -I -k --max-time 20 --resolve "$PREVIEW_DOMAIN:443:$SERVER_IP" "https://$PREVIEW_DOMAIN/"
|
|
202
239
|
|
|
203
240
|
- name: Upload generated config
|
|
204
241
|
uses: actions/upload-artifact@v4
|
|
@@ -207,6 +244,7 @@ jobs:
|
|
|
207
244
|
path: |
|
|
208
245
|
users/${{ matrix.handle }}/brain.yaml
|
|
209
246
|
users/${{ matrix.handle }}/.env
|
|
247
|
+
users/${{ matrix.handle }}/content
|
|
210
248
|
|
|
211
249
|
- name: Dump remote proxy diagnostics
|
|
212
250
|
if: failure()
|
|
@@ -255,8 +293,22 @@ jobs:
|
|
|
255
293
|
if git diff --quiet -- users views; then
|
|
256
294
|
exit 0
|
|
257
295
|
fi
|
|
296
|
+
|
|
297
|
+
git fetch origin "${{ github.ref_name }}"
|
|
298
|
+
if git diff --quiet "origin/${{ github.ref_name }}" -- users views; then
|
|
299
|
+
exit 0
|
|
300
|
+
fi
|
|
301
|
+
|
|
302
|
+
patch_file="$(mktemp)"
|
|
303
|
+
git diff --binary -- users views > "$patch_file"
|
|
304
|
+
git reset --hard "origin/${{ github.ref_name }}"
|
|
305
|
+
git apply --3way --index "$patch_file"
|
|
306
|
+
|
|
307
|
+
if git diff --cached --quiet -- users views; then
|
|
308
|
+
exit 0
|
|
309
|
+
fi
|
|
310
|
+
|
|
258
311
|
git config user.name "github-actions[bot]"
|
|
259
312
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
260
|
-
git add users views
|
|
261
313
|
git commit -m "chore(ops): reconcile generated config"
|
|
262
|
-
git push
|
|
314
|
+
git push origin HEAD:${{ github.ref_name }}
|
|
@@ -38,8 +38,22 @@ jobs:
|
|
|
38
38
|
if git diff --quiet -- views users; then
|
|
39
39
|
exit 0
|
|
40
40
|
fi
|
|
41
|
+
|
|
42
|
+
git fetch origin "${{ github.ref_name }}"
|
|
43
|
+
if git diff --quiet "origin/${{ github.ref_name }}" -- views users; then
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
patch_file="$(mktemp)"
|
|
48
|
+
git diff --binary -- views users > "$patch_file"
|
|
49
|
+
git reset --hard "origin/${{ github.ref_name }}"
|
|
50
|
+
git apply --3way --index "$patch_file"
|
|
51
|
+
|
|
52
|
+
if git diff --cached --quiet -- views users; then
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
41
56
|
git config user.name "github-actions[bot]"
|
|
42
57
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
43
|
-
git add views users
|
|
44
58
|
git commit -m "chore(ops): reconcile pilot outputs"
|
|
45
|
-
git push
|
|
59
|
+
git push origin HEAD:${{ github.ref_name }}
|