@autifyhq/aximo-cli 0.15.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.
Files changed (74) hide show
  1. package/README.md +19 -1
  2. package/dist/commands/device/device-config.d.ts +2 -2
  3. package/dist/commands/device/device-config.js +5 -8
  4. package/dist/commands/device/index.js +2 -1
  5. package/dist/commands/device/paths.d.ts +4 -0
  6. package/dist/commands/device/paths.js +12 -0
  7. package/dist/commands/device/reset.d.ts +1 -0
  8. package/dist/commands/device/reset.js +18 -3
  9. package/dist/commands/device/reset.test.js +13 -0
  10. package/dist/commands/device/testbox/box.d.ts +90 -0
  11. package/dist/commands/device/testbox/box.js +379 -0
  12. package/dist/commands/device/testbox/box.test.d.ts +1 -0
  13. package/dist/commands/device/testbox/box.test.js +815 -0
  14. package/dist/commands/device/testbox/container.d.ts +58 -0
  15. package/dist/commands/device/testbox/container.js +289 -0
  16. package/dist/commands/device/testbox/docker.d.ts +31 -0
  17. package/dist/commands/device/testbox/docker.js +78 -0
  18. package/dist/commands/device/testbox/docker.test.d.ts +1 -0
  19. package/dist/commands/device/testbox/docker.test.js +82 -0
  20. package/dist/commands/device/testbox/down.d.ts +7 -0
  21. package/dist/commands/device/testbox/down.js +14 -0
  22. package/dist/commands/device/testbox/errors.d.ts +4 -0
  23. package/dist/commands/device/testbox/errors.js +7 -0
  24. package/dist/commands/device/testbox/fake-docker.d.ts +25 -0
  25. package/dist/commands/device/testbox/fake-docker.js +35 -0
  26. package/dist/commands/device/testbox/index.d.ts +2 -0
  27. package/dist/commands/device/testbox/index.js +16 -0
  28. package/dist/commands/device/testbox/logs.d.ts +15 -0
  29. package/dist/commands/device/testbox/logs.js +43 -0
  30. package/dist/commands/device/testbox/prune.d.ts +11 -0
  31. package/dist/commands/device/testbox/prune.js +17 -0
  32. package/dist/commands/device/testbox/registration.d.ts +9 -0
  33. package/dist/commands/device/testbox/registration.js +56 -0
  34. package/dist/commands/device/testbox/reset.d.ts +7 -0
  35. package/dist/commands/device/testbox/reset.js +14 -0
  36. package/dist/commands/device/testbox/result.d.ts +8 -0
  37. package/dist/commands/device/testbox/result.js +23 -0
  38. package/dist/commands/device/testbox/result.test.d.ts +1 -0
  39. package/dist/commands/device/testbox/result.test.js +34 -0
  40. package/dist/commands/device/testbox/self-command.d.ts +12 -0
  41. package/dist/commands/device/testbox/self-command.js +36 -0
  42. package/dist/commands/device/testbox/self-command.test.d.ts +1 -0
  43. package/dist/commands/device/testbox/self-command.test.js +58 -0
  44. package/dist/commands/device/testbox/status.d.ts +7 -0
  45. package/dist/commands/device/testbox/status.js +11 -0
  46. package/dist/commands/device/testbox/up.d.ts +47 -0
  47. package/dist/commands/device/testbox/up.js +76 -0
  48. package/dist/commands/device/testbox/up.test.d.ts +1 -0
  49. package/dist/commands/device/testbox/up.test.js +52 -0
  50. package/dist/commands/device/testbox/url.d.ts +7 -0
  51. package/dist/commands/device/testbox/url.js +20 -0
  52. package/dist/commands/device/user-devices.d.ts +4 -0
  53. package/dist/commands/device/user-devices.js +18 -0
  54. package/dist/commands/mcp/server.d.ts +2 -2
  55. package/dist/commands/mcp/server.test.js +7 -2
  56. package/dist/commands/mcp/testbox-tools.test.d.ts +1 -0
  57. package/dist/commands/mcp/testbox-tools.test.js +278 -0
  58. package/dist/commands/mcp/tools.d.ts +8 -1
  59. package/dist/commands/mcp/tools.js +171 -20
  60. package/dist/commands/mcp/tools.test.js +59 -2
  61. package/dist/commands/schema.js +5 -1
  62. package/dist/commands/session/cancel.d.ts +20 -0
  63. package/dist/commands/session/cancel.js +39 -0
  64. package/dist/commands/session/index.js +1 -0
  65. package/dist/commands/session/session.test.js +38 -0
  66. package/dist/commands/trigger/plan/run.d.ts +1 -1
  67. package/dist/commands/trigger/plan/run.js +4 -1
  68. package/dist/errors.d.ts +2 -0
  69. package/dist/errors.js +5 -0
  70. package/dist/errors.test.js +6 -1
  71. package/dist/index.d.ts +1 -0
  72. package/dist/index.js +1 -0
  73. package/dist/types.d.ts +14 -0
  74. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
