@oml/cli 0.14.2 → 0.14.4
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 +254 -97
- package/out/auth/auth.js.map +1 -1
- package/out/auth/platform.d.ts +1 -1
- package/out/auth/platform.js +4 -26
- package/out/auth/platform.js.map +1 -1
- package/out/cli.js +59 -81
- package/out/cli.js.map +1 -1
- package/out/commands/server/actions.d.ts +3 -3
- package/out/commands/server/actions.js +88 -25
- 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 -28
- package/out/commands/server/rest.js.map +1 -1
- package/package.json +4 -3
- package/src/auth/auth.ts +292 -115
- package/src/auth/platform.ts +4 -30
- package/src/cli.ts +62 -87
- package/src/commands/server/actions.ts +108 -29
- package/src/commands/server/require.ts +1 -1
- package/src/commands/server/rest.ts +119 -29
|
@@ -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,62 +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
|
-
return headers;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
91
|
function ensureServerBaseUrl(state: ServerState | undefined): string {
|
|
81
92
|
if (!state) {
|
|
82
|
-
throw new Error(
|
|
93
|
+
throw new Error("OML server is not running. Start it with 'oml start'.");
|
|
83
94
|
}
|
|
84
95
|
return `http://${DEFAULT_HOST}:${state.port}`;
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
const text = await response.text();
|
|
98
|
+
function parseJsonResponse<T>(status: number, statusText: string, text: string): T {
|
|
89
99
|
if (!text.trim()) {
|
|
90
|
-
throw new Error(`Server returned HTTP ${
|
|
100
|
+
throw new Error(`Server returned HTTP ${status} ${statusText} with empty response body.`);
|
|
91
101
|
}
|
|
92
102
|
try {
|
|
93
103
|
return JSON.parse(text) as T;
|
|
94
104
|
} catch {
|
|
95
|
-
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,
|
|
96
148
|
}
|
|
97
149
|
}
|
|
98
150
|
|
|
99
151
|
export async function restGet<T>(route: string, authToken?: string): Promise<T> {
|
|
152
|
+
void authToken;
|
|
100
153
|
const baseUrl = ensureServerBaseUrl(await readRunningState());
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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}).`);
|
|
109
165
|
}
|
|
110
166
|
return payload;
|
|
111
167
|
}
|
|
112
168
|
|
|
113
169
|
export async function restPost<T>(route: string, body: Record<string, unknown>, authToken?: string): Promise<T> {
|
|
170
|
+
void authToken;
|
|
114
171
|
const baseUrl = ensureServerBaseUrl(await readRunningState());
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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}).`);
|
|
124
183
|
}
|
|
125
184
|
if (payload.ok === false) {
|
|
126
|
-
|
|
185
|
+
const message = normalizeServerError(payload.error);
|
|
186
|
+
throw new Error(message ?? `Server request failed: POST ${route}.`);
|
|
127
187
|
}
|
|
128
188
|
if (payload.result === undefined) {
|
|
129
189
|
throw new Error(`Server request failed: POST ${route} did not return a result.`);
|
|
130
190
|
}
|
|
131
191
|
return payload.result;
|
|
132
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
|
+
}
|