@onesi/genesis-cli 0.2.5 → 0.2.6
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 +1 -1
- package/dist/hives-node-transport.d.ts +2 -0
- package/dist/hives-node-transport.js +12 -0
- package/dist/hives-project.d.ts +20 -3
- package/dist/hives-project.js +80 -6
- package/dist/index.js +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type GenesisOptions } from '@onesi/genesis';
|
|
2
2
|
export declare function closeHivesNodeTransport(): Promise<void>;
|
|
3
3
|
export declare function hivesOperationBudget(id: string): number;
|
|
4
|
+
/** Live log streams must start within 45 s (15 s heartbeat + 15 s window + margin). The 330 s budget runs from the start of the request, so the body gets at least 285 s. */
|
|
5
|
+
export declare function hivesHeaderBudget(id: string): number | undefined;
|
|
4
6
|
/** Hives leaf transport. Caller fetch overrides stay authoritative; only native
|
|
5
7
|
* global fetch gets our Agent. Every operation retains an overall body deadline. */
|
|
6
8
|
export declare function createHivesNodeTransport(opts: GenesisOptions, callerSignal?: AbortSignal): typeof fetch;
|
|
@@ -24,6 +24,10 @@ export function hivesOperationBudget(id) {
|
|
|
24
24
|
return 100_000;
|
|
25
25
|
return 20_000;
|
|
26
26
|
}
|
|
27
|
+
/** Live log streams must start within 45 s (15 s heartbeat + 15 s window + margin). The 330 s budget runs from the start of the request, so the body gets at least 285 s. */
|
|
28
|
+
export function hivesHeaderBudget(id) {
|
|
29
|
+
return id === 'hives_stream_logs' || id === 'hives_stream_deployment_logs' ? 45_000 : undefined;
|
|
30
|
+
}
|
|
27
31
|
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
28
32
|
const templatePattern = (value) => new RegExp('^' + value.split(/(\{[^}]+\})/).map((part) => part.startsWith('{') ? '[^/]+' : escape(part)).join('') + '$');
|
|
29
33
|
/** Hives leaf transport. Caller fetch overrides stay authoritative; only native
|
|
@@ -78,6 +82,12 @@ export function createHivesNodeTransport(opts, callerSignal) {
|
|
|
78
82
|
controller.abort();
|
|
79
83
|
}, hivesOperationBudget(route.id));
|
|
80
84
|
timer.unref();
|
|
85
|
+
const headerBudget = hivesHeaderBudget(route.id);
|
|
86
|
+
const headerTimer = headerBudget === undefined ? undefined : setTimeout(() => {
|
|
87
|
+
message = 'Hives stream did not start within 45 s. Retry the stream.';
|
|
88
|
+
controller.abort();
|
|
89
|
+
}, headerBudget);
|
|
90
|
+
headerTimer?.unref();
|
|
81
91
|
let abortReject = () => { };
|
|
82
92
|
let reader;
|
|
83
93
|
let bodyController;
|
|
@@ -92,6 +102,7 @@ export function createHivesNodeTransport(opts, callerSignal) {
|
|
|
92
102
|
void abortPromise.catch(() => { });
|
|
93
103
|
const cleanup = () => {
|
|
94
104
|
clearTimeout(timer);
|
|
105
|
+
clearTimeout(headerTimer);
|
|
95
106
|
for (const signal of signals)
|
|
96
107
|
signal.removeEventListener('abort', canceled);
|
|
97
108
|
controller.signal.removeEventListener('abort', aborted);
|
|
@@ -112,6 +123,7 @@ export function createHivesNodeTransport(opts, callerSignal) {
|
|
|
112
123
|
void pending.then((response) => { if (controller.signal.aborted)
|
|
113
124
|
void response.body?.cancel().catch(() => { }); }, () => { });
|
|
114
125
|
const response = await Promise.race([pending, abortPromise]);
|
|
126
|
+
clearTimeout(headerTimer);
|
|
115
127
|
if (controller.signal.aborted)
|
|
116
128
|
throw safeError();
|
|
117
129
|
if (!response.body) {
|
package/dist/hives-project.d.ts
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
-
import { type Genesis } from '@onesi/genesis';
|
|
2
|
+
import { type Genesis, type OperationId, type OperationMeta } from '@onesi/genesis';
|
|
3
|
+
/**
|
|
4
|
+
* `genesis hives <words>` commands whose last argument is <project_id>, and the Hives operation each one sends.
|
|
5
|
+
* Only these can let project_id default, and only when that operation reads or creates (see hivesProjectMayDefault).
|
|
6
|
+
* A command that is not listed keeps an explicit project id. `test/hives-project-guard.test.ts` checks that every
|
|
7
|
+
* command with a trailing project_id is listed and that each one sends exactly this operation.
|
|
8
|
+
*/
|
|
9
|
+
export declare const HIVES_PROJECT_COMMANDS: Record<string, OperationId>;
|
|
10
|
+
/** DELETE, or a POST that ends a lifecycle: an omitted project id never defaults. */
|
|
11
|
+
export declare const isHivesDestructiveOperation: (id: string, op: Pick<OperationMeta, "method">) => boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether an omitted project id may default for this operation: reads (GET, and POST log search) and creates
|
|
14
|
+
* (POST hives_create_…). Deletes, lifecycle endings and writes that overwrite (PUT, PATCH, publish and other POSTs)
|
|
15
|
+
* take an explicit project id.
|
|
16
|
+
*/
|
|
17
|
+
export declare function hivesProjectMayDefault(id: string, op: Pick<OperationMeta, 'method'>): boolean;
|
|
18
|
+
/** The command's words under `genesis hives`, for example "remote sessions list". */
|
|
19
|
+
export declare const hivesCommandPath: (command: Command, hives: Command) => string;
|
|
3
20
|
/**
|
|
4
21
|
* Every `genesis hives` command with a <project_id>: the help says "Hives project id (prj_…)"; a Genesis pr_… id of
|
|
5
|
-
* the selected context resolves to its Hives binding, and where project_id is the last argument of a command
|
|
6
|
-
*
|
|
22
|
+
* the selected context resolves to its Hives binding, and where project_id is the last argument of a listed command
|
|
23
|
+
* whose operation reads or creates it may be omitted. The binding is read once per invocation, before the action.
|
|
7
24
|
*/
|
|
8
25
|
export declare function registerHivesProjectDefault(hives: Command, client: () => Promise<Genesis>): void;
|
package/dist/hives-project.js
CHANGED
|
@@ -1,18 +1,92 @@
|
|
|
1
|
-
import { HIVES_PROJECT_ID_DEFAULT, HIVES_PROJECT_ID_DESCRIPTION, isGenesisProjectId } from '@onesi/genesis';
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { HIVES_PROJECT_ID_DEFAULT, HIVES_PROJECT_ID_DESCRIPTION, isGenesisProjectId, operationTable } from '@onesi/genesis';
|
|
2
|
+
import { HIVES_COVERAGE_COMMANDS } from './hives-coverage.js';
|
|
3
|
+
/**
|
|
4
|
+
* `genesis hives <words>` commands whose last argument is <project_id>, and the Hives operation each one sends.
|
|
5
|
+
* Only these can let project_id default, and only when that operation reads or creates (see hivesProjectMayDefault).
|
|
6
|
+
* A command that is not listed keeps an explicit project id. `test/hives-project-guard.test.ts` checks that every
|
|
7
|
+
* command with a trailing project_id is listed and that each one sends exactly this operation.
|
|
8
|
+
*/
|
|
9
|
+
export const HIVES_PROJECT_COMMANDS = {
|
|
10
|
+
'settings get': 'hives_get_project_settings',
|
|
11
|
+
'settings update': 'hives_update_project_settings',
|
|
12
|
+
'crons list': 'hives_list_crons',
|
|
13
|
+
'crons create': 'hives_create_cron',
|
|
14
|
+
'metrics project': 'hives_get_project_metrics',
|
|
15
|
+
'metrics requests': 'hives_get_request_metrics',
|
|
16
|
+
'metrics latency': 'hives_get_latency_metrics',
|
|
17
|
+
'metrics errors': 'hives_get_error_metrics',
|
|
18
|
+
'metrics resources': 'hives_get_resource_usage',
|
|
19
|
+
'metrics dashboard': 'hives_get_dashboard_metrics',
|
|
20
|
+
'metrics realtime': 'hives_get_realtime_metrics',
|
|
21
|
+
'marketplace list': 'hives_marketplace_list_packages',
|
|
22
|
+
'marketplace categories': 'hives_marketplace_categories',
|
|
23
|
+
'marketplace featured': 'hives_marketplace_featured',
|
|
24
|
+
'marketplace publish': 'hives_marketplace_publish',
|
|
25
|
+
'marketplace installed': 'hives_marketplace_installed',
|
|
26
|
+
'cells list': 'hives_list_buckets',
|
|
27
|
+
'cells create': 'hives_create_bucket',
|
|
28
|
+
'queues list': 'hives_list_native_queue',
|
|
29
|
+
'queues create': 'hives_create_native_queue',
|
|
30
|
+
'topics list': 'hives_list_native_topic',
|
|
31
|
+
'topics create': 'hives_create_native_topic',
|
|
32
|
+
'volumes list': 'hives_list_volumes',
|
|
33
|
+
'volumes create': 'hives_create_volume',
|
|
34
|
+
'runtimes project': 'hives_list_project_runtimes',
|
|
35
|
+
'logs query': 'hives_query_logs',
|
|
36
|
+
'logs stream': 'hives_stream_logs',
|
|
37
|
+
'domains list': 'hives_list_domains',
|
|
38
|
+
'remote sessions list': 'hives_list_remote_sessions',
|
|
39
|
+
'deployments list': 'hives_list_deployments',
|
|
40
|
+
'functions list': 'hives_list_functions',
|
|
41
|
+
'environment list': 'hives_list_project_environment',
|
|
42
|
+
'environment upsert': 'hives_update_project_environment',
|
|
43
|
+
'secrets list': 'hives_list_secrets',
|
|
44
|
+
'secrets bulk': 'hives_bulk_upsert_secrets',
|
|
45
|
+
'projects get': 'hives_get_project',
|
|
46
|
+
'projects delete': 'hives_delete_project',
|
|
47
|
+
'projects activity': 'hives_get_project_activity',
|
|
48
|
+
...Object.fromEntries(Object.entries(HIVES_COVERAGE_COMMANDS).filter(([, id]) => {
|
|
49
|
+
const op = operationTable[id];
|
|
50
|
+
return op.pathParams.at(-1) === 'project_id';
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
/** POST operations that end, undo or tear down something. */
|
|
54
|
+
const LIFECYCLE_END = /(?:^|_)(?:stop|cancel|rollback|revoke|release|teardown|unlink|unsubscribe|close|reset|purge|uninstall|detach|unmount)(?:_|$)/;
|
|
55
|
+
/** DELETE, or a POST that ends a lifecycle: an omitted project id never defaults. */
|
|
56
|
+
export const isHivesDestructiveOperation = (id, op) => op.method === 'DELETE' || (op.method === 'POST' && LIFECYCLE_END.test(id));
|
|
57
|
+
/**
|
|
58
|
+
* Whether an omitted project id may default for this operation: reads (GET, and POST log search) and creates
|
|
59
|
+
* (POST hives_create_…). Deletes, lifecycle endings and writes that overwrite (PUT, PATCH, publish and other POSTs)
|
|
60
|
+
* take an explicit project id.
|
|
61
|
+
*/
|
|
62
|
+
export function hivesProjectMayDefault(id, op) {
|
|
63
|
+
if (isHivesDestructiveOperation(id, op))
|
|
64
|
+
return false;
|
|
65
|
+
if (op.method === 'GET' || op.method === 'HEAD')
|
|
66
|
+
return true;
|
|
67
|
+
return op.method === 'POST' && /^hives_(?:create|search)_/.test(id);
|
|
68
|
+
}
|
|
4
69
|
const projectArgument = (command) => command.registeredArguments.findIndex((a) => a.name() === 'project_id');
|
|
70
|
+
/** The command's words under `genesis hives`, for example "remote sessions list". */
|
|
71
|
+
export const hivesCommandPath = (command, hives) => {
|
|
72
|
+
const words = [];
|
|
73
|
+
for (let c = command; c && c !== hives; c = c.parent)
|
|
74
|
+
words.unshift(c.name());
|
|
75
|
+
return words.join(' ');
|
|
76
|
+
};
|
|
5
77
|
/**
|
|
6
78
|
* Every `genesis hives` command with a <project_id>: the help says "Hives project id (prj_…)"; a Genesis pr_… id of
|
|
7
|
-
* the selected context resolves to its Hives binding, and where project_id is the last argument of a command
|
|
8
|
-
*
|
|
79
|
+
* the selected context resolves to its Hives binding, and where project_id is the last argument of a listed command
|
|
80
|
+
* whose operation reads or creates it may be omitted. The binding is read once per invocation, before the action.
|
|
9
81
|
*/
|
|
10
82
|
export function registerHivesProjectDefault(hives, client) {
|
|
11
83
|
const visit = (command) => {
|
|
12
84
|
const at = projectArgument(command);
|
|
13
85
|
if (at >= 0 && command.parent?.name() !== 'context') {
|
|
14
86
|
const argument = command.registeredArguments[at];
|
|
15
|
-
const
|
|
87
|
+
const id = HIVES_PROJECT_COMMANDS[hivesCommandPath(command, hives)];
|
|
88
|
+
const op = id ? operationTable[id] : undefined;
|
|
89
|
+
const optional = at === command.registeredArguments.length - 1 && !argument.variadic && !!op && hivesProjectMayDefault(id, op);
|
|
16
90
|
argument.description = optional ? `${HIVES_PROJECT_ID_DESCRIPTION}; ${HIVES_PROJECT_ID_DEFAULT}` : HIVES_PROJECT_ID_DESCRIPTION;
|
|
17
91
|
if (optional)
|
|
18
92
|
argument.required = false;
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,7 @@ export function buildProgram(deps = {}) {
|
|
|
41
41
|
const auth = { ...authConfig(env, f), sleep: deps.sleep };
|
|
42
42
|
const program = new Command('genesis')
|
|
43
43
|
.description('One CLI for every Genesis service.')
|
|
44
|
-
.version('0.2.
|
|
44
|
+
.version('0.2.6')
|
|
45
45
|
.option('--json', 'machine-readable JSON output')
|
|
46
46
|
.option('--api-key <key>', 'use this API key (default: GENESIS_API_KEY, then your `genesis login`)')
|
|
47
47
|
.option('--base-url <url>', 'gateway URL (default: GENESIS_BASE_URL or https://os.one.ml/api)')
|
|
@@ -57,7 +57,7 @@ export function buildProgram(deps = {}) {
|
|
|
57
57
|
const opts = {
|
|
58
58
|
apiKey: o.apiKey ?? env.GENESIS_API_KEY, token: env.GENESIS_TOKEN,
|
|
59
59
|
baseUrl: o.baseUrl ?? env.GENESIS_BASE_URL, routing: o.direct || env.GENESIS_ROUTING === 'direct' ? 'direct' : 'gateway',
|
|
60
|
-
fetch: f, userAgent: 'genesis-cli/0.2.
|
|
60
|
+
fetch: f, userAgent: 'genesis-cli/0.2.6',
|
|
61
61
|
credentials: store ? savedLoginProvider(auth, store) : undefined,
|
|
62
62
|
};
|
|
63
63
|
return new Genesis(hivesTransport ? { ...opts, fetch: createHivesNodeTransport(opts, deps.signal) } : opts);
|
|
@@ -278,7 +278,7 @@ export function buildProgram(deps = {}) {
|
|
|
278
278
|
if (!key)
|
|
279
279
|
throw new Error('Give the enrollment key with --key-file, --key-stdin or GENESIS_ENROLLMENT_KEY; it is never accepted as an argument.');
|
|
280
280
|
const body = await readFunctionJson(o.bodyFile, 16 * 1024);
|
|
281
|
-
const result = await new Genesis({ baseUrl: g().baseUrl ?? env.GENESIS_BASE_URL, fetch: f, userAgent: 'genesis-cli/0.2.
|
|
281
|
+
const result = await new Genesis({ baseUrl: g().baseUrl ?? env.GENESIS_BASE_URL, fetch: f, userAgent: 'genesis-cli/0.2.6' }).iam.agents.enroll(key, body);
|
|
282
282
|
emit(result, () => JSON.stringify(result, null, 2));
|
|
283
283
|
});
|
|
284
284
|
// ---- auth ------------------------------------------------------------
|
|
@@ -813,7 +813,7 @@ export function buildProgram(deps = {}) {
|
|
|
813
813
|
const url = `${base.href.replace(/\/+$/, '')}/v1/status`;
|
|
814
814
|
let response;
|
|
815
815
|
try {
|
|
816
|
-
response = await f(url, { method: 'GET', headers: { accept: 'application/json', 'user-agent': 'genesis-cli/0.2.
|
|
816
|
+
response = await f(url, { method: 'GET', headers: { accept: 'application/json', 'user-agent': 'genesis-cli/0.2.6' }, signal: AbortSignal.timeout(15_000) });
|
|
817
817
|
}
|
|
818
818
|
catch (e) {
|
|
819
819
|
throw new Error(`Gateway unreachable at ${safe(url)}: ${safe(e.message)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onesi/genesis-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/l1felabs/genesis-api.git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"commander": "^14.0.0",
|
|
35
35
|
"undici": "6.29.0",
|
|
36
|
-
"@onesi/genesis": "0.2.
|
|
36
|
+
"@onesi/genesis": "0.2.6"
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
39
|
"@napi-rs/keyring": "^1.2.0"
|