@autifyhq/aximo-cli 0.16.0 → 0.17.0

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 CHANGED
@@ -693,7 +693,7 @@ claude mcp add aximo -- aximo mcp
693
693
 
694
694
  VS Code (Copilot agent mode) uses the same entry under a `servers` key in `.vscode/mcp.json`.
695
695
 
696
- The server exposes 40 tools: 18 read-only ones for inspecting sessions, cases, plans, plan runs, devices, and uploads (`aximo_get_session_timeline` is the load-bearing one for Playwright export; `aximo_wait_for_session` polls a run; `aximo_list_plans` and `aximo_get_plan` cover plan discovery; `aximo_list_plan_runs` and `aximo_get_plan_run` cover run-status polling; `aximo_list_devices`, `aximo_list_user_devices`, and `aximo_list_uploads` supply the device and upload tokens that case configs and run overrides take), 2 that export a session result to a local file (`aximo_export_session_pdf` for the test result report, `aximo_export_session_screenshots` for a ZIP of the step screenshots), 13 that author content (create, update, archive, restore, and delete cases — including a case's per-target device configuration — create, update, archive, restore, and delete plans, add or remove a plan's cases, save a session as a case), 3 that start runs (`aximo_run_case` starts a case run and returns the `sessionId`; `aximo_create_session` starts a session from a prompt with no saved case; `aximo_run_plan` starts a plan run and returns the `planRunId` — all spend organization credits, and the case and plan runs require `apiEnabled`), 1 that stops one (`aximo_cancel_session` cancels a queued or running session), and 3 that manage the local testbox (`aximo_start_testbox`, `aximo_get_testbox`, `aximo_stop_testbox`: a sandboxed Chromium in Docker, registered as a local device, so an agent can test an app on localhost without taking over your screen; see `aximo device testbox`). See the [MCP reference](./skills/aximo-cli/references/mcp.md) for per-client config paths, the full tool table, the Playwright export prompt, and a no-client smoke test.
696
+ The server exposes 41 tools: 19 read-only ones for inspecting sessions, cases, plans, plan runs, devices, uploads, and the credit balance (`aximo_get_credits` reads what is left before a run spends it; `aximo_get_session_timeline` is the load-bearing one for Playwright export; `aximo_wait_for_session` polls a run; `aximo_list_plans` and `aximo_get_plan` cover plan discovery; `aximo_list_plan_runs` and `aximo_get_plan_run` cover run-status polling; `aximo_list_devices`, `aximo_list_user_devices`, and `aximo_list_uploads` supply the device and upload tokens that case configs and run overrides take), 2 that export a session result to a local file (`aximo_export_session_pdf` for the test result report, `aximo_export_session_screenshots` for a ZIP of the step screenshots), 13 that author content (create, update, archive, restore, and delete cases — including a case's per-target device configuration — create, update, archive, restore, and delete plans, add or remove a plan's cases, save a session as a case), 3 that start runs (`aximo_run_case` starts a case run and returns the `sessionId`; `aximo_create_session` starts a session from a prompt with no saved case; `aximo_run_plan` starts a plan run and returns the `planRunId` — all spend organization credits, and the case and plan runs require `apiEnabled`), 1 that stops one (`aximo_cancel_session` cancels a queued or running session), and 3 that manage the local testbox (`aximo_start_testbox`, `aximo_get_testbox`, `aximo_stop_testbox`: a sandboxed Chromium in Docker, registered as a local device, so an agent can test an app on localhost without taking over your screen; see `aximo device testbox`). See the [MCP reference](./skills/aximo-cli/references/mcp.md) for per-client config paths, the full tool table, the Playwright export prompt, and a no-client smoke test.
697
697
 
698
698
  ## Agent Skills
699
699
 
@@ -24,12 +24,13 @@ async function connect() {
24
24
  };
25
25
  }