5
5
  import { z } from "zod";
6
- import { ARCHIVE_FIRST_HINT, handleApiError, isArchiveFirstConflict } from "../../errors.js";
6
+ import { ARCHIVE_FIRST_HINT, CREDIT_HINT, handleApiError, isArchiveFirstConflict, } from "../../errors.js";
7
7
  import { HttpResponse, http, server, TEST_BASE_URL } from "../../test-utils.js";
8
8
  import { defineTools } from "./tools.js";
9
9
  beforeAll(() => {
@@ -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",
@@ -49,6 +50,7 @@ describe("defineTools", () => {
49
50
  "aximo_delete_case",
50
51
  "aximo_run_case",
51
52
  "aximo_create_session",
53
+ "aximo_cancel_session",
52
54
  "aximo_create_plan",
53
55
  "aximo_add_case_to_plan",
54
56
  "aximo_remove_case_from_plan",
@@ -62,6 +64,9 @@ describe("defineTools", () => {
62
64
  "aximo_list_plan_runs",
63
65
  "aximo_get_plan_run",
64
66
  "aximo_save_session_as_case",
67
+ "aximo_start_testbox",
68
+ "aximo_get_testbox",
69
+ "aximo_stop_testbox",
65
70
  ]);
66
71
  });
67
72
  });
@@ -149,6 +154,37 @@ describe("aximo_list_cases", () => {
149
154
  expect(parsed.success).toBe(false);
150
155
  });
