lua-cli 1.2.0 → 1.3.0-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/CHANGELOG.md +51 -0
- package/README.md +83 -56
- package/dist/commands/agents.js +20 -16
- package/dist/commands/apiKey.js +24 -19
- package/dist/commands/compile.d.ts +1 -0
- package/dist/commands/compile.js +822 -0
- package/dist/commands/configure.js +93 -68
- package/dist/commands/deploy-new.d.ts +20 -0
- package/dist/commands/deploy-new.js +128 -0
- package/dist/commands/deploy.d.ts +19 -0
- package/dist/commands/deploy.js +102 -756
- package/dist/commands/destroy.js +26 -21
- package/dist/commands/index.d.ts +3 -2
- package/dist/commands/index.js +3 -2
- package/dist/commands/init.js +108 -61
- package/dist/commands/push.d.ts +22 -0
- package/dist/commands/push.js +138 -0
- package/dist/commands/test.js +14 -15
- package/dist/index.js +29 -19
- package/dist/services/auth.d.ts +20 -0
- package/dist/services/auth.js +43 -4
- package/dist/skill.d.ts +22 -1
- package/dist/skill.js +21 -1
- package/dist/types/index.d.ts +16 -2
- package/dist/types/index.js +16 -1
- package/dist/utils/cli.d.ts +34 -0
- package/dist/utils/cli.js +58 -0
- package/dist/utils/files.d.ts +1 -1
- package/dist/utils/files.js +4 -3
- package/package.json +4 -4
- package/template/.lua/deploy.json +3 -0
- package/template/index.ts +4 -1
- package/template/tools/AdvancedMathTool.ts +1 -1
- package/template/tools/CalculatorTool.ts +1 -1
|
@@ -1,80 +1,105 @@
|
|
|
1
1
|
import inquirer from "inquirer";
|
|
2
2
|
import { saveApiKey, checkApiKey, requestEmailOTP, verifyOTPAndGetToken, generateApiKey } from "../services/auth.js";
|
|
3
|
+
import { withErrorHandling, clearPromptLines, writeProgress, writeSuccess } from "../utils/cli.js";
|
|
3
4
|
export async function configureCommand() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
{
|
|
7
|
-
type: "list",
|
|
8
|
-
name: "authMethod",
|
|
9
|
-
message: "Choose authentication method:",
|
|
10
|
-
choices: [
|
|
11
|
-
{ name: "API Key", value: "api-key" },
|
|
12
|
-
{ name: "Email", value: "email" }
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
]);
|
|
16
|
-
if (authMethod === "api-key") {
|
|
17
|
-
// Existing API key flow
|
|
18
|
-
const answers = await inquirer.prompt([
|
|
5
|
+
return withErrorHandling(async () => {
|
|
6
|
+
// Choose authentication method
|
|
7
|
+
const { authMethod } = await inquirer.prompt([
|
|
19
8
|
{
|
|
20
|
-
type: "
|
|
21
|
-
name: "
|
|
22
|
-
message: "
|
|
23
|
-
|
|
24
|
-
|
|
9
|
+
type: "list",
|
|
10
|
+
name: "authMethod",
|
|
11
|
+
message: "Choose authentication method:",
|
|
12
|
+
choices: [
|
|
13
|
+
{ name: "API Key", value: "api-key" },
|
|
14
|
+
{ name: "Email", value: "email" }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
25
17
|
]);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
// Clear the authentication method prompt lines
|
|
19
|
+
clearPromptLines(2);
|
|
20
|
+
if (authMethod === "api-key") {
|
|
21
|
+
// Existing API key flow
|
|
22
|
+
const answers = await inquirer.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: "password",
|
|
25
|
+
name: "apiKey",
|
|
26
|
+
message: "Enter your API key",
|
|
27
|
+
mask: "*",
|
|
28
|
+
},
|
|
29
|
+
]);
|
|
30
|
+
const data = await checkApiKey(answers.apiKey);
|
|
31
|
+
if (!data) {
|
|
32
|
+
console.error("❌ Invalid API key");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
await saveApiKey(answers.apiKey);
|
|
36
|
+
writeSuccess("✅ API key saved securely.");
|
|
30
37
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
43
|
-
return emailRegex.test(input) || "Please enter a valid email address";
|
|
38
|
+
else if (authMethod === "email") {
|
|
39
|
+
// Email authentication flow
|
|
40
|
+
const { email } = await inquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
type: "input",
|
|
43
|
+
name: "email",
|
|
44
|
+
message: "Enter your email address:",
|
|
45
|
+
validate: (input) => {
|
|
46
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
47
|
+
return emailRegex.test(input) || "Please enter a valid email address";
|
|
48
|
+
}
|
|
44
49
|
}
|
|
50
|
+
]);
|
|
51
|
+
// Clear the email prompt lines
|
|
52
|
+
clearPromptLines(2);
|
|
53
|
+
writeProgress("📧 Sending OTP to your email...");
|
|
54
|
+
const otpSent = await requestEmailOTP(email);
|
|
55
|
+
if (!otpSent) {
|
|
56
|
+
console.error("❌ Failed to send OTP. Please try again.");
|
|
57
|
+
process.exit(1);
|
|
45
58
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
writeProgress("✅ OTP sent successfully!");
|
|
60
|
+
// OTP verification with retry logic
|
|
61
|
+
let otpVerified = false;
|
|
62
|
+
let attempts = 0;
|
|
63
|
+
let signInToken = null;
|
|
64
|
+
const maxAttempts = 3;
|
|
65
|
+
while (!otpVerified && attempts < maxAttempts) {
|
|
66
|
+
const { pin } = await inquirer.prompt([
|
|
67
|
+
{
|
|
68
|
+
type: "input",
|
|
69
|
+
name: "pin",
|
|
70
|
+
message: "Enter the OTP code:",
|
|
71
|
+
validate: (input) => {
|
|
72
|
+
return input.length === 6 || "OTP must be 6 digits";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
// Clear the OTP prompt lines
|
|
77
|
+
clearPromptLines(2);
|
|
78
|
+
writeProgress("🔐 Verifying OTP...");
|
|
79
|
+
signInToken = await verifyOTPAndGetToken(email, pin);
|
|
80
|
+
if (signInToken) {
|
|
81
|
+
otpVerified = true;
|
|
82
|
+
writeProgress("✅ OTP verified successfully!");
|
|
83
|
+
writeProgress("🔑 Generating API key...");
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
attempts++;
|
|
87
|
+
if (attempts < maxAttempts) {
|
|
88
|
+
console.log(`❌ Invalid OTP. ${maxAttempts - attempts} attempts remaining.`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.error("❌ Invalid OTP. Maximum attempts reached. Please try again later.");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
61
94
|
}
|
|
62
95
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
console.log("✅ OTP verified successfully!");
|
|
71
|
-
console.log("🔑 Generating API key...");
|
|
72
|
-
const apiKey = await generateApiKey(signInToken);
|
|
73
|
-
if (!apiKey) {
|
|
74
|
-
console.error("❌ Failed to generate API key. Please try again.");
|
|
75
|
-
process.exit(1);
|
|
96
|
+
const apiKey = await generateApiKey(signInToken);
|
|
97
|
+
if (!apiKey) {
|
|
98
|
+
console.error("❌ Failed to generate API key. Please try again.");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
await saveApiKey(apiKey);
|
|
102
|
+
writeSuccess("✅ API key generated and saved securely.");
|
|
76
103
|
}
|
|
77
|
-
|
|
78
|
-
console.log("✅ API key generated and saved securely.");
|
|
79
|
-
}
|
|
104
|
+
}, "configuration");
|
|
80
105
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface VersionInfo {
|
|
2
|
+
version: string;
|
|
3
|
+
createdDate: string;
|
|
4
|
+
createdBy: string;
|
|
5
|
+
isCurrent: boolean;
|
|
6
|
+
createdByEmail: string;
|
|
7
|
+
createdByFullName: string;
|
|
8
|
+
}
|
|
9
|
+
export interface VersionsResponse {
|
|
10
|
+
versions: VersionInfo[];
|
|
11
|
+
}
|
|
12
|
+
export interface PublishResponse {
|
|
13
|
+
message: string;
|
|
14
|
+
skillId: string;
|
|
15
|
+
activeVersionId: string;
|
|
16
|
+
publishedAt: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function fetchVersions(apiKey: string, agentId: string, skillId: string): Promise<VersionsResponse>;
|
|
19
|
+
export declare function publishVersion(apiKey: string, agentId: string, skillId: string, version: string): Promise<PublishResponse>;
|
|
20
|
+
export declare function deployCommand(): Promise<void>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { loadApiKey, checkApiKey } from '../services/auth.js';
|
|
5
|
+
export async function fetchVersions(apiKey, agentId, skillId) {
|
|
6
|
+
try {
|
|
7
|
+
const response = await fetch(`https://api.lua.dev/developer/skills/${agentId}/${skillId}/versions`, {
|
|
8
|
+
method: "GET",
|
|
9
|
+
headers: {
|
|
10
|
+
"accept": "application/json",
|
|
11
|
+
"Authorization": `Bearer ${apiKey}`
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(`Failed to fetch versions: ${response.status} ${response.statusText}`);
|
|
16
|
+
}
|
|
17
|
+
return await response.json();
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.error("❌ Error fetching versions:", error);
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function publishVersion(apiKey, agentId, skillId, version) {
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetch(`https://api.lua.dev/developer/skills/${agentId}/${skillId}/${version}/publish`, {
|
|
27
|
+
method: "PUT",
|
|
28
|
+
headers: {
|
|
29
|
+
"accept": "application/json",
|
|
30
|
+
"Authorization": `Bearer ${apiKey}`
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Failed to publish version: ${response.status} ${response.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
return await response.json();
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.error("❌ Error publishing version:", error);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function readTomlData() {
|
|
44
|
+
const tomlPath = path.join(process.cwd(), 'lua.skill.toml');
|
|
45
|
+
if (!fs.existsSync(tomlPath)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const tomlContent = fs.readFileSync(tomlPath, 'utf8');
|
|
49
|
+
const agentIdMatch = tomlContent.match(/agentId\s*=\s*["']([^"']+)["']/);
|
|
50
|
+
const skillIdMatch = tomlContent.match(/skillId\s*=\s*["']([^"']+)["']/);
|
|
51
|
+
if (!agentIdMatch || !skillIdMatch) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
agentId: agentIdMatch[1],
|
|
56
|
+
skillId: skillIdMatch[1]
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function formatVersionChoice(version) {
|
|
60
|
+
const currentIndicator = version.isCurrent ? ' (CURRENT)' : '';
|
|
61
|
+
const date = new Date(version.createdDate).toLocaleDateString();
|
|
62
|
+
return `${version.version}${currentIndicator} - Created: ${date} by ${version.createdByEmail}`;
|
|
63
|
+
}
|
|
64
|
+
export async function deployCommand() {
|
|
65
|
+
try {
|
|
66
|
+
// Check if we're in a skill directory
|
|
67
|
+
const tomlData = readTomlData();
|
|
68
|
+
if (!tomlData) {
|
|
69
|
+
console.error("❌ No lua.skill.toml found or missing agentId/skillId. Please run this command from a skill directory.");
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
// Load API key
|
|
73
|
+
const apiKey = await loadApiKey();
|
|
74
|
+
if (!apiKey) {
|
|
75
|
+
console.error("❌ No API key found. Please run 'lua configure' to set up your API key.");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
// Validate API key
|
|
79
|
+
const userData = await checkApiKey(apiKey);
|
|
80
|
+
console.log(`✅ Authenticated as ${userData.email}`);
|
|
81
|
+
// Fetch available versions
|
|
82
|
+
console.log("🔄 Fetching available versions...");
|
|
83
|
+
const versionsResponse = await fetchVersions(apiKey, tomlData.agentId, tomlData.skillId);
|
|
84
|
+
if (!versionsResponse.versions || versionsResponse.versions.length === 0) {
|
|
85
|
+
console.log("❌ No versions found for this skill.");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
// Sort versions by creation date (newest first)
|
|
89
|
+
const sortedVersions = versionsResponse.versions.sort((a, b) => new Date(b.createdDate).getTime() - new Date(a.createdDate).getTime());
|
|
90
|
+
// Let user select a version
|
|
91
|
+
const { selectedVersion } = await inquirer.prompt([
|
|
92
|
+
{
|
|
93
|
+
type: "list",
|
|
94
|
+
name: "selectedVersion",
|
|
95
|
+
message: "Select a version to deploy:",
|
|
96
|
+
choices: sortedVersions.map(version => ({
|
|
97
|
+
name: formatVersionChoice(version),
|
|
98
|
+
value: version.version
|
|
99
|
+
}))
|
|
100
|
+
}
|
|
101
|
+
]);
|
|
102
|
+
// Show warning and confirm deployment
|
|
103
|
+
const { confirmed } = await inquirer.prompt([
|
|
104
|
+
{
|
|
105
|
+
type: "confirm",
|
|
106
|
+
name: "confirmed",
|
|
107
|
+
message: "⚠️ Warning: This version will be deployed to all users. Do you want to proceed?",
|
|
108
|
+
default: false
|
|
109
|
+
}
|
|
110
|
+
]);
|
|
111
|
+
if (!confirmed) {
|
|
112
|
+
console.log("❌ Deployment cancelled.");
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
// Publish the selected version
|
|
116
|
+
console.log(`🔄 Publishing version ${selectedVersion}...`);
|
|
117
|
+
const publishResponse = await publishVersion(apiKey, tomlData.agentId, tomlData.skillId, selectedVersion);
|
|
118
|
+
console.log("✅ Deployment successful!");
|
|
119
|
+
console.log(`📦 Version: ${publishResponse.activeVersionId}`);
|
|
120
|
+
console.log(`🆔 Skill ID: ${publishResponse.skillId}`);
|
|
121
|
+
console.log(`📅 Published at: ${new Date(publishResponse.publishedAt).toLocaleString()}`);
|
|
122
|
+
console.log(`💬 ${publishResponse.message}`);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
console.error("❌ Error during deployment:", error.message);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -1 +1,20 @@
|
|
|
1
|
+
export interface VersionInfo {
|
|
2
|
+
version: string;
|
|
3
|
+
createdDate: string;
|
|
4
|
+
createdBy: string;
|
|
5
|
+
isCurrent: boolean;
|
|
6
|
+
createdByEmail: string;
|
|
7
|
+
createdByFullName: string;
|
|
8
|
+
}
|
|
9
|
+
export interface VersionsResponse {
|
|
10
|
+
versions: VersionInfo[];
|
|
11
|
+
}
|
|
12
|
+
export interface PublishResponse {
|
|
13
|
+
message: string;
|
|
14
|
+
skillId: string;
|
|
15
|
+
activeVersionId: string;
|
|
16
|
+
publishedAt: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function fetchVersions(apiKey: string, agentId: string, skillId: string): Promise<VersionsResponse>;
|
|
19
|
+
export declare function publishVersion(apiKey: string, agentId: string, skillId: string, version: string): Promise<PublishResponse>;
|
|
1
20
|
export declare function deployCommand(): Promise<void>;
|