@nlxai/cli 1.2.3-alpha.0 → 1.2.3-alpha.1
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/package.json +3 -3
- package/lib/commands/fetch.js +0 -59
- package/lib/commands/login.d.ts +0 -1
- package/lib/commands/login.js +0 -153
- package/lib/commands/modalities.js +0 -26
- package/lib/custom-modality.js +0 -4
- package/lib/index.d.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nlxai/cli",
|
|
3
|
-
"version": "1.2.3-alpha.
|
|
3
|
+
"version": "1.2.3-alpha.1",
|
|
4
4
|
"description": "Tools for integrating with NLX apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NLX",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@inquirer/prompts": "^7.8.4",
|
|
37
|
-
"@nlxai/core": "^1.
|
|
37
|
+
"@nlxai/core": "^1.2.3-alpha.1",
|
|
38
38
|
"boxen": "^8.0.1",
|
|
39
39
|
"chalk": "^4.1.0",
|
|
40
40
|
"commander": "^14.0",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"@vitest/ui": "^3.2.4",
|
|
59
59
|
"vitest": "^3.2.4"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "13e26a6c67af7aa6c8dc100ad83d9c33e1112a08"
|
|
62
62
|
}
|
package/lib/commands/fetch.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import { fetchManagementApi } from "../utils/index.js";
|
|
4
|
-
export const fetchCommand = new Command("fetch")
|
|
5
|
-
.description("Perform an authenticated request to the management API")
|
|
6
|
-
.argument("<method>", "HTTP method (GET, POST, PUT, DELETE, etc.)")
|
|
7
|
-
.argument("<path>", "API path (e.g. /models)")
|
|
8
|
-
.argument("[body]", "Request body as JSON string, name of a JSON file or -- for Standard Input")
|
|
9
|
-
.option("-p, --paginate", "Enable pagination", false)
|
|
10
|
-
.action(async (method, apiPath, body, opts) => {
|
|
11
|
-
if (body === "--") {
|
|
12
|
-
// Read from stdin
|
|
13
|
-
body = "";
|
|
14
|
-
process.stdin.setEncoding("utf8");
|
|
15
|
-
for await (const chunk of process.stdin)
|
|
16
|
-
body += chunk;
|
|
17
|
-
try {
|
|
18
|
-
body = JSON.parse(body);
|
|
19
|
-
}
|
|
20
|
-
catch (ed) {
|
|
21
|
-
console.error("Invalid JSON string from stdin: " + ed);
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
else if (body != null) {
|
|
26
|
-
// Try to parse as file path or JSON string
|
|
27
|
-
try {
|
|
28
|
-
if (fs.existsSync(body)) {
|
|
29
|
-
body = JSON.parse(fs.readFileSync(body, "utf8"));
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
body = JSON.parse(body);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
console.error("Invalid JSON string or file path: " + body);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
let result = await fetchManagementApi(apiPath +
|
|
41
|
-
(opts.paginate
|
|
42
|
-
? apiPath.includes("?")
|
|
43
|
-
? "&size=1000"
|
|
44
|
-
: "?size=2&page=0"
|
|
45
|
-
: ""), method.toUpperCase(), body);
|
|
46
|
-
let agg;
|
|
47
|
-
if (opts.paginate) {
|
|
48
|
-
const key = Object.keys(result).filter((k) => k !== "nextPageId")[0];
|
|
49
|
-
agg = result[key];
|
|
50
|
-
while (result.nextPageId) {
|
|
51
|
-
result = await fetchManagementApi(apiPath + `?pageId=${result.nextPageId}`, method.toUpperCase(), body);
|
|
52
|
-
agg.push(...result[key]);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
agg = result;
|
|
57
|
-
}
|
|
58
|
-
console.log(JSON.stringify(agg, null, 2));
|
|
59
|
-
});
|
package/lib/commands/login.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const loginCommand: import("commander").Command;
|
package/lib/commands/login.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import open from "open";
|
|
4
|
-
import * as os from "os";
|
|
5
|
-
import * as path from "path";
|
|
6
|
-
import { consola } from "consola";
|
|
7
|
-
const ACCOUNTS_PATH = path.join(os.homedir(), ".nlx-cli-auth.json");
|
|
8
|
-
async function saveTokens(account, tokenData) {
|
|
9
|
-
await keytar.setPassword("nlx-cli", account, JSON.stringify(tokenData));
|
|
10
|
-
}
|
|
11
|
-
async function loadTokens() {
|
|
12
|
-
try {
|
|
13
|
-
const data = fs.readFileSync(ACCOUNTS_PATH, "utf8");
|
|
14
|
-
const accounts = JSON.parse(data);
|
|
15
|
-
if (accounts.currentAccount) {
|
|
16
|
-
const res = await keytar.getPassword("nlx-cli", accounts.currentAccount);
|
|
17
|
-
if (res) return [accounts.currentAccount, JSON.parse(res)];
|
|
18
|
-
}
|
|
19
|
-
throw new Error("No tokens found for current account");
|
|
20
|
-
} catch {
|
|
21
|
-
throw new Error("Failed to load tokens");
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
async function refreshTokenIfNeeded() {
|
|
25
|
-
let account, tokens;
|
|
26
|
-
try {
|
|
27
|
-
[account, tokens] = await loadTokens();
|
|
28
|
-
} catch (error) {
|
|
29
|
-
consola.error("Error loading tokens");
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
if (!tokens || !tokens.refresh_token) return null;
|
|
33
|
-
// Check expiry
|
|
34
|
-
const now = Math.floor(Date.now() / 1000);
|
|
35
|
-
if (
|
|
36
|
-
tokens.expires_in &&
|
|
37
|
-
tokens.obtained_at &&
|
|
38
|
-
now < tokens.obtained_at + tokens.expires_in - 60
|
|
39
|
-
) {
|
|
40
|
-
consola.debug("Access token is still valid.");
|
|
41
|
-
return tokens.access_token;
|
|
42
|
-
}
|
|
43
|
-
consola.debug("Access token is expired or invalid. Refreshing...");
|
|
44
|
-
// Refresh
|
|
45
|
-
const res = await fetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
|
|
46
|
-
method: "POST",
|
|
47
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
48
|
-
body: new URLSearchParams({
|
|
49
|
-
grant_type: "refresh_token",
|
|
50
|
-
client_id: CLIENT_ID,
|
|
51
|
-
refresh_token: tokens.refresh_token,
|
|
52
|
-
}),
|
|
53
|
-
});
|
|
54
|
-
const newTokens = await res.json();
|
|
55
|
-
if (newTokens.access_token) {
|
|
56
|
-
newTokens.refresh_token = newTokens.refresh_token || tokens.refresh_token;
|
|
57
|
-
newTokens.obtained_at = now;
|
|
58
|
-
await saveTokens(account, newTokens);
|
|
59
|
-
return newTokens.access_token;
|
|
60
|
-
}
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
const AUTH0_DOMAIN = process.env.AUTH0_DOMAIN || "nlxdev.us.auth0.com"; // e.g. 'dev-xxxxxx.us.auth0.com'
|
|
64
|
-
const CLIENT_ID =
|
|
65
|
-
process.env.AUTH0_CLIENT_ID || "A0qluq7wJQjFjMLle9pvrWWaVHM1QHE3";
|
|
66
|
-
const AUDIENCE =
|
|
67
|
-
process.env.AUTH0_AUDIENCE || "https://nlxdev.us.auth0.com/api/v2/";
|
|
68
|
-
export const loginCommand = new Command("login")
|
|
69
|
-
.description("Authenticate with NLX")
|
|
70
|
-
.action(async () => {
|
|
71
|
-
// Step 1: Start device flow
|
|
72
|
-
const deviceCodeRes = await fetch(
|
|
73
|
-
`https://${AUTH0_DOMAIN}/oauth/device/code`,
|
|
74
|
-
{
|
|
75
|
-
method: "POST",
|
|
76
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
77
|
-
body: new URLSearchParams({
|
|
78
|
-
client_id: CLIENT_ID,
|
|
79
|
-
scope: "openid profile email offline_access",
|
|
80
|
-
audience: AUDIENCE,
|
|
81
|
-
}),
|
|
82
|
-
},
|
|
83
|
-
);
|
|
84
|
-
const deviceCodeData = await deviceCodeRes.json();
|
|
85
|
-
open(deviceCodeData.verification_uri_complete);
|
|
86
|
-
consola.box(
|
|
87
|
-
`Please visit ${deviceCodeData.verification_uri_complete} and enter code: ${deviceCodeData.user_code}`,
|
|
88
|
-
);
|
|
89
|
-
// Step 2: Poll for token
|
|
90
|
-
let tokenData;
|
|
91
|
-
while (!tokenData) {
|
|
92
|
-
await new Promise((r) => setTimeout(r, deviceCodeData.interval * 1000));
|
|
93
|
-
const tokenRes = await fetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
|
|
94
|
-
method: "POST",
|
|
95
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
96
|
-
body: new URLSearchParams({
|
|
97
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
98
|
-
device_code: deviceCodeData.device_code,
|
|
99
|
-
client_id: CLIENT_ID,
|
|
100
|
-
}),
|
|
101
|
-
});
|
|
102
|
-
const resData = await tokenRes.json();
|
|
103
|
-
if (resData.access_token) {
|
|
104
|
-
tokenData = resData;
|
|
105
|
-
} else if (resData.error !== "authorization_pending") {
|
|
106
|
-
consola.error("Error:", resData.error_description || resData.error);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
// Step 3: Fetch user object
|
|
111
|
-
let accounts = { currentAccount: null, accounts: [] };
|
|
112
|
-
if (fs.existsSync(ACCOUNTS_PATH)) {
|
|
113
|
-
const data = fs.readFileSync(ACCOUNTS_PATH, "utf8");
|
|
114
|
-
accounts = JSON.parse(data);
|
|
115
|
-
}
|
|
116
|
-
const userRes = await fetch(`https://${AUTH0_DOMAIN}/userinfo`, {
|
|
117
|
-
headers: {
|
|
118
|
-
Authorization: `Bearer ${tokenData.access_token}`,
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
const userData = await userRes.json();
|
|
122
|
-
if (!accounts.currentAccount) {
|
|
123
|
-
await fs.promises.writeFile(
|
|
124
|
-
ACCOUNTS_PATH,
|
|
125
|
-
JSON.stringify({
|
|
126
|
-
currentAccount: userData.email,
|
|
127
|
-
accounts: [userData.email],
|
|
128
|
-
}),
|
|
129
|
-
);
|
|
130
|
-
} else if (accounts.currentAccount !== userData.email) {
|
|
131
|
-
accounts.currentAccount = userData.email;
|
|
132
|
-
await fs.promises.writeFile(
|
|
133
|
-
ACCOUNTS_PATH,
|
|
134
|
-
JSON.stringify({
|
|
135
|
-
currentAccount: userData.email,
|
|
136
|
-
accounts: [userData.email, ...accounts.accounts],
|
|
137
|
-
}),
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
// Step 4: Store token securely
|
|
141
|
-
tokenData.obtained_at = Math.floor(Date.now() / 1000);
|
|
142
|
-
await saveTokens(userData.email, tokenData);
|
|
143
|
-
consola.success("Login successful! Access token stored securely.");
|
|
144
|
-
});
|
|
145
|
-
// Example usage: get a valid access token
|
|
146
|
-
export async function ensureToken() {
|
|
147
|
-
const accessToken = await refreshTokenIfNeeded();
|
|
148
|
-
if (!accessToken) {
|
|
149
|
-
consola.error("Not authenticated. Please run 'login' first.");
|
|
150
|
-
process.exit(1);
|
|
151
|
-
}
|
|
152
|
-
return accessToken;
|
|
153
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import { compile } from "json-schema-to-typescript";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import { fetchManagementApi } from "../utils/index.js";
|
|
6
|
-
export const modalitiesCommand = new Command("modalities")
|
|
7
|
-
.description("Fetch modalities and generate TypeScript interfaces")
|
|
8
|
-
.option("-o, --out <file>", "Output TypeScript file", "modalities-types.d.ts")
|
|
9
|
-
.action(async (opts) => {
|
|
10
|
-
const data = await fetchManagementApi(`models`);
|
|
11
|
-
// Generate TypeScript interfaces for each modelId
|
|
12
|
-
let output = "// Auto-generated from NLX\n// Please do not edit manually\n\n";
|
|
13
|
-
for (const item of data.items) {
|
|
14
|
-
const name = item.modelId.replace(/[^a-zA-Z0-9_]/g, "");
|
|
15
|
-
const schema = item.schema;
|
|
16
|
-
const ts = await compile(schema, name, {
|
|
17
|
-
bannerComment: "",
|
|
18
|
-
additionalProperties: false,
|
|
19
|
-
});
|
|
20
|
-
output += ts + "\n";
|
|
21
|
-
}
|
|
22
|
-
// Write to file specified by flag or default
|
|
23
|
-
const outPath = path.resolve(process.cwd(), opts.out);
|
|
24
|
-
fs.writeFileSync(outPath, output);
|
|
25
|
-
console.log(`TypeScript interfaces written to ${outPath}`);
|
|
26
|
-
});
|
package/lib/custom-modality.js
DELETED
package/lib/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|