@rizom/ops 0.2.0-alpha.5 → 0.2.0-alpha.50
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 +7 -4
- package/dist/age-key-bootstrap.d.ts +17 -0
- package/dist/brains-ops.js +301 -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 +301 -149
- 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/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 +2 -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 +6 -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 +78 -19
- package/templates/rover-pilot/.github/workflows/reconcile.yml +16 -2
- package/templates/rover-pilot/README.md +7 -4
- 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/docs/onboarding-checklist.md +30 -10
- package/templates/rover-pilot/docs/operator-playbook.md +53 -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 };
|
|
@@ -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>;
|
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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FetchLike } from "@brains/utils/origin-ca";
|
|
2
2
|
import type { LoadPilotRegistryOptions } from "./load-registry";
|
|
3
|
+
import { type LookupHost } from "./observed-status";
|
|
3
4
|
import type { ParsedArgs } from "./parse-args";
|
|
4
5
|
import { type RunCommand as OpsRunCommand } from "./run-subprocess";
|
|
5
6
|
import { type SshKeygen } from "./ssh-key-bootstrap";
|
|
@@ -13,7 +14,7 @@ export interface CommandDependencies extends LoadPilotRegistryOptions {
|
|
|
13
14
|
env?: NodeJS.ProcessEnv | undefined;
|
|
14
15
|
logger?: ((message: string) => void) | undefined;
|
|
15
16
|
fetchImpl?: FetchLike | undefined;
|
|
16
|
-
|
|
17
|
+
lookupHost?: LookupHost | undefined;
|
|
17
18
|
bootstrapRunCommand?: OpsRunCommand | undefined;
|
|
18
19
|
sshKeygen?: SshKeygen | undefined;
|
|
19
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.50",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
@@ -32,10 +32,13 @@
|
|
|
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": {
|
|
41
|
+
"@brains/deploy-templates": "workspace:*",
|
|
39
42
|
"@brains/eslint-config": "workspace:*",
|
|
40
43
|
"@brains/typescript-config": "workspace:*",
|
|
41
44
|
"@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
|
|
@@ -192,13 +228,21 @@ jobs:
|
|
|
192
228
|
env:
|
|
193
229
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
194
230
|
VERSION: brain-${{ steps.user_config.outputs.brain_version }}
|
|
231
|
+
IMAGE_REPOSITORY: ${{ steps.user_config.outputs.image_repository }}
|
|
232
|
+
REGISTRY_USERNAME: ${{ steps.user_config.outputs.registry_username }}
|
|
233
|
+
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
234
|
+
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
235
|
+
BRAIN_YAML_PATH: ${{ steps.user_config.outputs.brain_yaml_path }}
|
|
195
236
|
run: kamal setup --skip-push -c deploy/kamal/deploy.yml
|
|
196
237
|
|
|
197
238
|
- name: Verify origin TLS
|
|
198
239
|
env:
|
|
199
240
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
200
241
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
201
|
-
|
|
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/"
|
|
202
246
|
|
|
203
247
|
- name: Upload generated config
|
|
204
248
|
uses: actions/upload-artifact@v4
|
|
@@ -207,6 +251,7 @@ jobs:
|
|
|
207
251
|
path: |
|
|
208
252
|
users/${{ matrix.handle }}/brain.yaml
|
|
209
253
|
users/${{ matrix.handle }}/.env
|
|
254
|
+
users/${{ matrix.handle }}/content
|
|
210
255
|
|
|
211
256
|
- name: Dump remote proxy diagnostics
|
|
212
257
|
if: failure()
|
|
@@ -233,7 +278,7 @@ jobs:
|
|
|
233
278
|
steps:
|
|
234
279
|
- uses: actions/checkout@v5
|
|
235
280
|
with:
|
|
236
|
-
ref: ${{ github.sha }}
|
|
281
|
+
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.sha }}
|
|
237
282
|
|
|
238
283
|
- uses: oven-sh/setup-bun@v2
|
|
239
284
|
|
|
@@ -255,8 +300,22 @@ jobs:
|
|
|
255
300
|
if git diff --quiet -- users views; then
|
|
256
301
|
exit 0
|
|
257
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
|
+
|
|
258
318
|
git config user.name "github-actions[bot]"
|
|
259
319
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
260
|
-
git add users views
|
|
261
320
|
git commit -m "chore(ops): reconcile generated config"
|
|
262
|
-
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 }}
|