@rubytech/create-maxy 1.0.790 → 1.0.792

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.790",
3
+ "version": "1.0.792",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -1557,6 +1557,7 @@ server.tool("api-key-verify", "Verify the stored Anthropic API key works by maki
1557
1557
  * anthropic-setup state machine (action-relay pattern)
1558
1558
  *
1559
1559
  * Call 1: anthropic-setup({})
1560
+ * ├─ no public endpoint → { status: "not_needed" } ← Phase 0 gate
1560
1561
  * ├─ key stored + valid? → { status: "complete" }
1561
1562
  * ├─ key stored + billing → { status: "awaiting_credits" }
1562
1563
  * ├─ key stored + error → { status: "error" }
@@ -1630,20 +1631,92 @@ function anthropicResult(r) {
1630
1631
  ...(r.status === "error" ? { isError: true } : {}),
1631
1632
  };
1632
1633
  }
1634
+ /**
1635
+ * Check whether a public-facing hostname is configured. The Anthropic API key
1636
+ * powers the public agent only — admin runs on Claude OAuth — so without a
1637
+ * public endpoint there is nothing for the key to power.
1638
+ *
1639
+ * Mirrors the runtime `isPublicHost(host)` predicate in
1640
+ * `platform/ui/server/index.ts`: a host is public iff it starts with `public.`
1641
+ * OR is listed in `~/{configDir}/alias-domains.json`. Step 7's form submit
1642
+ * handler at `platform/ui/server/routes/admin/cloudflare.ts` writes secondary
1643
+ * hostnames (apex + custom public labels) to `alias-domains.json` and skips
1644
+ * `public.*` because the prefix itself is the signal — so the two predicates
1645
+ * together cover every shape the form can produce.
1646
+ *
1647
+ * Returns true if any configured hostname satisfies either predicate.
1648
+ */
1649
+ function hasPublicEndpointConfigured() {
1650
+ const aliasPath = join(CONFIG_DIR, "alias-domains.json");
1651
+ if (existsSync(aliasPath)) {
1652
+ try {
1653
+ const parsed = JSON.parse(readFileSync(aliasPath, "utf-8"));
1654
+ if (Array.isArray(parsed)) {
1655
+ const entries = parsed.filter((h) => typeof h === "string" && h.length > 0);
1656
+ if (entries.length > 0) {
1657
+ return { has: true, reason: `alias-domains.json: ${entries.join(", ")}` };
1658
+ }
1659
+ }
1660
+ }
1661
+ catch {
1662
+ // Malformed file — treat as empty; matches the server-side reader's tolerance.
1663
+ }
1664
+ }
1665
+ const cfConfigPath = join(CONFIG_DIR, "cloudflared", "config.yml");
1666
+ if (existsSync(cfConfigPath)) {
1667
+ try {
1668
+ const yaml = readFileSync(cfConfigPath, "utf-8");
1669
+ const hostnames = [...yaml.matchAll(/^\s*-\s*hostname:\s*(\S+)/gm)].map((m) => m[1]);
1670
+ const publicPrefixed = hostnames.find((h) => h.startsWith("public."));
1671
+ if (publicPrefixed) {
1672
+ return { has: true, reason: `cloudflared/config.yml ingress: ${publicPrefixed}` };
1673
+ }
1674
+ }
1675
+ catch {
1676
+ // Unreadable — treat as no public host.
1677
+ }
1678
+ }
1679
+ return {
1680
+ has: false,
1681
+ reason: "no entries in alias-domains.json and no public.* hostname in cloudflared/config.yml",
1682
+ };
1683
+ }
1633
1684
  server.tool("anthropic-setup", "Deterministic state machine for Anthropic API key acquisition. " +
1634
1685
  "Checks current state, advances as far as possible, and returns a structured JSON result. " +
1635
- "On first call (no consoleResult): checks if a valid key is already stored. If not, returns " +
1636
- "status 'awaiting_signin' with a browser_evaluate action the agent must open the browser to " +
1637
- "platform.claude.com, run the action's function via browser_evaluate, and pass the string " +
1638
- "result back as consoleResult on the next call. " +
1686
+ "On first call (no consoleResult): first verifies a public-facing endpoint is configured " +
1687
+ "(the key only powers the public agent); if not, returns status 'not_needed'. Otherwise " +
1688
+ "checks if a valid key is already stored. If not, returns status 'awaiting_signin' with " +
1689
+ "a browser_evaluate action the agent must open the browser to platform.claude.com, run " +
1690
+ "the action's function via browser_evaluate, and pass the string result back as " +
1691
+ "consoleResult on the next call. " +
1639
1692
  "On second call (with consoleResult): parses the Console API result, stores the key, " +
1640
1693
  "verifies it, and returns status 'complete'. " +
1641
- "Statuses: complete (key stored and verified), awaiting_signin (user must sign in to Console), " +
1642
- "awaiting_credits (signed in but no credits user must add credits), error (fatal relay message).", {
1694
+ "Statuses: complete (key stored and verified), not_needed (no public endpoint configured " +
1695
+ "skip the step), awaiting_signin (user must sign in to Console), awaiting_credits (signed in " +
1696
+ "but no credits — user must add credits), error (fatal — relay message).", {
1643
1697
  consoleResult: z.string().optional().describe("JSON string result from running the browser_evaluate action returned by a prior call. " +
1644
1698
  "Omit on the first call."),
1645
1699
  }, async ({ consoleResult }) => {
1646
1700
  const log = (msg) => console.error(`[anthropic-setup] ${msg}`);
1701
+ // ── Phase 0: gate on public endpoint existence ─────────────
1702
+ // The API key only powers the public-facing agent. If no public hostname
1703
+ // is configured (operator skipped step 7, or completed it without a
1704
+ // public/apex hostname) the key has no consumer — short-circuit before
1705
+ // any Console interaction so we never create keys the operator cannot use.
1706
+ if (!consoleResult) {
1707
+ const publicCheck = hasPublicEndpointConfigured();
1708
+ if (!publicCheck.has) {
1709
+ log(`gate: no public endpoint (${publicCheck.reason}) — returning not_needed`);
1710
+ return anthropicResult({
1711
+ status: "not_needed",
1712
+ message: "No public-facing endpoint is configured, so an Anthropic API key is not needed. " +
1713
+ "The key powers the public agent only — admin runs on your Claude OAuth session. " +
1714
+ "When you set up a public hostname via Cloudflare (or apex domain), come back and " +
1715
+ "ask to set up the API key then.",
1716
+ });
1717
+ }
1718
+ log(`gate: public endpoint present (${publicCheck.reason})`);
1719
+ }
1647
1720
  // ── Phase 1: check stored key ──────────────────────────────
1648
1721
  if (!consoleResult) {
1649
1722
  log("checking stored key");