@rizom/ops 0.2.0-alpha.140 → 0.2.0-alpha.142

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.
@@ -43,6 +43,7 @@ const handles = [
43
43
  .map((path) => {
44
44
  const match =
45
45
  path.match(/^users\/([^/]+)\/(?:\.env|brain\.yaml|content\/.*)$/) ??
46
+ path.match(/^users\/([^/]+)\.yaml$/) ??
46
47
  path.match(/^users\/([^/]+)\.secrets\.yaml\.age$/);
47
48
  return match?.[1] ?? null;
48
49
  })
@@ -1,4 +1,6 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { readFileSync } from "node:fs";
3
+ import { loadPilotRegistry } from "@rizom/ops";
2
4
 
3
5
  import { parseEnvFile, requireEnv, writeGitHubOutput } from "./helpers";
4
6
 
@@ -7,6 +9,14 @@ const envPath = `users/${handle}/.env`;
7
9
  const brainYamlPath = `users/${handle}/brain.yaml`;
8
10
 
9
11
  const envEntries = parseEnvFile(envPath);
12
+ const registry = await loadPilotRegistry(process.cwd());
13
+ const user = registry.users.find((candidate) => candidate.handle === handle);
14
+ if (!user) {
15
+ throw new Error(`Unknown user handle: ${handle}`);
16
+ }
17
+
18
+ const brainVersion = envEntries["BRAIN_VERSION"] ?? "";
19
+ const imageTag = resolveImageTag(registry, brainVersion);
10
20
  const repository = process.env["GITHUB_REPOSITORY"] ?? "";
11
21
  const repositoryOwner = repository.split("/")[0] ?? "";
12
22
 
@@ -17,23 +27,28 @@ if (!brainDomain) {
17
27
  throw new Error(`Missing domain in ${brainYamlPath}`);
18
28
  }
19
29
 
20
- const zone =
21
- brainDomain.startsWith(`${handle}.`) && brainDomain.length > handle.length + 1
22
- ? brainDomain.slice(handle.length + 1)
30
+ const pilotSubdomainPrefix = `${handle}.`;
31
+ const pilotZone =
32
+ brainDomain.startsWith(pilotSubdomainPrefix) &&
33
+ brainDomain.length > pilotSubdomainPrefix.length
34
+ ? brainDomain.slice(pilotSubdomainPrefix.length)
23
35
  : "";
24
- if (!zone) {
25
- throw new Error(`Could not derive preview domain from ${brainDomain}`);
26
- }
27
- const previewDomain = `${handle}-preview.${zone}`;
36
+ const previewDomain = pilotZone
37
+ ? `${handle}-preview.${pilotZone}`
38
+ : `preview.${brainDomain}`;
39
+ const wwwDomain = pilotZone ? "" : `www.${brainDomain}`;
28
40
 
29
41
  const outputs: Record<string, string> = {
30
- brain_version: envEntries["BRAIN_VERSION"] ?? "",
42
+ brain_version: brainVersion,
31
43
  content_repo: envEntries["CONTENT_REPO"] ?? "",
32
44
  brain_domain: brainDomain,
33
45
  preview_domain: previewDomain,
46
+ www_domain: wwwDomain,
47
+ cloudflare_zone_id: user.cloudflareZoneId ?? "",
34
48
  brain_yaml_path: brainYamlPath,
35
49
  instance_name: `rover-${handle}`,
36
50
  image_repository: `ghcr.io/${repository}`,
51
+ image_tag: imageTag,
37
52
  registry_username: repositoryOwner,
38
53
  };
39
54
 
@@ -47,3 +62,35 @@ for (const key of required) {
47
62
  for (const [key, value] of Object.entries(outputs)) {
48
63
  writeGitHubOutput(key, value);
49
64
  }
65
+
66
+ function resolveImageTag(
67
+ registry: Awaited<ReturnType<typeof loadPilotRegistry>>,
68
+ brainVersion: string,
69
+ ): string {
70
+ if (!brainVersion) {
71
+ return "";
72
+ }
73
+
74
+ const sitePackages = [
75
+ ...new Set(
76
+ registry.users
77
+ .filter((user) => user.brainVersion === brainVersion)
78
+ .flatMap((user) =>
79
+ user.siteOverride
80
+ ? [`${user.siteOverride.package}@${user.siteOverride.version}`]
81
+ : [],
82
+ ),
83
+ ),
84
+ ].sort();
85
+
86
+ if (sitePackages.length === 0) {
87
+ return `brain-${brainVersion}`;
88
+ }
89
+
90
+ const siteHash = createHash("sha256")
91
+ .update(sitePackages.join("\n"))
92
+ .digest("hex")
93
+ .slice(0, 12);
94
+
95
+ return `brain-${brainVersion}-sites-${siteHash}`;
96
+ }
@@ -54,40 +54,40 @@ Most people spend most of their time in Chat. The CMS becomes useful once you ha
54
54
  ---
55
55
 
56
56
  ## Other ways to chat with Rover
57
-
57
+
58
58
  `/chat` is the primary interface, but Rover can also be reached through messaging platforms. Discord is tested and working. Slack, WhatsApp Business, and similar should work out of the box — we'll send setup steps if that's part of your configuration.
59
59
 
60
60
  **Talking to another Rover**
61
-
61
+
62
62
  If you know another Rover user, you can add them to your contacts and call their Rover directly from your chat.
63
-
64
- - To add: *Add jane.rizom.ai to my contacts.*
65
- - To call: *Call jane.rizom.ai and ask [your question].*
66
- You'll need their address — in the format `name.rizom.ai` — which they can share with you directly.
67
-
63
+
64
+ - To add: _Add jane.rizom.ai to my contacts._
65
+ - To call: _Call jane.rizom.ai and ask [your question]._
66
+ You'll need their address — in the format `name.rizom.ai` — which they can share with you directly.
67
+
68
68
  ---
69
-
69
+
70
70
  ## Connecting Rover to other tools
71
-
71
+
72
72
  Rover can connect to external tools via MCP — a standard protocol that works in two directions: agentic AI clients (like Claude Desktop) can use it to talk to Rover, and Rover can use it to talk to productivity tools like Notion or Linear.
73
-
73
+
74
74
  Plugins exist for a number of tools already, and new ones can be built quickly in response to what you actually need. If there's a tool you'd want Rover to connect to, tell us.
75
-
75
+
76
76
  If MCP is part of your setup, we'll send specific instructions alongside your URL.
77
-
77
+
78
78
  ---
79
-
79
+
80
80
  ## A few common questions
81
-
81
+
82
82
  **How do I access my data?**
83
83
  Your primary interface is the CMS (`/cms`), where you can browse and edit everything you've saved. If you're set up with GitHub access, your content also lives in a private repository you can access directly.
84
-
84
+
85
85
  **Does Rover connect to the internet?**
86
86
  No — and that's intentional. Rover works with what you've put into it, not with whatever the internet currently says. That means its answers are grounded in your actual knowledge base, not in generic search results.
87
-
87
+
88
88
  **What does this cost?**
89
89
  Nothing during the pilot. We cover hosting costs while you're part of the programme.
90
-
90
+
91
91
  ---
92
92
 
93
93
  ## This is a pilot