lobstercage-cli 0.1.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 +64 -0
- package/dist/commands/auth.d.ts +39 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +393 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/billing.d.ts +11 -0
- package/dist/commands/billing.d.ts.map +1 -0
- package/dist/commands/billing.js +181 -0
- package/dist/commands/billing.js.map +1 -0
- package/dist/commands/cages.d.ts +87 -0
- package/dist/commands/cages.d.ts.map +1 -0
- package/dist/commands/cages.js +603 -0
- package/dist/commands/cages.js.map +1 -0
- package/dist/commands/env.d.ts +42 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +287 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/exec.d.ts +12 -0
- package/dist/commands/exec.d.ts.map +1 -0
- package/dist/commands/exec.js +127 -0
- package/dist/commands/exec.js.map +1 -0
- package/dist/commands/tunnel.d.ts +29 -0
- package/dist/commands/tunnel.d.ts.map +1 -0
- package/dist/commands/tunnel.js +232 -0
- package/dist/commands/tunnel.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +96 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +348 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/config.d.ts +63 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +193 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/docker.d.ts +52 -0
- package/dist/lib/docker.d.ts.map +1 -0
- package/dist/lib/docker.js +210 -0
- package/dist/lib/docker.js.map +1 -0
- package/dist/lib/error-handler.d.ts +30 -0
- package/dist/lib/error-handler.d.ts.map +1 -0
- package/dist/lib/error-handler.js +113 -0
- package/dist/lib/error-handler.js.map +1 -0
- package/dist/lib/output.d.ts +71 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +237 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/utils.d.ts +17 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +71 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/types/index.d.ts +158 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/lib/docker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAc,QAAQ,EAAE,MAAM,UAAU,CAAC;AAS3D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA4CD;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDzF;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAMjD;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAazD;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3D;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAQzE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7D;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAyB7F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Local Docker Engine for cage management
|
|
4
|
+
*
|
|
5
|
+
* Implements cage operations using Docker instead of the remote API.
|
|
6
|
+
* Used when --local flag is set or no API is configured.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.deployCage = deployCage;
|
|
13
|
+
exports.listCages = listCages;
|
|
14
|
+
exports.getCage = getCage;
|
|
15
|
+
exports.startCage = startCage;
|
|
16
|
+
exports.stopCage = stopCage;
|
|
17
|
+
exports.destroyCage = destroyCage;
|
|
18
|
+
exports.getCageLogs = getCageLogs;
|
|
19
|
+
exports.getContainerName = getContainerName;
|
|
20
|
+
const dockerode_1 = __importDefault(require("dockerode"));
|
|
21
|
+
const crypto_1 = require("crypto");
|
|
22
|
+
const docker = new dockerode_1.default();
|
|
23
|
+
const LABEL_PREFIX = 'lobstercage';
|
|
24
|
+
const CONTAINER_PREFIX = 'lobster-';
|
|
25
|
+
const DEFAULT_IMAGE = 'lobstercage/sandbox:latest';
|
|
26
|
+
const WORKSPACE_MOUNT = '/home/agent/workspace';
|
|
27
|
+
function containerName(name) {
|
|
28
|
+
return `${CONTAINER_PREFIX}${name}`;
|
|
29
|
+
}
|
|
30
|
+
function generateCageId() {
|
|
31
|
+
return `cage_local_${(0, crypto_1.randomBytes)(8).toString('hex')}`;
|
|
32
|
+
}
|
|
33
|
+
function containerToCage(container) {
|
|
34
|
+
const labels = container.Labels;
|
|
35
|
+
const name = labels[`${LABEL_PREFIX}.cage-name`] || container.Names[0]?.replace(/^\/lobster-/, '') || 'unknown';
|
|
36
|
+
const cageId = labels[`${LABEL_PREFIX}.cage-id`] || 'unknown';
|
|
37
|
+
let status;
|
|
38
|
+
const state = container.State;
|
|
39
|
+
if (state === 'running') {
|
|
40
|
+
status = 'running';
|
|
41
|
+
}
|
|
42
|
+
else if (state === 'exited' || state === 'dead') {
|
|
43
|
+
status = 'stopped';
|
|
44
|
+
}
|
|
45
|
+
else if (state === 'created' || state === 'restarting') {
|
|
46
|
+
status = 'pending';
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
status = 'stopped';
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
id: cageId,
|
|
53
|
+
name,
|
|
54
|
+
status,
|
|
55
|
+
image: container.Image,
|
|
56
|
+
resources: {
|
|
57
|
+
cpu: 1024,
|
|
58
|
+
memory: 2048,
|
|
59
|
+
storage: 20,
|
|
60
|
+
},
|
|
61
|
+
network: {
|
|
62
|
+
egressPolicy: 'allow-all',
|
|
63
|
+
},
|
|
64
|
+
createdAt: new Date(container.Created * 1000).toISOString(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Deploy a new cage as a local Docker container
|
|
69
|
+
*/
|
|
70
|
+
async function deployCage(name, options = {}) {
|
|
71
|
+
const cageId = generateCageId();
|
|
72
|
+
const cName = containerName(name);
|
|
73
|
+
// Determine image
|
|
74
|
+
let image = DEFAULT_IMAGE;
|
|
75
|
+
if (options.template) {
|
|
76
|
+
image = `lobstercage/sandbox:${options.template}`;
|
|
77
|
+
}
|
|
78
|
+
else if (options.image) {
|
|
79
|
+
image = options.image;
|
|
80
|
+
}
|
|
81
|
+
const cpuLimit = (options.cpu || 1) * 1e9; // nanoCPUs
|
|
82
|
+
const memoryLimit = (options.memory || 2048) * 1024 * 1024; // bytes
|
|
83
|
+
const volumeName = `lobster-${name}-workspace`;
|
|
84
|
+
const env = [
|
|
85
|
+
`CAGE_ID=${cageId}`,
|
|
86
|
+
`CAGE_NAME=${name}`,
|
|
87
|
+
`USER_ID=local`,
|
|
88
|
+
...Object.entries(options.env || {}).map(([k, v]) => `${k}=${v}`),
|
|
89
|
+
];
|
|
90
|
+
const container = await docker.createContainer({
|
|
91
|
+
name: cName,
|
|
92
|
+
Image: image,
|
|
93
|
+
Env: env,
|
|
94
|
+
Labels: {
|
|
95
|
+
[LABEL_PREFIX]: 'true',
|
|
96
|
+
[`${LABEL_PREFIX}.cage-name`]: name,
|
|
97
|
+
[`${LABEL_PREFIX}.cage-id`]: cageId,
|
|
98
|
+
},
|
|
99
|
+
HostConfig: {
|
|
100
|
+
NanoCpus: cpuLimit,
|
|
101
|
+
Memory: memoryLimit,
|
|
102
|
+
Binds: [`${volumeName}:${WORKSPACE_MOUNT}`],
|
|
103
|
+
},
|
|
104
|
+
Tty: true,
|
|
105
|
+
OpenStdin: true,
|
|
106
|
+
});
|
|
107
|
+
await container.start();
|
|
108
|
+
return {
|
|
109
|
+
id: cageId,
|
|
110
|
+
name,
|
|
111
|
+
status: 'running',
|
|
112
|
+
image,
|
|
113
|
+
resources: {
|
|
114
|
+
cpu: ((options.cpu || 1) * 1024),
|
|
115
|
+
memory: (options.memory || 2048),
|
|
116
|
+
storage: 20,
|
|
117
|
+
},
|
|
118
|
+
network: { egressPolicy: 'allow-all' },
|
|
119
|
+
createdAt: new Date().toISOString(),
|
|
120
|
+
startedAt: new Date().toISOString(),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* List all local cages
|
|
125
|
+
*/
|
|
126
|
+
async function listCages() {
|
|
127
|
+
const containers = await docker.listContainers({
|
|
128
|
+
all: true,
|
|
129
|
+
filters: { label: [`${LABEL_PREFIX}=true`] },
|
|
130
|
+
});
|
|
131
|
+
return containers.map(containerToCage);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get a single cage by name
|
|
135
|
+
*/
|
|
136
|
+
async function getCage(name) {
|
|
137
|
+
const containers = await docker.listContainers({
|
|
138
|
+
all: true,
|
|
139
|
+
filters: {
|
|
140
|
+
label: [`${LABEL_PREFIX}=true`, `${LABEL_PREFIX}.cage-name=${name}`],
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
if (containers.length === 0) {
|
|
144
|
+
throw new Error(`Cage not found: ${name}`);
|
|
145
|
+
}
|
|
146
|
+
return containerToCage(containers[0]);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Start a stopped cage
|
|
150
|
+
*/
|
|
151
|
+
async function startCage(name) {
|
|
152
|
+
const container = docker.getContainer(containerName(name));
|
|
153
|
+
await container.start();
|
|
154
|
+
return getCage(name);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Stop a running cage
|
|
158
|
+
*/
|
|
159
|
+
async function stopCage(name, force = false) {
|
|
160
|
+
const container = docker.getContainer(containerName(name));
|
|
161
|
+
if (force) {
|
|
162
|
+
await container.kill();
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
await container.stop();
|
|
166
|
+
}
|
|
167
|
+
return getCage(name);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Destroy a cage (remove container and optionally volume)
|
|
171
|
+
*/
|
|
172
|
+
async function destroyCage(name) {
|
|
173
|
+
const container = docker.getContainer(containerName(name));
|
|
174
|
+
await container.remove({ force: true });
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get cage logs
|
|
178
|
+
*/
|
|
179
|
+
async function getCageLogs(name, options = {}) {
|
|
180
|
+
const container = docker.getContainer(containerName(name));
|
|
181
|
+
const logStream = await container.logs({
|
|
182
|
+
stdout: true,
|
|
183
|
+
stderr: true,
|
|
184
|
+
tail: options.tail ?? 100,
|
|
185
|
+
since: options.since ? Math.floor(new Date(options.since).getTime() / 1000) : undefined,
|
|
186
|
+
timestamps: true,
|
|
187
|
+
});
|
|
188
|
+
const logStr = typeof logStream === 'string' ? logStream : logStream.toString('utf-8');
|
|
189
|
+
if (!logStr.trim())
|
|
190
|
+
return [];
|
|
191
|
+
return logStr.trim().split('\n').map(line => {
|
|
192
|
+
// Docker log lines may have 8-byte header prefix in multiplexed streams
|
|
193
|
+
const cleaned = line.replace(/^[\x00-\x08].{7}/, '');
|
|
194
|
+
const spaceIdx = cleaned.indexOf(' ');
|
|
195
|
+
const timestamp = spaceIdx > 0 ? cleaned.slice(0, spaceIdx) : new Date().toISOString();
|
|
196
|
+
const message = spaceIdx > 0 ? cleaned.slice(spaceIdx + 1) : cleaned;
|
|
197
|
+
return {
|
|
198
|
+
timestamp,
|
|
199
|
+
stream: 'stdout',
|
|
200
|
+
message,
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get the container name for exec (used by exec command with child_process)
|
|
206
|
+
*/
|
|
207
|
+
function getContainerName(name) {
|
|
208
|
+
return containerName(name);
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/lib/docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized Error Handler
|
|
3
|
+
*
|
|
4
|
+
* Replaces repeated try/catch boilerplate across CLI commands with a single
|
|
5
|
+
* `handleError()` function that handles spinner cleanup, contextual suggestions,
|
|
6
|
+
* JSON output, debug stack traces, and process.exit.
|
|
7
|
+
*/
|
|
8
|
+
import type { Ora } from 'ora';
|
|
9
|
+
export interface HandleErrorOptions {
|
|
10
|
+
spinner?: Ora;
|
|
11
|
+
exitCode?: number;
|
|
12
|
+
json?: boolean;
|
|
13
|
+
context?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if debug mode is enabled via --debug flag or DEBUG env var.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isDebug(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Centralized error handler. Call from catch blocks to replace boilerplate.
|
|
21
|
+
*
|
|
22
|
+
* - Stops the spinner if provided
|
|
23
|
+
* - For ApiClientError: prints message + contextual suggestion
|
|
24
|
+
* - For generic Error: prints context-aware message
|
|
25
|
+
* - In --json mode: outputs structured JSON error
|
|
26
|
+
* - In --debug mode: prints stack trace
|
|
27
|
+
* - Calls process.exit(exitCode)
|
|
28
|
+
*/
|
|
29
|
+
export declare function handleError(err: unknown, options?: HandleErrorOptions): never;
|
|
30
|
+
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/lib/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAkB/B,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,kBAAuB,GAAG,KAAK,CAmCjF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Centralized Error Handler
|
|
4
|
+
*
|
|
5
|
+
* Replaces repeated try/catch boilerplate across CLI commands with a single
|
|
6
|
+
* `handleError()` function that handles spinner cleanup, contextual suggestions,
|
|
7
|
+
* JSON output, debug stack traces, and process.exit.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.isDebug = isDebug;
|
|
44
|
+
exports.handleError = handleError;
|
|
45
|
+
const api_1 = require("./api");
|
|
46
|
+
const output = __importStar(require("./output"));
|
|
47
|
+
/**
|
|
48
|
+
* Error code → actionable suggestion mapping
|
|
49
|
+
*/
|
|
50
|
+
const SUGGESTIONS = {
|
|
51
|
+
CAGE_NOT_FOUND: 'Check cage name with `lobster list`',
|
|
52
|
+
CAGE_NOT_RUNNING: 'Start the cage first with `lobster start <name>`',
|
|
53
|
+
CAGE_RUNNING: 'Stop the cage first with `lobster stop <name>`',
|
|
54
|
+
NETWORK_ERROR: 'Check your internet connection and API URL',
|
|
55
|
+
AUTH_REQUIRED: 'Run `lobster login` first',
|
|
56
|
+
FORBIDDEN: "You don't have permission. Check your API key",
|
|
57
|
+
RATE_LIMITED: 'Too many requests. Wait a moment and try again',
|
|
58
|
+
INSUFFICIENT_CREDITS: 'Add credits with `lobster upgrade`',
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Check if debug mode is enabled via --debug flag or DEBUG env var.
|
|
62
|
+
*/
|
|
63
|
+
function isDebug() {
|
|
64
|
+
return !!(process.env.DEBUG || process.argv.includes('--debug'));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Centralized error handler. Call from catch blocks to replace boilerplate.
|
|
68
|
+
*
|
|
69
|
+
* - Stops the spinner if provided
|
|
70
|
+
* - For ApiClientError: prints message + contextual suggestion
|
|
71
|
+
* - For generic Error: prints context-aware message
|
|
72
|
+
* - In --json mode: outputs structured JSON error
|
|
73
|
+
* - In --debug mode: prints stack trace
|
|
74
|
+
* - Calls process.exit(exitCode)
|
|
75
|
+
*/
|
|
76
|
+
function handleError(err, options = {}) {
|
|
77
|
+
const { spinner, exitCode = 1, json, context } = options;
|
|
78
|
+
if (spinner) {
|
|
79
|
+
spinner.stop();
|
|
80
|
+
}
|
|
81
|
+
if (err instanceof api_1.ApiClientError) {
|
|
82
|
+
if (json) {
|
|
83
|
+
output.json({ error: err.code, message: err.message });
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
output.error(err.message);
|
|
87
|
+
const suggestion = SUGGESTIONS[err.code];
|
|
88
|
+
if (suggestion) {
|
|
89
|
+
output.info(suggestion);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (err instanceof Error) {
|
|
94
|
+
if (json) {
|
|
95
|
+
output.json({ error: 'UNKNOWN_ERROR', message: err.message });
|
|
96
|
+
}
|
|
97
|
+
else if (context) {
|
|
98
|
+
output.error(`Failed ${context}: ${err.message}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
output.error(err.message);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
output.error('An unexpected error occurred');
|
|
106
|
+
}
|
|
107
|
+
if (isDebug() && err instanceof Error && err.stack) {
|
|
108
|
+
console.error('\nStack trace:');
|
|
109
|
+
console.error(err.stack);
|
|
110
|
+
}
|
|
111
|
+
process.exit(exitCode);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/lib/error-handler.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BH,0BAEC;AAYD,kCAmCC;AA5ED,+BAAuC;AACvC,iDAAmC;AAEnC;;GAEG;AACH,MAAM,WAAW,GAA2B;IAC1C,cAAc,EAAE,qCAAqC;IACrD,gBAAgB,EAAE,kDAAkD;IACpE,YAAY,EAAE,gDAAgD;IAC9D,aAAa,EAAE,4CAA4C;IAC3D,aAAa,EAAE,2BAA2B;IAC1C,SAAS,EAAE,+CAA+C;IAC1D,YAAY,EAAE,gDAAgD;IAC9D,oBAAoB,EAAE,oCAAoC;CAC3D,CAAC;AASF;;GAEG;AACH,SAAgB,OAAO;IACrB,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,WAAW,CAAC,GAAY,EAAE,UAA8B,EAAE;IACxE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEzD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,EAAE,CAAC;IACjB,CAAC;IAED,IAAI,GAAG,YAAY,oBAAc,EAAE,CAAC;QAClC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC1B,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;SAAM,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChE,CAAC;aAAM,IAAI,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,OAAO,EAAE,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACnD,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output Formatting Utilities
|
|
3
|
+
*
|
|
4
|
+
* Consistent output formatting for the CLI.
|
|
5
|
+
*/
|
|
6
|
+
import type { Cage, CageStatus, EnvVar, SecurityWarning, LogEntry, ApiKey } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Format cage status with color
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatStatus(status: CageStatus): string;
|
|
11
|
+
/**
|
|
12
|
+
* Format a cage for display
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatCage(cage: Cage, verbose?: boolean): string;
|
|
15
|
+
/**
|
|
16
|
+
* Format a list of cages as a table
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatCageTable(cages: Cage[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Format environment variables
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatEnvVars(vars: EnvVar[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* Format security warnings
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatSecurityWarnings(warnings: SecurityWarning[]): string;
|
|
27
|
+
/**
|
|
28
|
+
* Format security severity
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatSeverity(severity: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Format log entries
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatLogs(logs: LogEntry[]): string;
|
|
35
|
+
/**
|
|
36
|
+
* Format API keys
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatApiKeys(keys: ApiKey[]): string;
|
|
39
|
+
/**
|
|
40
|
+
* Format a date string
|
|
41
|
+
*/
|
|
42
|
+
export declare function formatDate(dateStr: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Format a relative date (e.g., "2 hours ago")
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatRelativeDate(dateStr: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Print success message
|
|
49
|
+
*/
|
|
50
|
+
export declare function success(message: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Print error message
|
|
53
|
+
*/
|
|
54
|
+
export declare function error(message: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Print warning message
|
|
57
|
+
*/
|
|
58
|
+
export declare function warning(message: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Print info message
|
|
61
|
+
*/
|
|
62
|
+
export declare function info(message: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Print a section header
|
|
65
|
+
*/
|
|
66
|
+
export declare function header(title: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Print JSON output (for --json flag)
|
|
69
|
+
*/
|
|
70
|
+
export declare function json(data: unknown): void;
|
|
71
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAE5F;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAevD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,UAAQ,GAAG,MAAM,CAyB9D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAerD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAUpD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAY1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAavD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAMnD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAoBpD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAI1C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAExC"}
|