@abyssinia-labs/sokie 0.4.0 → 0.5.0-staging.125

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.
Files changed (2) hide show
  1. package/dist/sokie.mjs +337 -10
  2. package/package.json +2 -2
package/dist/sokie.mjs CHANGED
@@ -24033,9 +24033,9 @@ var require___dev__ = __commonJS((exports, module) => {
24033
24033
 
24034
24034
  // package.json
24035
24035
  var name = "@abyssinia-labs/sokie";
24036
- var version = "0.4.0";
24036
+ var version = "0.5.0-staging.125";
24037
24037
  var sokie = {
24038
- site: "https://sokie.sh"
24038
+ site: "https://staging.sokie.sh"
24039
24039
  };
24040
24040
 
24041
24041
  // src/command.ts
@@ -65881,6 +65881,332 @@ from ${DEV_SECRETS_FILE}, which must be gitignored.`,
65881
65881
  run: run5
65882
65882
  };
65883
65883
 
65884
+ // src/commands/domains.ts
65885
+ import { stdin as stdin4 } from "node:process";
65886
+
65887
+ // src/domains-api.ts
65888
+ var query2 = (name3) => makeFunctionReference(name3);
65889
+ var mutation2 = (name3) => makeFunctionReference(name3);
65890
+ var domainFn = {
65891
+ workspace: query2("domains:workspace"),
65892
+ hostnames: query2("domains:hostnames"),
65893
+ addWorkspace: mutation2("domains:add"),
65894
+ removeWorkspace: mutation2("domains:remove"),
65895
+ checkWorkspace: mutation2("domains:checkNow"),
65896
+ forServer: query2("serverDomains:forServer"),
65897
+ addExact: mutation2("serverDomains:addExact"),
65898
+ removeExact: mutation2("serverDomains:removeExact"),
65899
+ checkServer: mutation2("serverDomains:checkNow")
65900
+ };
65901
+
65902
+ // src/domains.ts
65903
+ var USAGE = new Set(["BAD_DOMAIN", "BARE_DOMAIN", "OUR_DOMAIN", "BAD_NAME"]);
65904
+ function refused3(out, error62) {
65905
+ if (!(error62 instanceof ConvexError))
65906
+ return null;
65907
+ const data = error62.data;
65908
+ if (typeof data !== "object" || data === null)
65909
+ return null;
65910
+ const { code: code2, message } = data;
65911
+ if (typeof code2 !== "string" || typeof message !== "string")
65912
+ return null;
65913
+ return fail(out, code2.toLowerCase(), message, USAGE.has(code2) ? EXIT.usage : EXIT.no);
65914
+ }
65915
+ var STATUS = {
65916
+ waiting: { text: "waiting for DNS", tone: "warn" },
65917
+ issuing: { text: "getting a certificate", tone: "warn" },
65918
+ live: { text: "live", tone: "ok" },
65919
+ problem: { text: "needs a fix", tone: "fail" }
65920
+ };
65921
+ function statusOf2(view) {
65922
+ return STATUS[view.status];
65923
+ }
65924
+ function recordLines(records) {
65925
+ const width = Math.max(...records.map((r) => r.name.length));
65926
+ return records.map((r) => ` ${r.type.padEnd(7)} ${r.name.padEnd(width)} ${r.value}`);
65927
+ }
65928
+ async function allHostnames(client) {
65929
+ const rows = [];
65930
+ let cursor = null;
65931
+ for (;; ) {
65932
+ const page = await client.query(domainFn.hostnames, {
65933
+ paginationOpts: { numItems: 100, cursor }
65934
+ });
65935
+ rows.push(...page.page);
65936
+ if (page.isDone)
65937
+ return rows;
65938
+ cursor = page.continueCursor;
65939
+ }
65940
+ }
65941
+ function hostnameLines(rows) {
65942
+ const server = Math.max(...rows.map((r) => r.serverName.length));
65943
+ const host = Math.max(...rows.map((r) => r.hostname.length));
65944
+ return rows.map((row) => {
65945
+ const status = statusOf2(row);
65946
+ const own2 = row.kind === "exact" ? " (its own domain)" : "";
65947
+ return {
65948
+ text: ` ${row.serverName.padEnd(server)} ${row.hostname.padEnd(host)} ${status.text}${own2}`,
65949
+ tone: status.tone
65950
+ };
65951
+ });
65952
+ }
65953
+ async function listDomains(out, client) {
65954
+ const setup = await client.query(domainFn.workspace, {});
65955
+ const hostnames = await allHostnames(client);
65956
+ if (!setup.configured) {
65957
+ await out.line("Custom domains aren't set up on this Sokie deployment yet.", "dim");
65958
+ } else if (!setup.available && !setup.domain && hostnames.length === 0) {
65959
+ await out.line("Custom domains come with Pro and Scale. Upgrade on the Billing page.", "dim");
65960
+ }
65961
+ const domain2 = setup.domain;
65962
+ if (domain2) {
65963
+ const verified = domain2.status === "verified";
65964
+ const servers = `${domain2.hostnameCount} server${domain2.hostnameCount === 1 ? "" : "s"}`;
65965
+ await out.line(`${domain2.domain} ${verified ? `verified ${servers}` : "waiting for its records"}`, verified ? "ok" : "warn");
65966
+ if (!verified)
65967
+ for (const line of recordLines(domain2.records))
65968
+ await out.line(line);
65969
+ }
65970
+ if (hostnames.length > 0) {
65971
+ if (domain2)
65972
+ await out.line();
65973
+ for (const line of hostnameLines(hostnames))
65974
+ await out.line(line.text, line.tone);
65975
+ }
65976
+ if (setup.configured && setup.available && !domain2 && hostnames.length === 0) {
65977
+ await out.line("No custom domains. sokie domains add mcp.acme.com verifies one for the workspace.");
65978
+ }
65979
+ await out.result({ ok: true, domain: domain2, hostnames });
65980
+ return EXIT.ok;
65981
+ }
65982
+ async function findDomain(client, domain2) {
65983
+ const wanted = domain2.trim().toLowerCase().replace(/\.+$/, "");
65984
+ const setup = await client.query(domainFn.workspace, {});
65985
+ if (setup.domain?.domain === wanted)
65986
+ return { workspace: setup.domain };
65987
+ const row = (await allHostnames(client)).find((h) => h.hostname === wanted);
65988
+ return row ? { hostname: row } : null;
65989
+ }
65990
+ async function checkDomains(out, client, server) {
65991
+ if (server)
65992
+ await client.mutation(domainFn.checkServer, { serverId: server.serverId });
65993
+ else
65994
+ await client.mutation(domainFn.checkWorkspace, {});
65995
+ await out.line(`Checking ${server ? `${server.slug}'s domains` : "the workspace domain"} now. sokie domains shows what Sokie finds.`);
65996
+ await out.result({ ok: true, checking: server ? server.slug : "workspace" });
65997
+ return EXIT.ok;
65998
+ }
65999
+
66000
+ // src/domains-add.ts
66001
+ var POLL_MS2 = 5000;
66002
+ var WAIT_MS2 = 30 * 60000;
66003
+ async function followWorkspace(out, client, deps) {
66004
+ const seen = { txt: false, cname: false };
66005
+ for (let waited = 0;waited < WAIT_MS2; waited += POLL_MS2) {
66006
+ const domain2 = (await client.query(domainFn.workspace, {})).domain;
66007
+ if (!domain2)
66008
+ return null;
66009
+ if (domain2.found.txt && !seen.txt)
66010
+ await out.line(" ✓ TXT found", "ok");
66011
+ if (domain2.found.cname && !seen.cname)
66012
+ await out.line(" ✓ CNAME found", "ok");
66013
+ Object.assign(seen, domain2.found);
66014
+ if (domain2.status === "verified") {
66015
+ await out.line(` ✓ ${domain2.domain} is verified`, "ok");
66016
+ return followNames(out, client, domain2.domain, deps, WAIT_MS2 - waited);
66017
+ }
66018
+ await deps.sleep(POLL_MS2);
66019
+ }
66020
+ await out.line("Still waiting. Sokie keeps checking; sokie domains shows where it stands.", "dim");
66021
+ return null;
66022
+ }
66023
+ async function followNames(out, client, domain2, deps, left) {
66024
+ let names = [];
66025
+ for (let waited = 0;waited < left; waited += POLL_MS2) {
66026
+ names = (await allHostnames(client)).filter((h) => h.hostname.endsWith(`.${domain2}`));
66027
+ if (names.length > 0 && names.every((h) => h.status === "live")) {
66028
+ const list = names.map((h) => h.serverName).join(", ");
66029
+ await out.line(` ● ${names.length} server${names.length === 1 ? "" : "s"} live under it: ${list}`, "ok");
66030
+ await out.line("New servers get theirs as they are made.", "dim");
66031
+ return names;
66032
+ }
66033
+ await deps.sleep(POLL_MS2);
66034
+ }
66035
+ await out.line("Servers are still getting their certificates; sokie domains shows each.", "dim");
66036
+ return names;
66037
+ }
66038
+ async function followExact(out, client, server, deps) {
66039
+ let said = "";
66040
+ for (let waited = 0;waited < WAIT_MS2; waited += POLL_MS2) {
66041
+ const card = await client.query(domainFn.forServer, { serverId: server.serverId });
66042
+ const exact = card.exact;
66043
+ if (!exact)
66044
+ return null;
66045
+ const now = exact.status === "problem" ? exact.problem ?? "problem" : exact.status;
66046
+ if (now !== said) {
66047
+ said = now;
66048
+ if (exact.status === "waiting")
66049
+ await out.line(" ◐ waiting for DNS", "warn");
66050
+ if (exact.status === "issuing")
66051
+ await out.line(" ◐ getting a certificate", "warn");
66052
+ if (exact.status === "problem")
66053
+ await out.line(` ✕ ${exact.problem ?? ""}`, "fail");
66054
+ }
66055
+ if (exact.status === "live") {
66056
+ const shown = exact.primary ? `, now the address Sokie shows for ${server.slug}` : "";
66057
+ await out.line(` ● live · https://${exact.hostname}/mcp${shown}`, "ok");
66058
+ const others = [card.name?.hostname, card.ownHost].filter(Boolean).join(" and ");
66059
+ if (others)
66060
+ await out.line(`${others} keep${others.includes(" and ") ? "" : "s"} working.`, "dim");
66061
+ return exact;
66062
+ }
66063
+ await deps.sleep(POLL_MS2);
66064
+ }
66065
+ await out.line("Still waiting. Sokie keeps checking; sokie domains shows where it stands.", "dim");
66066
+ return null;
66067
+ }
66068
+ async function addDomain(out, client, domain2, options, deps) {
66069
+ const { server, wait } = options;
66070
+ try {
66071
+ if (!server) {
66072
+ const added2 = await client.mutation(domainFn.addWorkspace, { domain: domain2 });
66073
+ await out.line("A workspace domain: every server here gets a name under it. Add these two records, once:", "strong");
66074
+ await out.line();
66075
+ for (const line of recordLines(added2.records))
66076
+ await out.line(line);
66077
+ await out.line();
66078
+ if (!wait) {
66079
+ await out.line("At Cloudflare, leave the CNAME DNS only. Sokie checks every minute.", "dim");
66080
+ await out.result({ ok: true, domain: added2 });
66081
+ return EXIT.ok;
66082
+ }
66083
+ await out.line("At Cloudflare, leave the CNAME DNS only. Waiting for both (Ctrl+C stops waiting; Sokie keeps checking)…", "dim");
66084
+ const names = await followWorkspace(out, client, deps);
66085
+ await out.result({ ok: true, domain: added2, ...names ? { hostnames: names } : {} });
66086
+ return EXIT.ok;
66087
+ }
66088
+ const added = await client.mutation(domainFn.addExact, { serverId: server.serverId, domain: domain2 });
66089
+ if (added.records) {
66090
+ await out.line(`An exact domain for ${server.slug}. Add these records:`, "strong");
66091
+ for (const line of recordLines(added.records))
66092
+ await out.line(line);
66093
+ await out.line("The TXT proves the domain is yours. At Cloudflare, leave the CNAME DNS only.", "dim");
66094
+ } else {
66095
+ await out.line(`${added.hostname} is under your workspace domain: nothing to add.`, "strong");
66096
+ }
66097
+ const live = wait ? await followExact(out, client, server, deps) : null;
66098
+ await out.result({ ok: true, server: server.slug, hostname: live ?? added });
66099
+ return EXIT.ok;
66100
+ } catch (error62) {
66101
+ const failure = refused3(out, error62);
66102
+ if (failure)
66103
+ return failure;
66104
+ throw error62;
66105
+ }
66106
+ }
66107
+ async function removeDomain(out, client, domain2, yes, deps) {
66108
+ const found = await findDomain(client, domain2);
66109
+ if (!found) {
66110
+ return fail(out, "not_found", `${domain2} isn't one of this workspace's domains.`, EXIT.no);
66111
+ }
66112
+ if ("hostname" in found && found.hostname.kind === "workspace") {
66113
+ return fail(out, "is_a_name", `${found.hostname.hostname} is ${found.hostname.serverName}'s name under the workspace domain. Rename it on the server's Settings tab, or remove the workspace domain.`, EXIT.no);
66114
+ }
66115
+ const what = "workspace" in found ? `${found.workspace.domain} and every server's name under it` : `${found.hostname.hostname} from ${found.hostname.serverName}`;
66116
+ if (!yes) {
66117
+ if (!deps.interactive || out.json) {
66118
+ return fail(out, "needs_confirmation", "Confirm it in a terminal, or pass --yes.", EXIT.usage);
66119
+ }
66120
+ if (!await deps.confirm(`Remove ${what}? It stops answering at once.`)) {
66121
+ await out.line("Nothing changed.", "dim");
66122
+ await out.result({ ok: false, error: { code: "declined", message: "Nothing changed." } });
66123
+ return EXIT.no;
66124
+ }
66125
+ }
66126
+ if ("workspace" in found)
66127
+ await client.mutation(domainFn.removeWorkspace, {});
66128
+ else
66129
+ await client.mutation(domainFn.removeExact, { serverId: found.hostname.serverId });
66130
+ await out.line(`Removed ${what}. Each server's own address keeps working.`, "ok");
66131
+ await out.result({
66132
+ ok: true,
66133
+ removed: "workspace" in found ? found.workspace.domain : found.hostname.hostname
66134
+ });
66135
+ return EXIT.ok;
66136
+ }
66137
+
66138
+ // src/commands/domains.ts
66139
+ var VERBS2 = ["list", "add", "remove", "check"];
66140
+ async function runDomains(argv, out, deps) {
66141
+ const { values, positionals } = parseCommandArgs(argv, {
66142
+ ...SITE_OPTION,
66143
+ server: { type: "string" },
66144
+ wait: { type: "boolean", default: false },
66145
+ yes: { type: "boolean", default: false }
66146
+ });
66147
+ const [verb = "list", domain2, ...rest] = positionals;
66148
+ if (!VERBS2.includes(verb)) {
66149
+ throw new UsageError(`No "${verb}": say add, remove or check, or nothing to list domains.`);
66150
+ }
66151
+ if ((verb === "add" || verb === "remove") && (!domain2 || rest.length > 0)) {
66152
+ throw new UsageError(`Give one domain: sokie domains ${verb} mcp.acme.com.`);
66153
+ }
66154
+ if ((verb === "list" || verb === "check") && domain2) {
66155
+ throw new UsageError(`sokie domains ${verb === "list" ? "" : "check "}takes no domain.`);
66156
+ }
66157
+ const wait = {
66158
+ interactive: deps.interactive ?? stdin4.isTTY === true,
66159
+ confirm: deps.confirm ?? confirm,
66160
+ sleep: deps.sleep ?? ((ms) => new Promise((done) => setTimeout(done, ms)))
66161
+ };
66162
+ return deps.signedIn(out, values.site, async (_info, client) => {
66163
+ try {
66164
+ const server = values.server ? await serverBySlug(client, values.server) : null;
66165
+ if (verb === "add") {
66166
+ return await addDomain(out, client, domain2 ?? "", { server, wait: values.wait }, wait);
66167
+ }
66168
+ if (verb === "remove")
66169
+ return await removeDomain(out, client, domain2 ?? "", values.yes, wait);
66170
+ if (verb === "check")
66171
+ return await checkDomains(out, client, server);
66172
+ return await listDomains(out, client);
66173
+ } catch (error62) {
66174
+ if (error62 instanceof DeployError)
66175
+ return fail(out, error62.code, error62.message, EXIT.no);
66176
+ throw error62;
66177
+ }
66178
+ });
66179
+ }
66180
+ var domainsCommand = {
66181
+ name: "domains",
66182
+ synopsis: "sokie domains [add <domain> [--server <slug>] [--wait] | remove <domain> [--yes] | check [--server <slug>]] [--site <url>]",
66183
+ summary: "Serve your servers at your own domain: one for the workspace, or one per server.",
66184
+ help: `With no verb, lists the workspace domain and every address its servers
66185
+ answer on, with where each stands. Custom domains come with Pro and Scale.
66186
+
66187
+ add <domain>
66188
+ Verifies a workspace domain, like mcp.acme.com: every server gets a name
66189
+ under it, like stripe-billing.mcp.acme.com. Prints the two DNS records to
66190
+ add (a TXT that proves it's yours, a wildcard CNAME).
66191
+ add <domain> --server <slug>
66192
+ Gives one server an exact domain, like billing.acme.com, shown as its
66193
+ address once live. Prints its TXT and CNAME records.
66194
+ --wait Follows the domain until it's live. Ctrl+C stops waiting; Sokie
66195
+ keeps checking every minute either way.
66196
+
66197
+ remove <domain>
66198
+ Removes the workspace domain, with every server's name under it, or a
66199
+ server's exact domain. Asks first; --yes doesn't.
66200
+
66201
+ check
66202
+ Looks at the DNS records now instead of at the next minute, and starts
66203
+ again if Sokie had stopped after three days. --server checks a server's.
66204
+
66205
+ A bare domain like acme.com can't point here: use a subdomain.
66206
+ ${SITE_HELP}`,
66207
+ run: (argv, out) => runDomains(argv, out, { signedIn: withSignedIn })
66208
+ };
66209
+
65884
66210
  // src/commands/edit.ts
