@autifyhq/aximo-cli 0.15.0 → 0.16.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 +19 -1
- package/dist/commands/device/device-config.d.ts +2 -2
- package/dist/commands/device/device-config.js +5 -8
- package/dist/commands/device/index.js +2 -1
- package/dist/commands/device/paths.d.ts +4 -0
- package/dist/commands/device/paths.js +12 -0
- package/dist/commands/device/reset.d.ts +1 -0
- package/dist/commands/device/reset.js +18 -3
- package/dist/commands/device/reset.test.js +13 -0
- package/dist/commands/device/testbox/box.d.ts +90 -0
- package/dist/commands/device/testbox/box.js +379 -0
- package/dist/commands/device/testbox/box.test.d.ts +1 -0
- package/dist/commands/device/testbox/box.test.js +815 -0
- package/dist/commands/device/testbox/container.d.ts +58 -0
- package/dist/commands/device/testbox/container.js +289 -0
- package/dist/commands/device/testbox/docker.d.ts +31 -0
- package/dist/commands/device/testbox/docker.js +78 -0
- package/dist/commands/device/testbox/docker.test.d.ts +1 -0
- package/dist/commands/device/testbox/docker.test.js +82 -0
- package/dist/commands/device/testbox/down.d.ts +7 -0
- package/dist/commands/device/testbox/down.js +14 -0
- package/dist/commands/device/testbox/errors.d.ts +4 -0
- package/dist/commands/device/testbox/errors.js +7 -0
- package/dist/commands/device/testbox/fake-docker.d.ts +25 -0
- package/dist/commands/device/testbox/fake-docker.js +35 -0
- package/dist/commands/device/testbox/index.d.ts +2 -0
- package/dist/commands/device/testbox/index.js +16 -0
- package/dist/commands/device/testbox/logs.d.ts +15 -0
- package/dist/commands/device/testbox/logs.js +43 -0
- package/dist/commands/device/testbox/prune.d.ts +11 -0
- package/dist/commands/device/testbox/prune.js +17 -0
- package/dist/commands/device/testbox/registration.d.ts +9 -0
- package/dist/commands/device/testbox/registration.js +56 -0
- package/dist/commands/device/testbox/reset.d.ts +7 -0
- package/dist/commands/device/testbox/reset.js +14 -0
- package/dist/commands/device/testbox/result.d.ts +8 -0
- package/dist/commands/device/testbox/result.js +23 -0
- package/dist/commands/device/testbox/result.test.d.ts +1 -0
- package/dist/commands/device/testbox/result.test.js +34 -0
- package/dist/commands/device/testbox/self-command.d.ts +12 -0
- package/dist/commands/device/testbox/self-command.js +36 -0
- package/dist/commands/device/testbox/self-command.test.d.ts +1 -0
- package/dist/commands/device/testbox/self-command.test.js +58 -0
- package/dist/commands/device/testbox/status.d.ts +7 -0
- package/dist/commands/device/testbox/status.js +11 -0
- package/dist/commands/device/testbox/up.d.ts +47 -0
- package/dist/commands/device/testbox/up.js +76 -0
- package/dist/commands/device/testbox/up.test.d.ts +1 -0
- package/dist/commands/device/testbox/up.test.js +52 -0
- package/dist/commands/device/testbox/url.d.ts +7 -0
- package/dist/commands/device/testbox/url.js +20 -0
- package/dist/commands/device/user-devices.d.ts +4 -0
- package/dist/commands/device/user-devices.js +18 -0
- package/dist/commands/mcp/server.d.ts +2 -2
- package/dist/commands/mcp/server.test.js +6 -2
- package/dist/commands/mcp/testbox-tools.test.d.ts +1 -0
- package/dist/commands/mcp/testbox-tools.test.js +278 -0
- package/dist/commands/mcp/tools.d.ts +8 -1
- package/dist/commands/mcp/tools.js +151 -18
- package/dist/commands/mcp/tools.test.js +27 -2
- package/dist/commands/schema.js +4 -0
- package/dist/commands/session/cancel.d.ts +20 -0
- package/dist/commands/session/cancel.js +39 -0
- package/dist/commands/session/index.js +1 -0
- package/dist/commands/session/session.test.js +38 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +5 -0
- package/dist/errors.test.js +6 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -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`);
|
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
|
}
|
package/dist/errors.test.js
CHANGED
|
@@ -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
|
@@ -305,6 +305,11 @@ export interface CreatePlanResult {
|
|
|
305
305
|
export interface PlanCaseResult {
|
|
306
306
|
success: boolean;
|
|
307
307
|
}
|
|
308
|
+
export interface CancelSessionResult {
|
|
309
|
+
success: boolean;
|
|
310
|
+
sessionId: string;
|
|
311
|
+
status: "terminated";
|
|
312
|
+
}
|
|
308
313
|
export interface SaveSessionAsCaseInput {
|
|
309
314
|
title?: string | undefined;
|
|
310
315
|
type?: Exclude<CaseType, "multi_device"> | undefined;
|