@nomac/cli 0.5.0 → 0.5.2

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@nomac/cli",
3
- "version": "0.5.0",
4
- "description": "Cloud Mac sessions over SSH and HTTPS, plus iOS builds and publishing \u2014 built for agents.",
3
+ "version": "0.5.2",
4
+ "description": "Cloud Mac sessions over SSH and HTTPS, plus iOS builds and publishing — built for agents.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
7
  "bin": {
@@ -25,8 +25,27 @@ export function transportOptions(options = {}) {
25
25
  return { url, token, agent: proxy ? new HttpsProxyAgent(proxy) : undefined, ca: options.ca };
26
26
  }
27
27
 
28
- /** Bounded requests deliberately do not require streaming responses or retry mutations. */
29
- export function macRequest(method, path, { body, raw, requestKey, ...options } = {}) {
28
+ /** How long a busy guest is retried before guest_busy reaches the caller. */
29
+ export const GUEST_BUSY_RETRY = { budgetMs: 75_000, pauseMs: 3_000 };
30
+
31
+ /**
32
+ * A saturated guest (e.g. a first Simulator boot) answers slowly for a minute or
33
+ * two. Guest operations are idempotent by job ID, request key and blob offset, so
34
+ * only guest_busy is retried; every other failure is returned for reconciliation.
35
+ */
36
+ export function macRequest(method, path, options = {}) {
37
+ const started = Date.now();
38
+ // The first attempt runs synchronously, so invalid input still throws immediately.
39
+ const attempt = () => macRequestOnce(method, path, options).catch(async error => {
40
+ if (error?.code !== "guest_busy" || Date.now() - started + GUEST_BUSY_RETRY.pauseMs + 15_000 > GUEST_BUSY_RETRY.budgetMs) throw error;
41
+ await new Promise(resolve => setTimeout(resolve, GUEST_BUSY_RETRY.pauseMs));
42
+ return attempt();
43
+ });
44
+ return attempt();
45
+ }
46
+
47
+ /** Bounded requests deliberately do not require streaming responses. */
48
+ function macRequestOnce(method, path, { body, raw, requestKey, ...options } = {}) {
30
49
  const { url, token, agent, ca } = transportOptions(options);
31
50
  if (!path.startsWith("/api/v1/mac/") || path.includes("#")) throw new CliError("Invalid Mac API path.");
32
51
  const target = new URL(path, url);
package/src/mcp.mjs CHANGED
@@ -45,12 +45,7 @@ export const TOOLS = [
45
45
  handler: async args => text(await api("POST", "/api/v1/billing/checkout", { body: args })),
46
46
  },
47
47
  {
48
- name: "request_refund", description: "Preview the first-purchase refund amount and subscription cancellation. Default confirm:false. Present the quote and effect to the customer; only after explicit authorization for both actions call with confirm:true and its request_id. A build failure alone does not authorize a refund. Reason is optional.",
49
- inputSchema: { type: "object", properties: { confirm: { type: "boolean", default: false }, request_id: { type: "string" }, reason: { type: "string", maxLength: 1000 } }, additionalProperties: false },
50
- handler: async args => text(await api("POST", "/api/v1/billing/refund", { body: args })),
51
- },
52
- {
53
- name: "get_refund_status", description: "Read the saved refund and cancellation progress. Does not submit a refund or cancel anything.",
48
+ name: "get_refund_status", description: "Read the progress of a refund the account owner requested at nomac.app/usage. Agents cannot request refunds; point the human to that page. Does not submit a refund or cancel anything.",
54
49
  inputSchema: { type: "object", properties: { request_id: { type: "string" } }, required: ["request_id"], additionalProperties: false },
55
50
  handler: async args => text(await api("GET", `/api/v1/billing/refund?request_id=${encodeURIComponent(args.request_id)}`)),
56
51
  },