@myapihq/cli 1.2.7 → 1.2.8
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.
|
@@ -12,4 +12,5 @@ export declare function list(flags: Flags): Promise<void>;
|
|
|
12
12
|
export declare function get(id: string, flags: Flags): Promise<void>;
|
|
13
13
|
export declare function del(id: string, flags: Flags): Promise<void>;
|
|
14
14
|
export declare function deploy(id: string, image: string, flags: Flags): Promise<void>;
|
|
15
|
+
export declare function logs(id: string, flags: Flags): Promise<void>;
|
|
15
16
|
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|
|
@@ -9,6 +9,7 @@ export const EXPOSES = [
|
|
|
9
9
|
'GET /container/orgs/{org_id}/containers/{id}',
|
|
10
10
|
'DELETE /container/orgs/{org_id}/containers/{id}',
|
|
11
11
|
'POST /container/orgs/{org_id}/containers/{id}/deploy',
|
|
12
|
+
'GET /container/orgs/{org_id}/containers/{id}/logs',
|
|
12
13
|
];
|
|
13
14
|
export const SCHEMA = {
|
|
14
15
|
name: 'string',
|
|
@@ -20,6 +21,7 @@ export const SCHEMA = {
|
|
|
20
21
|
'max-instances': 'number',
|
|
21
22
|
port: 'number',
|
|
22
23
|
env: 'string',
|
|
24
|
+
tail: 'number',
|
|
23
25
|
};
|
|
24
26
|
const CONTAINER_TYPES = ['service', 'worker', 'job'];
|
|
25
27
|
// Mirrors validateName in myapi-hq/internal/routes/container/crud.go —
|
|
@@ -176,6 +178,26 @@ export async function deploy(id, image, flags) {
|
|
|
176
178
|
info(`Scoped API key was rotated. New value (returned once — save it if you need it):`);
|
|
177
179
|
info(` ${result.scoped_api_key}`);
|
|
178
180
|
}
|
|
181
|
+
// logs prints the container's recent Cloud Run runtime logs, newest first.
|
|
182
|
+
export async function logs(id, flags) {
|
|
183
|
+
const config = requireConfig();
|
|
184
|
+
const orgId = requireOrg(flags, config, 'myapi container logs <id> [--tail <n>] [--org <id>]');
|
|
185
|
+
if (!id)
|
|
186
|
+
error('Missing id.\nUsage: myapi container logs <id> [--tail <n>]');
|
|
187
|
+
const tail = typeof flags.tail === 'number' ? flags.tail : undefined;
|
|
188
|
+
const entries = await sdkContainer.getContainerLogs(config.api_key, orgId, id, tail);
|
|
189
|
+
if (flags.json) {
|
|
190
|
+
printJson(entries);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (entries.length === 0) {
|
|
194
|
+
info('No logs — the container is not deployed yet, or has produced no output.');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
for (const e of entries) {
|
|
198
|
+
info(`${e.timestamp} ${(e.severity || '').padEnd(8)} ${e.text}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
179
201
|
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
180
202
|
const SUBCOMMAND_USAGE = {
|
|
181
203
|
'create': `myapi container create --name <name> [--type service|worker|job] [--cron <expr>]
|
|
@@ -203,6 +225,10 @@ Example:
|
|
|
203
225
|
myapi container deploy <id> registry.example.com/my-app:v2`,
|
|
204
226
|
'list': 'myapi container list [--org <id>] [--json]',
|
|
205
227
|
'get': 'myapi container get <id> [--org <id>] [--json]',
|
|
228
|
+
'logs': `myapi container logs <id> [--tail <n>] [--org <id>] [--json]
|
|
229
|
+
|
|
230
|
+
Recent Cloud Run runtime logs, newest first. --tail caps the count
|
|
231
|
+
(default 100, max 1000).`,
|
|
206
232
|
'delete': 'myapi container delete <id> [--org <id>]',
|
|
207
233
|
};
|
|
208
234
|
export async function run(subcommand, args, flags) {
|
|
@@ -218,6 +244,7 @@ Subcommands:
|
|
|
218
244
|
deploy <id> <image> Ship a pre-built image and go live
|
|
219
245
|
list List containers in your org
|
|
220
246
|
get <id> Inspect a container
|
|
247
|
+
logs <id> Show recent runtime logs (--tail <n>)
|
|
221
248
|
delete <id> Soft-delete and revoke its scoped API key
|
|
222
249
|
|
|
223
250
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
@@ -236,6 +263,7 @@ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
|
236
263
|
case 'deploy': return deploy(args[0], args[1], flags);
|
|
237
264
|
case 'list': return list(flags);
|
|
238
265
|
case 'get': return get(args[0], flags);
|
|
266
|
+
case 'logs': return logs(args[0], flags);
|
|
239
267
|
case 'delete': return del(args[0], flags);
|
|
240
268
|
default: error(`Unknown subcommand: ${subcommand}. Run "myapi container --help" for a list of valid subcommands.`);
|
|
241
269
|
}
|
package/dist/completion.js
CHANGED
|
@@ -57,7 +57,7 @@ export const SUBCOMMANDS = {
|
|
|
57
57
|
config: ['view', 'set-org', 'set-funnel', 'set-domain'],
|
|
58
58
|
fn: ['create', 'deploy', 'env', 'runs', 'list', 'get', 'delete'],
|
|
59
59
|
payments: ['connect', 'status', 'charge', 'list', 'get', 'refund'],
|
|
60
|
-
container: ['create', 'deploy', 'list', 'get', 'delete'],
|
|
60
|
+
container: ['create', 'deploy', 'list', 'get', 'logs', 'delete'],
|
|
61
61
|
completion: ['install', 'uninstall'],
|
|
62
62
|
};
|
|
63
63
|
// ── Completion request handling ──────────────────────────────────────────────
|
|
@@ -102,14 +102,38 @@ describe('container.deployContainer', () => {
|
|
|
102
102
|
.rejects.toMatchObject({ code: 'IMAGE_REQUIRED', status: 422 });
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
|
+
describe('container.getContainerLogs', () => {
|
|
106
|
+
it('GETs /logs and returns the entries', async () => {
|
|
107
|
+
fetchMock.mockResolvedValueOnce(ok([
|
|
108
|
+
{ timestamp: 't2', severity: 'INFO', text: 'started' },
|
|
109
|
+
{ timestamp: 't1', severity: 'ERROR', text: 'boom' },
|
|
110
|
+
]));
|
|
111
|
+
const entries = await container.getContainerLogs(API_KEY, ORG_ID, C_ID);
|
|
112
|
+
expect(entries).toHaveLength(2);
|
|
113
|
+
expect(entries[1].severity).toBe('ERROR');
|
|
114
|
+
const [url, init] = fetchMock.mock.calls[0];
|
|
115
|
+
expect(url).toContain(`/container/orgs/${ORG_ID}/containers/${C_ID}/logs`);
|
|
116
|
+
expect(init.method).toBe('GET');
|
|
117
|
+
});
|
|
118
|
+
it('appends ?tail=N when a tail count is given', async () => {
|
|
119
|
+
fetchMock.mockResolvedValueOnce(ok([]));
|
|
120
|
+
await container.getContainerLogs(API_KEY, ORG_ID, C_ID, 250);
|
|
121
|
+
expect(fetchMock.mock.calls[0][0]).toMatch(/\/logs\?tail=250$/);
|
|
122
|
+
});
|
|
123
|
+
it('returns an empty array for an undeployed container', async () => {
|
|
124
|
+
fetchMock.mockResolvedValueOnce(ok([]));
|
|
125
|
+
expect(await container.getContainerLogs(API_KEY, ORG_ID, C_ID)).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
105
128
|
describe('container.EXPOSES', () => {
|
|
106
|
-
it('covers the
|
|
129
|
+
it('covers the 6 container endpoints', () => {
|
|
107
130
|
expect(container.EXPOSES).toEqual([
|
|
108
131
|
'POST /container/orgs/{org_id}/containers',
|
|
109
132
|
'GET /container/orgs/{org_id}/containers',
|
|
110
133
|
'GET /container/orgs/{org_id}/containers/{id}',
|
|
111
134
|
'DELETE /container/orgs/{org_id}/containers/{id}',
|
|
112
135
|
'POST /container/orgs/{org_id}/containers/{id}/deploy',
|
|
136
|
+
'GET /container/orgs/{org_id}/containers/{id}/logs',
|
|
113
137
|
]);
|
|
114
138
|
});
|
|
115
139
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myapihq/cli",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.8",
|
|
5
5
|
"description": "MyAPI command-line interface",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"lint:changelog": "node ../../scripts/lint-changelog.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@myapihq/sdk": "^1.2.
|
|
32
|
+
"@myapihq/sdk": "^1.2.8"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^25.6.0",
|