151
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
+ });
152
188
  describe("aximo_list_projects", () => {
153
189
  it("calls /api/organizations/projects and returns the payload", async () => {
154
190
  const payload = { projects: [{ id: "p1", name: "Default" }] };
@@ -288,6 +324,24 @@ describe("aximo_wait_for_session", () => {
288
324
  expect(parsePayload(result)).toEqual({ error: "Session not found" });
289
325
  });
290
326
  });
327
+ describe("aximo_cancel_session", () => {
328
+ const sessionId = "78787878-7878-4787-9787-787878787878";
329
+ it("POSTs the cancel endpoint and returns the terminated session", async () => {
330
+ const payload = { success: true, sessionId, status: "terminated" };
331
+ server.use(http.post(`${TEST_BASE_URL}/api/sessions/${sessionId}/cancel`, () => HttpResponse.json(payload)));
332
+ const result = await byName(defineTools(), "aximo_cancel_session").handler({ sessionId });
333
+ expect(result.isError).toBeUndefined();
334
+ expect(parsePayload(result)).toEqual(payload);
335
+ });
336
+ it("reports a finished session as an error with no archive hint", async () => {
337
+ server.use(http.post(`${TEST_BASE_URL}/api/sessions/${sessionId}/cancel`, () => HttpResponse.json({ error: "Cannot cancel a session that has already finished" }, { status: 409 })));
338
+ const result = await byName(defineTools(), "aximo_cancel_session").handler({ sessionId });
339
+ expect(result.isError).toBe(true);
340
+ expect(parsePayload(result)).toEqual({
341
+ error: "Cannot cancel a session that has already finished",
342
+ });
343
+ });
344
+ });
291
345
  describe("aximo_create_session", () => {
292
346
  const projectId = "12121212-1212-4212-9212-121212121212";
293
347
  it("POSTs /api/sessions with the prompt and device and returns sessionId", async () => {
@@ -370,7 +424,10 @@ describe("aximo_run_case", () => {
370
424
  const tool = byName(defineTools(), "aximo_run_case");
371
425
  const result = await tool.handler({ caseId });
372
426
  expect(result.isError).toBe(true);
373
- expect(parsePayload(result).error).toBe("Insufficient credits to start session");
427
+ expect(parsePayload(result)).toEqual({
428
+ error: "Insufficient credits to start session",
429
+ hint: CREDIT_HINT,
430
+ });
374
431
  });
375
432
  it("maps a 429 to a rate-limit error an agent can back off on", async () => {
376
433
  server.use(http.post(`${TEST_BASE_URL}/api/triggers/cases/${caseId}/run`, () => HttpResponse.json({ error: "Rate limit exceeded. Try again later." }, { status: 429, headers: { "Retry-After": "60" } })));
@@ -42,6 +42,10 @@ export function getSchema() {
42
42
  cmd("get", "GET /api/sessions/{sessionId}", [
43
43
  arg("sessionId", "positional", "Session ID", true),
44
44
  ]),
45
+ cmd("cancel", "POST /api/sessions/{sessionId}/cancel", [
46
+ arg("sessionId", "positional", "Session ID", true),
47
+ arg("dry-run", "boolean", "Validate and show request without calling API"),
48
+ ]),
45
49
  cmd("save-as-case", "POST /api/sessions/{sessionId}/save-as-case", [
46
50
  arg("sessionId", "positional", "Session ID", true),
47
51
  arg("title", "string", ""),
@@ -105,7 +109,7 @@ export function getSchema() {
105
109
  cmd("run", "POST /api/triggers/plans/{planId}/run", [
106
110
  arg("planId", "positional", "Plan ID", true),
107
111
  arg("model", "string", ""),
108
- 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"),
109
113
  ...MUTATE_ARGS,
110
114
  ]),
111
115
  cmd("get", "GET /api/triggers/plans/{planId}/runs/{planRunId}", [
@@ -0,0 +1,20 @@
1
+ import { type ClientDeps } from "../../client.js";
2
+ import { type DryRunResult } from "../../mutate.js";
3
+ import type { CancelSessionResult, Envelope } from "../../types.js";
4
+ export interface SessionCancelOptions {
5
+ sessionId?: unknown;
6
+ dryRun?: unknown;
7
+ }
8
+ export declare function runSessionCancel(options: SessionCancelOptions, deps?: ClientDeps): Promise<Envelope<CancelSessionResult | DryRunResult>>;
9
+ declare const _default: import("citty").CommandDef<{
10
+ readonly sessionId: {
11
+ readonly type: "positional";
12
+ readonly description: "Session ID";
13
+ readonly required: true;
14
+ };
15
+ readonly "dry-run": {
16
+ readonly type: "boolean";
17
+ readonly description: "Validate and show request without calling API";
18
+ };
19
+ }>;
20
+ export default _default;
@@ -0,0 +1,39 @@
1
+ import { defineCommand } from "citty";
2
+ import { request } from "../../client.js";
3
+ import { handleApiError } from "../../errors.js";
4
+ import { dryRunEnvelope } from "../../mutate.js";
5
+ import { formatSuccess, printResult, validationError } from "../../output.js";
6
+ import { requireId } from "../../validate.js";
7
+ export async function runSessionCancel(options, deps) {
8
+ let sessionId;
9
+ try {
10
+ sessionId = requireId("sessionId", options.sessionId);
11
+ }
12
+ catch (err) {
13
+ return validationError(err);
14
+ }
15
+ const path = `/api/sessions/${encodeURIComponent(sessionId)}/cancel`;
16
+ if (options.dryRun) {
17
+ return dryRunEnvelope("POST", path);
18
+ }
19
+ try {
20
+ const res = await request(path, { method: "POST" }, deps ?? {});
21
+ return formatSuccess(res);
22
+ }
23
+ catch (err) {
24
+ return handleApiError(err);
25
+ }
26
+ }
27
+ export default defineCommand({
28
+ meta: {
29
+ name: "cancel",
30
+ description: "Cancel a queued or running session (stops it on its device)",
31
+ },
32
+ args: {
33
+ sessionId: { type: "positional", description: "Session ID", required: true },
34
+ "dry-run": { type: "boolean", description: "Validate and show request without calling API" },
35
+ },
36
+ async run({ args }) {
37
+ printResult(await runSessionCancel({ sessionId: args.sessionId, dryRun: args["dry-run"] }));
38
+ },
39
+ });
@@ -4,6 +4,7 @@ export default defineCommand({
4
4
  subCommands: {
5
5
  create: () => import("./create.js").then((m) => m.default),
6
6
  get: () => import("./get.js").then((m) => m.default),
7
+ cancel: () => import("./cancel.js").then((m) => m.default),
7
8
  export: () => import("./export.js").then((m) => m.default),
8
9
  "save-as-case": () => import("./save-as-case.js").then((m) => m.default),
9
10
  },
@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
3
3
  import { join, resolve } from "node:path";
4
4
  import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
5
5
  import { server, http, HttpResponse, TEST_BASE_URL } from "../../test-utils.js";
6
+ import { runSessionCancel } from "./cancel.js";
6
7
  import { runSessionCreate } from "./create.js";
7
8
  import { runSessionExport } from "./export.js";
8
9
  import { runSessionGet } from "./get.js";
@@ -130,6 +131,43 @@ describe("runSessionGet", () => {
130
131
  expect(result.error.code).toBe("not_found");
131
132
  });
132
133
  });
134
+ describe("runSessionCancel", () => {
135
+ it("POSTs the cancel endpoint and returns the terminated session", async () => {
136
+ let method;
137
+ server.use(http.post(`${TEST_BASE_URL}/api/sessions/s1/cancel`, ({ request: req }) => {
138
+ method = req.method;
139
+ return HttpResponse.json({ success: true, sessionId: "s1", status: "terminated" });
140
+ }));
141
+ const result = await runSessionCancel({ sessionId: "s1" });
142
+ expect(method).toBe("POST");
143
+ expect(result).toEqual({
144
+ ok: true,
145
+ data: { success: true, sessionId: "s1", status: "terminated" },
146
+ });
147
+ });
148
+ it("shows the request without calling the API on --dry-run", async () => {
149
+ const result = await runSessionCancel({ sessionId: "s1", dryRun: true });
150
+ expect(result).toEqual({
151
+ ok: true,
152
+ data: { dryRun: true, method: "POST", url: `${TEST_BASE_URL}/api/sessions/s1/cancel` },
153
+ });
154
+ });
155
+ it("rejects an empty id", async () => {
156
+ const result = await runSessionCancel({ sessionId: "" });
157
+ expect(result.ok).toBe(false);
158
+ if (!result.ok)
159
+ expect(result.error.code).toBe("validation_error");
160
+ });
161
+ it("maps a finished session to conflict without the archive hint", async () => {
162
+ server.use(http.post(`${TEST_BASE_URL}/api/sessions/s1/cancel`, () => HttpResponse.json({ error: "Cannot cancel a session that has already finished" }, { status: 409 })));
163
+ const result = await runSessionCancel({ sessionId: "s1" });
164
+ expect(result.ok).toBe(false);
165
+ if (!result.ok) {
166
+ expect(result.error.code).toBe("conflict");
167
+ expect(result.error.hint).toBeUndefined();
168
+ }
169
+ });
170
+ });
133
171
  describe("runSessionExport", () => {
134
172
  const sessionId = "44444444-4444-4444-9444-444444444444";
135
173
  const defaultPdf = resolve(`${sessionId}.pdf`);
@@ -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/errors.d.ts CHANGED
@@ -2,6 +2,8 @@ import type { ErrorEnvelope } from "./types.js";
2
2
  export declare function describeDetails(details: unknown): string | undefined;
3
3
  export declare function withDetails(message: string, details: unknown): string;
4
4
  export declare function featureHint(message: string, details: unknown): string | undefined;
5
+ export declare const CREDIT_HINT = "The organization is out of credits. Ask an Aximo organization admin to add credits, then retry.";
6
+ export declare function creditHint(message: string): string | undefined;
5
7
  export declare const ARCHIVE_FIRST_HINT = "Archive the resource first, then retry: `aximo case archive <caseId>` / `aximo plan archive <planId>` (MCP: aximo_archive_case / aximo_archive_plan).";
6
8
  export declare function isArchiveFirstConflict(message: string): boolean;
7
9
  export declare function handleApiError(error: unknown): ErrorEnvelope;
package/dist/errors.js CHANGED
@@ -85,6 +85,10 @@ export function featureHint(message, details) {
85
85
  }
86
86
  return undefined;
87
87
  }
88
+ export const CREDIT_HINT = "The organization is out of credits. Ask an Aximo organization admin to add credits, then retry.";
89
+ export function creditHint(message) {
90
+ return /insufficient credits/i.test(message) ? CREDIT_HINT : undefined;
91
+ }
88
92
  const NOT_FOUND_HINT = "Check the ID exists and you have access. List resources: aximo project list / aximo device list / aximo upload list";
89
93
  const AUTH_HINT = "Run `aximo configure` to save credentials, or set AXIMO_API_KEY via `export AXIMO_API_KEY=your-key` or `node --env-file=.env`.";
90
94
  // 403 is a permission outcome, not a credentials problem, so it no longer routes
@@ -142,6 +146,7 @@ export function handleApiError(error) {
142
146
  // when the server says which role is missing. Never the credentials hint: a
143
147
  // 403 means the key is valid.
144
148
  const hint = featureHint(error.message, details) ??
149
+ creditHint(error.message) ??
145
150
  (/admin|owner/i.test(message) ? ADMIN_ROLE_HINT : undefined);
146
151
  return formatError("forbidden", message, false, hint);
147
152
  }
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { describeDetails, featureHint, handleApiError, withDetails } from "./errors.js";
2
+ import { CREDIT_HINT, describeDetails, featureHint, handleApiError, withDetails, } from "./errors.js";
3
3
  function httpErr(status, message = `status ${status}`, retryAfter) {
4
4
  const err = Object.assign(new Error(message), { status });
5
5
  if (retryAfter !== undefined)
@@ -27,6 +27,11 @@ describe("handleApiError", () => {
27
27
  expect(r.error.code).toBe("forbidden");
28
28
  expect(r.error.hint).toContain("admin or owner");
29
29
  });
30
+ it("adds the credit hint when the organization is out of credits", () => {
31
+ const r = handleApiError(httpErr(403, "Insufficient credits to start session"));
32
+ expect(r.error.code).toBe("forbidden");
33
+ expect(r.error.hint).toBe(CREDIT_HINT);
34
+ });
30
35
  it("maps 404 to not_found with list hint", () => {
31
36
  const r = handleApiError(httpErr(404));
32
37
  expect(r.error.code).toBe("not_found");
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export { runPlanRemoveCase } from "./commands/plan/remove-case.js";
12
12
  export { runProjectList } from "./commands/project/list.js";
13
13
  export { runSessionCreate } from "./commands/session/create.js";
14
14
  export { runSessionGet } from "./commands/session/get.js";
15
+ export { runSessionCancel } from "./commands/session/cancel.js";
15
16
  export { runSessionSaveAsCase } from "./commands/session/save-as-case.js";
16
17
  export { runTriggerCaseGet } from "./commands/trigger/case/get.js";
17
18
  export { runTriggerCaseRun } from "./commands/trigger/case/run.js";
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ export { runPlanRemoveCase } from "./commands/plan/remove-case.js";
11
11
  export { runProjectList } from "./commands/project/list.js";
12
12
  export { runSessionCreate } from "./commands/session/create.js";
13
13
  export { runSessionGet } from "./commands/session/get.js";
14
+ export { runSessionCancel } from "./commands/session/cancel.js";
14
15
  export { runSessionSaveAsCase } from "./commands/session/save-as-case.js";
15
16
  export { runTriggerCaseGet } from "./commands/trigger/case/get.js";
16
17
  export { runTriggerCaseRun } from "./commands/trigger/case/run.js";
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;
@@ -305,6 +314,11 @@ export interface CreatePlanResult {
305
314
  export interface PlanCaseResult {
306
315
  success: boolean;
307
316
  }
317
+ export interface CancelSessionResult {
318
+ success: boolean;
319
+ sessionId: string;
320
+ status: "terminated";
321
+ }
308
322
  export interface SaveSessionAsCaseInput {
309
323
  title?: string | undefined;
310
324
  type?: Exclude<CaseType, "multi_device"> | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autifyhq/aximo-cli",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Autify Aximo Command Line Interface (CLI)",
5
5
  "keywords": [
6
6
  "autify",