@oml/cli 0.14.1 → 0.14.3
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/out/auth/auth.d.ts +3 -3
- package/out/auth/auth.js +232 -97
- package/out/auth/auth.js.map +1 -1
- package/out/auth/platform.d.ts +1 -1
- package/out/auth/platform.js +14 -11
- package/out/auth/platform.js.map +1 -1
- package/out/cli.js +59 -78
- package/out/cli.js.map +1 -1
- package/out/commands/server/actions.d.ts +3 -3
- package/out/commands/server/actions.js +87 -22
- package/out/commands/server/actions.js.map +1 -1
- package/out/commands/server/require.js +1 -1
- package/out/commands/server/require.js.map +1 -1
- package/out/commands/server/rest.js +103 -31
- package/out/commands/server/rest.js.map +1 -1
- package/package.json +5 -4
- package/src/auth/auth.ts +261 -115
- package/src/auth/platform.ts +14 -13
- package/src/cli.ts +62 -84
- package/src/commands/server/actions.ts +107 -26
- package/src/commands/server/require.ts +1 -1
- package/src/commands/server/rest.ts +119 -32
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// Copyright (c) 2026 Modelware. All rights reserved.
|
|
2
2
|
|
|
3
|
+
import * as http from 'node:http';
|
|
3
4
|
import * as fs from 'node:fs/promises';
|
|
4
5
|
import * as os from 'node:os';
|
|
5
6
|
import * as path from 'node:path';
|
|
6
7
|
import { createHash } from 'node:crypto';
|
|
8
|
+
import { URL } from 'node:url';
|
|
7
9
|
|
|
8
10
|
const DEFAULT_HOST = '127.0.0.1';
|
|
9
11
|
|
|
@@ -18,6 +20,21 @@ type Envelope<T> = {
|
|
|
18
20
|
error?: string;
|
|
19
21
|
};
|
|
20
22
|
|
|
23
|
+
type ErrorEnvelope = {
|
|
24
|
+
error?: string | {
|
|
25
|
+
code?: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
status?: number;
|
|
28
|
+
retryable?: boolean;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type HttpJsonResponse = {
|
|
33
|
+
status: number;
|
|
34
|
+
statusText: string;
|
|
35
|
+
body: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
21
38
|
function workspaceHash(workspaceRoot: string): string {
|
|
22
39
|
return createHash('sha256').update(path.resolve(workspaceRoot)).digest('hex');
|
|
23
40
|
}
|
|
@@ -71,65 +88,135 @@ async function readRunningState(workspaceRoot = process.cwd()): Promise<ServerSt
|
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
function createHeaders(token: string | undefined): Headers {
|
|
75
|
-
const headers = new Headers();
|
|
76
|
-
headers.set('content-type', 'application/json');
|
|
77
|
-
if (token && token.trim().length > 0) {
|
|
78
|
-
headers.set('authorization', `Bearer ${token.trim()}`);
|
|
79
|
-
}
|
|
80
|
-
return headers;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
91
|
function ensureServerBaseUrl(state: ServerState | undefined): string {
|
|
84
92
|
if (!state) {
|
|
85
|
-
throw new Error(
|
|
93
|
+
throw new Error("OML server is not running. Start it with 'oml start'.");
|
|
86
94
|
}
|
|
87
95
|
return `http://${DEFAULT_HOST}:${state.port}`;
|
|
88
96
|
}
|
|
89
97
|
|
|
90
|
-
|
|
91
|
-
const text = await response.text();
|
|
98
|
+
function parseJsonResponse<T>(status: number, statusText: string, text: string): T {
|
|
92
99
|
if (!text.trim()) {
|
|
93
|
-
throw new Error(`Server returned HTTP ${
|
|
100
|
+
throw new Error(`Server returned HTTP ${status} ${statusText} with empty response body.`);
|
|
94
101
|
}
|
|
95
102
|
try {
|
|
96
103
|
return JSON.parse(text) as T;
|
|
97
104
|
} catch {
|
|
98
|
-
throw new Error(`Server returned HTTP ${
|
|
105
|
+
throw new Error(`Server returned HTTP ${status} ${statusText} with invalid JSON response.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function requestJson(method: 'GET' | 'POST', url: string, body?: string): Promise<HttpJsonResponse> {
|
|
110
|
+
const target = new URL(url);
|
|
111
|
+
return new Promise<HttpJsonResponse>((resolve, reject) => {
|
|
112
|
+
const req = http.request({
|
|
113
|
+
protocol: target.protocol,
|
|
114
|
+
hostname: target.hostname,
|
|
115
|
+
port: target.port,
|
|
116
|
+
path: `${target.pathname}${target.search}`,
|
|
117
|
+
method,
|
|
118
|
+
headers: {
|
|
119
|
+
'content-type': 'application/json',
|
|
120
|
+
accept: 'application/json',
|
|
121
|
+
},
|
|
122
|
+
}, (res) => {
|
|
123
|
+
let payload = '';
|
|
124
|
+
res.setEncoding('utf-8');
|
|
125
|
+
res.on('data', (chunk: string) => {
|
|
126
|
+
payload += chunk;
|
|
127
|
+
});
|
|
128
|
+
res.on('end', () => {
|
|
129
|
+
resolve({
|
|
130
|
+
status: res.statusCode ?? 0,
|
|
131
|
+
statusText: res.statusMessage ?? '',
|
|
132
|
+
body: payload,
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
req.once('error', reject);
|
|
137
|
+
if (body !== undefined) {
|
|
138
|
+
req.write(body, 'utf-8');
|
|
139
|
+
}
|
|
140
|
+
req.end();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function buildResponseLike(response: HttpJsonResponse): { ok: boolean; status: number } {
|
|
145
|
+
return {
|
|
146
|
+
ok: response.status >= 200 && response.status < 300,
|
|
147
|
+
status: response.status,
|
|
99
148
|
}
|
|
100
149
|
}
|
|
101
150
|
|
|
102
151
|
export async function restGet<T>(route: string, authToken?: string): Promise<T> {
|
|
152
|
+
void authToken;
|
|
103
153
|
const baseUrl = ensureServerBaseUrl(await readRunningState());
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
154
|
+
let response: HttpJsonResponse;
|
|
155
|
+
try {
|
|
156
|
+
response = await requestJson('GET', `${baseUrl}${route}`);
|
|
157
|
+
} catch {
|
|
158
|
+
throw new Error(`OML server is unreachable at ${baseUrl}. Start it with 'oml start' or check connectivity.`);
|
|
159
|
+
}
|
|
160
|
+
const responseLike = buildResponseLike(response);
|
|
161
|
+
const payload = parseJsonResponse<T & ErrorEnvelope>(response.status, response.statusText, response.body);
|
|
162
|
+
if (!responseLike.ok) {
|
|
163
|
+
const message = normalizeServerError(payload.error);
|
|
164
|
+
throw new Error(message ?? `Server request failed: GET ${route} (${responseLike.status}).`);
|
|
112
165
|
}
|
|
113
166
|
return payload;
|
|
114
167
|
}
|
|
115
168
|
|
|
116
169
|
export async function restPost<T>(route: string, body: Record<string, unknown>, authToken?: string): Promise<T> {
|
|
170
|
+
void authToken;
|
|
117
171
|
const baseUrl = ensureServerBaseUrl(await readRunningState());
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
172
|
+
let response: HttpJsonResponse;
|
|
173
|
+
try {
|
|
174
|
+
response = await requestJson('POST', `${baseUrl}${route}`, JSON.stringify(body));
|
|
175
|
+
} catch {
|
|
176
|
+
throw new Error(`OML server is unreachable at ${baseUrl}. Start it with 'oml start' or check connectivity.`);
|
|
177
|
+
}
|
|
178
|
+
const responseLike = buildResponseLike(response);
|
|
179
|
+
const payload = parseJsonResponse<Envelope<T> & ErrorEnvelope>(response.status, response.statusText, response.body);
|
|
180
|
+
if (!responseLike.ok) {
|
|
181
|
+
const message = normalizeServerError(payload.error);
|
|
182
|
+
throw new Error(message ?? `Server request failed: POST ${route} (${responseLike.status}).`);
|
|
127
183
|
}
|
|
128
184
|
if (payload.ok === false) {
|
|
129
|
-
|
|
185
|
+
const message = normalizeServerError(payload.error);
|
|
186
|
+
throw new Error(message ?? `Server request failed: POST ${route}.`);
|
|
130
187
|
}
|
|
131
188
|
if (payload.result === undefined) {
|
|
132
189
|
throw new Error(`Server request failed: POST ${route} did not return a result.`);
|
|
133
190
|
}
|
|
134
191
|
return payload.result;
|
|
135
192
|
}
|
|
193
|
+
|
|
194
|
+
function normalizeServerError(error: ErrorEnvelope['error']): string | undefined {
|
|
195
|
+
if (typeof error === 'string' && error.trim().length > 0) {
|
|
196
|
+
return error.trim();
|
|
197
|
+
}
|
|
198
|
+
if (error && typeof error === 'object') {
|
|
199
|
+
const code = typeof error.code === 'string' ? error.code.trim().toLowerCase() : '';
|
|
200
|
+
if (code === 'auth_required') {
|
|
201
|
+
return "OML server authentication is required. Sign in and restart the server with 'oml start'.";
|
|
202
|
+
}
|
|
203
|
+
if (code === 'entitlements_pending') {
|
|
204
|
+
return 'OML entitlements are still loading. Retry in a moment.';
|
|
205
|
+
}
|
|
206
|
+
if (code === 'entitlements_unavailable') {
|
|
207
|
+
return "OML entitlements are unavailable. Sign in again with 'oml login' and restart the server.";
|
|
208
|
+
}
|
|
209
|
+
if (code === 'not_entitled') {
|
|
210
|
+
const message = typeof error.message === 'string' ? error.message.trim() : '';
|
|
211
|
+
return message.length > 0 ? message : 'This command is not enabled for your account.';
|
|
212
|
+
}
|
|
213
|
+
const message = typeof error.message === 'string' ? error.message.trim() : '';
|
|
214
|
+
if (message.length > 0) {
|
|
215
|
+
return message;
|
|
216
|
+
}
|
|
217
|
+
if (code.length > 0) {
|
|
218
|
+
return code;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|