26
26
  describe("createMcpServer wiring", () => {
27
- it("exposes all 40 tools through tools/list with valid JSON Schema", async () => {
27
+ it("exposes all 41 tools through tools/list with valid JSON Schema", async () => {
28
28
  const { client, cleanup } = await connect();
29
29
  try {
30
30
  const { tools } = await client.listTools();
31
31
  expect(tools.map((t) => t.name).sort()).toEqual([
32
32
  "aximo_get_case",
33
+ "aximo_get_credits",
33
34
  "aximo_get_session",
34
35
  "aximo_wait_for_session",
35
36
  "aximo_cancel_session",
@@ -102,7 +103,7 @@ describe("createMcpServer wiring", () => {
102
103
  const { client, cleanup } = await connect();
103
104
  try {
104
105
  const { tools } = await client.listTools();
105
- expect(tools.length).toBe(40);
106
+ expect(tools.length).toBe(41);
106
107
  const result = await client.callTool({ name: "aximo_list_projects", arguments: {} });
107
108
  expect(result.isError).toBe(true);
108
109
  const content = result.content;
@@ -161,6 +161,24 @@ export function defineTools(deps = {}) {
161
161
  return res;
162
162
  }),
163
163
  },
164
+ {
165
+ name: "aximo_get_credits",
166
+ title: "Get credit balance",
167
+ description: "Get your organization's credit balance for the current period: granted, used and remaining, in the unit named by creditUnit (legacy or platform). onDemandRemaining is on-demand credit still available once remaining runs out, so remaining can be 0 while runs still start. blocked is true when a new session or run would be refused for lack of credits; metered is false when credits are not enforced. Check it before starting runs to warn when the balance is low.",
168
+ inputSchema: {},
169
+ handler: async () => call(async () => {
170
+ try {
171
+ return await request("/api/organizations/credits", {}, deps);
172
+ }
173
+ catch (e) {
174
+ // Servers before the route answer with Next.js's HTML 404 page.
175
+ if (e.status === 404) {
176
+ throw Object.assign(new Error("This Aximo server does not support the credit balance yet"), { status: 404 });
177
+ }
178
+ throw e;
179
+ }
180
+ }),
181
+ },
164
182
  {
165
183
  name: "aximo_list_devices",
166
184
  title: "List Device Farm devices",
@@ -661,7 +679,7 @@ export function defineTools(deps = {}) {
661
679
  {
662
680
  name: "aximo_run_plan",
663
681
  title: "Run test plan",
664
- description: "Start a run of a test plan (all its cases) and return the new planRunId, totalCases, and the effective executionMode. The plan must be apiEnabled (aximo_get_plan shows this). All fields except planId are per-run overrides of the plan's stored settings: omit them to run with the saved defaults. Poll aximo_get_plan_run with the returned planRunId to follow the run; wait at least 5 seconds between polls.",
682
+ description: "Start a run of a test plan (all its cases) and return the new planRunId, totalCases, and the effective executionMode. The plan must be apiEnabled (aximo_get_plan shows this). All fields except planId and mobileConfig are per-run overrides of the plan's stored settings: omit them to run with the saved defaults. mobileConfig is only a fallback (see its description). Poll aximo_get_plan_run with the returned planRunId to follow the run; wait at least 5 seconds between polls.",
665
683
  inputSchema: {
666
684
  planId: z.string().uuid(),
667
685
  model: z
@@ -685,7 +703,7 @@ export function defineTools(deps = {}) {
685
703
  .describe("Takes precedence over the single-app uploadArn/uploadType/appName fields."),
686
704
  })
687
705
  .optional()
688
- .describe("Device + app for the plan's mobile cases; overrides the plan's stored config."),
706
+ .describe("Fallback device + app, used only for mobile cases with no device saved on the plan or the case. A saved device always wins and keeps its own app; to run on another device, save it on the case or the plan."),
689
707
  desktopConfig: z
690
708
  .object({
691
709
  devices: z.array(z.object({ userDeviceId: z.string().uuid() })).min(1),
@@ -27,6 +27,7 @@ describe("defineTools", () => {
27
27
  const names = defineTools().map((t) => t.name);
28
28
  expect(names).toEqual([
29
29
  "aximo_list_projects",
30
+ "aximo_get_credits",
30
31
  "aximo_list_devices",
31
32
  "aximo_list_user_devices",
32
33
  "aximo_list_uploads",
@@ -153,6 +154,37 @@ describe("aximo_list_cases", () => {
153
154
  expect(parsed.success).toBe(false);
154
155
  });
155
156
  });
157
+ describe("aximo_get_credits", () => {
158
+ it("calls /api/organizations/credits and returns the payload", async () => {
159
+ const payload = {
160
+ creditUnit: "platform",
161
+ granted: 1000,
162
+ used: 250.5,
163
+ remaining: 749.5,
164
+ onDemandRemaining: 0,
165
+ metered: true,
166
+ blocked: false,
167
+ };
168
+ let captured = null;
169
+ server.use(http.get(`${TEST_BASE_URL}/api/organizations/credits`, ({ request: req }) => {
170
+ captured = { method: req.method, url: req.url };
171
+ return HttpResponse.json(payload);
172
+ }));
173
+ const result = await byName(defineTools(), "aximo_get_credits").handler({});
174
+ expect(result.isError).toBeUndefined();
175
+ expect(parsePayload(result)).toEqual(payload);
176
+ expect(captured.method).toBe("GET");
177
+ expect(new URL(captured.url).pathname).toBe("/api/organizations/credits");
178
+ });
179
+ it("says the server is too old instead of returning its HTML 404 page", async () => {
180
+ server.use(http.get(`${TEST_BASE_URL}/api/organizations/credits`, () => new HttpResponse("<!DOCTYPE html><html>404</html>", { status: 404 })));
181
+ const result = await byName(defineTools(), "aximo_get_credits").handler({});
182
+ expect(result.isError).toBe(true);
183
+ expect(parsePayload(result)).toEqual({
184
+ error: "This Aximo server does not support the credit balance yet",
185
+ });
186
+ });
187
+ });
156
188
  describe("aximo_list_projects", () => {
157
189
  it("calls /api/organizations/projects and returns the payload", async () => {
158
190
  const payload = { projects: [{ id: "p1", name: "Default" }] };
@@ -109,7 +109,7 @@ export function getSchema() {
109
109
  cmd("run", "POST /api/triggers/plans/{planId}/run", [
110
110
  arg("planId", "positional", "Plan ID", true),
111
111
  arg("model", "string", ""),
112
- arg("mobile-config", "string", "JSON object string"),
112
+ arg("mobile-config", "string", "JSON object string: fallback device + app, used only for mobile cases with no device saved on the plan or the case"),
113
113
  ...MUTATE_ARGS,
114
114
  ]),
115
115
  cmd("get", "GET /api/triggers/plans/{planId}/runs/{planRunId}", [
@@ -20,7 +20,7 @@ declare const _default: import("citty").CommandDef<{
20
20
  };
21
21
  readonly "mobile-config": {
22
22
  readonly type: "string";
23
- readonly description: "JSON object string";
23
+ readonly description: "JSON object string: fallback device + app, used only for mobile cases with no device saved on the plan or the case";
24
24
  };
25
25
  readonly "execution-mode": {
26
26
  readonly type: "string";
@@ -48,7 +48,10 @@ export default defineCommand({
48
48
  args: {
49
49
  planId: { type: "positional", required: true },
50
50
  model: { type: "string" },
51
- "mobile-config": { type: "string", description: "JSON object string" },
51
+ "mobile-config": {
52
+ type: "string",
53
+ description: "JSON object string: fallback device + app, used only for mobile cases with no device saved on the plan or the case",
54
+ },
52
55
  "execution-mode": {
53
56
  type: "string",
54
57
  description: "parallel or sequential (defaults to the plan's stored setting)",
package/dist/types.d.ts CHANGED
@@ -231,6 +231,15 @@ export interface Project {
231
231
  name?: string | undefined;
232
232
  [key: string]: unknown;
233
233
  }
234
+ export interface CreditBalance {
235
+ creditUnit: "legacy" | "platform";
236
+ granted: number;
237
+ used: number;
238
+ remaining: number;
239
+ onDemandRemaining: number;
240
+ metered: boolean;
241
+ blocked: boolean;
242
+ }
234
243
  export interface CreateCaseInput {
235
244
  projectId: string;
236
245
  scenario: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autifyhq/aximo-cli",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Autify Aximo Command Line Interface (CLI)",
5
5
  "keywords": [
6
6
  "autify",