@zaflun/lumio-cli 0.2.0 → 0.3.0
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/dist/index.js +262 -91
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ function registerLogin(program2) {
|
|
|
75
75
|
const state = crypto.randomBytes(16).toString("hex");
|
|
76
76
|
const port = 9876;
|
|
77
77
|
const redirectUri = `http://localhost:${port}/callback`;
|
|
78
|
-
await new Promise((
|
|
78
|
+
await new Promise((resolve10, reject) => {
|
|
79
79
|
const server = http.createServer(async (req, res) => {
|
|
80
80
|
if (!req.url?.startsWith("/callback")) {
|
|
81
81
|
res.writeHead(404);
|
|
@@ -118,7 +118,7 @@ function registerLogin(program2) {
|
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
server.close();
|
|
121
|
-
|
|
121
|
+
resolve10();
|
|
122
122
|
});
|
|
123
123
|
server.listen(port, async () => {
|
|
124
124
|
const pkceUrl = `${options.apiUrl}/v1/auth/cli/authorize?code_challenge=${challenge}&code_challenge_method=S256&redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}`;
|
|
@@ -288,7 +288,7 @@ function registerInit(program2) {
|
|
|
288
288
|
}
|
|
289
289
|
} catch {
|
|
290
290
|
}
|
|
291
|
-
const
|
|
291
|
+
const baseAnswers = await inquirer.prompt([
|
|
292
292
|
{ type: "input", name: "name", message: "Extension name:", default: name },
|
|
293
293
|
{ type: "input", name: "description", message: "Description:" },
|
|
294
294
|
{
|
|
@@ -296,16 +296,35 @@ function registerInit(program2) {
|
|
|
296
296
|
name: "category",
|
|
297
297
|
message: "Category:",
|
|
298
298
|
choices: ["widget", "extension", "overlay", "bot_module", "integration", "theme"]
|
|
299
|
-
}
|
|
300
|
-
{
|
|
301
|
-
type: "checkbox",
|
|
302
|
-
name: "targets",
|
|
303
|
-
message: "Targets:",
|
|
304
|
-
choices: ["editor", "layer", "interactive"],
|
|
305
|
-
default: ["editor", "layer"]
|
|
306
|
-
},
|
|
307
|
-
{ type: "confirm", name: "server", message: "Server functions?", default: false }
|
|
299
|
+
}
|
|
308
300
|
]);
|
|
301
|
+
const answers = { ...baseAnswers, targets: [], server: false };
|
|
302
|
+
if (answers.category === "bot_module") {
|
|
303
|
+
const { platforms } = await inquirer.prompt([{
|
|
304
|
+
type: "checkbox",
|
|
305
|
+
name: "platforms",
|
|
306
|
+
message: "Which platforms should this bot module support?",
|
|
307
|
+
choices: ["twitch", "youtube", "kick", "trovo", "discord"],
|
|
308
|
+
default: ["twitch"],
|
|
309
|
+
validate: (input) => input.length > 0 || "Select at least one platform"
|
|
310
|
+
}]);
|
|
311
|
+
answers.platforms = platforms;
|
|
312
|
+
answers.targets = [];
|
|
313
|
+
answers.server = true;
|
|
314
|
+
} else {
|
|
315
|
+
const targetAndServer = await inquirer.prompt([
|
|
316
|
+
{
|
|
317
|
+
type: "checkbox",
|
|
318
|
+
name: "targets",
|
|
319
|
+
message: "Targets:",
|
|
320
|
+
choices: ["editor", "layer", "interactive"],
|
|
321
|
+
default: ["editor", "layer"]
|
|
322
|
+
},
|
|
323
|
+
{ type: "confirm", name: "server", message: "Server functions?", default: false }
|
|
324
|
+
]);
|
|
325
|
+
answers.targets = targetAndServer.targets;
|
|
326
|
+
answers.server = targetAndServer.server;
|
|
327
|
+
}
|
|
309
328
|
const createBody = {
|
|
310
329
|
name: answers.name,
|
|
311
330
|
description: answers.description,
|
|
@@ -327,11 +346,21 @@ function registerInit(program2) {
|
|
|
327
346
|
version: "0.1.0",
|
|
328
347
|
targets: answers.targets,
|
|
329
348
|
server: answers.server,
|
|
330
|
-
permissions: [],
|
|
349
|
+
permissions: answers.category === "bot_module" ? ["chat:read", "chat:send"] : [],
|
|
331
350
|
egress: { allowHosts: [], allowPorts: [443] },
|
|
332
351
|
visibility: "private",
|
|
333
352
|
pricing: { type: "free" }
|
|
334
353
|
};
|
|
354
|
+
if (answers.category === "bot_module") {
|
|
355
|
+
config.platforms = answers.platforms;
|
|
356
|
+
config.triggers = {
|
|
357
|
+
commands: [{ name: "hello", description: "Say hello", cooldownGlobal: 5, cooldownUser: 10, minRole: "everyone" }],
|
|
358
|
+
keywords: [],
|
|
359
|
+
patterns: [],
|
|
360
|
+
events: [],
|
|
361
|
+
timers: []
|
|
362
|
+
};
|
|
363
|
+
}
|
|
335
364
|
fs2.writeFileSync(
|
|
336
365
|
path2.join(projectDir, "lumio.config.json"),
|
|
337
366
|
JSON.stringify(config, null, 2)
|
|
@@ -364,8 +393,15 @@ function registerInit(program2) {
|
|
|
364
393
|
`;
|
|
365
394
|
fs2.writeFileSync(path2.join(projectDir, "CHANGELOG.md"), changelog);
|
|
366
395
|
fs2.mkdirSync(path2.join(projectDir, "src"), { recursive: true });
|
|
367
|
-
|
|
368
|
-
|
|
396
|
+
if (answers.category === "bot_module") {
|
|
397
|
+
fs2.mkdirSync(path2.join(projectDir, "server"), { recursive: true });
|
|
398
|
+
const serverFunctions = 'import { command } from "@zaflun/lumio-sdk/server";\n\nexport const hello = command("hello", async (ctx, args) => {\n return { reply: `Hello ${ctx.user?.name ?? "stranger"}!` };\n});\n';
|
|
399
|
+
fs2.writeFileSync(path2.join(projectDir, "server", "functions.ts"), serverFunctions);
|
|
400
|
+
const schema = 'import { defineSchema } from "@zaflun/lumio-sdk/server";\n\nexport default defineSchema({});\n';
|
|
401
|
+
fs2.writeFileSync(path2.join(projectDir, "server", "schema.ts"), schema);
|
|
402
|
+
} else {
|
|
403
|
+
for (const target of answers.targets) {
|
|
404
|
+
const content = `import { Lumio, CompactView, Text } from "@zaflun/lumio-sdk";
|
|
369
405
|
|
|
370
406
|
function App() {
|
|
371
407
|
return (
|
|
@@ -377,14 +413,15 @@ function App() {
|
|
|
377
413
|
|
|
378
414
|
Lumio.render(<App />, { target: "${target}" });
|
|
379
415
|
`;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
416
|
+
fs2.writeFileSync(path2.join(projectDir, "src", `${target}.tsx`), content);
|
|
417
|
+
}
|
|
418
|
+
if (answers.server) {
|
|
419
|
+
fs2.mkdirSync(path2.join(projectDir, "server"), { recursive: true });
|
|
420
|
+
const schema = 'import { defineSchema, defineTable, v } from "@zaflun/lumio-sdk/server";\n\nexport default defineSchema({\n items: defineTable({\n text: v.string(),\n completed: v.boolean(),\n }).storage("instance"),\n});\n';
|
|
421
|
+
fs2.writeFileSync(path2.join(projectDir, "server", "schema.ts"), schema);
|
|
422
|
+
const functions = 'import { queryRows, insertRow, patchRow, deleteRow, arg } from "@zaflun/lumio-sdk/server";\n\nexport const getItems = queryRows("items");\nexport const addItem = insertRow("items", { text: arg("text"), completed: false }, { editorOnly: true });\nexport const toggleItem = patchRow("items", arg("id"), { completed: arg("completed") }, { editorOnly: true });\nexport const removeItem = deleteRow("items", arg("id"), { editorOnly: true });\n';
|
|
423
|
+
fs2.writeFileSync(path2.join(projectDir, "server", "functions.ts"), functions);
|
|
424
|
+
}
|
|
388
425
|
}
|
|
389
426
|
console.log(`
|
|
390
427
|
Done! Created ${projectDir}/`);
|
|
@@ -393,17 +430,6 @@ Done! Created ${projectDir}/`);
|
|
|
393
430
|
}
|
|
394
431
|
|
|
395
432
|
// src/commands/dev.ts
|
|
396
|
-
function registerDev(program2) {
|
|
397
|
-
program2.command("dev").description("Start local dev server with hot-reload and mock events").option("-p, --port <port>", "Dev server port", "3100").action(async (options) => {
|
|
398
|
-
console.log(`Starting Lumio dev server on port ${options.port}...`);
|
|
399
|
-
console.log("Mock events enabled. Hot-reload active.");
|
|
400
|
-
console.log("Open http://localhost:" + options.port + " in your browser.");
|
|
401
|
-
console.log("\nDev server implementation requires @zaflun/lumio-sdk and Vite.");
|
|
402
|
-
console.log("Full implementation coming in Phase 12 (Live-Code-Editor).");
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// src/commands/build.ts
|
|
407
433
|
import * as fs3 from "fs";
|
|
408
434
|
import * as path3 from "path";
|
|
409
435
|
|
|
@@ -429,6 +455,8 @@ function parseConfigFile(jsonString) {
|
|
|
429
455
|
allowHosts: raw.egress?.allowHosts ?? [],
|
|
430
456
|
allowPorts: raw.egress?.allowPorts ?? []
|
|
431
457
|
},
|
|
458
|
+
platforms: raw.platforms,
|
|
459
|
+
triggers: raw.triggers,
|
|
432
460
|
fonts: raw.fonts,
|
|
433
461
|
visibility: raw.visibility ?? "private",
|
|
434
462
|
pricing: raw.pricing ?? { type: "free" }
|
|
@@ -441,7 +469,11 @@ function validateConfig(config) {
|
|
|
441
469
|
if (!config.name) {
|
|
442
470
|
throw new ConfigError("name is required in lumio.config.json");
|
|
443
471
|
}
|
|
444
|
-
if (
|
|
472
|
+
if (config.category === "bot_module") {
|
|
473
|
+
if (!config.platforms || config.platforms.length === 0) {
|
|
474
|
+
throw new ConfigError("At least one platform is required for bot_module (twitch, youtube, kick, trovo, discord)");
|
|
475
|
+
}
|
|
476
|
+
} else if (!config.targets || config.targets.length === 0) {
|
|
445
477
|
throw new ConfigError("At least one target is required (editor, layer, or interactive)");
|
|
446
478
|
}
|
|
447
479
|
if (!config.version) {
|
|
@@ -449,28 +481,130 @@ function validateConfig(config) {
|
|
|
449
481
|
}
|
|
450
482
|
}
|
|
451
483
|
|
|
484
|
+
// src/commands/dev-bot-module.ts
|
|
485
|
+
import * as readline from "readline";
|
|
486
|
+
import chalk from "chalk";
|
|
487
|
+
function parseUserPrefix(input) {
|
|
488
|
+
const defaultUser = {
|
|
489
|
+
id: "user-001",
|
|
490
|
+
name: "dev_user",
|
|
491
|
+
displayName: "Dev User",
|
|
492
|
+
platform: "twitch",
|
|
493
|
+
role: "everyone",
|
|
494
|
+
isMod: false,
|
|
495
|
+
isVip: false,
|
|
496
|
+
isSub: false
|
|
497
|
+
};
|
|
498
|
+
if (!input.startsWith("@")) {
|
|
499
|
+
return { user: defaultUser, message: input };
|
|
500
|
+
}
|
|
501
|
+
const spaceIdx = input.indexOf(" ");
|
|
502
|
+
if (spaceIdx === -1) return { user: defaultUser, message: input };
|
|
503
|
+
const prefix = input.substring(1, spaceIdx);
|
|
504
|
+
const message = input.substring(spaceIdx + 1);
|
|
505
|
+
const parts = prefix.split(":");
|
|
506
|
+
const name = parts[parts.length - 1];
|
|
507
|
+
const rolePrefix = parts.length > 1 ? parts[0] : "everyone";
|
|
508
|
+
return {
|
|
509
|
+
user: {
|
|
510
|
+
id: `user-${name}`,
|
|
511
|
+
name,
|
|
512
|
+
displayName: name,
|
|
513
|
+
platform: "twitch",
|
|
514
|
+
role: rolePrefix,
|
|
515
|
+
isMod: rolePrefix === "mod",
|
|
516
|
+
isVip: rolePrefix === "vip",
|
|
517
|
+
isSub: ["sub", "vip", "mod", "broadcaster"].includes(rolePrefix)
|
|
518
|
+
},
|
|
519
|
+
message
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
async function runBotModuleDevServer(config) {
|
|
523
|
+
console.log(chalk.bold("\n\u{1F916} Bot Module Dev Server"));
|
|
524
|
+
console.log(`Extension: ${config.name}`);
|
|
525
|
+
console.log(`Platforms: ${config.platforms?.join(", ") ?? "none"}`);
|
|
526
|
+
console.log(chalk.dim("\nType chat messages. Prefix with @username or @role:username."));
|
|
527
|
+
console.log(chalk.dim("Examples: !hello | @sub:viewer !points 100 | @mod:admin gg"));
|
|
528
|
+
console.log(chalk.dim("Type !quit to exit.\n"));
|
|
529
|
+
const rl = readline.createInterface({
|
|
530
|
+
input: process.stdin,
|
|
531
|
+
output: process.stdout,
|
|
532
|
+
prompt: chalk.cyan("> ")
|
|
533
|
+
});
|
|
534
|
+
rl.prompt();
|
|
535
|
+
rl.on("line", async (line) => {
|
|
536
|
+
const trimmed = line.trim();
|
|
537
|
+
if (!trimmed) {
|
|
538
|
+
rl.prompt();
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if (trimmed === "!quit") {
|
|
542
|
+
rl.close();
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const { user, message } = parseUserPrefix(trimmed);
|
|
546
|
+
console.log(chalk.dim(` [${user.role}:${user.name}] ${message}`));
|
|
547
|
+
console.log(chalk.yellow(" \u2192 Handler execution not yet wired (requires esbuild + V8)"));
|
|
548
|
+
rl.prompt();
|
|
549
|
+
});
|
|
550
|
+
rl.on("close", () => {
|
|
551
|
+
console.log(chalk.dim("\nBot Module Dev Server stopped."));
|
|
552
|
+
process.exit(0);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/commands/dev.ts
|
|
557
|
+
function registerDev(program2) {
|
|
558
|
+
program2.command("dev").description("Start local dev server with hot-reload and mock events").option("-p, --port <port>", "Dev server port", "3100").option("--bot-module", "Start interactive bot module chat simulator").action(async (options) => {
|
|
559
|
+
const configPath = path3.resolve("lumio.config.json");
|
|
560
|
+
if (options.botModule || fs3.existsSync(configPath)) {
|
|
561
|
+
const configExists = fs3.existsSync(configPath);
|
|
562
|
+
if (configExists) {
|
|
563
|
+
const config = parseConfigFile(fs3.readFileSync(configPath, "utf-8"));
|
|
564
|
+
if (options.botModule || config.category === "bot_module") {
|
|
565
|
+
await runBotModuleDevServer(config);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
} else if (options.botModule) {
|
|
569
|
+
console.error("lumio.config.json not found. Run `lumio init` first.");
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
console.log(`Starting Lumio dev server on port ${options.port}...`);
|
|
574
|
+
console.log("Mock events enabled. Hot-reload active.");
|
|
575
|
+
console.log("Open http://localhost:" + options.port + " in your browser.");
|
|
576
|
+
console.log("\nDev server implementation requires @zaflun/lumio-sdk and Vite.");
|
|
577
|
+
console.log("Full implementation coming in Phase 12 (Live-Code-Editor).");
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
452
581
|
// src/commands/build.ts
|
|
582
|
+
import * as fs4 from "fs";
|
|
583
|
+
import * as path4 from "path";
|
|
453
584
|
function registerBuild(program2) {
|
|
454
585
|
program2.command("build").description("Bundle extension as standalone HTML + source archive").action(async () => {
|
|
455
|
-
const configPath =
|
|
456
|
-
if (!
|
|
586
|
+
const configPath = path4.resolve("lumio.config.json");
|
|
587
|
+
if (!fs4.existsSync(configPath)) {
|
|
457
588
|
console.error("lumio.config.json not found. Run `lumio init` first.");
|
|
458
589
|
process.exit(1);
|
|
459
590
|
}
|
|
460
|
-
const configJson =
|
|
591
|
+
const configJson = fs4.readFileSync(configPath, "utf-8");
|
|
461
592
|
const config = parseConfigFile(configJson);
|
|
462
593
|
validateConfig(config);
|
|
463
|
-
const changelogPath =
|
|
464
|
-
if (!
|
|
594
|
+
const changelogPath = path4.resolve("CHANGELOG.md");
|
|
595
|
+
if (!fs4.existsSync(changelogPath)) {
|
|
465
596
|
console.warn("Warning: No CHANGELOG.md found. It is recommended to maintain a changelog.");
|
|
466
597
|
}
|
|
467
598
|
console.log(`Building ${config.name} v${config.version}...`);
|
|
468
|
-
const distDir =
|
|
469
|
-
|
|
599
|
+
const distDir = path4.resolve("dist");
|
|
600
|
+
fs4.mkdirSync(distDir, { recursive: true });
|
|
470
601
|
const { build } = await import("esbuild");
|
|
602
|
+
if (config.category === "bot_module" && config.targets.length === 0) {
|
|
603
|
+
console.log(" Bot module \u2014 no UI targets to bundle.");
|
|
604
|
+
}
|
|
471
605
|
for (const target of config.targets) {
|
|
472
|
-
const entryPoint =
|
|
473
|
-
if (!
|
|
606
|
+
const entryPoint = path4.resolve("src", `${target}.tsx`);
|
|
607
|
+
if (!fs4.existsSync(entryPoint)) {
|
|
474
608
|
console.warn(`Warning: Entry point ${entryPoint} not found, skipping target "${target}".`);
|
|
475
609
|
continue;
|
|
476
610
|
}
|
|
@@ -479,7 +613,7 @@ function registerBuild(program2) {
|
|
|
479
613
|
bundle: true,
|
|
480
614
|
minify: true,
|
|
481
615
|
format: "esm",
|
|
482
|
-
outfile:
|
|
616
|
+
outfile: path4.join(distDir, `${target}.js`),
|
|
483
617
|
jsx: "automatic",
|
|
484
618
|
target: "es2022",
|
|
485
619
|
external: [],
|
|
@@ -490,17 +624,17 @@ function registerBuild(program2) {
|
|
|
490
624
|
console.log(` Built: dist/${target}.js`);
|
|
491
625
|
}
|
|
492
626
|
const { default: archiver } = await import("archiver");
|
|
493
|
-
const sourceArchivePath =
|
|
494
|
-
const output =
|
|
627
|
+
const sourceArchivePath = path4.join(distDir, "source.tar.gz");
|
|
628
|
+
const output = fs4.createWriteStream(sourceArchivePath);
|
|
495
629
|
const archive = archiver("tar", { gzip: true });
|
|
496
630
|
archive.pipe(output);
|
|
497
631
|
archive.directory("src/", "src");
|
|
498
|
-
if (config.server &&
|
|
632
|
+
if ((config.server || config.category === "bot_module") && fs4.existsSync("server")) {
|
|
499
633
|
archive.directory("server/", "server");
|
|
500
634
|
}
|
|
501
635
|
archive.file("lumio.config.json", { name: "lumio.config.json" });
|
|
502
636
|
archive.file("package.json", { name: "package.json" });
|
|
503
|
-
if (
|
|
637
|
+
if (fs4.existsSync("CHANGELOG.md")) {
|
|
504
638
|
archive.file("CHANGELOG.md", { name: "CHANGELOG.md" });
|
|
505
639
|
}
|
|
506
640
|
await archive.finalize();
|
|
@@ -510,8 +644,8 @@ function registerBuild(program2) {
|
|
|
510
644
|
}
|
|
511
645
|
|
|
512
646
|
// src/commands/deploy.ts
|
|
513
|
-
import * as
|
|
514
|
-
import * as
|
|
647
|
+
import * as fs5 from "fs";
|
|
648
|
+
import * as path5 from "path";
|
|
515
649
|
|
|
516
650
|
// src/changelog/parser.ts
|
|
517
651
|
var VERSION_HEADER_REGEX = /^## \[([^\]]+)\](?:\s*-\s*(\d{4}-\d{2}-\d{2}))?/;
|
|
@@ -557,20 +691,20 @@ function registerDeploy(program2) {
|
|
|
557
691
|
console.error("Not logged in. Run `lumio login` first.");
|
|
558
692
|
process.exit(1);
|
|
559
693
|
}
|
|
560
|
-
const configPath =
|
|
561
|
-
if (!
|
|
694
|
+
const configPath = path5.resolve("lumio.config.json");
|
|
695
|
+
if (!fs5.existsSync(configPath)) {
|
|
562
696
|
console.error("lumio.config.json not found. Run `lumio init` first.");
|
|
563
697
|
process.exit(1);
|
|
564
698
|
}
|
|
565
|
-
const configJson =
|
|
699
|
+
const configJson = fs5.readFileSync(configPath, "utf-8");
|
|
566
700
|
const config = parseConfigFile(configJson);
|
|
567
701
|
validateConfig(config);
|
|
568
702
|
const version = options.version ?? config.version;
|
|
569
703
|
let releaseNotes = options.description ?? "";
|
|
570
704
|
if (!releaseNotes) {
|
|
571
|
-
const changelogPath =
|
|
572
|
-
if (
|
|
573
|
-
const changelog =
|
|
705
|
+
const changelogPath = path5.resolve("CHANGELOG.md");
|
|
706
|
+
if (fs5.existsSync(changelogPath)) {
|
|
707
|
+
const changelog = fs5.readFileSync(changelogPath, "utf-8");
|
|
574
708
|
const entry = getVersionEntry(changelog, version);
|
|
575
709
|
if (entry) {
|
|
576
710
|
releaseNotes = entry.content;
|
|
@@ -581,17 +715,17 @@ function registerDeploy(program2) {
|
|
|
581
715
|
}
|
|
582
716
|
}
|
|
583
717
|
}
|
|
584
|
-
const distDir =
|
|
585
|
-
if (!
|
|
718
|
+
const distDir = path5.resolve("dist");
|
|
719
|
+
if (!fs5.existsSync(distDir)) {
|
|
586
720
|
console.error("No dist/ directory found. Run `lumio build` first.");
|
|
587
721
|
process.exit(1);
|
|
588
722
|
}
|
|
589
723
|
console.log(`Deploying ${config.name} v${version}...`);
|
|
590
724
|
const client = new LumioApiClient(store.getApiUrl(), token);
|
|
591
725
|
const formData = new FormData();
|
|
592
|
-
const sourceArchive =
|
|
593
|
-
if (
|
|
594
|
-
const sourceBuffer =
|
|
726
|
+
const sourceArchive = path5.join(distDir, "source.tar.gz");
|
|
727
|
+
if (fs5.existsSync(sourceArchive)) {
|
|
728
|
+
const sourceBuffer = fs5.readFileSync(sourceArchive);
|
|
595
729
|
formData.append("source", new Blob([sourceBuffer]), "source.tar.gz");
|
|
596
730
|
}
|
|
597
731
|
const manifest = JSON.parse(configJson);
|
|
@@ -618,8 +752,8 @@ function registerDeploy(program2) {
|
|
|
618
752
|
}
|
|
619
753
|
|
|
620
754
|
// src/commands/status.ts
|
|
621
|
-
import * as
|
|
622
|
-
import * as
|
|
755
|
+
import * as fs6 from "fs";
|
|
756
|
+
import * as path6 from "path";
|
|
623
757
|
function registerStatus(program2) {
|
|
624
758
|
program2.command("status").description("Show review status of the latest version").action(async () => {
|
|
625
759
|
const store = new CredentialStore();
|
|
@@ -628,12 +762,12 @@ function registerStatus(program2) {
|
|
|
628
762
|
console.error("Not logged in. Run `lumio login` first.");
|
|
629
763
|
process.exit(1);
|
|
630
764
|
}
|
|
631
|
-
const configPath =
|
|
632
|
-
if (!
|
|
765
|
+
const configPath = path6.resolve("lumio.config.json");
|
|
766
|
+
if (!fs6.existsSync(configPath)) {
|
|
633
767
|
console.error("lumio.config.json not found.");
|
|
634
768
|
process.exit(1);
|
|
635
769
|
}
|
|
636
|
-
const config = parseConfigFile(
|
|
770
|
+
const config = parseConfigFile(fs6.readFileSync(configPath, "utf-8"));
|
|
637
771
|
const client = new LumioApiClient(store.getApiUrl(), token);
|
|
638
772
|
try {
|
|
639
773
|
const versions = await client.get(
|
|
@@ -662,8 +796,8 @@ function registerStatus(program2) {
|
|
|
662
796
|
}
|
|
663
797
|
|
|
664
798
|
// src/commands/logs.ts
|
|
665
|
-
import * as
|
|
666
|
-
import * as
|
|
799
|
+
import * as fs7 from "fs";
|
|
800
|
+
import * as path7 from "path";
|
|
667
801
|
function registerLogs(program2) {
|
|
668
802
|
program2.command("logs").description("Stream server function logs via SSE").option("-f, --follow", "Follow log output", true).option("-n, --lines <count>", "Number of recent lines", "50").action(async (options) => {
|
|
669
803
|
const store = new CredentialStore();
|
|
@@ -672,12 +806,12 @@ function registerLogs(program2) {
|
|
|
672
806
|
console.error("Not logged in. Run `lumio login` first.");
|
|
673
807
|
process.exit(1);
|
|
674
808
|
}
|
|
675
|
-
const configPath =
|
|
676
|
-
if (!
|
|
809
|
+
const configPath = path7.resolve("lumio.config.json");
|
|
810
|
+
if (!fs7.existsSync(configPath)) {
|
|
677
811
|
console.error("lumio.config.json not found.");
|
|
678
812
|
process.exit(1);
|
|
679
813
|
}
|
|
680
|
-
const config = parseConfigFile(
|
|
814
|
+
const config = parseConfigFile(fs7.readFileSync(configPath, "utf-8"));
|
|
681
815
|
const apiUrl = store.getApiUrl();
|
|
682
816
|
const logsUrl = `${apiUrl}/v1/developer/extensions/${config.extensionId}/logs?lines=${options.lines}`;
|
|
683
817
|
console.log(`Streaming logs for ${config.name}...`);
|
|
@@ -714,8 +848,8 @@ function registerLogs(program2) {
|
|
|
714
848
|
}
|
|
715
849
|
|
|
716
850
|
// src/commands/env.ts
|
|
717
|
-
import * as
|
|
718
|
-
import * as
|
|
851
|
+
import * as fs8 from "fs";
|
|
852
|
+
import * as path8 from "path";
|
|
719
853
|
function validateSecretKey(key) {
|
|
720
854
|
return key.length > 0 && /^[A-Z0-9_]+$/.test(key);
|
|
721
855
|
}
|
|
@@ -724,12 +858,12 @@ function formatSecretList(secrets) {
|
|
|
724
858
|
return secrets.map((s) => ` ${s.key} (created ${s.created_at})`).join("\n");
|
|
725
859
|
}
|
|
726
860
|
function loadConfig() {
|
|
727
|
-
const configPath =
|
|
728
|
-
if (!
|
|
861
|
+
const configPath = path8.resolve("lumio.config.json");
|
|
862
|
+
if (!fs8.existsSync(configPath)) {
|
|
729
863
|
console.error("lumio.config.json not found. Run `lumio init` first.");
|
|
730
864
|
process.exit(1);
|
|
731
865
|
}
|
|
732
|
-
return parseConfigFile(
|
|
866
|
+
return parseConfigFile(fs8.readFileSync(configPath, "utf-8"));
|
|
733
867
|
}
|
|
734
868
|
function requireClient() {
|
|
735
869
|
const store = new CredentialStore();
|
|
@@ -771,8 +905,27 @@ function registerEnv(program2) {
|
|
|
771
905
|
}
|
|
772
906
|
|
|
773
907
|
// src/commands/run.ts
|
|
774
|
-
import * as
|
|
775
|
-
import * as
|
|
908
|
+
import * as fs9 from "fs";
|
|
909
|
+
import * as path9 from "path";
|
|
910
|
+
var VALID_FUNCTION_TYPES = [
|
|
911
|
+
"query",
|
|
912
|
+
"mutation",
|
|
913
|
+
"action",
|
|
914
|
+
"command",
|
|
915
|
+
"keyword",
|
|
916
|
+
"pattern",
|
|
917
|
+
"event",
|
|
918
|
+
"timer",
|
|
919
|
+
"moderate"
|
|
920
|
+
];
|
|
921
|
+
var BOT_HANDLER_TYPES = [
|
|
922
|
+
"command",
|
|
923
|
+
"keyword",
|
|
924
|
+
"pattern",
|
|
925
|
+
"event",
|
|
926
|
+
"timer",
|
|
927
|
+
"moderate"
|
|
928
|
+
];
|
|
776
929
|
function parseFunctionRef(ref) {
|
|
777
930
|
const colonIndex = ref.indexOf(":");
|
|
778
931
|
if (colonIndex === -1) {
|
|
@@ -780,20 +933,22 @@ function parseFunctionRef(ref) {
|
|
|
780
933
|
}
|
|
781
934
|
const type = ref.slice(0, colonIndex);
|
|
782
935
|
const name = ref.slice(colonIndex + 1);
|
|
783
|
-
if (!
|
|
784
|
-
throw new Error(
|
|
936
|
+
if (!VALID_FUNCTION_TYPES.includes(type)) {
|
|
937
|
+
throw new Error(
|
|
938
|
+
`Function type must be one of: ${VALID_FUNCTION_TYPES.join(", ")}. Got: "${type}"`
|
|
939
|
+
);
|
|
785
940
|
}
|
|
786
941
|
return { type, name };
|
|
787
942
|
}
|
|
788
943
|
function registerRun(program2) {
|
|
789
|
-
program2.command("run <type:name> [args]").description("Execute a server function locally (e.g. lumio run query:getPlayers)").action(async (typeAndName, argsJson) => {
|
|
944
|
+
program2.command("run <type:name> [args]").description("Execute a server function locally (e.g. lumio run query:getPlayers)").option("--user <name>", "Mock user name for bot handlers", "testuser").option("--role <role>", "Mock user role for bot handlers", "everyone").option("--message <text>", "Mock chat message for bot handlers").option("--platform <platform>", "Mock platform for bot handlers", "twitch").action(async (typeAndName, argsJson, options) => {
|
|
790
945
|
const fnRef = parseFunctionRef(typeAndName);
|
|
791
|
-
const configPath =
|
|
792
|
-
if (!
|
|
946
|
+
const configPath = path9.resolve("lumio.config.json");
|
|
947
|
+
if (!fs9.existsSync(configPath)) {
|
|
793
948
|
console.error("lumio.config.json not found. Run `lumio init` first.");
|
|
794
949
|
process.exit(1);
|
|
795
950
|
}
|
|
796
|
-
const config = parseConfigFile(
|
|
951
|
+
const config = parseConfigFile(fs9.readFileSync(configPath, "utf-8"));
|
|
797
952
|
const store = new CredentialStore();
|
|
798
953
|
const token = store.getToken();
|
|
799
954
|
if (!token) {
|
|
@@ -802,10 +957,26 @@ function registerRun(program2) {
|
|
|
802
957
|
}
|
|
803
958
|
const client = new LumioApiClient(store.getApiUrl(), token);
|
|
804
959
|
const args = argsJson ? JSON.parse(argsJson) : {};
|
|
960
|
+
const body = { type: fnRef.type, args };
|
|
961
|
+
if (BOT_HANDLER_TYPES.includes(fnRef.type)) {
|
|
962
|
+
body.user = {
|
|
963
|
+
id: "test-user-001",
|
|
964
|
+
name: options.user,
|
|
965
|
+
displayName: options.user,
|
|
966
|
+
platform: options.platform,
|
|
967
|
+
role: options.role,
|
|
968
|
+
isMod: options.role === "moderator",
|
|
969
|
+
isVip: options.role === "vip",
|
|
970
|
+
isSub: ["subscriber", "vip", "moderator", "broadcaster"].includes(options.role)
|
|
971
|
+
};
|
|
972
|
+
if (options.message) {
|
|
973
|
+
body.message = { text: options.message, platformMessageId: "test-msg-001" };
|
|
974
|
+
}
|
|
975
|
+
}
|
|
805
976
|
try {
|
|
806
977
|
const result = await client.post(
|
|
807
978
|
`/v1/extension-installs/${config.extensionId}/execute/${fnRef.name}`,
|
|
808
|
-
|
|
979
|
+
body
|
|
809
980
|
);
|
|
810
981
|
console.log(JSON.stringify(result, null, 2));
|
|
811
982
|
} catch (err) {
|
|
@@ -816,16 +987,16 @@ function registerRun(program2) {
|
|
|
816
987
|
}
|
|
817
988
|
|
|
818
989
|
// src/commands/dashboard.ts
|
|
819
|
-
import * as
|
|
820
|
-
import * as
|
|
990
|
+
import * as fs10 from "fs";
|
|
991
|
+
import * as path10 from "path";
|
|
821
992
|
function registerDashboard(program2) {
|
|
822
993
|
program2.command("dashboard").description("Open the extension dashboard in browser").action(async () => {
|
|
823
|
-
const configPath =
|
|
824
|
-
if (!
|
|
994
|
+
const configPath = path10.resolve("lumio.config.json");
|
|
995
|
+
if (!fs10.existsSync(configPath)) {
|
|
825
996
|
console.error("lumio.config.json not found. Run `lumio init` first.");
|
|
826
997
|
process.exit(1);
|
|
827
998
|
}
|
|
828
|
-
const config = parseConfigFile(
|
|
999
|
+
const config = parseConfigFile(fs10.readFileSync(configPath, "utf-8"));
|
|
829
1000
|
const baseUrl = process.env.LUMIO_WEB_URL ?? "https://lumio.vision";
|
|
830
1001
|
const url = `${baseUrl}/dashboard/extensions/${config.extensionId}`;
|
|
831
1002
|
console.log(`Opening ${url}...`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zaflun/lumio-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Lumio Extension CLI — Build, test, and deploy Lumio extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"inquirer": "^12.6.0",
|
|
23
23
|
"open": "^10.1.2",
|
|
24
24
|
"ora": "^8.2.0",
|
|
25
|
-
"@zaflun/lumio-extension-types": "0.
|
|
25
|
+
"@zaflun/lumio-extension-types": "0.3.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/archiver": "^6.0.3",
|