65885
66211
  import { resolve as resolve7 } from "node:path";
65886
66212
 
@@ -66332,10 +66658,10 @@ the file would change.`,
66332
66658
  // src/commands/init.ts
66333
66659
  import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
66334
66660
  import { basename as basename2, dirname as dirname7, extname, join as join13, relative as relative2, resolve as resolve9, sep as sep3 } from "node:path";
66335
- import { stdin as stdin4, stdout } from "node:process";
66661
+ import { stdin as stdin5, stdout } from "node:process";
66336
66662
  var FETCH_TIMEOUT_MS = 30000;
66337
66663
  var TERMINAL3 = {
66338
- interactive: stdin4.isTTY === true && stdout.isTTY === true,
66664
+ interactive: stdin5.isTTY === true && stdout.isTTY === true,
66339
66665
  askSecret: readSecretValue
66340
66666
  };
66341
66667
  function perCaller(auth2) {
@@ -66482,8 +66808,8 @@ function openBrowser(url2) {
66482
66808
  }
66483
66809
 
66484
66810
  // src/commands/link.ts
66485
- var POLL_MS2 = 2000;
66486
- var WAIT_MS2 = 10 * 60 * 1000;
66811
+ var POLL_MS3 = 2000;
66812
+ var WAIT_MS3 = 10 * 60 * 1000;
66487
66813
  var DIALOG_WAIT_MS = 20 * 60 * 1000;
66488
66814
  var DEPS = {
66489
66815
  signedIn: withSignedIn,
@@ -66524,7 +66850,7 @@ async function runLink(argv, out, deps = DEPS) {
66524
66850
  deps.open(started.url);
66525
66851
  if (!repo)
66526
66852
  await out.line("Pick the repository and folder there. Waiting for the link…");
66527
- const deadline = deps.now() + (repo ? WAIT_MS2 : DIALOG_WAIT_MS);
66853
+ const deadline = deps.now() + (repo ? WAIT_MS3 : DIALOG_WAIT_MS);
66528
66854
  for (;; ) {
66529
66855
  const status = await client.query(fn.linkRequest, { requestId: started.requestId });
66530
66856
  if (status.status === "linked") {
@@ -66536,7 +66862,7 @@ async function runLink(argv, out, deps = DEPS) {
66536
66862
  if (status.status !== "pending" || deps.now() > deadline) {
66537
66863
  return fail(out, "link_failed", status.message ?? "The link request expired. Run `sokie link` again.", EXIT.no);
66538
66864
  }
66539
- await deps.sleep(POLL_MS2);
66865
+ await deps.sleep(POLL_MS3);
66540
66866
  }
66541
66867
  } catch (error62) {
66542
66868
  if (error62 instanceof DeployError)
@@ -66630,7 +66956,7 @@ function startCallbackServer() {
66630
66956
  }
66631
66957
 
66632
66958
  // src/commands/login.ts
66633
- var WAIT_MS3 = 5 * 60 * 1000;
66959
+ var WAIT_MS4 = 5 * 60 * 1000;
66634
66960
  var PASTE_PORT = 53682;
66635
66961
  async function clientIdFor(info2) {
66636
66962
  const stored = readCredentials(info2.site);
@@ -66669,7 +66995,7 @@ async function run7(argv, out) {
66669
66995
  await out.line(url2, "dim");
66670
66996
  openBrowser(url2);
66671
66997
  try {
66672
- ({ code: code2, iss } = await server.wait(state, WAIT_MS3));
66998
+ ({ code: code2, iss } = await server.wait(state, WAIT_MS4));
66673
66999
  } catch (error62) {
66674
67000
  return fail(out, "login_refused", error62 instanceof Error ? error62.message : String(error62), EXIT.no);
66675
67001
  } finally {
@@ -67576,6 +67902,7 @@ var COMMANDS = [
67576
67902
  deploysCommand,
67577
67903
  linkCommand,
67578
67904
  secretsCommand,
67905
+ domainsCommand,
67579
67906
  logsCommand,
67580
67907
  auditCommand,
67581
67908
  configCommand,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@abyssinia-labs/sokie",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.5.0-staging.125",
5
5
  "description": "Turn an OpenAPI or Swagger spec into an MCP server, run it on your machine, and deploy it to Sokie. The sokie command (alias sok).",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://sokie.sh/docs/cli",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "author": "Abyssinia Labs <hello@sokie.sh> (https://sokie.sh)",
12
12
  "sokie": {
13
- "site": "https://sokie.sh"
13
+ "site": "https://staging.sokie.sh"
14
14
  },
15
15
  "keywords": [
16
16
  "mcp",