@rizom/ops 0.2.0-alpha.6 → 0.2.0-alpha.61
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 -3
- package/dist/age-key-bootstrap.d.ts +17 -0
- package/dist/brains-ops.js +305 -149
- package/dist/cert-bootstrap.d.ts +2 -2
- package/dist/content-repo.d.ts +13 -0
- package/dist/default-user-runner.d.ts +1 -1
- package/dist/deploy.js +24 -24
- package/dist/index.d.ts +3 -0
- package/dist/index.js +305 -149
- package/dist/load-registry.d.ts +19 -3
- package/dist/onboard-user.d.ts +2 -2
- package/dist/parse-args.d.ts +2 -0
- package/dist/push-secrets.d.ts +1 -1
- 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 +0 -1
- package/dist/run-subprocess.d.ts +1 -0
- package/dist/schema.d.ts +100 -0
- package/dist/secrets-encrypt.d.ts +32 -0
- package/dist/secrets-push.d.ts +1 -1
- package/dist/user-add.d.ts +15 -0
- package/dist/user-runner.d.ts +5 -0
- package/package.json +7 -3
- package/templates/rover-pilot/.env.schema +11 -0
- package/templates/rover-pilot/.github/workflows/build.yml +1 -0
- package/templates/rover-pilot/.github/workflows/deploy.yml +74 -19
- package/templates/rover-pilot/.github/workflows/reconcile.yml +16 -2
- package/templates/rover-pilot/README.md +6 -3
- 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 +15 -4
- 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/deploy/scripts/update-dns.ts +14 -4
- package/templates/rover-pilot/docs/onboarding-checklist.md +28 -11
- package/templates/rover-pilot/docs/operator-playbook.md +43 -5
- package/templates/rover-pilot/docs/user-onboarding.md +505 -0
- package/templates/rover-pilot/package.json +3 -0
- package/templates/rover-pilot/pilot.yaml +4 -0
- package/templates/rover-pilot/users/alice.yaml +5 -1
- package/dist/user-secret-names.d.ts +0 -6
- package/templates/rover-pilot/.kamal/hooks/pre-deploy +0 -9
- package/templates/rover-pilot/deploy/Dockerfile +0 -15
- package/templates/rover-pilot/deploy/kamal/deploy.yml +0 -39
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 };
|
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>;
|
package/dist/parse-args.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface ParsedArgs {
|
|
|
6
6
|
version?: boolean | undefined;
|
|
7
7
|
dryRun?: boolean | undefined;
|
|
8
8
|
pushTo?: string | undefined;
|
|
9
|
+
cohort?: string | undefined;
|
|
10
|
+
anchorId?: string | undefined;
|
|
9
11
|
};
|
|
10
12
|
}
|
|
11
13
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
package/dist/push-secrets.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ export interface PushSecretsOptions {
|
|
|
5
5
|
runCommand?: RunCommand | undefined;
|
|
6
6
|
logger?: ((message: string) => void) | undefined;
|
|
7
7
|
}
|
|
8
|
-
export declare function pushSecretsToBackend(
|
|
8
|
+
export declare function pushSecretsToBackend(_target: PushTarget, secrets: readonly SecretPair[], options?: PushSecretsOptions): Promise<void>;
|
|
9
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
|
@@ -15,7 +15,6 @@ export interface CommandDependencies extends LoadPilotRegistryOptions {
|
|
|
15
15
|
logger?: ((message: string) => void) | undefined;
|
|
16
16
|
fetchImpl?: FetchLike | undefined;
|
|
17
17
|
lookupHost?: LookupHost | undefined;
|
|
18
|
-
secretRunCommand?: OpsRunCommand | undefined;
|
|
19
18
|
bootstrapRunCommand?: OpsRunCommand | undefined;
|
|
20
19
|
sshKeygen?: SshKeygen | undefined;
|
|
21
20
|
}
|
package/dist/run-subprocess.d.ts
CHANGED
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,7 +13,12 @@ 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
|
+
contentRepoAdminToken: z.ZodString;
|
|
18
|
+
mcpAuthToken: z.ZodString;
|
|
19
|
+
agePublicKey: z.ZodString;
|
|
15
20
|
}, "strict", z.ZodTypeAny, {
|
|
21
|
+
agePublicKey: string;
|
|
16
22
|
schemaVersion: 1;
|
|
17
23
|
brainVersion: string;
|
|
18
24
|
model: "rover";
|
|
@@ -21,7 +27,11 @@ export declare const pilotSchema: z.ZodObject<{
|
|
|
21
27
|
domainSuffix: string;
|
|
22
28
|
preset: "default" | "core" | "pro";
|
|
23
29
|
aiApiKey: string;
|
|
30
|
+
gitSyncToken: string;
|
|
31
|
+
contentRepoAdminToken: string;
|
|
32
|
+
mcpAuthToken: string;
|
|
24
33
|
}, {
|
|
34
|
+
agePublicKey: string;
|
|
25
35
|
schemaVersion: 1;
|
|
26
36
|
brainVersion: string;
|
|
27
37
|
model: "rover";
|
|
@@ -30,53 +40,143 @@ export declare const pilotSchema: z.ZodObject<{
|
|
|
30
40
|
domainSuffix: string;
|
|
31
41
|
preset: "default" | "core" | "pro";
|
|
32
42
|
aiApiKey: string;
|
|
43
|
+
gitSyncToken: string;
|
|
44
|
+
contentRepoAdminToken: string;
|
|
45
|
+
mcpAuthToken: string;
|
|
33
46
|
}>;
|
|
34
47
|
export declare const userSchema: z.ZodObject<{
|
|
35
48
|
handle: z.ZodString;
|
|
36
49
|
discord: z.ZodObject<{
|
|
37
50
|
enabled: z.ZodBoolean;
|
|
51
|
+
anchorUserId: z.ZodOptional<z.ZodString>;
|
|
38
52
|
}, "strict", z.ZodTypeAny, {
|
|
39
53
|
enabled: boolean;
|
|
54
|
+
anchorUserId?: string | undefined;
|
|
40
55
|
}, {
|
|
41
56
|
enabled: boolean;
|
|
57
|
+
anchorUserId?: string | undefined;
|
|
42
58
|
}>;
|
|
43
59
|
aiApiKeyOverride: z.ZodOptional<z.ZodString>;
|
|
60
|
+
gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
|
|
61
|
+
mcpAuthTokenOverride: z.ZodOptional<z.ZodString>;
|
|
62
|
+
anchorProfile: z.ZodOptional<z.ZodObject<{
|
|
63
|
+
name: z.ZodOptional<z.ZodString>;
|
|
64
|
+
description: z.ZodOptional<z.ZodString>;
|
|
65
|
+
website: z.ZodOptional<z.ZodString>;
|
|
66
|
+
email: z.ZodOptional<z.ZodString>;
|
|
67
|
+
story: z.ZodOptional<z.ZodString>;
|
|
68
|
+
socialLinks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
69
|
+
platform: z.ZodEnum<["github", "instagram", "linkedin", "email", "website"]>;
|
|
70
|
+
url: z.ZodString;
|
|
71
|
+
label: z.ZodOptional<z.ZodString>;
|
|
72
|
+
}, "strict", z.ZodTypeAny, {
|
|
73
|
+
url: string;
|
|
74
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
75
|
+
label?: string | undefined;
|
|
76
|
+
}, {
|
|
77
|
+
url: string;
|
|
78
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
79
|
+
label?: string | undefined;
|
|
80
|
+
}>, "many">>;
|
|
81
|
+
}, "strict", z.ZodTypeAny, {
|
|
82
|
+
name?: string | undefined;
|
|
83
|
+
email?: string | undefined;
|
|
84
|
+
website?: string | undefined;
|
|
85
|
+
description?: string | undefined;
|
|
86
|
+
story?: string | undefined;
|
|
87
|
+
socialLinks?: {
|
|
88
|
+
url: string;
|
|
89
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
90
|
+
label?: string | undefined;
|
|
91
|
+
}[] | undefined;
|
|
92
|
+
}, {
|
|
93
|
+
name?: string | undefined;
|
|
94
|
+
email?: string | undefined;
|
|
95
|
+
website?: string | undefined;
|
|
96
|
+
description?: string | undefined;
|
|
97
|
+
story?: string | undefined;
|
|
98
|
+
socialLinks?: {
|
|
99
|
+
url: string;
|
|
100
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
101
|
+
label?: string | undefined;
|
|
102
|
+
}[] | undefined;
|
|
103
|
+
}>>;
|
|
44
104
|
}, "strict", z.ZodTypeAny, {
|
|
45
105
|
handle: string;
|
|
46
106
|
discord: {
|
|
47
107
|
enabled: boolean;
|
|
108
|
+
anchorUserId?: string | undefined;
|
|
48
109
|
};
|
|
49
110
|
aiApiKeyOverride?: string | undefined;
|
|
111
|
+
gitSyncTokenOverride?: string | undefined;
|
|
112
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
113
|
+
anchorProfile?: {
|
|
114
|
+
name?: string | undefined;
|
|
115
|
+
email?: string | undefined;
|
|
116
|
+
website?: string | undefined;
|
|
117
|
+
description?: string | undefined;
|
|
118
|
+
story?: string | undefined;
|
|
119
|
+
socialLinks?: {
|
|
120
|
+
url: string;
|
|
121
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
122
|
+
label?: string | undefined;
|
|
123
|
+
}[] | undefined;
|
|
124
|
+
} | undefined;
|
|
50
125
|
}, {
|
|
51
126
|
handle: string;
|
|
52
127
|
discord: {
|
|
53
128
|
enabled: boolean;
|
|
129
|
+
anchorUserId?: string | undefined;
|
|
54
130
|
};
|
|
55
131
|
aiApiKeyOverride?: string | undefined;
|
|
132
|
+
gitSyncTokenOverride?: string | undefined;
|
|
133
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
134
|
+
anchorProfile?: {
|
|
135
|
+
name?: string | undefined;
|
|
136
|
+
email?: string | undefined;
|
|
137
|
+
website?: string | undefined;
|
|
138
|
+
description?: string | undefined;
|
|
139
|
+
story?: string | undefined;
|
|
140
|
+
socialLinks?: {
|
|
141
|
+
url: string;
|
|
142
|
+
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
143
|
+
label?: string | undefined;
|
|
144
|
+
}[] | undefined;
|
|
145
|
+
} | undefined;
|
|
56
146
|
}>;
|
|
57
147
|
export declare const cohortSchema: z.ZodEffects<z.ZodObject<{
|
|
58
148
|
members: z.ZodArray<z.ZodString, "many">;
|
|
59
149
|
brainVersionOverride: z.ZodOptional<z.ZodString>;
|
|
60
150
|
presetOverride: z.ZodOptional<z.ZodEnum<["core", "default", "pro"]>>;
|
|
61
151
|
aiApiKeyOverride: z.ZodOptional<z.ZodString>;
|
|
152
|
+
gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
|
|
153
|
+
mcpAuthTokenOverride: z.ZodOptional<z.ZodString>;
|
|
62
154
|
}, "strict", z.ZodTypeAny, {
|
|
63
155
|
members: string[];
|
|
64
156
|
aiApiKeyOverride?: string | undefined;
|
|
157
|
+
gitSyncTokenOverride?: string | undefined;
|
|
158
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
65
159
|
brainVersionOverride?: string | undefined;
|
|
66
160
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
67
161
|
}, {
|
|
68
162
|
members: string[];
|
|
69
163
|
aiApiKeyOverride?: string | undefined;
|
|
164
|
+
gitSyncTokenOverride?: string | undefined;
|
|
165
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
70
166
|
brainVersionOverride?: string | undefined;
|
|
71
167
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
72
168
|
}>, {
|
|
73
169
|
members: string[];
|
|
74
170
|
aiApiKeyOverride?: string | undefined;
|
|
171
|
+
gitSyncTokenOverride?: string | undefined;
|
|
172
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
75
173
|
brainVersionOverride?: string | undefined;
|
|
76
174
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
77
175
|
}, {
|
|
78
176
|
members: string[];
|
|
79
177
|
aiApiKeyOverride?: string | undefined;
|
|
178
|
+
gitSyncTokenOverride?: string | undefined;
|
|
179
|
+
mcpAuthTokenOverride?: string | undefined;
|
|
80
180
|
brainVersionOverride?: string | undefined;
|
|
81
181
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
82
182
|
}>;
|
|
@@ -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
|
@@ -10,4 +10,4 @@ export interface SecretsPushResult {
|
|
|
10
10
|
skippedKeys: string[];
|
|
11
11
|
dryRun?: boolean | undefined;
|
|
12
12
|
}
|
|
13
|
-
export declare function pushPilotSecrets(rootDir: string,
|
|
13
|
+
export declare function pushPilotSecrets(rootDir: string, options?: SecretsPushOptions): Promise<SecretsPushResult>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface AddPilotUserOptions {
|
|
2
|
+
cohort: string;
|
|
3
|
+
anchorId?: string | undefined;
|
|
4
|
+
}
|
|
5
|
+
export interface AddPilotUserResult {
|
|
6
|
+
handle: string;
|
|
7
|
+
cohort: string;
|
|
8
|
+
userPath: string;
|
|
9
|
+
secretsTemplatePath: string;
|
|
10
|
+
cohortPath: string;
|
|
11
|
+
createdUser: boolean;
|
|
12
|
+
createdSecretsTemplate: boolean;
|
|
13
|
+
addedToCohort: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function addPilotUser(rootDir: string, handle: string, options: AddPilotUserOptions): Promise<AddPilotUserResult>;
|
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.61",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
@@ -32,10 +32,14 @@
|
|
|
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
|
+
"test:smoke": "RUN_SMOKE_TESTS=1 bun test --timeout 60000"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"age-encryption": "^0.3.0"
|
|
36
40
|
},
|
|
37
|
-
"dependencies": {},
|
|
38
41
|
"devDependencies": {
|
|
42
|
+
"@brains/deploy-templates": "workspace:*",
|
|
39
43
|
"@brains/eslint-config": "workspace:*",
|
|
40
44
|
"@brains/typescript-config": "workspace:*",
|
|
41
45
|
"@brains/utils": "workspace:*",
|
|
@@ -4,18 +4,29 @@
|
|
|
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
|
|
|
17
|
+
# Content repo administration
|
|
18
|
+
# Local/operator secret only. Used by brains-ops to create missing GitHub repos;
|
|
19
|
+
# do not deploy it into Rover runtime config.
|
|
20
|
+
# @required @sensitive
|
|
21
|
+
CONTENT_REPO_ADMIN_TOKEN=
|
|
22
|
+
|
|
14
23
|
# MCP interface
|
|
24
|
+
# Comes from the decrypted users/<handle>.secrets.yaml.age file.
|
|
15
25
|
# @required @sensitive
|
|
16
26
|
MCP_AUTH_TOKEN=
|
|
17
27
|
|
|
18
28
|
# Discord (optional, per-user)
|
|
29
|
+
# Comes from the decrypted users/<handle>.secrets.yaml.age file when enabled.
|
|
19
30
|
# @sensitive
|
|
20
31
|
DISCORD_BOT_TOKEN=
|
|
21
32
|
|
|
@@ -12,8 +12,14 @@ 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
|
|
19
|
+
workflow_run:
|
|
20
|
+
workflows: [Reconcile]
|
|
21
|
+
types: [completed]
|
|
22
|
+
branches: [main]
|
|
17
23
|
|
|
18
24
|
permissions:
|
|
19
25
|
contents: write
|
|
@@ -21,13 +27,16 @@ permissions:
|
|
|
21
27
|
|
|
22
28
|
jobs:
|
|
23
29
|
resolve_handles:
|
|
30
|
+
if: >
|
|
31
|
+
github.event_name != 'workflow_run' ||
|
|
32
|
+
github.event.workflow_run.conclusion == 'success'
|
|
24
33
|
runs-on: ubuntu-latest
|
|
25
34
|
outputs:
|
|
26
35
|
handles_json: ${{ steps.resolve.outputs.handles_json }}
|
|
27
36
|
steps:
|
|
28
37
|
- uses: actions/checkout@v5
|
|
29
38
|
with:
|
|
30
|
-
ref: ${{ github.sha }}
|
|
39
|
+
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.sha }}
|
|
31
40
|
fetch-depth: 0
|
|
32
41
|
|
|
33
42
|
- uses: oven-sh/setup-bun@v2
|
|
@@ -39,7 +48,7 @@ jobs:
|
|
|
39
48
|
id: resolve
|
|
40
49
|
env:
|
|
41
50
|
HANDLE_INPUT: ${{ inputs.handle || '' }}
|
|
42
|
-
BEFORE_SHA: ${{ github.event.before || '' }}
|
|
51
|
+
BEFORE_SHA: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.event.before || '' }}
|
|
43
52
|
run: bun deploy/scripts/resolve-deploy-handles.ts
|
|
44
53
|
|
|
45
54
|
no_changes:
|
|
@@ -66,15 +75,25 @@ jobs:
|
|
|
66
75
|
steps:
|
|
67
76
|
- uses: actions/checkout@v5
|
|
68
77
|
with:
|
|
69
|
-
ref: ${{ github.sha }}
|
|
78
|
+
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.sha }}
|
|
70
79
|
|
|
71
80
|
- uses: oven-sh/setup-bun@v2
|
|
72
81
|
|
|
73
82
|
- name: Install operator tooling
|
|
74
83
|
run: bun install
|
|
75
84
|
|
|
85
|
+
- name: Decrypt user secrets
|
|
86
|
+
id: user_secrets
|
|
87
|
+
env:
|
|
88
|
+
AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }}
|
|
89
|
+
run: bun deploy/scripts/decrypt-user-secrets.ts "$HANDLE"
|
|
90
|
+
|
|
76
91
|
- name: Reconcile selected user config
|
|
77
|
-
|
|
92
|
+
env:
|
|
93
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
94
|
+
run: |
|
|
95
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
96
|
+
bunx brains-ops onboard "$GITHUB_WORKSPACE" "$HANDLE"
|
|
78
97
|
|
|
79
98
|
- name: Resolve generated user config
|
|
80
99
|
id: user_config
|
|
@@ -82,10 +101,9 @@ jobs:
|
|
|
82
101
|
|
|
83
102
|
- name: Validate selected secrets
|
|
84
103
|
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] || '' }}
|
|
104
|
+
SHARED_AI_API_KEY: ${{ secrets[steps.user_secrets.outputs.shared_ai_api_key_secret_name] }}
|
|
105
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
106
|
+
SHARED_MCP_AUTH_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_mcp_auth_token_secret_name] }}
|
|
89
107
|
HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }}
|
|
90
108
|
HCLOUD_SSH_KEY_NAME: ${{ secrets.HCLOUD_SSH_KEY_NAME }}
|
|
91
109
|
HCLOUD_SERVER_TYPE: ${{ secrets.HCLOUD_SERVER_TYPE }}
|
|
@@ -96,7 +114,19 @@ jobs:
|
|
|
96
114
|
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
|
97
115
|
CERTIFICATE_PEM: ${{ secrets.CERTIFICATE_PEM }}
|
|
98
116
|
PRIVATE_KEY_PEM: ${{ secrets.PRIVATE_KEY_PEM }}
|
|
99
|
-
run:
|
|
117
|
+
run: |
|
|
118
|
+
export AI_API_KEY="${AI_API_KEY:-$SHARED_AI_API_KEY}"
|
|
119
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
120
|
+
export MCP_AUTH_TOKEN="${MCP_AUTH_TOKEN:-$SHARED_MCP_AUTH_TOKEN}"
|
|
121
|
+
bun deploy/scripts/validate-secrets.ts
|
|
122
|
+
|
|
123
|
+
- name: Seed content repo
|
|
124
|
+
env:
|
|
125
|
+
CONTENT_REPO: ${{ steps.user_config.outputs.content_repo }}
|
|
126
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
127
|
+
run: |
|
|
128
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
129
|
+
bun deploy/scripts/sync-content-repo.ts
|
|
100
130
|
|
|
101
131
|
- name: Log in to GHCR
|
|
102
132
|
uses: docker/login-action@v3
|
|
@@ -143,14 +173,17 @@ jobs:
|
|
|
143
173
|
|
|
144
174
|
- name: Write .kamal/secrets
|
|
145
175
|
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] || '' }}
|
|
176
|
+
SHARED_AI_API_KEY: ${{ secrets[steps.user_secrets.outputs.shared_ai_api_key_secret_name] }}
|
|
177
|
+
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
178
|
+
SHARED_MCP_AUTH_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_mcp_auth_token_secret_name] }}
|
|
150
179
|
KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
|
|
151
180
|
CERTIFICATE_PEM: ${{ secrets.CERTIFICATE_PEM }}
|
|
152
181
|
PRIVATE_KEY_PEM: ${{ secrets.PRIVATE_KEY_PEM }}
|
|
153
|
-
run:
|
|
182
|
+
run: |
|
|
183
|
+
export AI_API_KEY="${AI_API_KEY:-$SHARED_AI_API_KEY}"
|
|
184
|
+
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
185
|
+
export MCP_AUTH_TOKEN="${MCP_AUTH_TOKEN:-$SHARED_MCP_AUTH_TOKEN}"
|
|
186
|
+
bun deploy/scripts/write-kamal-secrets.ts
|
|
154
187
|
|
|
155
188
|
- name: Provision server
|
|
156
189
|
id: provision
|
|
@@ -167,8 +200,11 @@ jobs:
|
|
|
167
200
|
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
|
168
201
|
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
|
169
202
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
203
|
+
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
170
204
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
171
|
-
run:
|
|
205
|
+
run: |
|
|
206
|
+
bun deploy/scripts/update-dns.ts
|
|
207
|
+
BRAIN_DOMAIN="$PREVIEW_DOMAIN" bun deploy/scripts/update-dns.ts
|
|
172
208
|
|
|
173
209
|
- name: Validate SSH key
|
|
174
210
|
run: ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null
|
|
@@ -195,6 +231,7 @@ jobs:
|
|
|
195
231
|
IMAGE_REPOSITORY: ${{ steps.user_config.outputs.image_repository }}
|
|
196
232
|
REGISTRY_USERNAME: ${{ steps.user_config.outputs.registry_username }}
|
|
197
233
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
234
|
+
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
198
235
|
BRAIN_YAML_PATH: ${{ steps.user_config.outputs.brain_yaml_path }}
|
|
199
236
|
run: kamal setup --skip-push -c deploy/kamal/deploy.yml
|
|
200
237
|
|
|
@@ -202,7 +239,10 @@ jobs:
|
|
|
202
239
|
env:
|
|
203
240
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
204
241
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
205
|
-
|
|
242
|
+
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
243
|
+
run: |
|
|
244
|
+
curl -I -k --max-time 20 --resolve "$BRAIN_DOMAIN:443:$SERVER_IP" "https://$BRAIN_DOMAIN/health"
|
|
245
|
+
curl -I -k --max-time 20 --resolve "$PREVIEW_DOMAIN:443:$SERVER_IP" "https://$PREVIEW_DOMAIN/"
|
|
206
246
|
|
|
207
247
|
- name: Upload generated config
|
|
208
248
|
uses: actions/upload-artifact@v4
|
|
@@ -211,6 +251,7 @@ jobs:
|
|
|
211
251
|
path: |
|
|
212
252
|
users/${{ matrix.handle }}/brain.yaml
|
|
213
253
|
users/${{ matrix.handle }}/.env
|
|
254
|
+
users/${{ matrix.handle }}/content
|
|
214
255
|
|
|
215
256
|
- name: Dump remote proxy diagnostics
|
|
216
257
|
if: failure()
|
|
@@ -237,7 +278,7 @@ jobs:
|
|
|
237
278
|
steps:
|
|
238
279
|
- uses: actions/checkout@v5
|
|
239
280
|
with:
|
|
240
|
-
ref: ${{ github.sha }}
|
|
281
|
+
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.sha }}
|
|
241
282
|
|
|
242
283
|
- uses: oven-sh/setup-bun@v2
|
|
243
284
|
|
|
@@ -259,8 +300,22 @@ jobs:
|
|
|
259
300
|
if git diff --quiet -- users views; then
|
|
260
301
|
exit 0
|
|
261
302
|
fi
|
|
303
|
+
|
|
304
|
+
git fetch origin "${{ github.ref_name }}"
|
|
305
|
+
if git diff --quiet "origin/${{ github.ref_name }}" -- users views; then
|
|
306
|
+
exit 0
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
patch_file="$(mktemp)"
|
|
310
|
+
git diff --binary -- users views > "$patch_file"
|
|
311
|
+
git reset --hard "origin/${{ github.ref_name }}"
|
|
312
|
+
git apply --3way --index "$patch_file"
|
|
313
|
+
|
|
314
|
+
if git diff --cached --quiet -- users views; then
|
|
315
|
+
exit 0
|
|
316
|
+
fi
|
|
317
|
+
|
|
262
318
|
git config user.name "github-actions[bot]"
|
|
263
319
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
264
|
-
git add users views
|
|
265
320
|
git commit -m "chore(ops): reconcile generated config"
|
|
266
|
-
git push
|
|
321
|
+
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 }}
|