lua-cli 3.16.2 → 3.17.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/dist/api-exports.d.ts +89 -0
- package/dist/api-exports.js +51 -2
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +2026 -238
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +20 -0
- package/package.json +3 -3
- package/template/lua.skill.yaml +7 -0
- package/template/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -132,13 +132,14 @@ var init_semver = __esm({
|
|
|
132
132
|
// src/config/constants.ts
|
|
133
133
|
import { join } from "path";
|
|
134
134
|
import { homedir } from "os";
|
|
135
|
-
var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, BASE_URLS, CREDENTIALS_FILE, PREFERRED_AGENT_TYPES, SANDBOX_STORAGE_FILE, POSTHOG_API_KEY, POSTHOG_HOST;
|
|
135
|
+
var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, CLI_CACHE_FILE, BASE_URLS, CREDENTIALS_FILE, PREFERRED_AGENT_TYPES, SANDBOX_STORAGE_FILE, AUTH_STORAGE_FILE, POSTHOG_API_KEY, POSTHOG_HOST;
|
|
136
136
|
var init_constants = __esm({
|
|
137
137
|
"src/config/constants.ts"() {
|
|
138
138
|
"use strict";
|
|
139
139
|
CLI_CONFIG_DIR = join(homedir(), ".lua-cli");
|
|
140
140
|
VERSION_CHECK_FILE = join(CLI_CONFIG_DIR, "version-check.json");
|
|
141
141
|
TELEMETRY_FILE = join(CLI_CONFIG_DIR, "telemetry.json");
|
|
142
|
+
CLI_CACHE_FILE = join(CLI_CONFIG_DIR, "cache.json");
|
|
142
143
|
BASE_URLS = {
|
|
143
144
|
API: process.env.LUA_API_URL || "https://api.heylua.ai",
|
|
144
145
|
AUTH: process.env.LUA_AUTH_URL || "https://auth.heylua.ai",
|
|
@@ -153,6 +154,7 @@ var init_constants = __esm({
|
|
|
153
154
|
"base"
|
|
154
155
|
];
|
|
155
156
|
SANDBOX_STORAGE_FILE = join(CLI_CONFIG_DIR, "sandbox.json");
|
|
157
|
+
AUTH_STORAGE_FILE = join(CLI_CONFIG_DIR, "auth.json");
|
|
156
158
|
POSTHOG_API_KEY = "phc_W7Qsquwlflshmdkm2hWSqRpXuxGbVFo7LEX8H9HrSjC";
|
|
157
159
|
POSTHOG_HOST = "https://us.i.posthog.com";
|
|
158
160
|
}
|
|
@@ -1198,7 +1200,14 @@ Feel free to add, remove, or rename sections. Your persona can be a single parag
|
|
|
1198
1200
|
// `Data.get('call:<sessionId>')` to drive post-call analytics, follow-ups,
|
|
1199
1201
|
// or QA workflows. Defaults to false — most calls don't need to keep
|
|
1200
1202
|
// a transcript copy.
|
|
1201
|
-
persistTranscript: z.boolean().optional()
|
|
1203
|
+
persistTranscript: z.boolean().optional(),
|
|
1204
|
+
// Spoken acknowledgement played when a tool call fails. When a tool
|
|
1205
|
+
// throws, times out, or returns an unsupported result, the adapter calls
|
|
1206
|
+
// `session.say(text)` once per failed call before surfacing the error
|
|
1207
|
+
// to the LLM as a ToolError — fills the 2–3s gap before the LLM's own
|
|
1208
|
+
// recovery response. Persona-specific (keep it short and on-brand);
|
|
1209
|
+
// absent → no spoken fallback (the LLM's recovery is the only signal).
|
|
1210
|
+
onToolFailureSay: z.string().min(1).max(200).optional()
|
|
1202
1211
|
});
|
|
1203
1212
|
LuaVoiceConfigSchema = LuaVoiceConfigInnerSchema.superRefine((cfg, ctx) => {
|
|
1204
1213
|
const isRealtime = cfg.llm.kind === "realtime";
|
|
@@ -1526,7 +1535,7 @@ var init_artifact_loader = __esm({
|
|
|
1526
1535
|
});
|
|
1527
1536
|
|
|
1528
1537
|
// src/api/backup.api.service.ts
|
|
1529
|
-
var BackupApi;
|
|
1538
|
+
var BackupApi, backup_api_service_default;
|
|
1530
1539
|
var init_backup_api_service = __esm({
|
|
1531
1540
|
"src/api/backup.api.service.ts"() {
|
|
1532
1541
|
"use strict";
|
|
@@ -1645,7 +1654,20 @@ var init_backup_api_service = __esm({
|
|
|
1645
1654
|
Authorization: `Bearer ${this.apiKey}`
|
|
1646
1655
|
});
|
|
1647
1656
|
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Get the list of backup versions for this agent.
|
|
1659
|
+
*
|
|
1660
|
+
* @param all - If true, lifts the server-side 50-version cap
|
|
1661
|
+
* @returns Promise resolving to the list of version summaries (newest first)
|
|
1662
|
+
*/
|
|
1663
|
+
async getBackupVersions(all = false) {
|
|
1664
|
+
const qs = all ? "?all=true" : "";
|
|
1665
|
+
return this.httpGet(`/developer/agents/${this.agentId}/backup/versions${qs}`, {
|
|
1666
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1648
1669
|
};
|
|
1670
|
+
backup_api_service_default = BackupApi;
|
|
1649
1671
|
}
|
|
1650
1672
|
});
|
|
1651
1673
|
|
|
@@ -1662,7 +1684,7 @@ async function ensureBundlesUploaded(apiKey, agentId, bundles) {
|
|
|
1662
1684
|
uploaded: 0
|
|
1663
1685
|
};
|
|
1664
1686
|
}
|
|
1665
|
-
const api = new
|
|
1687
|
+
const api = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
|
|
1666
1688
|
const hashes = Array.from(bundles.keys());
|
|
1667
1689
|
const urlResponse = await api.getBlobUploadUrls(hashes);
|
|
1668
1690
|
if (!urlResponse.success) {
|
|
@@ -5053,11 +5075,11 @@ var init_mcp_server_plugin = __esm({
|
|
|
5053
5075
|
// ../shared-source-sync/dist/index.mjs
|
|
5054
5076
|
import { createHash } from "crypto";
|
|
5055
5077
|
import { extname } from "path";
|
|
5056
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
5078
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
5057
5079
|
import { join as join4, sep } from "path";
|
|
5058
5080
|
import { gunzipSync, gzipSync } from "zlib";
|
|
5059
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
5060
|
-
import { dirname as
|
|
5081
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync22, statSync as statSync22, writeFileSync as writeFileSync6 } from "fs";
|
|
5082
|
+
import { dirname as dirname4, join as join22, resolve, sep as sep2 } from "path";
|
|
5061
5083
|
import { mkdirSync as mkdirSync22, readdirSync as readdirSync22, readFileSync as readFileSync32, statSync as statSync3, writeFileSync as writeFileSync22 } from "fs";
|
|
5062
5084
|
import { dirname as dirname22, join as join32, sep as sep3 } from "path";
|
|
5063
5085
|
import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "zlib";
|
|
@@ -5117,7 +5139,7 @@ function walkWorkspace(rootDir, opts = {}) {
|
|
|
5117
5139
|
if (stats.size > maxBytes) continue;
|
|
5118
5140
|
let content;
|
|
5119
5141
|
try {
|
|
5120
|
-
content =
|
|
5142
|
+
content = readFileSync7(abs);
|
|
5121
5143
|
} catch {
|
|
5122
5144
|
continue;
|
|
5123
5145
|
}
|
|
@@ -5234,10 +5256,10 @@ function restoreFromBlobs(manifest, blobs, targetDir, opts = {}) {
|
|
|
5234
5256
|
continue;
|
|
5235
5257
|
}
|
|
5236
5258
|
}
|
|
5237
|
-
|
|
5259
|
+
mkdirSync6(dirname4(targetPath), {
|
|
5238
5260
|
recursive: true
|
|
5239
5261
|
});
|
|
5240
|
-
|
|
5262
|
+
writeFileSync6(targetPath, content);
|
|
5241
5263
|
filesWritten++;
|
|
5242
5264
|
}
|
|
5243
5265
|
return {
|
|
@@ -5432,12 +5454,12 @@ var init_dist2 = __esm({
|
|
|
5432
5454
|
this.options = options;
|
|
5433
5455
|
this.fetchFn = options.fetch ?? fetch;
|
|
5434
5456
|
}
|
|
5435
|
-
url(
|
|
5457
|
+
url(path19) {
|
|
5436
5458
|
const base = this.options.baseUrl.replace(/\/$/, "");
|
|
5437
|
-
return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${
|
|
5459
|
+
return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path19}`;
|
|
5438
5460
|
}
|
|
5439
|
-
async json(method,
|
|
5440
|
-
const endpoint = this.url(
|
|
5461
|
+
async json(method, path19, body) {
|
|
5462
|
+
const endpoint = this.url(path19);
|
|
5441
5463
|
const res = await this.fetchFn(endpoint, {
|
|
5442
5464
|
method,
|
|
5443
5465
|
headers: {
|
|
@@ -5456,7 +5478,7 @@ var init_dist2 = __esm({
|
|
|
5456
5478
|
} catch {
|
|
5457
5479
|
text2 = void 0;
|
|
5458
5480
|
}
|
|
5459
|
-
throw new BackupHttpError(`Backup request failed: ${method} ${
|
|
5481
|
+
throw new BackupHttpError(`Backup request failed: ${method} ${path19} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
|
|
5460
5482
|
}
|
|
5461
5483
|
const text = await res.text();
|
|
5462
5484
|
if (!text) return void 0;
|
|
@@ -5523,16 +5545,16 @@ var init_dist2 = __esm({
|
|
|
5523
5545
|
});
|
|
5524
5546
|
|
|
5525
5547
|
// src/compiler/utils/common.ts
|
|
5526
|
-
import
|
|
5548
|
+
import path4 from "path";
|
|
5527
5549
|
function hashContent(content) {
|
|
5528
5550
|
return hashContentTruncated(content);
|
|
5529
5551
|
}
|
|
5530
5552
|
function isInside(child, parent) {
|
|
5531
|
-
const resolvedChild =
|
|
5532
|
-
const resolvedParent =
|
|
5553
|
+
const resolvedChild = path4.resolve(child);
|
|
5554
|
+
const resolvedParent = path4.resolve(parent);
|
|
5533
5555
|
if (resolvedChild === resolvedParent) return true;
|
|
5534
|
-
const rel =
|
|
5535
|
-
return !!rel && !rel.startsWith("..") && !
|
|
5556
|
+
const rel = path4.relative(resolvedParent, resolvedChild);
|
|
5557
|
+
return !!rel && !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
5536
5558
|
}
|
|
5537
5559
|
function classifyProjectFile(name) {
|
|
5538
5560
|
if (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js")) {
|
|
@@ -5561,20 +5583,20 @@ var init_common = __esm({
|
|
|
5561
5583
|
});
|
|
5562
5584
|
|
|
5563
5585
|
// src/compiler/utils/path-resolver.ts
|
|
5564
|
-
import * as
|
|
5565
|
-
import * as
|
|
5586
|
+
import * as fs4 from "fs";
|
|
5587
|
+
import * as path5 from "path";
|
|
5566
5588
|
function getTsConfigPathMappings(rootDir) {
|
|
5567
|
-
const resolvedRoot =
|
|
5589
|
+
const resolvedRoot = path5.resolve(rootDir);
|
|
5568
5590
|
if (mappingsCache.has(resolvedRoot)) {
|
|
5569
5591
|
return mappingsCache.get(resolvedRoot) ?? null;
|
|
5570
5592
|
}
|
|
5571
|
-
const tsconfigPath =
|
|
5572
|
-
if (!
|
|
5593
|
+
const tsconfigPath = path5.join(resolvedRoot, "tsconfig.json");
|
|
5594
|
+
if (!fs4.existsSync(tsconfigPath)) {
|
|
5573
5595
|
mappingsCache.set(resolvedRoot, null);
|
|
5574
5596
|
return null;
|
|
5575
5597
|
}
|
|
5576
5598
|
try {
|
|
5577
|
-
const tsconfigContent =
|
|
5599
|
+
const tsconfigContent = fs4.readFileSync(tsconfigPath, "utf-8");
|
|
5578
5600
|
const tsconfig = JSON.parse(tsconfigContent);
|
|
5579
5601
|
const compilerOptions = tsconfig.compilerOptions || {};
|
|
5580
5602
|
if (compilerOptions.paths) {
|
|
@@ -5601,7 +5623,7 @@ function resolvePathAlias(specifier, rootDir) {
|
|
|
5601
5623
|
if (match) {
|
|
5602
5624
|
const captured = match[1] || "";
|
|
5603
5625
|
const targetPath = targets[0].replace("*", captured);
|
|
5604
|
-
return
|
|
5626
|
+
return path5.join(mappings.rootDir, mappings.baseUrl, targetPath);
|
|
5605
5627
|
}
|
|
5606
5628
|
}
|
|
5607
5629
|
return null;
|
|
@@ -5613,20 +5635,20 @@ function resolveModuleSpecifier(specifier, fromFile, rootDir) {
|
|
|
5613
5635
|
return tryExtensions(aliasResolved);
|
|
5614
5636
|
}
|
|
5615
5637
|
if (normalized.startsWith("./") || normalized.startsWith("../")) {
|
|
5616
|
-
const basePath =
|
|
5638
|
+
const basePath = path5.resolve(path5.dirname(fromFile), normalized);
|
|
5617
5639
|
return tryExtensions(basePath);
|
|
5618
5640
|
}
|
|
5619
5641
|
return void 0;
|
|
5620
5642
|
}
|
|
5621
5643
|
function tryExtensions(basePath) {
|
|
5622
5644
|
for (const ext of MODULE_EXTENSIONS) {
|
|
5623
|
-
const full = ext.startsWith("/") ?
|
|
5624
|
-
if (
|
|
5645
|
+
const full = ext.startsWith("/") ? path5.join(basePath, ext) : basePath + ext;
|
|
5646
|
+
if (fs4.existsSync(full)) return full;
|
|
5625
5647
|
}
|
|
5626
5648
|
return void 0;
|
|
5627
5649
|
}
|
|
5628
5650
|
function registerProjectRootDir(project, rootDir) {
|
|
5629
|
-
projectRootDirs.set(project,
|
|
5651
|
+
projectRootDirs.set(project, path5.resolve(rootDir));
|
|
5630
5652
|
}
|
|
5631
5653
|
function getProjectRootDir(project) {
|
|
5632
5654
|
return projectRootDirs.get(project);
|
|
@@ -6226,7 +6248,7 @@ var init_reference_resolver = __esm({
|
|
|
6226
6248
|
});
|
|
6227
6249
|
|
|
6228
6250
|
// src/compiler/plugins/skill.plugin.ts
|
|
6229
|
-
import
|
|
6251
|
+
import fs5 from "fs/promises";
|
|
6230
6252
|
import { Node as Node11 } from "ts-morph";
|
|
6231
6253
|
function findEnclosingVariableDeclaration(config) {
|
|
6232
6254
|
let current = config.getParent();
|
|
@@ -6559,7 +6581,7 @@ var init_skill_plugin = __esm({
|
|
|
6559
6581
|
description: metadata.description,
|
|
6560
6582
|
metadata: this.sanitizeForPersistence(metadata.metadata)
|
|
6561
6583
|
}, null, 2);
|
|
6562
|
-
const originalSource = await
|
|
6584
|
+
const originalSource = await fs5.readFile(metadata.sourcePath, "utf-8");
|
|
6563
6585
|
return {
|
|
6564
6586
|
code,
|
|
6565
6587
|
sourceMap: "",
|
|
@@ -6663,6 +6685,31 @@ var init_skill_plugin = __esm({
|
|
|
6663
6685
|
|
|
6664
6686
|
// src/compiler/plugins/agent.plugin.ts
|
|
6665
6687
|
import { Node as Node12 } from "ts-morph";
|
|
6688
|
+
function shapeModelSettings(raw) {
|
|
6689
|
+
const KNOWN_KEYS = [
|
|
6690
|
+
"temperature",
|
|
6691
|
+
"topP",
|
|
6692
|
+
"topK",
|
|
6693
|
+
"maxOutputTokens",
|
|
6694
|
+
"presencePenalty",
|
|
6695
|
+
"frequencyPenalty",
|
|
6696
|
+
"stopSequences",
|
|
6697
|
+
"seed"
|
|
6698
|
+
];
|
|
6699
|
+
const result = {};
|
|
6700
|
+
for (const key of KNOWN_KEYS) {
|
|
6701
|
+
const value = raw[key];
|
|
6702
|
+
if (value === void 0) continue;
|
|
6703
|
+
if (key === "stopSequences") {
|
|
6704
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
6705
|
+
result[key] = value;
|
|
6706
|
+
}
|
|
6707
|
+
} else if (typeof value === "number" && Number.isFinite(value)) {
|
|
6708
|
+
result[key] = value;
|
|
6709
|
+
}
|
|
6710
|
+
}
|
|
6711
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
6712
|
+
}
|
|
6666
6713
|
var AgentPlugin, agentPlugin;
|
|
6667
6714
|
var init_agent_plugin = __esm({
|
|
6668
6715
|
"src/compiler/plugins/agent.plugin.ts"() {
|
|
@@ -6695,7 +6742,8 @@ var init_agent_plugin = __esm({
|
|
|
6695
6742
|
"name",
|
|
6696
6743
|
"description",
|
|
6697
6744
|
"persona",
|
|
6698
|
-
"model"
|
|
6745
|
+
"model",
|
|
6746
|
+
"modelSettings"
|
|
6699
6747
|
]
|
|
6700
6748
|
};
|
|
6701
6749
|
supportsClassDefinition = true;
|
|
@@ -6752,6 +6800,9 @@ var init_agent_plugin = __esm({
|
|
|
6752
6800
|
const governanceHit = findClassMember(classDecl, "governance", "property");
|
|
6753
6801
|
const governanceObj = governanceHit && Node12.isPropertyDeclaration(governanceHit.node) ? evaluateNodeAsObject(governanceHit.node.getInitializer()) : void 0;
|
|
6754
6802
|
const governance = governanceObj && typeof governanceObj.mode === "string" ? governanceObj : void 0;
|
|
6803
|
+
const modelSettingsHit = findClassMember(classDecl, "modelSettings", "property");
|
|
6804
|
+
const modelSettingsObj = modelSettingsHit && Node12.isPropertyDeclaration(modelSettingsHit.node) ? evaluateNodeAsObject(modelSettingsHit.node.getInitializer()) : void 0;
|
|
6805
|
+
const modelSettings = modelSettingsObj ? shapeModelSettings(modelSettingsObj) : void 0;
|
|
6755
6806
|
return {
|
|
6756
6807
|
kind: this.kind,
|
|
6757
6808
|
name,
|
|
@@ -6766,6 +6817,7 @@ var init_agent_plugin = __esm({
|
|
|
6766
6817
|
persona,
|
|
6767
6818
|
model,
|
|
6768
6819
|
hasModelResolver,
|
|
6820
|
+
modelSettings,
|
|
6769
6821
|
batching,
|
|
6770
6822
|
governance
|
|
6771
6823
|
}
|
|
@@ -6795,6 +6847,7 @@ var init_agent_plugin = __esm({
|
|
|
6795
6847
|
"text"
|
|
6796
6848
|
]) ?? "";
|
|
6797
6849
|
const { model, hasModelResolver } = this.extractModelInfo(config);
|
|
6850
|
+
const modelSettings = this.extractModelSettings(config);
|
|
6798
6851
|
const batching = this.extractBatchingInfo(config);
|
|
6799
6852
|
const governance = this.extractGovernanceInfo(config);
|
|
6800
6853
|
const { voiceRefNames, voiceRefSourcePaths } = this.extractVoiceRefs(config);
|
|
@@ -6811,6 +6864,7 @@ var init_agent_plugin = __esm({
|
|
|
6811
6864
|
persona,
|
|
6812
6865
|
model,
|
|
6813
6866
|
hasModelResolver,
|
|
6867
|
+
modelSettings,
|
|
6814
6868
|
batching,
|
|
6815
6869
|
governance,
|
|
6816
6870
|
voiceRefNames,
|
|
@@ -6881,6 +6935,17 @@ var init_agent_plugin = __esm({
|
|
|
6881
6935
|
return governance;
|
|
6882
6936
|
}
|
|
6883
6937
|
/**
|
|
6938
|
+
* Extract `modelSettings` from the config-literal path. Delegates to the
|
|
6939
|
+
* shared `shapeModelSettings` helper so the class-definition extraction
|
|
6940
|
+
* path applies the exact same key whitelist + type narrowing — without
|
|
6941
|
+
* the shared helper, the two paths would drift (see Bugbot #ref1).
|
|
6942
|
+
*/
|
|
6943
|
+
extractModelSettings(config) {
|
|
6944
|
+
const raw = extractObjectProperty(config, "modelSettings");
|
|
6945
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
6946
|
+
return shapeModelSettings(raw);
|
|
6947
|
+
}
|
|
6948
|
+
/**
|
|
6884
6949
|
* Extract model info from agent config.
|
|
6885
6950
|
* Returns the static model string (if any) and whether a resolver function exists.
|
|
6886
6951
|
*/
|
|
@@ -6962,6 +7027,7 @@ var init_agent_plugin = __esm({
|
|
|
6962
7027
|
persona: agentMeta.persona,
|
|
6963
7028
|
model: agentMeta.model,
|
|
6964
7029
|
hasModelResolver: agentMeta.hasModelResolver || void 0,
|
|
7030
|
+
modelSettings: agentMeta.modelSettings,
|
|
6965
7031
|
batching: agentMeta.batching,
|
|
6966
7032
|
governance: agentMeta.governance,
|
|
6967
7033
|
voiceRefs
|
|
@@ -7023,6 +7089,7 @@ var init_agent_plugin = __esm({
|
|
|
7023
7089
|
return rest;
|
|
7024
7090
|
}
|
|
7025
7091
|
};
|
|
7092
|
+
__name(shapeModelSettings, "shapeModelSettings");
|
|
7026
7093
|
agentPlugin = new AgentPlugin();
|
|
7027
7094
|
}
|
|
7028
7095
|
});
|
|
@@ -7313,10 +7380,10 @@ var init_device_trigger_plugin = __esm({
|
|
|
7313
7380
|
});
|
|
7314
7381
|
|
|
7315
7382
|
// src/compiler/utils/primitive-rewrite.ts
|
|
7316
|
-
import { readFileSync as
|
|
7383
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
7317
7384
|
import { Node as Node14, Project, ts as ts3 } from "ts-morph";
|
|
7318
7385
|
function rewritePrimitiveSource(metadata, opts) {
|
|
7319
|
-
const sourceCode =
|
|
7386
|
+
const sourceCode = readFileSync9(metadata.sourcePath, "utf-8");
|
|
7320
7387
|
const localProject = new Project({
|
|
7321
7388
|
useInMemoryFileSystem: true,
|
|
7322
7389
|
compilerOptions: {
|
|
@@ -7655,7 +7722,7 @@ var init_cross_file_specs = __esm({
|
|
|
7655
7722
|
});
|
|
7656
7723
|
|
|
7657
7724
|
// src/compiler/plugins/voice.plugin.ts
|
|
7658
|
-
import
|
|
7725
|
+
import fs6 from "fs/promises";
|
|
7659
7726
|
import { Node as Node15 } from "ts-morph";
|
|
7660
7727
|
function astObjectToPlain(obj) {
|
|
7661
7728
|
const result = {};
|
|
@@ -8086,6 +8153,7 @@ var init_voice_plugin = __esm({
|
|
|
8086
8153
|
const tts = extractModelField(config, "tts");
|
|
8087
8154
|
const krispEnabled = extractBooleanProperty(config, "krispEnabled");
|
|
8088
8155
|
const persistTranscript = extractBooleanProperty(config, "persistTranscript");
|
|
8156
|
+
const onToolFailureSay = extractStringProperty(config, "onToolFailureSay");
|
|
8089
8157
|
const volume = extractNumberProperty(config, "volume");
|
|
8090
8158
|
const interruption = extractObjectProperty(config, "interruption");
|
|
8091
8159
|
const pronunciationsRaw = extractObjectProperty(config, "pronunciations");
|
|
@@ -8122,6 +8190,7 @@ var init_voice_plugin = __esm({
|
|
|
8122
8190
|
hasTools: hasArrayProperty(config, "tools"),
|
|
8123
8191
|
krispEnabled,
|
|
8124
8192
|
persistTranscript,
|
|
8193
|
+
onToolFailureSay,
|
|
8125
8194
|
volume,
|
|
8126
8195
|
pronunciations,
|
|
8127
8196
|
backgroundAudio,
|
|
@@ -8181,6 +8250,7 @@ var init_voice_plugin = __esm({
|
|
|
8181
8250
|
if (fields.sttLanguage !== void 0) candidate.sttLanguage = fields.sttLanguage;
|
|
8182
8251
|
if (fields.krispEnabled !== void 0) candidate.krispEnabled = fields.krispEnabled;
|
|
8183
8252
|
if (fields.persistTranscript !== void 0) candidate.persistTranscript = fields.persistTranscript;
|
|
8253
|
+
if (fields.onToolFailureSay !== void 0) candidate.onToolFailureSay = fields.onToolFailureSay;
|
|
8184
8254
|
if (fields.volume !== void 0) candidate.volume = fields.volume;
|
|
8185
8255
|
if (fields.pronunciations !== void 0) candidate.pronunciations = fields.pronunciations;
|
|
8186
8256
|
if (fields.backgroundAudio !== void 0) candidate.backgroundAudio = fields.backgroundAudio;
|
|
@@ -8196,8 +8266,8 @@ var init_voice_plugin = __esm({
|
|
|
8196
8266
|
}, "isMissingTopLevelRequired");
|
|
8197
8267
|
for (const issue of parsed.error.issues) {
|
|
8198
8268
|
if (isMissingTopLevelRequired(issue)) continue;
|
|
8199
|
-
const
|
|
8200
|
-
errors.push(validationError(`Voice config invalid at \`${
|
|
8269
|
+
const path19 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
|
|
8270
|
+
errors.push(validationError(`Voice config invalid at \`${path19}\`: ${issue.message}`, {
|
|
8201
8271
|
line
|
|
8202
8272
|
}));
|
|
8203
8273
|
}
|
|
@@ -8278,7 +8348,7 @@ var init_voice_plugin = __esm({
|
|
|
8278
8348
|
description: metadata.description,
|
|
8279
8349
|
metadata: sanitized
|
|
8280
8350
|
}, null, 2);
|
|
8281
|
-
const originalSource = await
|
|
8351
|
+
const originalSource = await fs6.readFile(metadata.sourcePath, "utf-8");
|
|
8282
8352
|
return {
|
|
8283
8353
|
code,
|
|
8284
8354
|
sourceMap: "",
|
|
@@ -8335,6 +8405,7 @@ var init_voice_plugin = __esm({
|
|
|
8335
8405
|
volume: fields.volume,
|
|
8336
8406
|
pronunciations: fields.pronunciations,
|
|
8337
8407
|
persistTranscript: fields.persistTranscript,
|
|
8408
|
+
onToolFailureSay: fields.onToolFailureSay,
|
|
8338
8409
|
interruption: fields.interruption
|
|
8339
8410
|
};
|
|
8340
8411
|
return entry;
|
|
@@ -8431,8 +8502,8 @@ var init_registry = __esm({
|
|
|
8431
8502
|
});
|
|
8432
8503
|
|
|
8433
8504
|
// src/compiler/bundler.ts
|
|
8434
|
-
import
|
|
8435
|
-
import
|
|
8505
|
+
import fs7 from "fs/promises";
|
|
8506
|
+
import path6 from "path";
|
|
8436
8507
|
import crypto4 from "crypto";
|
|
8437
8508
|
import { build } from "esbuild";
|
|
8438
8509
|
var Bundler;
|
|
@@ -8501,9 +8572,9 @@ var init_bundler = __esm({
|
|
|
8501
8572
|
* Bundle a primitive's entry point using esbuild.
|
|
8502
8573
|
*/
|
|
8503
8574
|
async bundle(metadata, entryPointCode) {
|
|
8504
|
-
const tempDir =
|
|
8505
|
-
const outfile =
|
|
8506
|
-
const sourceDir =
|
|
8575
|
+
const tempDir = path6.join(this.options.outDir, ".temp");
|
|
8576
|
+
const outfile = path6.join(tempDir, `${metadata.kind}-${metadata.name}.bundle.js`);
|
|
8577
|
+
const sourceDir = path6.dirname(metadata.sourcePath);
|
|
8507
8578
|
if (this.options.debug) {
|
|
8508
8579
|
this.verbose(` Source: ${metadata.sourcePath}`);
|
|
8509
8580
|
this.verbose(` Output: ${outfile}`);
|
|
@@ -8533,15 +8604,15 @@ var init_bundler = __esm({
|
|
|
8533
8604
|
],
|
|
8534
8605
|
logLevel: this.options.debug ? "debug" : "silent"
|
|
8535
8606
|
});
|
|
8536
|
-
const code = await
|
|
8607
|
+
const code = await fs7.readFile(outfile, "utf-8");
|
|
8537
8608
|
let sourceMap = "";
|
|
8538
8609
|
try {
|
|
8539
|
-
sourceMap = await
|
|
8610
|
+
sourceMap = await fs7.readFile(outfile + ".map", "utf-8");
|
|
8540
8611
|
} catch {
|
|
8541
8612
|
}
|
|
8542
|
-
const originalSource = await
|
|
8613
|
+
const originalSource = await fs7.readFile(metadata.sourcePath, "utf-8");
|
|
8543
8614
|
const hash = crypto4.createHash("sha256").update(code).digest("hex").slice(0, 16);
|
|
8544
|
-
const stats = await
|
|
8615
|
+
const stats = await fs7.stat(outfile);
|
|
8545
8616
|
return {
|
|
8546
8617
|
code,
|
|
8547
8618
|
sourceMap,
|
|
@@ -8558,14 +8629,14 @@ var init_bundler = __esm({
|
|
|
8558
8629
|
const bundler = this;
|
|
8559
8630
|
const sourcePath = metadata.sourcePath;
|
|
8560
8631
|
const exportName = metadata.exportName;
|
|
8561
|
-
const sourceDir =
|
|
8632
|
+
const sourceDir = path6.dirname(sourcePath);
|
|
8562
8633
|
return {
|
|
8563
8634
|
name: "lua-virtual-source",
|
|
8564
8635
|
setup(build2) {
|
|
8565
8636
|
build2.onResolve({
|
|
8566
8637
|
filter: /.*/
|
|
8567
8638
|
}, (args2) => {
|
|
8568
|
-
const resolved = args2.path.startsWith(".") ?
|
|
8639
|
+
const resolved = args2.path.startsWith(".") ? path6.resolve(args2.resolveDir, args2.path) : args2.path;
|
|
8569
8640
|
const normalizedResolved = resolved.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
8570
8641
|
const normalizedSource = sourcePath.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
8571
8642
|
if (normalizedResolved === normalizedSource) {
|
|
@@ -8581,8 +8652,8 @@ var init_bundler = __esm({
|
|
|
8581
8652
|
namespace: "lua-virtual-source"
|
|
8582
8653
|
}, async () => {
|
|
8583
8654
|
const sourceFile = bundler.project.getSourceFile(sourcePath);
|
|
8584
|
-
let content = sourceFile?.getText() ?? await
|
|
8585
|
-
content = bundler.stripLuaCliImportsAST(content,
|
|
8655
|
+
let content = sourceFile?.getText() ?? await fs7.readFile(sourcePath, "utf-8");
|
|
8656
|
+
content = bundler.stripLuaCliImportsAST(content, path6.basename(sourcePath));
|
|
8586
8657
|
const virtualContent = metadata.isDefaultExport ? content : `${content}
|
|
8587
8658
|
export { ${exportName} as __lua_target__ };
|
|
8588
8659
|
`;
|
|
@@ -8610,17 +8681,17 @@ export { ${exportName} as __lua_target__ };
|
|
|
8610
8681
|
namespace: "file"
|
|
8611
8682
|
}, async (args2) => {
|
|
8612
8683
|
if (args2.path.includes("node_modules")) return null;
|
|
8613
|
-
const contents = await
|
|
8684
|
+
const contents = await fs7.readFile(args2.path, "utf-8");
|
|
8614
8685
|
if (!contents.includes("lua-cli") && !contents.includes("api-exports")) {
|
|
8615
8686
|
return null;
|
|
8616
8687
|
}
|
|
8617
8688
|
try {
|
|
8618
|
-
const modifiedContents = stripImports(contents,
|
|
8689
|
+
const modifiedContents = stripImports(contents, path6.basename(args2.path));
|
|
8619
8690
|
if (modifiedContents !== contents) {
|
|
8620
8691
|
return {
|
|
8621
8692
|
contents: modifiedContents,
|
|
8622
8693
|
loader: args2.path.endsWith(".ts") || args2.path.endsWith(".tsx") ? "ts" : "js",
|
|
8623
|
-
resolveDir:
|
|
8694
|
+
resolveDir: path6.dirname(args2.path)
|
|
8624
8695
|
};
|
|
8625
8696
|
}
|
|
8626
8697
|
return null;
|
|
@@ -8657,8 +8728,8 @@ export { ${exportName} as __lua_target__ };
|
|
|
8657
8728
|
|
|
8658
8729
|
// src/compiler/agent-traverser.ts
|
|
8659
8730
|
import { Project as Project2, Node as Node16 } from "ts-morph";
|
|
8660
|
-
import
|
|
8661
|
-
import
|
|
8731
|
+
import path7 from "path";
|
|
8732
|
+
import fs8 from "fs";
|
|
8662
8733
|
var PRIMITIVE_TYPES, AgentTraverser;
|
|
8663
8734
|
var init_agent_traverser = __esm({
|
|
8664
8735
|
"src/compiler/agent-traverser.ts"() {
|
|
@@ -8731,7 +8802,7 @@ var init_agent_traverser = __esm({
|
|
|
8731
8802
|
this.rootDir = rootDir;
|
|
8732
8803
|
this.debug = debug;
|
|
8733
8804
|
this.project = new Project2({
|
|
8734
|
-
tsConfigFilePath:
|
|
8805
|
+
tsConfigFilePath: path7.join(rootDir, "tsconfig.json"),
|
|
8735
8806
|
skipAddingFilesFromTsConfig: true
|
|
8736
8807
|
});
|
|
8737
8808
|
registerProjectRootDir(this.project, this.rootDir);
|
|
@@ -8815,15 +8886,15 @@ var init_agent_traverser = __esm({
|
|
|
8815
8886
|
*/
|
|
8816
8887
|
detectAgent() {
|
|
8817
8888
|
const priorityFiles = [
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8889
|
+
path7.join(this.rootDir, "index.ts"),
|
|
8890
|
+
path7.join(this.rootDir, "src", "index.ts"),
|
|
8891
|
+
path7.join(this.rootDir, "agent.ts"),
|
|
8892
|
+
path7.join(this.rootDir, "src", "agent.ts"),
|
|
8893
|
+
path7.join(this.rootDir, "main.ts"),
|
|
8894
|
+
path7.join(this.rootDir, "src", "main.ts")
|
|
8824
8895
|
];
|
|
8825
8896
|
for (const filePath of priorityFiles) {
|
|
8826
|
-
if (
|
|
8897
|
+
if (fs8.existsSync(filePath)) {
|
|
8827
8898
|
try {
|
|
8828
8899
|
const sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
8829
8900
|
const agent = this.detectAgentInFile(sourceFile);
|
|
@@ -8975,7 +9046,7 @@ var init_agent_traverser = __esm({
|
|
|
8975
9046
|
console.warn(formatSarifWarning({
|
|
8976
9047
|
ruleId: "lua/missing-primitive-declaration",
|
|
8977
9048
|
filePath: sourcePath,
|
|
8978
|
-
message: `agent references ${kind} "${refName}" but ${
|
|
9049
|
+
message: `agent references ${kind} "${refName}" but ${path7.basename(sourcePath)} does not define a matching primitive`,
|
|
8979
9050
|
hint: `export a named ${plugin.displayName.toLowerCase()} declaration as "${refName}", or adjust the import so it points to the file that does`
|
|
8980
9051
|
}));
|
|
8981
9052
|
return null;
|
|
@@ -8995,20 +9066,20 @@ var init_agent_traverser = __esm({
|
|
|
8995
9066
|
});
|
|
8996
9067
|
|
|
8997
9068
|
// src/compiler/utils/workspace.ts
|
|
8998
|
-
import
|
|
8999
|
-
import
|
|
9069
|
+
import fs9 from "fs";
|
|
9070
|
+
import path8 from "path";
|
|
9000
9071
|
function findWorkspaceRoot(rootDir) {
|
|
9001
|
-
const resolvedInput =
|
|
9072
|
+
const resolvedInput = path8.resolve(rootDir);
|
|
9002
9073
|
const cached = workspaceRootCache.get(resolvedInput);
|
|
9003
9074
|
if (cached !== void 0) return cached;
|
|
9004
9075
|
let current = resolvedInput;
|
|
9005
|
-
const { root } =
|
|
9076
|
+
const { root } = path8.parse(current);
|
|
9006
9077
|
while (true) {
|
|
9007
9078
|
if (isWorkspaceRoot(current)) {
|
|
9008
9079
|
workspaceRootCache.set(resolvedInput, current);
|
|
9009
9080
|
return current;
|
|
9010
9081
|
}
|
|
9011
|
-
const parent =
|
|
9082
|
+
const parent = path8.dirname(current);
|
|
9012
9083
|
if (parent === current || current === root) break;
|
|
9013
9084
|
current = parent;
|
|
9014
9085
|
}
|
|
@@ -9016,22 +9087,22 @@ function findWorkspaceRoot(rootDir) {
|
|
|
9016
9087
|
return resolvedInput;
|
|
9017
9088
|
}
|
|
9018
9089
|
function isWorkspaceRoot(dir) {
|
|
9019
|
-
if (fileExists(
|
|
9020
|
-
if (fileExists(
|
|
9021
|
-
if (packageJsonHasWorkspaces(
|
|
9022
|
-
if (dirExists(
|
|
9090
|
+
if (fileExists(path8.join(dir, "pnpm-workspace.yaml"))) return true;
|
|
9091
|
+
if (fileExists(path8.join(dir, "lerna.json"))) return true;
|
|
9092
|
+
if (packageJsonHasWorkspaces(path8.join(dir, "package.json"))) return true;
|
|
9093
|
+
if (dirExists(path8.join(dir, ".git"))) return true;
|
|
9023
9094
|
return false;
|
|
9024
9095
|
}
|
|
9025
9096
|
function fileExists(p) {
|
|
9026
9097
|
try {
|
|
9027
|
-
return
|
|
9098
|
+
return fs9.statSync(p).isFile();
|
|
9028
9099
|
} catch {
|
|
9029
9100
|
return false;
|
|
9030
9101
|
}
|
|
9031
9102
|
}
|
|
9032
9103
|
function dirExists(p) {
|
|
9033
9104
|
try {
|
|
9034
|
-
return
|
|
9105
|
+
return fs9.statSync(p).isDirectory();
|
|
9035
9106
|
} catch {
|
|
9036
9107
|
return false;
|
|
9037
9108
|
}
|
|
@@ -9039,7 +9110,7 @@ function dirExists(p) {
|
|
|
9039
9110
|
function packageJsonHasWorkspaces(pkgPath) {
|
|
9040
9111
|
if (!fileExists(pkgPath)) return false;
|
|
9041
9112
|
try {
|
|
9042
|
-
const raw =
|
|
9113
|
+
const raw = fs9.readFileSync(pkgPath, "utf-8");
|
|
9043
9114
|
const json = JSON.parse(raw);
|
|
9044
9115
|
if (!json.workspaces) return false;
|
|
9045
9116
|
return Array.isArray(json.workspaces) || typeof json.workspaces === "object";
|
|
@@ -9061,8 +9132,8 @@ var init_workspace = __esm({
|
|
|
9061
9132
|
});
|
|
9062
9133
|
|
|
9063
9134
|
// src/compiler/compiler.ts
|
|
9064
|
-
import
|
|
9065
|
-
import
|
|
9135
|
+
import fs10 from "fs/promises";
|
|
9136
|
+
import path9 from "path";
|
|
9066
9137
|
import { Project as Project3 } from "ts-morph";
|
|
9067
9138
|
async function compile(options) {
|
|
9068
9139
|
const compiler = new Compiler(options);
|
|
@@ -9115,7 +9186,7 @@ var init_compiler = __esm({
|
|
|
9115
9186
|
...options
|
|
9116
9187
|
};
|
|
9117
9188
|
this.project = new Project3({
|
|
9118
|
-
tsConfigFilePath:
|
|
9189
|
+
tsConfigFilePath: path9.join(options.rootDir, "tsconfig.json")
|
|
9119
9190
|
});
|
|
9120
9191
|
registerProjectRootDir(this.project, this.options.rootDir);
|
|
9121
9192
|
this.bundler = new Bundler({
|
|
@@ -9192,10 +9263,10 @@ var init_compiler = __esm({
|
|
|
9192
9263
|
return await this.createResult(false, [], errors, warnings, startTime);
|
|
9193
9264
|
}
|
|
9194
9265
|
this.verbose("\u{1F4E6} Bundling primitives...");
|
|
9195
|
-
await
|
|
9266
|
+
await fs10.mkdir(this.options.outDir, {
|
|
9196
9267
|
recursive: true
|
|
9197
9268
|
});
|
|
9198
|
-
await
|
|
9269
|
+
await fs10.mkdir(path9.join(this.options.outDir, ".temp"), {
|
|
9199
9270
|
recursive: true
|
|
9200
9271
|
});
|
|
9201
9272
|
const CONCURRENCY = 4;
|
|
@@ -9215,7 +9286,7 @@ var init_compiler = __esm({
|
|
|
9215
9286
|
this.verbose("\u{1F4BE} Writing artifacts...");
|
|
9216
9287
|
await this.writeArtifacts(compiledPrimitives);
|
|
9217
9288
|
if (!this.options.debug) {
|
|
9218
|
-
await
|
|
9289
|
+
await fs10.rm(path9.join(this.options.outDir, ".temp"), {
|
|
9219
9290
|
recursive: true,
|
|
9220
9291
|
force: true
|
|
9221
9292
|
});
|
|
@@ -9258,7 +9329,7 @@ var init_compiler = __esm({
|
|
|
9258
9329
|
*/
|
|
9259
9330
|
getWorkspaceRoot() {
|
|
9260
9331
|
if (this._workspaceRoot === void 0) {
|
|
9261
|
-
this._workspaceRoot = findWorkspaceRoot(
|
|
9332
|
+
this._workspaceRoot = findWorkspaceRoot(path9.resolve(this.options.rootDir));
|
|
9262
9333
|
}
|
|
9263
9334
|
return this._workspaceRoot;
|
|
9264
9335
|
}
|
|
@@ -9272,18 +9343,18 @@ var init_compiler = __esm({
|
|
|
9272
9343
|
* - Files outside the detected workspace root (not our code)
|
|
9273
9344
|
*/
|
|
9274
9345
|
collectExternalWorkspaceFiles(traverser) {
|
|
9275
|
-
const rootDir =
|
|
9346
|
+
const rootDir = path9.resolve(this.options.rootDir);
|
|
9276
9347
|
const workspaceRoot = this.getWorkspaceRoot();
|
|
9277
9348
|
if (workspaceRoot === rootDir) return [];
|
|
9278
9349
|
const loaded = traverser.getAllLoadedSourceFilePaths();
|
|
9279
9350
|
const result = [];
|
|
9280
9351
|
const seen = /* @__PURE__ */ new Set();
|
|
9281
9352
|
for (const raw of loaded) {
|
|
9282
|
-
const abs =
|
|
9353
|
+
const abs = path9.resolve(raw);
|
|
9283
9354
|
if (seen.has(abs)) continue;
|
|
9284
9355
|
seen.add(abs);
|
|
9285
9356
|
if (isInside(abs, rootDir)) continue;
|
|
9286
|
-
if (abs.includes(`${
|
|
9357
|
+
if (abs.includes(`${path9.sep}node_modules${path9.sep}`)) continue;
|
|
9287
9358
|
if (!isInside(abs, workspaceRoot)) continue;
|
|
9288
9359
|
result.push(abs);
|
|
9289
9360
|
}
|
|
@@ -9416,9 +9487,9 @@ var init_compiler = __esm({
|
|
|
9416
9487
|
* @param primitives - All compiled primitives to write
|
|
9417
9488
|
*/
|
|
9418
9489
|
async writeArtifacts(primitives) {
|
|
9419
|
-
const artifactsDir =
|
|
9420
|
-
const sourcesDir =
|
|
9421
|
-
await
|
|
9490
|
+
const artifactsDir = path9.join(this.options.outDir, "artifacts");
|
|
9491
|
+
const sourcesDir = path9.join(this.options.outDir, "sources");
|
|
9492
|
+
await fs10.mkdir(sourcesDir, {
|
|
9422
9493
|
recursive: true
|
|
9423
9494
|
});
|
|
9424
9495
|
const writtenSources = /* @__PURE__ */ new Map();
|
|
@@ -9426,7 +9497,7 @@ var init_compiler = __esm({
|
|
|
9426
9497
|
this.verbose(" \u{1F4C2} Collecting project files...");
|
|
9427
9498
|
const projectFiles = await this.storeProjectFiles(sourcesDir, writtenSources);
|
|
9428
9499
|
const manifest = await this.createManifest(primitives, projectFiles);
|
|
9429
|
-
await
|
|
9500
|
+
await fs10.writeFile(path9.join(this.options.outDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
9430
9501
|
this.verbose(` \u{1F4E6} Total files stored: ${writtenSources.size} (deduplicated)`);
|
|
9431
9502
|
this.verbose(` \u{1F4C1} Project files: ${projectFiles.length}`);
|
|
9432
9503
|
if (this.options.verbose || this.options.debug) {
|
|
@@ -9438,25 +9509,25 @@ var init_compiler = __esm({
|
|
|
9438
9509
|
*/
|
|
9439
9510
|
async writePrimitiveArtifacts(primitives, artifactsDir, sourcesDir, writtenSources) {
|
|
9440
9511
|
for (const primitive of primitives) {
|
|
9441
|
-
const primitiveDir =
|
|
9442
|
-
await
|
|
9512
|
+
const primitiveDir = path9.join(artifactsDir, primitive.kind);
|
|
9513
|
+
await fs10.mkdir(primitiveDir, {
|
|
9443
9514
|
recursive: true
|
|
9444
9515
|
});
|
|
9445
9516
|
const baseName = primitive.name;
|
|
9446
|
-
await
|
|
9517
|
+
await fs10.writeFile(path9.join(primitiveDir, `${baseName}.js`), primitive.artifact.code);
|
|
9447
9518
|
if (primitive.artifact.sourceMap) {
|
|
9448
|
-
await
|
|
9519
|
+
await fs10.writeFile(path9.join(primitiveDir, `${baseName}.js.map`), primitive.artifact.sourceMap);
|
|
9449
9520
|
}
|
|
9450
9521
|
const sourceHash = hashContent(primitive.artifact.originalSource);
|
|
9451
9522
|
if (!writtenSources.has(sourceHash)) {
|
|
9452
9523
|
const ext = primitive.sourcePath.endsWith(".tsx") ? ".tsx" : ".ts";
|
|
9453
|
-
await
|
|
9454
|
-
const relativePath =
|
|
9524
|
+
await fs10.writeFile(path9.join(sourcesDir, `${sourceHash}${ext}`), primitive.artifact.originalSource);
|
|
9525
|
+
const relativePath = path9.relative(this.options.rootDir, primitive.sourcePath);
|
|
9455
9526
|
writtenSources.set(sourceHash, relativePath);
|
|
9456
9527
|
}
|
|
9457
9528
|
const sidecarPlugin = pluginRegistry.get(primitive.kind);
|
|
9458
9529
|
const sidecarMetadata = sidecarPlugin?.sanitizeForPersistence ? sidecarPlugin.sanitizeForPersistence(primitive.metadata) : primitive.metadata;
|
|
9459
|
-
await
|
|
9530
|
+
await fs10.writeFile(path9.join(primitiveDir, `${baseName}.json`), JSON.stringify({
|
|
9460
9531
|
kind: primitive.kind,
|
|
9461
9532
|
name: primitive.name,
|
|
9462
9533
|
description: primitive.description,
|
|
@@ -9478,12 +9549,12 @@ var init_compiler = __esm({
|
|
|
9478
9549
|
const allFiles = await this.collectProjectFiles();
|
|
9479
9550
|
const workspaceRoot = this.getWorkspaceRoot();
|
|
9480
9551
|
for (const file of allFiles) {
|
|
9481
|
-
const absPath = file.external ?
|
|
9482
|
-
const content = await
|
|
9552
|
+
const absPath = file.external ? path9.join(workspaceRoot, file.relativePath) : path9.join(this.options.rootDir, file.relativePath);
|
|
9553
|
+
const content = await fs10.readFile(absPath, "utf-8");
|
|
9483
9554
|
const hash = hashContent(content);
|
|
9484
9555
|
if (!writtenSources.has(hash)) {
|
|
9485
|
-
const ext =
|
|
9486
|
-
await
|
|
9556
|
+
const ext = path9.extname(file.relativePath) || ".txt";
|
|
9557
|
+
await fs10.writeFile(path9.join(sourcesDir, `${hash}${ext}`), content);
|
|
9487
9558
|
writtenSources.set(hash, file.relativePath);
|
|
9488
9559
|
}
|
|
9489
9560
|
projectFiles.push({
|
|
@@ -9559,12 +9630,12 @@ var init_compiler = __esm({
|
|
|
9559
9630
|
async collectProjectFiles() {
|
|
9560
9631
|
const files = [];
|
|
9561
9632
|
const scanDir = /* @__PURE__ */ __name(async (dir, relativeBase = "") => {
|
|
9562
|
-
const entries = await
|
|
9633
|
+
const entries = await fs10.readdir(dir, {
|
|
9563
9634
|
withFileTypes: true
|
|
9564
9635
|
});
|
|
9565
9636
|
for (const entry of entries) {
|
|
9566
9637
|
const relativePath = relativeBase ? `${relativeBase}/${entry.name}` : entry.name;
|
|
9567
|
-
const fullPath =
|
|
9638
|
+
const fullPath = path9.join(dir, entry.name);
|
|
9568
9639
|
if (entry.isDirectory()) {
|
|
9569
9640
|
if (shouldSkipDirectory(entry.name)) continue;
|
|
9570
9641
|
await scanDir(fullPath, relativePath);
|
|
@@ -9581,10 +9652,10 @@ var init_compiler = __esm({
|
|
|
9581
9652
|
const workspaceRoot = this.getWorkspaceRoot();
|
|
9582
9653
|
const externalSeen = /* @__PURE__ */ new Set();
|
|
9583
9654
|
for (const abs of this.externalWorkspaceFiles) {
|
|
9584
|
-
const rel =
|
|
9655
|
+
const rel = path9.relative(workspaceRoot, abs).split(path9.sep).join("/");
|
|
9585
9656
|
if (!rel || rel.startsWith("..")) continue;
|
|
9586
9657
|
if (externalSeen.has(rel)) continue;
|
|
9587
|
-
const name =
|
|
9658
|
+
const name = path9.basename(abs);
|
|
9588
9659
|
if (shouldSkipFile(rel) || shouldSkipFile(name)) continue;
|
|
9589
9660
|
externalSeen.add(rel);
|
|
9590
9661
|
files.push({
|
|
@@ -9685,13 +9756,13 @@ var init_compiler = __esm({
|
|
|
9685
9756
|
});
|
|
9686
9757
|
|
|
9687
9758
|
// src/compiler/utils/file-discovery.ts
|
|
9688
|
-
import
|
|
9689
|
-
import
|
|
9759
|
+
import path10 from "path";
|
|
9760
|
+
import fs11 from "fs";
|
|
9690
9761
|
function findEntryPoint(projectDir) {
|
|
9691
9762
|
const baseDir = projectDir || process.cwd();
|
|
9692
9763
|
for (const relativePath of ENTRY_POINT_PRIORITY) {
|
|
9693
|
-
const fullPath =
|
|
9694
|
-
if (
|
|
9764
|
+
const fullPath = path10.join(baseDir, relativePath);
|
|
9765
|
+
if (fs11.existsSync(fullPath)) {
|
|
9695
9766
|
return fullPath;
|
|
9696
9767
|
}
|
|
9697
9768
|
}
|
|
@@ -9728,8 +9799,8 @@ var init_file_discovery = __esm({
|
|
|
9728
9799
|
|
|
9729
9800
|
// src/compiler/source-writer.ts
|
|
9730
9801
|
import { Project as Project4, Node as Node17 } from "ts-morph";
|
|
9731
|
-
import
|
|
9732
|
-
import
|
|
9802
|
+
import fs12 from "fs";
|
|
9803
|
+
import path11 from "path";
|
|
9733
9804
|
function resolveEntryPath(options) {
|
|
9734
9805
|
let indexPath;
|
|
9735
9806
|
try {
|
|
@@ -9741,14 +9812,14 @@ function resolveEntryPath(options) {
|
|
|
9741
9812
|
indexPath = entryPoint;
|
|
9742
9813
|
} else {
|
|
9743
9814
|
const baseDir = options?.projectDir || process.cwd();
|
|
9744
|
-
indexPath =
|
|
9815
|
+
indexPath = path11.join(baseDir, "src", "index.ts");
|
|
9745
9816
|
}
|
|
9746
9817
|
}
|
|
9747
9818
|
} catch {
|
|
9748
9819
|
const baseDir = options?.projectDir || process.cwd();
|
|
9749
|
-
indexPath =
|
|
9820
|
+
indexPath = path11.join(baseDir, "src", "index.ts");
|
|
9750
9821
|
}
|
|
9751
|
-
if (!
|
|
9822
|
+
if (!fs12.existsSync(indexPath)) {
|
|
9752
9823
|
console.warn(`Warning: Entry file not found at ${indexPath}`);
|
|
9753
9824
|
return null;
|
|
9754
9825
|
}
|
|
@@ -13738,6 +13809,164 @@ __name(promptAuthMethod, "promptAuthMethod");
|
|
|
13738
13809
|
|
|
13739
13810
|
// src/commands/configure.ts
|
|
13740
13811
|
init_analytics();
|
|
13812
|
+
|
|
13813
|
+
// src/utils/versioning-mode-cache.ts
|
|
13814
|
+
import * as fs3 from "fs";
|
|
13815
|
+
import * as path3 from "path";
|
|
13816
|
+
|
|
13817
|
+
// src/api/agent-version.api.service.ts
|
|
13818
|
+
init_http_client();
|
|
13819
|
+
var AgentVersionApi = class extends HttpClient {
|
|
13820
|
+
static {
|
|
13821
|
+
__name(this, "AgentVersionApi");
|
|
13822
|
+
}
|
|
13823
|
+
apiKey;
|
|
13824
|
+
agentId;
|
|
13825
|
+
constructor(baseUrl, apiKey, agentId) {
|
|
13826
|
+
super(baseUrl);
|
|
13827
|
+
this.apiKey = apiKey;
|
|
13828
|
+
this.agentId = agentId;
|
|
13829
|
+
}
|
|
13830
|
+
get basePath() {
|
|
13831
|
+
return `/developer/agents/${encodeURIComponent(this.agentId)}`;
|
|
13832
|
+
}
|
|
13833
|
+
get authHeader() {
|
|
13834
|
+
return {
|
|
13835
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
13836
|
+
};
|
|
13837
|
+
}
|
|
13838
|
+
// ---------------------------------------------------------------------------
|
|
13839
|
+
// Versioning mode
|
|
13840
|
+
// ---------------------------------------------------------------------------
|
|
13841
|
+
async getVersioningMode() {
|
|
13842
|
+
return this.httpGet(`${this.basePath}/versioning/mode`, this.authHeader);
|
|
13843
|
+
}
|
|
13844
|
+
// ---------------------------------------------------------------------------
|
|
13845
|
+
// Version CRUD
|
|
13846
|
+
// ---------------------------------------------------------------------------
|
|
13847
|
+
async createVersion(body) {
|
|
13848
|
+
return this.httpPost(`${this.basePath}/versions`, body, this.authHeader);
|
|
13849
|
+
}
|
|
13850
|
+
async listVersions(query) {
|
|
13851
|
+
const params = new URLSearchParams();
|
|
13852
|
+
if (query?.all !== void 0) params.append("all", String(query.all));
|
|
13853
|
+
if (query?.limit !== void 0) params.append("limit", String(query.limit));
|
|
13854
|
+
if (query?.status !== void 0) params.append("status", query.status);
|
|
13855
|
+
const qs = params.toString();
|
|
13856
|
+
const url = qs ? `${this.basePath}/versions?${qs}` : `${this.basePath}/versions`;
|
|
13857
|
+
return this.httpGet(url, this.authHeader);
|
|
13858
|
+
}
|
|
13859
|
+
async getVersion(version) {
|
|
13860
|
+
return this.httpGet(`${this.basePath}/versions/${version}`, this.authHeader);
|
|
13861
|
+
}
|
|
13862
|
+
async deleteVersion(version) {
|
|
13863
|
+
return this.httpDelete(`${this.basePath}/versions/${version}`, this.authHeader);
|
|
13864
|
+
}
|
|
13865
|
+
// ---------------------------------------------------------------------------
|
|
13866
|
+
// Diff
|
|
13867
|
+
// ---------------------------------------------------------------------------
|
|
13868
|
+
async diffVersions(from, to) {
|
|
13869
|
+
const url = `${this.basePath}/versions/diff?from=${from}&to=${to}`;
|
|
13870
|
+
return this.httpGet(url, this.authHeader);
|
|
13871
|
+
}
|
|
13872
|
+
// ---------------------------------------------------------------------------
|
|
13873
|
+
// Promote
|
|
13874
|
+
// ---------------------------------------------------------------------------
|
|
13875
|
+
async promoteVersion(version) {
|
|
13876
|
+
return this.httpPost(`${this.basePath}/versions/${version}/promote`, {}, this.authHeader);
|
|
13877
|
+
}
|
|
13878
|
+
// ---------------------------------------------------------------------------
|
|
13879
|
+
// Patch commit hash — called by `lua version create` after a successful git
|
|
13880
|
+
// commit + tag to propagate the SHA to the backend AgentVersion record.
|
|
13881
|
+
// Failure is non-fatal; the version snapshot is valid whether or not this
|
|
13882
|
+
// PATCH lands.
|
|
13883
|
+
// ---------------------------------------------------------------------------
|
|
13884
|
+
async patchCommitHash(version, commitHash) {
|
|
13885
|
+
return this.httpPatch(`${this.basePath}/versions/${version}/commit-hash`, {
|
|
13886
|
+
commitHash
|
|
13887
|
+
}, this.authHeader);
|
|
13888
|
+
}
|
|
13889
|
+
};
|
|
13890
|
+
|
|
13891
|
+
// src/utils/versioning-mode-cache.ts
|
|
13892
|
+
init_constants();
|
|
13893
|
+
init_auth_error();
|
|
13894
|
+
init_constants();
|
|
13895
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
13896
|
+
function readCacheFile() {
|
|
13897
|
+
try {
|
|
13898
|
+
const raw = fs3.readFileSync(CLI_CACHE_FILE, "utf-8");
|
|
13899
|
+
return JSON.parse(raw);
|
|
13900
|
+
} catch {
|
|
13901
|
+
return {};
|
|
13902
|
+
}
|
|
13903
|
+
}
|
|
13904
|
+
__name(readCacheFile, "readCacheFile");
|
|
13905
|
+
function writeCacheFile(data) {
|
|
13906
|
+
try {
|
|
13907
|
+
fs3.mkdirSync(path3.dirname(CLI_CACHE_FILE), {
|
|
13908
|
+
recursive: true
|
|
13909
|
+
});
|
|
13910
|
+
fs3.writeFileSync(CLI_CACHE_FILE, JSON.stringify(data, null, 2));
|
|
13911
|
+
} catch {
|
|
13912
|
+
}
|
|
13913
|
+
}
|
|
13914
|
+
__name(writeCacheFile, "writeCacheFile");
|
|
13915
|
+
function isFresh(entry) {
|
|
13916
|
+
const age = Date.now() - new Date(entry.cachedAt).getTime();
|
|
13917
|
+
return age >= 0 && age < CACHE_TTL_MS;
|
|
13918
|
+
}
|
|
13919
|
+
__name(isFresh, "isFresh");
|
|
13920
|
+
async function getVersioningModeCached(apiKey, agentId) {
|
|
13921
|
+
const file = readCacheFile();
|
|
13922
|
+
const existing = file.versioningMode?.[agentId];
|
|
13923
|
+
if (existing && isFresh(existing)) {
|
|
13924
|
+
return {
|
|
13925
|
+
enabled: existing.enabled
|
|
13926
|
+
};
|
|
13927
|
+
}
|
|
13928
|
+
try {
|
|
13929
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
13930
|
+
const response = await api.getVersioningMode();
|
|
13931
|
+
if (!response.success || !response.data) {
|
|
13932
|
+
if (existing) return {
|
|
13933
|
+
enabled: existing.enabled
|
|
13934
|
+
};
|
|
13935
|
+
return {
|
|
13936
|
+
enabled: false
|
|
13937
|
+
};
|
|
13938
|
+
}
|
|
13939
|
+
const enabled = response.data.enabled === true;
|
|
13940
|
+
file.versioningMode = file.versioningMode ?? {};
|
|
13941
|
+
file.versioningMode[agentId] = {
|
|
13942
|
+
enabled,
|
|
13943
|
+
cachedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13944
|
+
};
|
|
13945
|
+
writeCacheFile(file);
|
|
13946
|
+
return {
|
|
13947
|
+
enabled
|
|
13948
|
+
};
|
|
13949
|
+
} catch (err) {
|
|
13950
|
+
if (err instanceof AuthenticationError) throw err;
|
|
13951
|
+
if (err instanceof Error && /access denied|forbidden|403|401/i.test(err.message)) throw err;
|
|
13952
|
+
if (existing) return {
|
|
13953
|
+
enabled: existing.enabled
|
|
13954
|
+
};
|
|
13955
|
+
return {
|
|
13956
|
+
enabled: false
|
|
13957
|
+
};
|
|
13958
|
+
}
|
|
13959
|
+
}
|
|
13960
|
+
__name(getVersioningModeCached, "getVersioningModeCached");
|
|
13961
|
+
function invalidateVersioningModeCache() {
|
|
13962
|
+
const file = readCacheFile();
|
|
13963
|
+
if (!file.versioningMode) return;
|
|
13964
|
+
delete file.versioningMode;
|
|
13965
|
+
writeCacheFile(file);
|
|
13966
|
+
}
|
|
13967
|
+
__name(invalidateVersioningModeCache, "invalidateVersioningModeCache");
|
|
13968
|
+
|
|
13969
|
+
// src/commands/configure.ts
|
|
13741
13970
|
async function configureCommand(options = {}) {
|
|
13742
13971
|
return withErrorHandling(async () => {
|
|
13743
13972
|
const { apiKey, email, otp } = options;
|
|
@@ -13747,11 +13976,13 @@ async function configureCommand(options = {}) {
|
|
|
13747
13976
|
authMethod = "api-key";
|
|
13748
13977
|
nonInteractive = true;
|
|
13749
13978
|
await handleApiKeyAuthNonInteractive(apiKey);
|
|
13979
|
+
invalidateVersioningModeCache();
|
|
13750
13980
|
} else if (email) {
|
|
13751
13981
|
authMethod = "email";
|
|
13752
13982
|
nonInteractive = true;
|
|
13753
13983
|
if (otp) {
|
|
13754
13984
|
await handleEmailOtpVerify(email, otp);
|
|
13985
|
+
invalidateVersioningModeCache();
|
|
13755
13986
|
} else {
|
|
13756
13987
|
await handleEmailOtpRequest(email);
|
|
13757
13988
|
}
|
|
@@ -13760,8 +13991,10 @@ async function configureCommand(options = {}) {
|
|
|
13760
13991
|
clearPromptLines(2);
|
|
13761
13992
|
if (authMethod === "api-key") {
|
|
13762
13993
|
await handleApiKeyAuth();
|
|
13994
|
+
invalidateVersioningModeCache();
|
|
13763
13995
|
} else if (authMethod === "email") {
|
|
13764
13996
|
await handleEmailAuth();
|
|
13997
|
+
invalidateVersioningModeCache();
|
|
13765
13998
|
}
|
|
13766
13999
|
}
|
|
13767
14000
|
trackEvent("cli_auth_completed", {
|
|
@@ -14433,8 +14666,8 @@ init_compile_constants();
|
|
|
14433
14666
|
init_artifact_loader();
|
|
14434
14667
|
init_backup_api_service();
|
|
14435
14668
|
init_constants();
|
|
14436
|
-
import
|
|
14437
|
-
import
|
|
14669
|
+
import fs13 from "fs";
|
|
14670
|
+
import path12 from "path";
|
|
14438
14671
|
var BACKUP_CACHE_FILENAME = "backup-manifest.json";
|
|
14439
14672
|
function calculateProjectHash(projectFiles) {
|
|
14440
14673
|
return combineFileHashes(projectFiles);
|
|
@@ -14447,11 +14680,11 @@ function getCurrentProjectHash(projectPath = process.cwd()) {
|
|
|
14447
14680
|
}
|
|
14448
14681
|
__name(getCurrentProjectHash, "getCurrentProjectHash");
|
|
14449
14682
|
function loadSourceByHash(hash, relativePath, projectPath = process.cwd()) {
|
|
14450
|
-
const sourcesDir =
|
|
14451
|
-
const ext =
|
|
14452
|
-
const sourcePath =
|
|
14453
|
-
if (
|
|
14454
|
-
return
|
|
14683
|
+
const sourcesDir = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
|
|
14684
|
+
const ext = path12.extname(relativePath) || ".txt";
|
|
14685
|
+
const sourcePath = path12.join(sourcesDir, `${hash}${ext}`);
|
|
14686
|
+
if (fs13.existsSync(sourcePath)) {
|
|
14687
|
+
return fs13.readFileSync(sourcePath, "utf-8");
|
|
14455
14688
|
}
|
|
14456
14689
|
return null;
|
|
14457
14690
|
}
|
|
@@ -14470,30 +14703,30 @@ function prepareFileRefs(projectPath = process.cwd()) {
|
|
|
14470
14703
|
}
|
|
14471
14704
|
__name(prepareFileRefs, "prepareFileRefs");
|
|
14472
14705
|
function reconcileManifestWithDisk(projectPath = process.cwd()) {
|
|
14473
|
-
const manifestPath =
|
|
14474
|
-
if (!
|
|
14706
|
+
const manifestPath = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "manifest.json");
|
|
14707
|
+
if (!fs13.existsSync(manifestPath)) return;
|
|
14475
14708
|
const manifest = loadManifest(projectPath);
|
|
14476
|
-
const sourcesDir =
|
|
14477
|
-
|
|
14709
|
+
const sourcesDir = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
|
|
14710
|
+
fs13.mkdirSync(sourcesDir, {
|
|
14478
14711
|
recursive: true
|
|
14479
14712
|
});
|
|
14480
14713
|
let changed = false;
|
|
14481
14714
|
for (const file of manifest.projectFiles) {
|
|
14482
14715
|
if (file.external) continue;
|
|
14483
|
-
const absPath =
|
|
14484
|
-
if (!
|
|
14716
|
+
const absPath = path12.join(projectPath, file.relativePath);
|
|
14717
|
+
if (!fs13.existsSync(absPath)) continue;
|
|
14485
14718
|
let content;
|
|
14486
14719
|
try {
|
|
14487
|
-
content =
|
|
14720
|
+
content = fs13.readFileSync(absPath, "utf-8");
|
|
14488
14721
|
} catch {
|
|
14489
14722
|
continue;
|
|
14490
14723
|
}
|
|
14491
14724
|
const freshHash = hashContent(content);
|
|
14492
14725
|
if (freshHash === file.hash) continue;
|
|
14493
|
-
const ext =
|
|
14494
|
-
const sourceTargetPath =
|
|
14726
|
+
const ext = path12.extname(file.relativePath) || ".txt";
|
|
14727
|
+
const sourceTargetPath = path12.join(sourcesDir, `${freshHash}${ext}`);
|
|
14495
14728
|
try {
|
|
14496
|
-
|
|
14729
|
+
fs13.writeFileSync(sourceTargetPath, content, "utf-8");
|
|
14497
14730
|
} catch {
|
|
14498
14731
|
continue;
|
|
14499
14732
|
}
|
|
@@ -14502,7 +14735,7 @@ function reconcileManifestWithDisk(projectPath = process.cwd()) {
|
|
|
14502
14735
|
changed = true;
|
|
14503
14736
|
}
|
|
14504
14737
|
if (changed) {
|
|
14505
|
-
|
|
14738
|
+
fs13.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
14506
14739
|
}
|
|
14507
14740
|
}
|
|
14508
14741
|
__name(reconcileManifestWithDisk, "reconcileManifestWithDisk");
|
|
@@ -14575,7 +14808,7 @@ function checkRestoreConflicts(manifest, targetDir) {
|
|
|
14575
14808
|
const existingFiles = [];
|
|
14576
14809
|
for (const file of manifest.files) {
|
|
14577
14810
|
const filePath = resolveBackupFileTarget2(file, targetDir);
|
|
14578
|
-
if (
|
|
14811
|
+
if (fs13.existsSync(filePath)) {
|
|
14579
14812
|
existingFiles.push(file.relativePath);
|
|
14580
14813
|
} else {
|
|
14581
14814
|
newFiles.push(file.relativePath);
|
|
@@ -14601,16 +14834,16 @@ function createBackupTracking(projectHash) {
|
|
|
14601
14834
|
}
|
|
14602
14835
|
__name(createBackupTracking, "createBackupTracking");
|
|
14603
14836
|
function getBackupCachePath(projectPath = process.cwd()) {
|
|
14604
|
-
return
|
|
14837
|
+
return path12.join(projectPath, ".lua", BACKUP_CACHE_FILENAME);
|
|
14605
14838
|
}
|
|
14606
14839
|
__name(getBackupCachePath, "getBackupCachePath");
|
|
14607
14840
|
function writeBackupManifestCache(cache, projectPath = process.cwd()) {
|
|
14608
14841
|
const cachePath = getBackupCachePath(projectPath);
|
|
14609
14842
|
try {
|
|
14610
|
-
|
|
14843
|
+
fs13.mkdirSync(path12.dirname(cachePath), {
|
|
14611
14844
|
recursive: true
|
|
14612
14845
|
});
|
|
14613
|
-
|
|
14846
|
+
fs13.writeFileSync(cachePath, JSON.stringify(cache, null, 2), "utf-8");
|
|
14614
14847
|
} catch (error) {
|
|
14615
14848
|
const message = error instanceof Error ? error.message : String(error);
|
|
14616
14849
|
console.warn(`Could not write backup manifest cache: ${message}`);
|
|
@@ -14619,9 +14852,9 @@ function writeBackupManifestCache(cache, projectPath = process.cwd()) {
|
|
|
14619
14852
|
__name(writeBackupManifestCache, "writeBackupManifestCache");
|
|
14620
14853
|
function readBackupManifestCache(projectPath = process.cwd()) {
|
|
14621
14854
|
const cachePath = getBackupCachePath(projectPath);
|
|
14622
|
-
if (!
|
|
14855
|
+
if (!fs13.existsSync(cachePath)) return null;
|
|
14623
14856
|
try {
|
|
14624
|
-
const raw =
|
|
14857
|
+
const raw = fs13.readFileSync(cachePath, "utf-8");
|
|
14625
14858
|
const parsed = JSON.parse(raw);
|
|
14626
14859
|
if (typeof parsed.lastHash !== "string" || typeof parsed.lastPushedAt !== "string" || !Array.isArray(parsed.files) || !parsed.files.every((f) => f && typeof f.relativePath === "string" && typeof f.hash === "string")) {
|
|
14627
14860
|
return null;
|
|
@@ -14648,18 +14881,18 @@ function detectLocalConflictsForPull(incomingFiles, projectPath = process.cwd())
|
|
|
14648
14881
|
f.hash
|
|
14649
14882
|
]));
|
|
14650
14883
|
const conflicts = /* @__PURE__ */ new Set();
|
|
14651
|
-
const projectRoot =
|
|
14884
|
+
const projectRoot = path12.resolve(projectPath);
|
|
14652
14885
|
for (const incoming of incomingFiles) {
|
|
14653
14886
|
if (COMPILE_MANAGED_FILES.has(incoming.relativePath)) continue;
|
|
14654
14887
|
if (incoming.external) continue;
|
|
14655
|
-
const absPath =
|
|
14656
|
-
if (absPath !== projectRoot && !absPath.startsWith(projectRoot +
|
|
14888
|
+
const absPath = path12.resolve(projectRoot, incoming.relativePath);
|
|
14889
|
+
if (absPath !== projectRoot && !absPath.startsWith(projectRoot + path12.sep)) {
|
|
14657
14890
|
continue;
|
|
14658
14891
|
}
|
|
14659
|
-
if (!
|
|
14892
|
+
if (!fs13.existsSync(absPath)) continue;
|
|
14660
14893
|
let diskHash;
|
|
14661
14894
|
try {
|
|
14662
|
-
const bytes =
|
|
14895
|
+
const bytes = fs13.readFileSync(absPath, "utf-8");
|
|
14663
14896
|
diskHash = hashContent(bytes);
|
|
14664
14897
|
} catch {
|
|
14665
14898
|
conflicts.add(incoming.relativePath);
|
|
@@ -14686,7 +14919,7 @@ function detectLocalConflictsForPull(incomingFiles, projectPath = process.cwd())
|
|
|
14686
14919
|
__name(detectLocalConflictsForPull, "detectLocalConflictsForPull");
|
|
14687
14920
|
async function fetchBackupManifestForPull(apiKey, agentId) {
|
|
14688
14921
|
try {
|
|
14689
|
-
const backupApi = new
|
|
14922
|
+
const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
|
|
14690
14923
|
const response = await backupApi.getBackupManifest();
|
|
14691
14924
|
return {
|
|
14692
14925
|
fetched: response.data ?? null
|
|
@@ -14717,7 +14950,7 @@ __name(decideBackupFreshness, "decideBackupFreshness");
|
|
|
14717
14950
|
async function checkServerBackupNewerThanLocal(apiKey, agentId, projectPath = process.cwd()) {
|
|
14718
14951
|
const local = readBackupManifestCache(projectPath);
|
|
14719
14952
|
try {
|
|
14720
|
-
const api = new
|
|
14953
|
+
const api = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
|
|
14721
14954
|
const resp = await api.getBackupMetadata();
|
|
14722
14955
|
const metadata = resp.success && resp.data ? {
|
|
14723
14956
|
projectHash: resp.data.projectHash
|
|
@@ -14730,7 +14963,7 @@ async function checkServerBackupNewerThanLocal(apiKey, agentId, projectPath = pr
|
|
|
14730
14963
|
__name(checkServerBackupNewerThanLocal, "checkServerBackupNewerThanLocal");
|
|
14731
14964
|
async function checkBackupAvailability(apiKey, agentId) {
|
|
14732
14965
|
try {
|
|
14733
|
-
const backupApi = new
|
|
14966
|
+
const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
|
|
14734
14967
|
const metadata = await backupApi.getBackupMetadata();
|
|
14735
14968
|
if (metadata.data && metadata.data.fileCount > 0) {
|
|
14736
14969
|
return {
|
|
@@ -14752,7 +14985,7 @@ __name(checkBackupAvailability, "checkBackupAvailability");
|
|
|
14752
14985
|
async function restoreFromBackupForSync(apiKey, agentId, expectedPrimitives = [], projectPath, preFetchedManifest) {
|
|
14753
14986
|
const targetDir = projectPath || process.cwd();
|
|
14754
14987
|
try {
|
|
14755
|
-
const backupApi = new
|
|
14988
|
+
const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
|
|
14756
14989
|
const manifest = preFetchedManifest ?? (await backupApi.getBackupManifest()).data ?? null;
|
|
14757
14990
|
if (!manifest || !manifest.files?.length) {
|
|
14758
14991
|
return {
|
|
@@ -14838,17 +15071,17 @@ function writeBackupFilesWithByteCompare(manifest, blobs, targetDir) {
|
|
|
14838
15071
|
}
|
|
14839
15072
|
restoredContents.set(makeRestoredContentsKey(file), content);
|
|
14840
15073
|
const filePath = resolveBackupFileTarget2(file, targetDir);
|
|
14841
|
-
if (
|
|
14842
|
-
const existing =
|
|
15074
|
+
if (fs13.existsSync(filePath)) {
|
|
15075
|
+
const existing = fs13.readFileSync(filePath);
|
|
14843
15076
|
if (existing.equals(content)) {
|
|
14844
15077
|
filesUnchanged++;
|
|
14845
15078
|
continue;
|
|
14846
15079
|
}
|
|
14847
15080
|
}
|
|
14848
|
-
|
|
15081
|
+
fs13.mkdirSync(path12.dirname(filePath), {
|
|
14849
15082
|
recursive: true
|
|
14850
15083
|
});
|
|
14851
|
-
|
|
15084
|
+
fs13.writeFileSync(filePath, content);
|
|
14852
15085
|
filesWritten++;
|
|
14853
15086
|
}
|
|
14854
15087
|
return {
|
|
@@ -15590,6 +15823,33 @@ var AgentHandler = class {
|
|
|
15590
15823
|
success: false
|
|
15591
15824
|
};
|
|
15592
15825
|
}
|
|
15826
|
+
const modelSettings = agent?.modelSettings ?? null;
|
|
15827
|
+
writeProgress("\n\u{1F321}\uFE0F Pushing model settings...");
|
|
15828
|
+
try {
|
|
15829
|
+
const success2 = await this.pushModelSettings({
|
|
15830
|
+
apiKey,
|
|
15831
|
+
agentId
|
|
15832
|
+
}, modelSettings);
|
|
15833
|
+
result.modelSettings = {
|
|
15834
|
+
success: success2
|
|
15835
|
+
};
|
|
15836
|
+
if (success2) {
|
|
15837
|
+
if (modelSettings !== null) {
|
|
15838
|
+
const keys = Object.keys(modelSettings).join(", ");
|
|
15839
|
+
writeSuccess(` \u2705 Model settings pushed (${keys})`);
|
|
15840
|
+
} else {
|
|
15841
|
+
writeSuccess(" \u2705 Model settings cleared (using provider defaults)");
|
|
15842
|
+
}
|
|
15843
|
+
} else {
|
|
15844
|
+
console.error(" \u274C Failed to push model settings");
|
|
15845
|
+
}
|
|
15846
|
+
} catch (error) {
|
|
15847
|
+
if (AuthenticationError.isAuthenticationError(error)) throw error;
|
|
15848
|
+
console.error(` \u274C Failed to push model settings: ${error.message}`);
|
|
15849
|
+
result.modelSettings = {
|
|
15850
|
+
success: false
|
|
15851
|
+
};
|
|
15852
|
+
}
|
|
15593
15853
|
const batching = agent?.batching ?? null;
|
|
15594
15854
|
writeProgress("\n\u23F1 Pushing batching configuration...");
|
|
15595
15855
|
try {
|
|
@@ -15723,6 +15983,13 @@ var AgentHandler = class {
|
|
|
15723
15983
|
});
|
|
15724
15984
|
return result.success;
|
|
15725
15985
|
}
|
|
15986
|
+
async pushModelSettings(ctx, modelSettings) {
|
|
15987
|
+
const agentApi = new AgentApi(BASE_URLS.API, ctx.apiKey);
|
|
15988
|
+
const result = await agentApi.updateAgent(ctx.agentId, {
|
|
15989
|
+
modelSettings
|
|
15990
|
+
});
|
|
15991
|
+
return result.success;
|
|
15992
|
+
}
|
|
15726
15993
|
async pushBatching(ctx, batchingConfig) {
|
|
15727
15994
|
const agentApi = new AgentApi(BASE_URLS.API, ctx.apiKey);
|
|
15728
15995
|
const result = await agentApi.updateAgent(ctx.agentId, {
|
|
@@ -16574,7 +16841,7 @@ async function checkAndRestoreBackup(apiKey, agentId, options) {
|
|
|
16574
16841
|
if (!confirm) return false;
|
|
16575
16842
|
}
|
|
16576
16843
|
try {
|
|
16577
|
-
const backupApi = new
|
|
16844
|
+
const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
|
|
16578
16845
|
const urlsResponse = await backupApi.getBlobUrls(manifest.files.map((f) => f.hash));
|
|
16579
16846
|
if (!urlsResponse.success || !urlsResponse.data) {
|
|
16580
16847
|
writeError(`Failed to fetch download URLs: ${urlsResponse.error?.message ?? "unknown error"}`);
|
|
@@ -16628,6 +16895,7 @@ async function destroyCommand(options) {
|
|
|
16628
16895
|
if (options?.force) {
|
|
16629
16896
|
const deleted2 = deleteApiKey();
|
|
16630
16897
|
if (deleted2) {
|
|
16898
|
+
invalidateVersioningModeCache();
|
|
16631
16899
|
writeSuccess("\u2705 API key deleted successfully.");
|
|
16632
16900
|
} else {
|
|
16633
16901
|
writeProgress("\u274C Failed to delete API key.");
|
|
@@ -16653,6 +16921,7 @@ async function destroyCommand(options) {
|
|
|
16653
16921
|
if (confirm) {
|
|
16654
16922
|
deleted = deleteApiKey();
|
|
16655
16923
|
if (deleted) {
|
|
16924
|
+
invalidateVersioningModeCache();
|
|
16656
16925
|
writeSuccess("\u2705 API key deleted successfully.");
|
|
16657
16926
|
} else {
|
|
16658
16927
|
writeProgress("\u274C Failed to delete API key.");
|
|
@@ -16723,13 +16992,13 @@ __name(apiKeyCommand, "apiKeyCommand");
|
|
|
16723
16992
|
init_cli();
|
|
16724
16993
|
init_files();
|
|
16725
16994
|
init_auth();
|
|
16726
|
-
import
|
|
16995
|
+
import path14 from "path";
|
|
16727
16996
|
|
|
16728
16997
|
// src/commands/sync.ts
|
|
16729
16998
|
init_dist();
|
|
16730
16999
|
init_cli();
|
|
16731
|
-
import
|
|
16732
|
-
import
|
|
17000
|
+
import fs14 from "fs";
|
|
17001
|
+
import path13 from "path";
|
|
16733
17002
|
|
|
16734
17003
|
// src/utils/prompt-handler.ts
|
|
16735
17004
|
init_cli();
|
|
@@ -16754,6 +17023,18 @@ async function safePrompt(questions) {
|
|
|
16754
17023
|
}
|
|
16755
17024
|
}
|
|
16756
17025
|
__name(safePrompt, "safePrompt");
|
|
17026
|
+
async function confirmAction(message) {
|
|
17027
|
+
const answer = await safePrompt([
|
|
17028
|
+
{
|
|
17029
|
+
type: "confirm",
|
|
17030
|
+
name: "confirmed",
|
|
17031
|
+
message,
|
|
17032
|
+
default: false
|
|
17033
|
+
}
|
|
17034
|
+
]);
|
|
17035
|
+
return answer?.confirmed ?? false;
|
|
17036
|
+
}
|
|
17037
|
+
__name(confirmAction, "confirmAction");
|
|
16757
17038
|
|
|
16758
17039
|
// src/commands/sync.ts
|
|
16759
17040
|
init_command_utils();
|
|
@@ -16940,8 +17221,8 @@ init_mcp_server_handler();
|
|
|
16940
17221
|
async function syncCommand(options) {
|
|
16941
17222
|
return withErrorHandling(async () => {
|
|
16942
17223
|
const preCompileYaml = readYamlConfig();
|
|
16943
|
-
const yamlPath =
|
|
16944
|
-
const preCompileYamlBytes =
|
|
17224
|
+
const yamlPath = path13.resolve(process.cwd(), "lua.skill.yaml");
|
|
17225
|
+
const preCompileYamlBytes = fs14.existsSync(yamlPath) ? fs14.readFileSync(yamlPath, "utf-8") : null;
|
|
16945
17226
|
let syncCompletedSuccessfully = false;
|
|
16946
17227
|
try {
|
|
16947
17228
|
writeProgress("\u{1F504} Compiling to get latest local state...");
|
|
@@ -17002,7 +17283,7 @@ async function syncCommand(options) {
|
|
|
17002
17283
|
} finally {
|
|
17003
17284
|
if (!syncCompletedSuccessfully && preCompileYamlBytes !== null) {
|
|
17004
17285
|
try {
|
|
17005
|
-
|
|
17286
|
+
fs14.writeFileSync(yamlPath, preCompileYamlBytes, "utf-8");
|
|
17006
17287
|
} catch {
|
|
17007
17288
|
}
|
|
17008
17289
|
}
|
|
@@ -18488,6 +18769,7 @@ var VoiceHandler = class extends BaseVersionedHandler {
|
|
|
18488
18769
|
if (voice.hasTools !== void 0) body.hasTools = voice.hasTools;
|
|
18489
18770
|
if (voice.krispEnabled !== void 0) body.krispEnabled = voice.krispEnabled;
|
|
18490
18771
|
if (voice.persistTranscript !== void 0) body.persistTranscript = voice.persistTranscript;
|
|
18772
|
+
if (voice.onToolFailureSay !== void 0) body.onToolFailureSay = voice.onToolFailureSay;
|
|
18491
18773
|
if (voice.volume !== void 0) body.volume = voice.volume;
|
|
18492
18774
|
if (voice.pronunciations !== void 0) body.pronunciations = voice.pronunciations;
|
|
18493
18775
|
if (voice.backgroundAudio !== void 0) body.backgroundAudio = voice.backgroundAudio;
|
|
@@ -18559,7 +18841,7 @@ async function compileCommand(options) {
|
|
|
18559
18841
|
}
|
|
18560
18842
|
writeProgress("\u{1F528} Compiling...");
|
|
18561
18843
|
const rootDir = process.cwd();
|
|
18562
|
-
const outDir =
|
|
18844
|
+
const outDir = path14.join(rootDir, "dist-v2");
|
|
18563
18845
|
const result = await compile({
|
|
18564
18846
|
rootDir,
|
|
18565
18847
|
outDir,
|
|
@@ -18723,12 +19005,12 @@ init_command_utils();
|
|
|
18723
19005
|
|
|
18724
19006
|
// src/utils/sandbox.ts
|
|
18725
19007
|
import vm3 from "vm";
|
|
18726
|
-
import
|
|
19008
|
+
import path17 from "path";
|
|
18727
19009
|
|
|
18728
19010
|
// ../shared-sandbox/dist/index.mjs
|
|
18729
19011
|
import vm from "vm";
|
|
18730
19012
|
import { createRequire } from "module";
|
|
18731
|
-
import
|
|
19013
|
+
import path15 from "path";
|
|
18732
19014
|
import dns from "dns";
|
|
18733
19015
|
import net from "net";
|
|
18734
19016
|
import { promisify } from "util";
|
|
@@ -18898,7 +19180,7 @@ var REQUIRE_BLOCKLIST = /* @__PURE__ */ new Set([
|
|
|
18898
19180
|
var SANDBOX_FAKE_DIRNAME = "/";
|
|
18899
19181
|
var SANDBOX_FAKE_FILENAME = "/index.ts";
|
|
18900
19182
|
function buildSandboxRequire(opts) {
|
|
18901
|
-
const realRequire = createRequire(
|
|
19183
|
+
const realRequire = createRequire(path15.join(opts.cwd, "package.json"));
|
|
18902
19184
|
const wrapped = /* @__PURE__ */ __name4((id) => {
|
|
18903
19185
|
if (REQUIRE_BLOCKLIST.has(id)) {
|
|
18904
19186
|
const evt = {
|
|
@@ -19568,14 +19850,14 @@ __name(runBundleInContext, "runBundleInContext");
|
|
|
19568
19850
|
__name4(runBundleInContext, "runBundleInContext");
|
|
19569
19851
|
|
|
19570
19852
|
// src/utils/env-loader.utils.ts
|
|
19571
|
-
import
|
|
19572
|
-
import
|
|
19853
|
+
import path16 from "path";
|
|
19854
|
+
import fs15 from "fs";
|
|
19573
19855
|
function parseEnvFile(filePath) {
|
|
19574
|
-
if (!
|
|
19856
|
+
if (!fs15.existsSync(filePath)) {
|
|
19575
19857
|
return {};
|
|
19576
19858
|
}
|
|
19577
19859
|
try {
|
|
19578
|
-
const content =
|
|
19860
|
+
const content = fs15.readFileSync(filePath, "utf8");
|
|
19579
19861
|
if (!content.trim()) return {};
|
|
19580
19862
|
const envVars = {};
|
|
19581
19863
|
content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).forEach((line) => {
|
|
@@ -19601,7 +19883,7 @@ async function loadEnvironmentVariables(context) {
|
|
|
19601
19883
|
}
|
|
19602
19884
|
__name(loadEnvironmentVariables, "loadEnvironmentVariables");
|
|
19603
19885
|
function loadSandboxEnvVariables() {
|
|
19604
|
-
const envFilePath =
|
|
19886
|
+
const envFilePath = path16.join(process.cwd(), ".env");
|
|
19605
19887
|
const envMap = parseEnvFile(envFilePath);
|
|
19606
19888
|
return Object.entries(envMap).map(([key, value]) => ({
|
|
19607
19889
|
key,
|
|
@@ -19696,7 +19978,7 @@ function loadEnvironmentVariables2() {
|
|
|
19696
19978
|
envVars[key] = value;
|
|
19697
19979
|
}
|
|
19698
19980
|
}
|
|
19699
|
-
const envFilePath =
|
|
19981
|
+
const envFilePath = path17.join(process.cwd(), ".env");
|
|
19700
19982
|
const fileEnvVars = parseEnvFile(envFilePath);
|
|
19701
19983
|
Object.assign(envVars, fileEnvVars);
|
|
19702
19984
|
return envVars;
|
|
@@ -21582,6 +21864,7 @@ init_dist();
|
|
|
21582
21864
|
init_files();
|
|
21583
21865
|
init_cli();
|
|
21584
21866
|
init_command_utils();
|
|
21867
|
+
init_auth();
|
|
21585
21868
|
init_semver();
|
|
21586
21869
|
init_auth_error();
|
|
21587
21870
|
init_constants();
|
|
@@ -21648,7 +21931,7 @@ async function runBackupPush(opts) {
|
|
|
21648
21931
|
projectHash: manifestProjectHash
|
|
21649
21932
|
};
|
|
21650
21933
|
}
|
|
21651
|
-
const backupApi = new
|
|
21934
|
+
const backupApi = new backup_api_service_default(BASE_URLS.API, opts.apiKey, opts.agentId);
|
|
21652
21935
|
const allHashes = opts.fresh ? [
|
|
21653
21936
|
...new Set(fileRefs.map((f) => f.hash))
|
|
21654
21937
|
] : getAllFileHashes(projectPath);
|
|
@@ -21709,8 +21992,8 @@ async function runBackupPush(opts) {
|
|
|
21709
21992
|
}
|
|
21710
21993
|
__name(runBackupPush, "runBackupPush");
|
|
21711
21994
|
async function uploadBlobsFromDisk(uploadUrls, files, projectPath, concurrency = 10) {
|
|
21712
|
-
const
|
|
21713
|
-
const
|
|
21995
|
+
const fs17 = await import("fs");
|
|
21996
|
+
const path19 = await import("path");
|
|
21714
21997
|
const zlib2 = await import("zlib");
|
|
21715
21998
|
const hashToPath = /* @__PURE__ */ new Map();
|
|
21716
21999
|
for (const f of files) {
|
|
@@ -21724,8 +22007,8 @@ async function uploadBlobsFromDisk(uploadUrls, files, projectPath, concurrency =
|
|
|
21724
22007
|
if (!rel) {
|
|
21725
22008
|
throw new Error(`No file found for hash: ${hash}`);
|
|
21726
22009
|
}
|
|
21727
|
-
const abs =
|
|
21728
|
-
const content =
|
|
22010
|
+
const abs = path19.join(projectPath, rel);
|
|
22011
|
+
const content = fs17.readFileSync(abs);
|
|
21729
22012
|
const compressed = zlib2.gzipSync(content);
|
|
21730
22013
|
const response = await fetch(uploadUrls[hash], {
|
|
21731
22014
|
method: "PUT",
|
|
@@ -21816,6 +22099,769 @@ init_analytics();
|
|
|
21816
22099
|
init_dist2();
|
|
21817
22100
|
init_skills_api_service();
|
|
21818
22101
|
|
|
22102
|
+
// src/utils/try-git-commit.ts
|
|
22103
|
+
init_cli();
|
|
22104
|
+
init_files();
|
|
22105
|
+
|
|
22106
|
+
// src/utils/git.ts
|
|
22107
|
+
import { spawn } from "child_process";
|
|
22108
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
22109
|
+
async function runGit(args2, opts = {}) {
|
|
22110
|
+
return new Promise((resolve6, reject) => {
|
|
22111
|
+
const child = spawn("git", args2, {
|
|
22112
|
+
cwd: opts.cwd,
|
|
22113
|
+
shell: false,
|
|
22114
|
+
stdio: [
|
|
22115
|
+
"ignore",
|
|
22116
|
+
"pipe",
|
|
22117
|
+
"pipe"
|
|
22118
|
+
]
|
|
22119
|
+
});
|
|
22120
|
+
let stdout = "";
|
|
22121
|
+
let stderr = "";
|
|
22122
|
+
let settled = false;
|
|
22123
|
+
const timer = setTimeout(() => {
|
|
22124
|
+
settled = true;
|
|
22125
|
+
child.kill("SIGKILL");
|
|
22126
|
+
reject(new Error(`git ${args2.join(" ")} timed out after ${opts.timeout ?? DEFAULT_TIMEOUT_MS}ms`));
|
|
22127
|
+
}, opts.timeout ?? DEFAULT_TIMEOUT_MS);
|
|
22128
|
+
child.stdout?.on("data", (chunk) => {
|
|
22129
|
+
stdout += chunk.toString();
|
|
22130
|
+
});
|
|
22131
|
+
child.stdout?.on("error", (err) => {
|
|
22132
|
+
if (settled) return;
|
|
22133
|
+
settled = true;
|
|
22134
|
+
clearTimeout(timer);
|
|
22135
|
+
reject(err);
|
|
22136
|
+
});
|
|
22137
|
+
child.stderr?.on("data", (chunk) => {
|
|
22138
|
+
stderr += chunk.toString();
|
|
22139
|
+
});
|
|
22140
|
+
child.stderr?.on("error", () => {
|
|
22141
|
+
});
|
|
22142
|
+
child.on("error", (err) => {
|
|
22143
|
+
if (settled) return;
|
|
22144
|
+
settled = true;
|
|
22145
|
+
clearTimeout(timer);
|
|
22146
|
+
reject(err);
|
|
22147
|
+
});
|
|
22148
|
+
child.on("close", (code) => {
|
|
22149
|
+
if (settled) return;
|
|
22150
|
+
settled = true;
|
|
22151
|
+
clearTimeout(timer);
|
|
22152
|
+
resolve6({
|
|
22153
|
+
stdout,
|
|
22154
|
+
stderr,
|
|
22155
|
+
code: code ?? 0
|
|
22156
|
+
});
|
|
22157
|
+
});
|
|
22158
|
+
});
|
|
22159
|
+
}
|
|
22160
|
+
__name(runGit, "runGit");
|
|
22161
|
+
function classifyGitError(err, stderr = "") {
|
|
22162
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
22163
|
+
return "no-git";
|
|
22164
|
+
}
|
|
22165
|
+
const s = stderr.toLowerCase();
|
|
22166
|
+
if (s.includes("not a git repository")) return "no-repo";
|
|
22167
|
+
if (s.includes("head detached") || s.includes("does not point to a branch") || s.includes("src refspec head does not match")) return "detached-head";
|
|
22168
|
+
if (s.includes("pre-commit hook") || s.includes("hook failed")) return "hook-failed";
|
|
22169
|
+
if (s.includes("already exists")) return "tag-exists";
|
|
22170
|
+
if (s.includes("nothing to commit")) return "nothing-to-commit";
|
|
22171
|
+
if (s.includes("authentication failed") || s.includes("could not read username")) return "auth";
|
|
22172
|
+
return "unknown";
|
|
22173
|
+
}
|
|
22174
|
+
__name(classifyGitError, "classifyGitError");
|
|
22175
|
+
async function isGitAvailable() {
|
|
22176
|
+
try {
|
|
22177
|
+
const { code } = await runGit([
|
|
22178
|
+
"--version"
|
|
22179
|
+
]);
|
|
22180
|
+
return code === 0;
|
|
22181
|
+
} catch {
|
|
22182
|
+
return false;
|
|
22183
|
+
}
|
|
22184
|
+
}
|
|
22185
|
+
__name(isGitAvailable, "isGitAvailable");
|
|
22186
|
+
async function isInsideGitRepo(cwd) {
|
|
22187
|
+
try {
|
|
22188
|
+
const { code } = await runGit([
|
|
22189
|
+
"rev-parse",
|
|
22190
|
+
"--is-inside-work-tree"
|
|
22191
|
+
], {
|
|
22192
|
+
cwd
|
|
22193
|
+
});
|
|
22194
|
+
return code === 0;
|
|
22195
|
+
} catch {
|
|
22196
|
+
return false;
|
|
22197
|
+
}
|
|
22198
|
+
}
|
|
22199
|
+
__name(isInsideGitRepo, "isInsideGitRepo");
|
|
22200
|
+
async function gitUserConfigured(cwd) {
|
|
22201
|
+
const readOne = /* @__PURE__ */ __name(async (key) => {
|
|
22202
|
+
try {
|
|
22203
|
+
const { stdout, code } = await runGit([
|
|
22204
|
+
"config",
|
|
22205
|
+
"--get",
|
|
22206
|
+
key
|
|
22207
|
+
], {
|
|
22208
|
+
cwd
|
|
22209
|
+
});
|
|
22210
|
+
if (code !== 0) return void 0;
|
|
22211
|
+
const trimmed = stdout.trim();
|
|
22212
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
22213
|
+
} catch {
|
|
22214
|
+
return void 0;
|
|
22215
|
+
}
|
|
22216
|
+
}, "readOne");
|
|
22217
|
+
const [name, email] = await Promise.all([
|
|
22218
|
+
readOne("user.name"),
|
|
22219
|
+
readOne("user.email")
|
|
22220
|
+
]);
|
|
22221
|
+
return {
|
|
22222
|
+
name,
|
|
22223
|
+
email
|
|
22224
|
+
};
|
|
22225
|
+
}
|
|
22226
|
+
__name(gitUserConfigured, "gitUserConfigured");
|
|
22227
|
+
async function getCommitSha(cwd) {
|
|
22228
|
+
try {
|
|
22229
|
+
const { stdout, code } = await runGit([
|
|
22230
|
+
"rev-parse",
|
|
22231
|
+
"HEAD"
|
|
22232
|
+
], {
|
|
22233
|
+
cwd
|
|
22234
|
+
});
|
|
22235
|
+
if (code !== 0) return null;
|
|
22236
|
+
const sha = stdout.trim();
|
|
22237
|
+
return sha.length > 0 ? sha : null;
|
|
22238
|
+
} catch {
|
|
22239
|
+
return null;
|
|
22240
|
+
}
|
|
22241
|
+
}
|
|
22242
|
+
__name(getCommitSha, "getCommitSha");
|
|
22243
|
+
|
|
22244
|
+
// src/utils/try-git-commit.ts
|
|
22245
|
+
init_analytics();
|
|
22246
|
+
|
|
22247
|
+
// src/utils/auto-push-hook.ts
|
|
22248
|
+
init_cli();
|
|
22249
|
+
|
|
22250
|
+
// src/utils/git-auth-store.ts
|
|
22251
|
+
init_constants();
|
|
22252
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
22253
|
+
import { dirname as dirname6 } from "path";
|
|
22254
|
+
function readStore() {
|
|
22255
|
+
try {
|
|
22256
|
+
const raw = readFileSync10(AUTH_STORAGE_FILE, "utf8");
|
|
22257
|
+
const parsed = JSON.parse(raw);
|
|
22258
|
+
return {
|
|
22259
|
+
providers: parsed.providers ?? {}
|
|
22260
|
+
};
|
|
22261
|
+
} catch {
|
|
22262
|
+
return {
|
|
22263
|
+
providers: {}
|
|
22264
|
+
};
|
|
22265
|
+
}
|
|
22266
|
+
}
|
|
22267
|
+
__name(readStore, "readStore");
|
|
22268
|
+
function writeStore(store) {
|
|
22269
|
+
mkdirSync7(dirname6(AUTH_STORAGE_FILE), {
|
|
22270
|
+
recursive: true
|
|
22271
|
+
});
|
|
22272
|
+
writeFileSync7(AUTH_STORAGE_FILE, JSON.stringify(store, null, 2), {
|
|
22273
|
+
mode: 384
|
|
22274
|
+
});
|
|
22275
|
+
}
|
|
22276
|
+
__name(writeStore, "writeStore");
|
|
22277
|
+
async function saveAuth(provider, record) {
|
|
22278
|
+
const store = readStore();
|
|
22279
|
+
store.providers[provider] = record;
|
|
22280
|
+
writeStore(store);
|
|
22281
|
+
}
|
|
22282
|
+
__name(saveAuth, "saveAuth");
|
|
22283
|
+
async function getToken2(provider) {
|
|
22284
|
+
return readStore().providers[provider]?.token ?? null;
|
|
22285
|
+
}
|
|
22286
|
+
__name(getToken2, "getToken");
|
|
22287
|
+
async function getStatus(provider) {
|
|
22288
|
+
const record = readStore().providers[provider];
|
|
22289
|
+
if (!record) return {
|
|
22290
|
+
linked: false
|
|
22291
|
+
};
|
|
22292
|
+
return {
|
|
22293
|
+
linked: true,
|
|
22294
|
+
username: record.username,
|
|
22295
|
+
scopes: record.scopes,
|
|
22296
|
+
linkedAt: record.linkedAt
|
|
22297
|
+
};
|
|
22298
|
+
}
|
|
22299
|
+
__name(getStatus, "getStatus");
|
|
22300
|
+
async function clearAuth(provider) {
|
|
22301
|
+
const store = readStore();
|
|
22302
|
+
delete store.providers[provider];
|
|
22303
|
+
writeStore(store);
|
|
22304
|
+
}
|
|
22305
|
+
__name(clearAuth, "clearAuth");
|
|
22306
|
+
|
|
22307
|
+
// src/utils/git-providers/github-provider.ts
|
|
22308
|
+
import { createServer } from "http";
|
|
22309
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
22310
|
+
import { request } from "undici";
|
|
22311
|
+
import open from "open";
|
|
22312
|
+
|
|
22313
|
+
// src/config/git-providers.constants.ts
|
|
22314
|
+
var GITHUB_CLIENT_ID = "Ov23lipe1jgBlCsoi9OO";
|
|
22315
|
+
var GITHUB_OAUTH_BASE_URL = "https://github.com";
|
|
22316
|
+
var GITHUB_API_BASE_URL = "https://api.github.com";
|
|
22317
|
+
var GITHUB_OAUTH_SCOPE = "repo";
|
|
22318
|
+
var LOOPBACK_PORT_MIN = 49152;
|
|
22319
|
+
var LOOPBACK_PORT_MAX = 65535;
|
|
22320
|
+
var LOOPBACK_PORT_ATTEMPTS = 3;
|
|
22321
|
+
var LOOPBACK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
22322
|
+
var DEVICE_FLOW_DEFAULT_INTERVAL_MS = 5e3;
|
|
22323
|
+
var DEVICE_FLOW_SLOW_DOWN_INCREMENT_MS = 5e3;
|
|
22324
|
+
var PUSH_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
22325
|
+
|
|
22326
|
+
// src/utils/pkce.ts
|
|
22327
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
22328
|
+
function generateCodeVerifier() {
|
|
22329
|
+
return base64url(randomBytes(64));
|
|
22330
|
+
}
|
|
22331
|
+
__name(generateCodeVerifier, "generateCodeVerifier");
|
|
22332
|
+
function computeCodeChallenge(verifier) {
|
|
22333
|
+
return base64url(createHash2("sha256").update(verifier).digest());
|
|
22334
|
+
}
|
|
22335
|
+
__name(computeCodeChallenge, "computeCodeChallenge");
|
|
22336
|
+
function base64url(buf) {
|
|
22337
|
+
return buf.toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
22338
|
+
}
|
|
22339
|
+
__name(base64url, "base64url");
|
|
22340
|
+
|
|
22341
|
+
// src/interfaces/git-providers.ts
|
|
22342
|
+
var GitPushError = class extends Error {
|
|
22343
|
+
static {
|
|
22344
|
+
__name(this, "GitPushError");
|
|
22345
|
+
}
|
|
22346
|
+
status;
|
|
22347
|
+
scrubbedStderr;
|
|
22348
|
+
constructor(message, status, scrubbedStderr) {
|
|
22349
|
+
super(message), this.status = status, this.scrubbedStderr = scrubbedStderr;
|
|
22350
|
+
this.name = "GitPushError";
|
|
22351
|
+
}
|
|
22352
|
+
};
|
|
22353
|
+
|
|
22354
|
+
// src/utils/git-providers/github-provider.ts
|
|
22355
|
+
init_cli();
|
|
22356
|
+
|
|
22357
|
+
// src/utils/scrub-token.ts
|
|
22358
|
+
function scrubToken(s, token) {
|
|
22359
|
+
if (!token) return s;
|
|
22360
|
+
return s.split(token).join("<redacted>");
|
|
22361
|
+
}
|
|
22362
|
+
__name(scrubToken, "scrubToken");
|
|
22363
|
+
|
|
22364
|
+
// src/utils/git-providers/github-provider.ts
|
|
22365
|
+
var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>Lua CLI</title>
|
|
22366
|
+
<body style="font-family: system-ui; padding: 4rem; text-align: center;">
|
|
22367
|
+
<h1>✓ Connected</h1>
|
|
22368
|
+
<p>You can close this tab and return to the terminal.</p>
|
|
22369
|
+
</body>`;
|
|
22370
|
+
var GitHubProvider = class {
|
|
22371
|
+
static {
|
|
22372
|
+
__name(this, "GitHubProvider");
|
|
22373
|
+
}
|
|
22374
|
+
name = "github";
|
|
22375
|
+
async login(opts) {
|
|
22376
|
+
const token = opts.device ? await this.deviceFlow() : await this.loopbackFlow();
|
|
22377
|
+
const username = await this.fetchUsername(token);
|
|
22378
|
+
await saveAuth(this.name, {
|
|
22379
|
+
token,
|
|
22380
|
+
username,
|
|
22381
|
+
scopes: [
|
|
22382
|
+
GITHUB_OAUTH_SCOPE
|
|
22383
|
+
],
|
|
22384
|
+
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22385
|
+
});
|
|
22386
|
+
return {
|
|
22387
|
+
username
|
|
22388
|
+
};
|
|
22389
|
+
}
|
|
22390
|
+
async logout() {
|
|
22391
|
+
await clearAuth(this.name);
|
|
22392
|
+
}
|
|
22393
|
+
async status() {
|
|
22394
|
+
return getStatus(this.name);
|
|
22395
|
+
}
|
|
22396
|
+
async push(opts) {
|
|
22397
|
+
const parsed = parseGitHubHttpsUrl(opts.remoteUrl);
|
|
22398
|
+
const authedUrl = `https://x-access-token:${opts.token}@github.com/${parsed.owner}/${parsed.repo}`;
|
|
22399
|
+
let result;
|
|
22400
|
+
try {
|
|
22401
|
+
result = await runGit([
|
|
22402
|
+
"push",
|
|
22403
|
+
authedUrl,
|
|
22404
|
+
opts.branch
|
|
22405
|
+
], {
|
|
22406
|
+
cwd: opts.cwd,
|
|
22407
|
+
timeout: PUSH_TIMEOUT_MS
|
|
22408
|
+
});
|
|
22409
|
+
} catch (err) {
|
|
22410
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
22411
|
+
const scrubbed2 = scrubToken(raw, opts.token);
|
|
22412
|
+
throw new GitPushError(`git push failed: ${scrubbed2}`, 0, scrubbed2);
|
|
22413
|
+
}
|
|
22414
|
+
if (result.code === 0) return;
|
|
22415
|
+
const scrubbed = scrubToken(result.stderr, opts.token);
|
|
22416
|
+
const status = matchPushStatus(scrubbed);
|
|
22417
|
+
throw new GitPushError(status === 401 ? "GitHub authentication failed (token revoked or insufficient scope)." : status === 403 ? "GitHub rejected the push (403 \u2014 possible SSO enforcement or missing scope)." : "git push failed.", status, scrubbed);
|
|
22418
|
+
}
|
|
22419
|
+
async loopbackFlow() {
|
|
22420
|
+
const verifier = generateCodeVerifier();
|
|
22421
|
+
const challenge = computeCodeChallenge(verifier);
|
|
22422
|
+
const state = randomBytes2(16).toString("hex");
|
|
22423
|
+
const { code, redirectUri } = await this.captureLoopbackCode({
|
|
22424
|
+
state,
|
|
22425
|
+
challenge
|
|
22426
|
+
});
|
|
22427
|
+
return this.exchangeCodeForToken({
|
|
22428
|
+
code,
|
|
22429
|
+
verifier,
|
|
22430
|
+
redirectUri
|
|
22431
|
+
});
|
|
22432
|
+
}
|
|
22433
|
+
async captureLoopbackCode(args2) {
|
|
22434
|
+
let lastErr;
|
|
22435
|
+
for (let i = 0; i < LOOPBACK_PORT_ATTEMPTS; i++) {
|
|
22436
|
+
const port = randomPort();
|
|
22437
|
+
try {
|
|
22438
|
+
return await this.runLoopbackServer(port, args2.state, args2.challenge);
|
|
22439
|
+
} catch (err) {
|
|
22440
|
+
lastErr = err;
|
|
22441
|
+
if (!isPortInUse(err)) throw err;
|
|
22442
|
+
}
|
|
22443
|
+
}
|
|
22444
|
+
throw new Error(`Could not bind a loopback port after ${LOOPBACK_PORT_ATTEMPTS} attempts. Try \`lua git auth github --device\` instead.${lastErr ? ` (${lastErr instanceof Error ? lastErr.message : String(lastErr)})` : ""}`);
|
|
22445
|
+
}
|
|
22446
|
+
runLoopbackServer(port, state, challenge) {
|
|
22447
|
+
return new Promise((resolveOuter, rejectOuter) => {
|
|
22448
|
+
let settled = false;
|
|
22449
|
+
const settle = /* @__PURE__ */ __name((fn) => {
|
|
22450
|
+
if (settled) return;
|
|
22451
|
+
settled = true;
|
|
22452
|
+
fn();
|
|
22453
|
+
}, "settle");
|
|
22454
|
+
const server = createServer((req, res) => {
|
|
22455
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
22456
|
+
if (url.pathname !== "/callback") {
|
|
22457
|
+
res.writeHead(404).end();
|
|
22458
|
+
return;
|
|
22459
|
+
}
|
|
22460
|
+
const code = url.searchParams.get("code");
|
|
22461
|
+
const gotState = url.searchParams.get("state");
|
|
22462
|
+
if (!code || gotState !== state) {
|
|
22463
|
+
res.writeHead(400, {
|
|
22464
|
+
"content-type": "text/plain"
|
|
22465
|
+
}).end("State mismatch.");
|
|
22466
|
+
server.close();
|
|
22467
|
+
settle(() => {
|
|
22468
|
+
clearTimeout(timeout);
|
|
22469
|
+
rejectOuter(new Error("OAuth callback state mismatch (possible CSRF). Re-run the command."));
|
|
22470
|
+
});
|
|
22471
|
+
return;
|
|
22472
|
+
}
|
|
22473
|
+
res.writeHead(200, {
|
|
22474
|
+
"content-type": "text/html"
|
|
22475
|
+
}).end(SUCCESS_HTML);
|
|
22476
|
+
server.close();
|
|
22477
|
+
settle(() => {
|
|
22478
|
+
clearTimeout(timeout);
|
|
22479
|
+
resolveOuter({
|
|
22480
|
+
code,
|
|
22481
|
+
redirectUri
|
|
22482
|
+
});
|
|
22483
|
+
});
|
|
22484
|
+
});
|
|
22485
|
+
const timeout = setTimeout(() => {
|
|
22486
|
+
server.close();
|
|
22487
|
+
settle(() => rejectOuter(new Error("OAuth flow timed out (5 min). Re-run `lua git auth github`.")));
|
|
22488
|
+
}, LOOPBACK_TIMEOUT_MS);
|
|
22489
|
+
timeout.unref();
|
|
22490
|
+
server.on("error", (err) => {
|
|
22491
|
+
server.close();
|
|
22492
|
+
settle(() => {
|
|
22493
|
+
clearTimeout(timeout);
|
|
22494
|
+
rejectOuter(err);
|
|
22495
|
+
});
|
|
22496
|
+
});
|
|
22497
|
+
let redirectUri = "";
|
|
22498
|
+
server.listen(port, "127.0.0.1", () => {
|
|
22499
|
+
const actualPort = server.address().port;
|
|
22500
|
+
redirectUri = `http://127.0.0.1:${actualPort}/callback`;
|
|
22501
|
+
const authUrl = new URL(`${GITHUB_OAUTH_BASE_URL}/login/oauth/authorize`);
|
|
22502
|
+
authUrl.searchParams.set("client_id", GITHUB_CLIENT_ID);
|
|
22503
|
+
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
22504
|
+
authUrl.searchParams.set("scope", GITHUB_OAUTH_SCOPE);
|
|
22505
|
+
authUrl.searchParams.set("code_challenge", challenge);
|
|
22506
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
22507
|
+
authUrl.searchParams.set("state", state);
|
|
22508
|
+
open(authUrl.toString()).catch(() => {
|
|
22509
|
+
server.close();
|
|
22510
|
+
settle(() => {
|
|
22511
|
+
clearTimeout(timeout);
|
|
22512
|
+
rejectOuter(new Error("Couldn't open a browser \u2014 try `lua git auth github --device` for the device flow."));
|
|
22513
|
+
});
|
|
22514
|
+
});
|
|
22515
|
+
});
|
|
22516
|
+
});
|
|
22517
|
+
}
|
|
22518
|
+
async exchangeCodeForToken(args2) {
|
|
22519
|
+
const res = await request(`${GITHUB_OAUTH_BASE_URL}/login/oauth/access_token`, {
|
|
22520
|
+
method: "POST",
|
|
22521
|
+
headers: {
|
|
22522
|
+
accept: "application/json",
|
|
22523
|
+
"content-type": "application/json"
|
|
22524
|
+
},
|
|
22525
|
+
body: JSON.stringify({
|
|
22526
|
+
client_id: GITHUB_CLIENT_ID,
|
|
22527
|
+
code: args2.code,
|
|
22528
|
+
code_verifier: args2.verifier,
|
|
22529
|
+
redirect_uri: args2.redirectUri,
|
|
22530
|
+
grant_type: "authorization_code"
|
|
22531
|
+
})
|
|
22532
|
+
});
|
|
22533
|
+
const body = await res.body.json();
|
|
22534
|
+
if (!body.access_token) {
|
|
22535
|
+
throw new Error(`GitHub rejected the token exchange: ${body.error ?? "unknown error"}`);
|
|
22536
|
+
}
|
|
22537
|
+
return body.access_token;
|
|
22538
|
+
}
|
|
22539
|
+
async fetchUsername(token) {
|
|
22540
|
+
const res = await request(`${GITHUB_API_BASE_URL}/user`, {
|
|
22541
|
+
method: "GET",
|
|
22542
|
+
headers: {
|
|
22543
|
+
accept: "application/vnd.github+json",
|
|
22544
|
+
authorization: `Bearer ${token}`,
|
|
22545
|
+
"user-agent": "lua-cli"
|
|
22546
|
+
}
|
|
22547
|
+
});
|
|
22548
|
+
const body = await res.body.json();
|
|
22549
|
+
if (!body.login) throw new Error("Could not read GitHub username after auth.");
|
|
22550
|
+
return body.login;
|
|
22551
|
+
}
|
|
22552
|
+
async deviceFlow() {
|
|
22553
|
+
const codeRes = await request(`${GITHUB_OAUTH_BASE_URL}/login/device/code`, {
|
|
22554
|
+
method: "POST",
|
|
22555
|
+
headers: {
|
|
22556
|
+
accept: "application/json",
|
|
22557
|
+
"content-type": "application/json"
|
|
22558
|
+
},
|
|
22559
|
+
body: JSON.stringify({
|
|
22560
|
+
client_id: GITHUB_CLIENT_ID,
|
|
22561
|
+
scope: GITHUB_OAUTH_SCOPE
|
|
22562
|
+
})
|
|
22563
|
+
});
|
|
22564
|
+
const codeBody = await codeRes.body.json();
|
|
22565
|
+
if (!codeBody.device_code || !codeBody.user_code || !codeBody.verification_uri) {
|
|
22566
|
+
throw new Error(`GitHub rejected device-code request: ${codeBody.error ?? "unknown error"}`);
|
|
22567
|
+
}
|
|
22568
|
+
writeInfo(`Open ${codeBody.verification_uri} and enter the code: ${codeBody.user_code}`);
|
|
22569
|
+
let intervalMs = codeBody.interval != null ? codeBody.interval * 1e3 : DEVICE_FLOW_DEFAULT_INTERVAL_MS;
|
|
22570
|
+
const deadline = Date.now() + (codeBody.expires_in ?? 900) * 1e3;
|
|
22571
|
+
while (Date.now() < deadline) {
|
|
22572
|
+
await sleep(intervalMs);
|
|
22573
|
+
if (Date.now() >= deadline) break;
|
|
22574
|
+
const pollRes = await request(`${GITHUB_OAUTH_BASE_URL}/login/oauth/access_token`, {
|
|
22575
|
+
method: "POST",
|
|
22576
|
+
headers: {
|
|
22577
|
+
accept: "application/json",
|
|
22578
|
+
"content-type": "application/json"
|
|
22579
|
+
},
|
|
22580
|
+
body: JSON.stringify({
|
|
22581
|
+
client_id: GITHUB_CLIENT_ID,
|
|
22582
|
+
device_code: codeBody.device_code,
|
|
22583
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
22584
|
+
})
|
|
22585
|
+
});
|
|
22586
|
+
const body = await pollRes.body.json();
|
|
22587
|
+
if (body.access_token) return body.access_token;
|
|
22588
|
+
if (body.error === "authorization_pending") continue;
|
|
22589
|
+
if (body.error === "slow_down") {
|
|
22590
|
+
intervalMs += DEVICE_FLOW_SLOW_DOWN_INCREMENT_MS;
|
|
22591
|
+
continue;
|
|
22592
|
+
}
|
|
22593
|
+
if (body.error === "expired_token") {
|
|
22594
|
+
throw new Error("Device code expired. Re-run `lua git auth github --device`.");
|
|
22595
|
+
}
|
|
22596
|
+
throw new Error(`GitHub device-flow error: ${body.error ?? "unknown"}`);
|
|
22597
|
+
}
|
|
22598
|
+
throw new Error("Device flow timed out. Re-run `lua git auth github --device`.");
|
|
22599
|
+
}
|
|
22600
|
+
};
|
|
22601
|
+
function randomPort() {
|
|
22602
|
+
const span = LOOPBACK_PORT_MAX - LOOPBACK_PORT_MIN + 1;
|
|
22603
|
+
return LOOPBACK_PORT_MIN + Math.floor(Math.random() * span);
|
|
22604
|
+
}
|
|
22605
|
+
__name(randomPort, "randomPort");
|
|
22606
|
+
function isPortInUse(err) {
|
|
22607
|
+
return !!err && typeof err === "object" && "code" in err && err.code === "EADDRINUSE";
|
|
22608
|
+
}
|
|
22609
|
+
__name(isPortInUse, "isPortInUse");
|
|
22610
|
+
function sleep(ms) {
|
|
22611
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
22612
|
+
}
|
|
22613
|
+
__name(sleep, "sleep");
|
|
22614
|
+
function isGitHubUrl(url) {
|
|
22615
|
+
try {
|
|
22616
|
+
const u = new URL(url);
|
|
22617
|
+
return u.protocol === "https:" && u.hostname === "github.com";
|
|
22618
|
+
} catch {
|
|
22619
|
+
return false;
|
|
22620
|
+
}
|
|
22621
|
+
}
|
|
22622
|
+
__name(isGitHubUrl, "isGitHubUrl");
|
|
22623
|
+
function parseGitHubHttpsUrl(url) {
|
|
22624
|
+
let u;
|
|
22625
|
+
try {
|
|
22626
|
+
u = new URL(url);
|
|
22627
|
+
} catch {
|
|
22628
|
+
throw new Error(`Not a parseable URL: ${url}. v1 only supports https GitHub remotes.`);
|
|
22629
|
+
}
|
|
22630
|
+
if (u.protocol !== "https:" || u.hostname !== "github.com") {
|
|
22631
|
+
throw new Error(`Not a GitHub HTTPS remote: ${url}. v1 only supports https://github.com/...`);
|
|
22632
|
+
}
|
|
22633
|
+
const parts = u.pathname.replace(/^\/+/, "").replace(/\.git$/, "").split("/");
|
|
22634
|
+
if (parts.length < 2 || !parts[0] || !parts[1]) {
|
|
22635
|
+
throw new Error(`Unexpected GitHub URL shape: ${url}`);
|
|
22636
|
+
}
|
|
22637
|
+
return {
|
|
22638
|
+
owner: parts[0],
|
|
22639
|
+
repo: parts[1]
|
|
22640
|
+
};
|
|
22641
|
+
}
|
|
22642
|
+
__name(parseGitHubHttpsUrl, "parseGitHubHttpsUrl");
|
|
22643
|
+
function matchPushStatus(stderr) {
|
|
22644
|
+
const s = stderr.toLowerCase();
|
|
22645
|
+
if (s.includes("401") || s.includes("authentication failed")) return 401;
|
|
22646
|
+
if (s.includes("403") || s.includes("permission to") || s.includes("forbidden")) return 403;
|
|
22647
|
+
return 0;
|
|
22648
|
+
}
|
|
22649
|
+
__name(matchPushStatus, "matchPushStatus");
|
|
22650
|
+
|
|
22651
|
+
// src/utils/auto-push-hook.ts
|
|
22652
|
+
init_analytics();
|
|
22653
|
+
var warned = /* @__PURE__ */ new Set();
|
|
22654
|
+
function warnOnce(key, message) {
|
|
22655
|
+
if (warned.has(key)) return;
|
|
22656
|
+
warned.add(key);
|
|
22657
|
+
writeInfo(`\u26A0\uFE0F ${message}`);
|
|
22658
|
+
}
|
|
22659
|
+
__name(warnOnce, "warnOnce");
|
|
22660
|
+
async function tryGitAutoPush(ctx) {
|
|
22661
|
+
try {
|
|
22662
|
+
if (ctx.config?.git?.autoPush !== true) return;
|
|
22663
|
+
const token = await getToken2("github");
|
|
22664
|
+
if (!token) {
|
|
22665
|
+
warnOnce("missing-github-token", "git.autoPush is enabled but no GitHub auth \u2014 run `lua git auth github`.");
|
|
22666
|
+
return;
|
|
22667
|
+
}
|
|
22668
|
+
const remote = await readRemoteOriginUrl(ctx.cwd);
|
|
22669
|
+
if (!remote) {
|
|
22670
|
+
warnOnce("missing-github-remote", "git.autoPush is enabled but no `remote.origin.url` is set. Skipping push.");
|
|
22671
|
+
return;
|
|
22672
|
+
}
|
|
22673
|
+
if (!isGitHubUrl(remote)) {
|
|
22674
|
+
warnOnce("non-github-remote", `git.autoPush is enabled but origin (${remote}) is not a supported GitHub HTTPS URL. v1 only supports https://github.com/<owner>/<repo> \u2014 skipping push.`);
|
|
22675
|
+
return;
|
|
22676
|
+
}
|
|
22677
|
+
const branch = await readCurrentBranch(ctx.cwd);
|
|
22678
|
+
if (!branch) {
|
|
22679
|
+
warnOnce("detached-head", "Cannot auto-push from a detached HEAD. Skipping push.");
|
|
22680
|
+
return;
|
|
22681
|
+
}
|
|
22682
|
+
const provider = new GitHubProvider();
|
|
22683
|
+
try {
|
|
22684
|
+
await provider.push({
|
|
22685
|
+
token,
|
|
22686
|
+
remoteUrl: remote,
|
|
22687
|
+
branch,
|
|
22688
|
+
cwd: ctx.cwd ?? process.cwd()
|
|
22689
|
+
});
|
|
22690
|
+
writeInfo(`\u2713 Pushed ${ctx.commitSha?.slice(0, 7) ?? ""} to ${remote} (${branch})`);
|
|
22691
|
+
trackEvent("cli_auto_push_succeeded", {
|
|
22692
|
+
action: ctx.action
|
|
22693
|
+
});
|
|
22694
|
+
} catch (err) {
|
|
22695
|
+
const status = err instanceof GitPushError ? err.status : 0;
|
|
22696
|
+
const message = err instanceof GitPushError ? err.scrubbedStderr || err.message : err instanceof Error ? err.message : String(err);
|
|
22697
|
+
if (status === 401) {
|
|
22698
|
+
warnOnce("push-revoked", "GitHub auth was revoked \u2014 run `lua git auth github` to re-link. Commit kept locally.");
|
|
22699
|
+
} else if (status === 403) {
|
|
22700
|
+
warnOnce("push-rejected-403", `GitHub rejected the push: ${message}. Commit kept locally.`);
|
|
22701
|
+
} else {
|
|
22702
|
+
warnOnce("push-failed-other", `Auto-push failed: ${message}. Commit kept locally; run \`git push\` to retry.`);
|
|
22703
|
+
}
|
|
22704
|
+
trackEvent("cli_auto_push_failed", {
|
|
22705
|
+
action: ctx.action,
|
|
22706
|
+
status
|
|
22707
|
+
});
|
|
22708
|
+
}
|
|
22709
|
+
} catch {
|
|
22710
|
+
warnOnce("hook-errored", "Auto-push hook errored unexpectedly. Commit kept locally; run `git push` to retry.");
|
|
22711
|
+
trackEvent("cli_auto_push_failed", {
|
|
22712
|
+
action: ctx.action,
|
|
22713
|
+
status: 0
|
|
22714
|
+
});
|
|
22715
|
+
}
|
|
22716
|
+
}
|
|
22717
|
+
__name(tryGitAutoPush, "tryGitAutoPush");
|
|
22718
|
+
async function readRemoteOriginUrl(cwd) {
|
|
22719
|
+
const res = await runGit([
|
|
22720
|
+
"config",
|
|
22721
|
+
"--get",
|
|
22722
|
+
"remote.origin.url"
|
|
22723
|
+
], {
|
|
22724
|
+
cwd
|
|
22725
|
+
});
|
|
22726
|
+
if (res.code !== 0) return null;
|
|
22727
|
+
const trimmed = res.stdout.trim();
|
|
22728
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
22729
|
+
}
|
|
22730
|
+
__name(readRemoteOriginUrl, "readRemoteOriginUrl");
|
|
22731
|
+
async function readCurrentBranch(cwd) {
|
|
22732
|
+
const res = await runGit([
|
|
22733
|
+
"rev-parse",
|
|
22734
|
+
"--abbrev-ref",
|
|
22735
|
+
"HEAD"
|
|
22736
|
+
], {
|
|
22737
|
+
cwd
|
|
22738
|
+
});
|
|
22739
|
+
if (res.code !== 0) return null;
|
|
22740
|
+
const trimmed = res.stdout.trim();
|
|
22741
|
+
if (!trimmed || trimmed === "HEAD") return null;
|
|
22742
|
+
return trimmed;
|
|
22743
|
+
}
|
|
22744
|
+
__name(readCurrentBranch, "readCurrentBranch");
|
|
22745
|
+
|
|
22746
|
+
// src/utils/try-git-commit.ts
|
|
22747
|
+
async function tryGitCommit(opts) {
|
|
22748
|
+
const config = readYamlConfig();
|
|
22749
|
+
if (!config?.git?.enabled) {
|
|
22750
|
+
return {
|
|
22751
|
+
committed: false
|
|
22752
|
+
};
|
|
22753
|
+
}
|
|
22754
|
+
const runOpts = {
|
|
22755
|
+
cwd: opts.cwd
|
|
22756
|
+
};
|
|
22757
|
+
if (!opts.allowEmpty) {
|
|
22758
|
+
try {
|
|
22759
|
+
const addResult = await runGit([
|
|
22760
|
+
"add",
|
|
22761
|
+
"-A"
|
|
22762
|
+
], runOpts);
|
|
22763
|
+
if (addResult.code !== 0) {
|
|
22764
|
+
const reason = classifyGitError(null, addResult.stderr);
|
|
22765
|
+
reportFailure("add", reason);
|
|
22766
|
+
return {
|
|
22767
|
+
committed: false
|
|
22768
|
+
};
|
|
22769
|
+
}
|
|
22770
|
+
} catch (err) {
|
|
22771
|
+
const reason = classifyGitError(err);
|
|
22772
|
+
reportFailure("add", reason);
|
|
22773
|
+
return {
|
|
22774
|
+
committed: false
|
|
22775
|
+
};
|
|
22776
|
+
}
|
|
22777
|
+
}
|
|
22778
|
+
const commitArgs = [
|
|
22779
|
+
"commit"
|
|
22780
|
+
];
|
|
22781
|
+
if (opts.allowEmpty) commitArgs.push("--allow-empty");
|
|
22782
|
+
commitArgs.push("-m", opts.message);
|
|
22783
|
+
try {
|
|
22784
|
+
const commitResult = await runGit(commitArgs, runOpts);
|
|
22785
|
+
if (commitResult.code !== 0) {
|
|
22786
|
+
const reason = classifyGitError(null, commitResult.stderr);
|
|
22787
|
+
if (reason !== "nothing-to-commit") {
|
|
22788
|
+
reportFailure("commit", reason);
|
|
22789
|
+
}
|
|
22790
|
+
return {
|
|
22791
|
+
committed: false
|
|
22792
|
+
};
|
|
22793
|
+
}
|
|
22794
|
+
} catch (err) {
|
|
22795
|
+
const reason = classifyGitError(err);
|
|
22796
|
+
if (reason !== "nothing-to-commit") {
|
|
22797
|
+
reportFailure("commit", reason);
|
|
22798
|
+
}
|
|
22799
|
+
return {
|
|
22800
|
+
committed: false
|
|
22801
|
+
};
|
|
22802
|
+
}
|
|
22803
|
+
const sha = await getCommitSha(opts.cwd) ?? void 0;
|
|
22804
|
+
let tagged;
|
|
22805
|
+
if (opts.tag) {
|
|
22806
|
+
try {
|
|
22807
|
+
const tagResult = await runGit([
|
|
22808
|
+
"tag",
|
|
22809
|
+
opts.tag
|
|
22810
|
+
], runOpts);
|
|
22811
|
+
if (tagResult.code !== 0) {
|
|
22812
|
+
const reason = classifyGitError(null, tagResult.stderr);
|
|
22813
|
+
reportFailure("tag", reason);
|
|
22814
|
+
tagged = false;
|
|
22815
|
+
} else {
|
|
22816
|
+
tagged = true;
|
|
22817
|
+
}
|
|
22818
|
+
} catch (err) {
|
|
22819
|
+
const reason = classifyGitError(err);
|
|
22820
|
+
reportFailure("tag", reason);
|
|
22821
|
+
tagged = false;
|
|
22822
|
+
}
|
|
22823
|
+
}
|
|
22824
|
+
trackEvent("cli_git_sideeffect_succeeded", {
|
|
22825
|
+
action: opts.action,
|
|
22826
|
+
has_sha: Boolean(sha),
|
|
22827
|
+
has_tag: tagged === true
|
|
22828
|
+
});
|
|
22829
|
+
await tryGitAutoPush({
|
|
22830
|
+
config,
|
|
22831
|
+
cwd: opts.cwd,
|
|
22832
|
+
commitSha: sha,
|
|
22833
|
+
action: opts.action
|
|
22834
|
+
});
|
|
22835
|
+
return {
|
|
22836
|
+
committed: true,
|
|
22837
|
+
sha,
|
|
22838
|
+
tagged
|
|
22839
|
+
};
|
|
22840
|
+
}
|
|
22841
|
+
__name(tryGitCommit, "tryGitCommit");
|
|
22842
|
+
function reportFailure(action, reason) {
|
|
22843
|
+
writeInfo(`\u26A0\uFE0F Git ${action} skipped: ${reason}`);
|
|
22844
|
+
trackEvent("cli_git_sideeffect_failed", {
|
|
22845
|
+
action,
|
|
22846
|
+
reason
|
|
22847
|
+
});
|
|
22848
|
+
}
|
|
22849
|
+
__name(reportFailure, "reportFailure");
|
|
22850
|
+
|
|
22851
|
+
// src/utils/git-messages.ts
|
|
22852
|
+
var GIT_MESSAGES = {
|
|
22853
|
+
pushStaged: "lua: push staged code",
|
|
22854
|
+
createVersion: /* @__PURE__ */ __name((version, message) => {
|
|
22855
|
+
const trimmed = message?.trim();
|
|
22856
|
+
return trimmed ? `lua: create version v${version}: ${trimmed}` : `lua: create version v${version}`;
|
|
22857
|
+
}, "createVersion"),
|
|
22858
|
+
promote: /* @__PURE__ */ __name((version) => `lua: promote v${version} to production`, "promote"),
|
|
22859
|
+
delete: /* @__PURE__ */ __name((version) => `lua: delete version v${version}`, "delete"),
|
|
22860
|
+
pullLatest: "lua: pull latest backup",
|
|
22861
|
+
pullVersion: /* @__PURE__ */ __name((version) => `lua: pull version v${version}`, "pullVersion")
|
|
22862
|
+
};
|
|
22863
|
+
var tagName = /* @__PURE__ */ __name((version) => `lua/v${version}`, "tagName");
|
|
22864
|
+
|
|
21819
22865
|
// src/commands/push-helpers.ts
|
|
21820
22866
|
function pickPushAllNextStepVariant(opts) {
|
|
21821
22867
|
if (opts.pushedSomething && opts.failedCount === 0) return "success";
|
|
@@ -22130,6 +23176,34 @@ async function pushCommand(type, cmdObj) {
|
|
|
22130
23176
|
allowAll: true
|
|
22131
23177
|
});
|
|
22132
23178
|
}
|
|
23179
|
+
const earlyApiKey = loadApiKey();
|
|
23180
|
+
const earlyConfig = readYamlConfig();
|
|
23181
|
+
const earlyAgentId = earlyConfig?.agent?.agentId;
|
|
23182
|
+
const versioning = earlyApiKey && earlyAgentId ? await getVersioningModeCached(earlyApiKey, earlyAgentId) : {
|
|
23183
|
+
enabled: false
|
|
23184
|
+
};
|
|
23185
|
+
const isStageAll = !type && versioning.enabled;
|
|
23186
|
+
const isGranular = Boolean(type && type !== "all" && versioning.enabled);
|
|
23187
|
+
const isAutoDeployNoOp = versioning.enabled && (type === "all" || !type) && Boolean(options.autoDeploy);
|
|
23188
|
+
if (isAutoDeployNoOp) {
|
|
23189
|
+
writeInfo("\u26A0\uFE0F --auto-deploy is ignored when agent versioning is on. Use `lua version promote <version>` after `lua version create`.");
|
|
23190
|
+
options.autoDeploy = false;
|
|
23191
|
+
}
|
|
23192
|
+
if (options.entityName && !type) {
|
|
23193
|
+
console.log("\nUsage:");
|
|
23194
|
+
console.log(" lua push skill --name mySkill --set-version 1.0.5 Push specific skill");
|
|
23195
|
+
console.log(" lua push webhook --name myWebhook --set-version 2.0.0 Push specific webhook");
|
|
23196
|
+
throw new Error("Type must be specified when using the --name option.");
|
|
23197
|
+
}
|
|
23198
|
+
if (isStageAll) {
|
|
23199
|
+
return await pushAllCommand({
|
|
23200
|
+
...options,
|
|
23201
|
+
autoDeployNoopWarned: isAutoDeployNoOp
|
|
23202
|
+
});
|
|
23203
|
+
}
|
|
23204
|
+
if (isGranular) {
|
|
23205
|
+
writeInfo("\u26A0\uFE0F Granular push is deprecated when agent versioning is on. Use `lua push` (no args) to stage all changes, then `lua version create`.");
|
|
23206
|
+
}
|
|
22133
23207
|
if (type === "all") {
|
|
22134
23208
|
if (!options.force) {
|
|
22135
23209
|
console.log("\nUsage:");
|
|
@@ -22137,13 +23211,10 @@ async function pushCommand(type, cmdObj) {
|
|
|
22137
23211
|
console.log(" lua push all --force --auto-deploy Push and deploy all to production");
|
|
22138
23212
|
throw new Error('The "all" type requires the --force flag');
|
|
22139
23213
|
}
|
|
22140
|
-
return await pushAllCommand(
|
|
22141
|
-
|
|
22142
|
-
|
|
22143
|
-
|
|
22144
|
-
console.log(" lua push skill --name mySkill --set-version 1.0.5 Push specific skill");
|
|
22145
|
-
console.log(" lua push webhook --name myWebhook --set-version 2.0.0 Push specific webhook");
|
|
22146
|
-
throw new Error("Type must be specified when using the --name option.");
|
|
23214
|
+
return await pushAllCommand({
|
|
23215
|
+
...options,
|
|
23216
|
+
autoDeployNoopWarned: isAutoDeployNoOp
|
|
23217
|
+
});
|
|
22147
23218
|
}
|
|
22148
23219
|
if (type) {
|
|
22149
23220
|
selectedType = type;
|
|
@@ -22284,13 +23355,17 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
|
|
|
22284
23355
|
bundles_total: versionedPushDeployResult?.bundleStats?.total ?? 0,
|
|
22285
23356
|
bundles_uploaded: versionedPushDeployResult?.bundleStats?.uploaded ?? 0,
|
|
22286
23357
|
bundles_existed: versionedPushDeployResult?.bundleStats?.alreadyExisted ?? 0,
|
|
22287
|
-
//
|
|
22288
|
-
//
|
|
22289
|
-
//
|
|
23358
|
+
// `--include-source` telemetry. Tracks adoption of the flag and whether
|
|
23359
|
+
// the per-skill source attach actually lands cleanly — useful for the
|
|
23360
|
+
// eventual "flip default to ON" decision.
|
|
22290
23361
|
include_source_requested: options.includeSource || false,
|
|
22291
23362
|
include_source_attempted: trackedIncludeSource?.attempted ?? 0,
|
|
22292
23363
|
include_source_attached: trackedIncludeSource?.attached ?? 0,
|
|
22293
|
-
include_source_failed: trackedIncludeSource?.failed ?? 0
|
|
23364
|
+
include_source_failed: trackedIncludeSource?.failed ?? 0,
|
|
23365
|
+
// Agent-versioning telemetry. Driven by isStageAll / isGranular.
|
|
23366
|
+
versioning_mode: versioning.enabled ? isGranular ? "granular" : "all" : "flag-off",
|
|
23367
|
+
granular_deprecation_warned: isGranular,
|
|
23368
|
+
auto_deploy_noop_warned: isAutoDeployNoOp
|
|
22294
23369
|
});
|
|
22295
23370
|
const productionDeployedActually = versionedPushDeployResult?.productionDeploySucceeded === true || !!agentOutcome && !agentOutcome.cancelled && agentOutcome.productionDeployedWithAutoDeploy;
|
|
22296
23371
|
const personaAutoDeployRanAndFailed = selectedType === "agent" && options.autoDeploy && !!agentOutcome && !agentOutcome.cancelled && "personaAutoDeployFailed" in agentOutcome && agentOutcome.personaAutoDeployFailed;
|
|
@@ -22583,6 +23658,7 @@ async function pushAllCommand(options) {
|
|
|
22583
23658
|
}
|
|
22584
23659
|
const apiKey = await requireAuthOrExit(false);
|
|
22585
23660
|
const agentId = config.agent.agentId;
|
|
23661
|
+
const pushAllVersioning = await getVersioningModeCached(apiKey, agentId);
|
|
22586
23662
|
let manifest;
|
|
22587
23663
|
try {
|
|
22588
23664
|
manifest = loadManifest();
|
|
@@ -22879,15 +23955,26 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
|
|
|
22879
23955
|
bundles_total: bundleStatsAggregate.total,
|
|
22880
23956
|
bundles_uploaded: bundleStatsAggregate.uploaded,
|
|
22881
23957
|
bundles_existed: bundleStatsAggregate.existed,
|
|
22882
|
-
//
|
|
23958
|
+
// `--include-source` telemetry, parallel to the single-push
|
|
22883
23959
|
// `cli_push_completed` shape. Without these the `lua push all` path —
|
|
22884
|
-
//
|
|
22885
|
-
//
|
|
23960
|
+
// the highest-volume one for multi-skill agents — would be invisible
|
|
23961
|
+
// to adoption monitoring.
|
|
22886
23962
|
include_source_requested: options.includeSource || false,
|
|
22887
23963
|
include_source_attempted: includeSourceStats?.attempted ?? 0,
|
|
22888
23964
|
include_source_attached: includeSourceStats?.attached ?? 0,
|
|
22889
|
-
include_source_failed: includeSourceStats?.failed ?? 0
|
|
23965
|
+
include_source_failed: includeSourceStats?.failed ?? 0,
|
|
23966
|
+
// Agent-versioning telemetry.
|
|
23967
|
+
versioning_mode: pushAllVersioning.enabled ? "stage-all" : "flag-off",
|
|
23968
|
+
granular_deprecation_warned: false,
|
|
23969
|
+
auto_deploy_noop_warned: options.autoDeployNoopWarned || false
|
|
22890
23970
|
});
|
|
23971
|
+
try {
|
|
23972
|
+
await tryGitCommit({
|
|
23973
|
+
message: GIT_MESSAGES.pushStaged,
|
|
23974
|
+
action: "push-staged"
|
|
23975
|
+
});
|
|
23976
|
+
} catch {
|
|
23977
|
+
}
|
|
22891
23978
|
const pushedSomething = allResults.length > 0 || mcpPushedCount > 0 || !!personaPushResult || backupSuccess;
|
|
22892
23979
|
const variant = pickPushAllNextStepVariant({
|
|
22893
23980
|
pushedSomething,
|
|
@@ -22895,7 +23982,11 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
|
|
|
22895
23982
|
});
|
|
22896
23983
|
if (variant === "success") {
|
|
22897
23984
|
console.log("");
|
|
22898
|
-
if (
|
|
23985
|
+
if (pushAllVersioning.enabled) {
|
|
23986
|
+
if (!options.suppressVersionCreateHint) {
|
|
23987
|
+
writeInfo(`Run \`lua version create [-m "<message>"]\` to snapshot the staged state into a named version, then \`lua version promote <version>\` to deploy.`);
|
|
23988
|
+
}
|
|
23989
|
+
} else if (options.autoDeploy) {
|
|
22899
23990
|
writeHintBlock({
|
|
22900
23991
|
headline: "All deployed. Generate test traffic first, then inspect:",
|
|
22901
23992
|
lines: [
|
|
@@ -23163,6 +24254,10 @@ async function deployCommand(type, cmdObj) {
|
|
|
23163
24254
|
selectedType = answer.type;
|
|
23164
24255
|
}
|
|
23165
24256
|
const apiKey = await requireAuthOrExit();
|
|
24257
|
+
const versioning = await getVersioningModeCached(apiKey, agentId);
|
|
24258
|
+
if (versioning.enabled) {
|
|
24259
|
+
writeInfo("\u26A0\uFE0F `lua deploy` is deprecated when agent versioning is on. Use `lua version promote <version>` for instant, atomic promotion.");
|
|
24260
|
+
}
|
|
23166
24261
|
let personaDeployed = false;
|
|
23167
24262
|
let versionedOutcome = null;
|
|
23168
24263
|
if (selectedType === "persona") {
|
|
@@ -23175,7 +24270,8 @@ async function deployCommand(type, cmdObj) {
|
|
|
23175
24270
|
primitive_type: selectedType,
|
|
23176
24271
|
force_mode: options.force || false,
|
|
23177
24272
|
entity_selected_by_name: !!options.name,
|
|
23178
|
-
version_selected_by_flag: !!options.version
|
|
24273
|
+
version_selected_by_flag: !!options.version,
|
|
24274
|
+
granular_deprecation_warned: versioning.enabled
|
|
23179
24275
|
});
|
|
23180
24276
|
const deployed = selectedType === "persona" ? personaDeployed : versionedOutcome?.deployed ?? false;
|
|
23181
24277
|
const hintPrintedAlready = selectedType !== "persona" && !!versionedOutcome?.hintPrinted;
|
|
@@ -23669,11 +24765,11 @@ init_cli();
|
|
|
23669
24765
|
|
|
23670
24766
|
// src/utils/sandbox-storage.ts
|
|
23671
24767
|
init_constants();
|
|
23672
|
-
import { readFileSync as
|
|
23673
|
-
import { dirname as
|
|
23674
|
-
function
|
|
24768
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
|
|
24769
|
+
import { dirname as dirname7 } from "path";
|
|
24770
|
+
function readStore2() {
|
|
23675
24771
|
try {
|
|
23676
|
-
const raw =
|
|
24772
|
+
const raw = readFileSync11(SANDBOX_STORAGE_FILE, "utf8");
|
|
23677
24773
|
const parsed = JSON.parse(raw);
|
|
23678
24774
|
return {
|
|
23679
24775
|
skills: parsed.skills ?? {},
|
|
@@ -23690,28 +24786,28 @@ function readStore() {
|
|
|
23690
24786
|
};
|
|
23691
24787
|
}
|
|
23692
24788
|
}
|
|
23693
|
-
__name(
|
|
23694
|
-
function
|
|
24789
|
+
__name(readStore2, "readStore");
|
|
24790
|
+
function writeStore2(store) {
|
|
23695
24791
|
try {
|
|
23696
|
-
|
|
24792
|
+
mkdirSync8(dirname7(SANDBOX_STORAGE_FILE), {
|
|
23697
24793
|
recursive: true
|
|
23698
24794
|
});
|
|
23699
|
-
|
|
24795
|
+
writeFileSync8(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
|
|
23700
24796
|
} catch {
|
|
23701
24797
|
}
|
|
23702
24798
|
}
|
|
23703
|
-
__name(
|
|
24799
|
+
__name(writeStore2, "writeStore");
|
|
23704
24800
|
async function getSandboxSkillId(skillName) {
|
|
23705
|
-
const store =
|
|
24801
|
+
const store = readStore2();
|
|
23706
24802
|
const key = skillName ?? "__default__";
|
|
23707
24803
|
return store.skills[key] ?? null;
|
|
23708
24804
|
}
|
|
23709
24805
|
__name(getSandboxSkillId, "getSandboxSkillId");
|
|
23710
24806
|
async function setSandboxSkillId(sandboxId, skillName) {
|
|
23711
|
-
const store =
|
|
24807
|
+
const store = readStore2();
|
|
23712
24808
|
const key = skillName ?? "__default__";
|
|
23713
24809
|
store.skills[key] = sandboxId;
|
|
23714
|
-
|
|
24810
|
+
writeStore2(store);
|
|
23715
24811
|
}
|
|
23716
24812
|
__name(setSandboxSkillId, "setSandboxSkillId");
|
|
23717
24813
|
async function getAllSandboxSkillIds(yamlConfig) {
|
|
@@ -23737,14 +24833,14 @@ async function getAllSandboxSkillIds(yamlConfig) {
|
|
|
23737
24833
|
}
|
|
23738
24834
|
__name(getAllSandboxSkillIds, "getAllSandboxSkillIds");
|
|
23739
24835
|
async function getSandboxPreProcessorId(preprocessorName) {
|
|
23740
|
-
const store =
|
|
24836
|
+
const store = readStore2();
|
|
23741
24837
|
return store.preprocessors[preprocessorName] ?? null;
|
|
23742
24838
|
}
|
|
23743
24839
|
__name(getSandboxPreProcessorId, "getSandboxPreProcessorId");
|
|
23744
24840
|
async function setSandboxPreProcessorId(sandboxId, preprocessorName) {
|
|
23745
|
-
const store =
|
|
24841
|
+
const store = readStore2();
|
|
23746
24842
|
store.preprocessors[preprocessorName] = sandboxId;
|
|
23747
|
-
|
|
24843
|
+
writeStore2(store);
|
|
23748
24844
|
}
|
|
23749
24845
|
__name(setSandboxPreProcessorId, "setSandboxPreProcessorId");
|
|
23750
24846
|
async function getAllSandboxPreProcessorIds(config) {
|
|
@@ -23769,14 +24865,14 @@ async function getAllSandboxPreProcessorIds(config) {
|
|
|
23769
24865
|
}
|
|
23770
24866
|
__name(getAllSandboxPreProcessorIds, "getAllSandboxPreProcessorIds");
|
|
23771
24867
|
async function getSandboxPostProcessorId(postprocessorName) {
|
|
23772
|
-
const store =
|
|
24868
|
+
const store = readStore2();
|
|
23773
24869
|
return store.postprocessors[postprocessorName] ?? null;
|
|
23774
24870
|
}
|
|
23775
24871
|
__name(getSandboxPostProcessorId, "getSandboxPostProcessorId");
|
|
23776
24872
|
async function setSandboxPostProcessorId(sandboxId, postprocessorName) {
|
|
23777
|
-
const store =
|
|
24873
|
+
const store = readStore2();
|
|
23778
24874
|
store.postprocessors[postprocessorName] = sandboxId;
|
|
23779
|
-
|
|
24875
|
+
writeStore2(store);
|
|
23780
24876
|
}
|
|
23781
24877
|
__name(setSandboxPostProcessorId, "setSandboxPostProcessorId");
|
|
23782
24878
|
async function getAllSandboxPostProcessorIds(config) {
|
|
@@ -24200,7 +25296,15 @@ var MIME_TYPES = {
|
|
|
24200
25296
|
".json": "application/json",
|
|
24201
25297
|
// Email
|
|
24202
25298
|
".eml": "message/rfc822",
|
|
24203
|
-
".msg": "application/vnd.ms-outlook"
|
|
25299
|
+
".msg": "application/vnd.ms-outlook",
|
|
25300
|
+
// Audio. lua-core picks the path per provider: types in the provider's
|
|
25301
|
+
// native allowlist go straight to the model, the rest are transcribed by
|
|
25302
|
+
// Deepgram via FileConversionService.
|
|
25303
|
+
".m4a": "audio/mp4",
|
|
25304
|
+
".mp3": "audio/mpeg",
|
|
25305
|
+
".wav": "audio/wav",
|
|
25306
|
+
".ogg": "audio/ogg",
|
|
25307
|
+
".opus": "audio/opus"
|
|
24204
25308
|
};
|
|
24205
25309
|
var IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
24206
25310
|
"image/png",
|
|
@@ -25010,8 +26114,8 @@ init_cli();
|
|
|
25010
26114
|
init_constants();
|
|
25011
26115
|
init_command_utils();
|
|
25012
26116
|
init_developer_api_service();
|
|
25013
|
-
import
|
|
25014
|
-
import
|
|
26117
|
+
import fs16 from "fs";
|
|
26118
|
+
import path18 from "path";
|
|
25015
26119
|
import inquirer10 from "inquirer";
|
|
25016
26120
|
init_analytics();
|
|
25017
26121
|
function resolveEnvironment(env, hasNonInteractiveFlags) {
|
|
@@ -25370,10 +26474,10 @@ function variablesToEnvContent(variables) {
|
|
|
25370
26474
|
}
|
|
25371
26475
|
__name(variablesToEnvContent, "variablesToEnvContent");
|
|
25372
26476
|
function saveSandboxEnvVariables(variables) {
|
|
25373
|
-
const envFilePath =
|
|
26477
|
+
const envFilePath = path18.join(process.cwd(), ".env");
|
|
25374
26478
|
try {
|
|
25375
26479
|
const content = variablesToEnvContent(variables);
|
|
25376
|
-
|
|
26480
|
+
fs16.writeFileSync(envFilePath, content, "utf8");
|
|
25377
26481
|
return true;
|
|
25378
26482
|
} catch (error) {
|
|
25379
26483
|
console.error("\u274C Error saving .env file:", error);
|
|
@@ -27100,7 +28204,7 @@ __name(viewResourceInteractive, "viewResourceInteractive");
|
|
|
27100
28204
|
init_cli();
|
|
27101
28205
|
init_command_utils();
|
|
27102
28206
|
init_analytics();
|
|
27103
|
-
import
|
|
28207
|
+
import open2 from "open";
|
|
27104
28208
|
async function adminCommand() {
|
|
27105
28209
|
return withErrorHandling(async () => {
|
|
27106
28210
|
writeProgress("Opening Lua Admin Dashboard...");
|
|
@@ -27108,7 +28212,7 @@ async function adminCommand() {
|
|
|
27108
28212
|
showProgress: false
|
|
27109
28213
|
});
|
|
27110
28214
|
const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
|
|
27111
|
-
await
|
|
28215
|
+
await open2(adminUrl);
|
|
27112
28216
|
writeSuccess("Lua Admin Dashboard opened in your browser");
|
|
27113
28217
|
console.log(`
|
|
27114
28218
|
Dashboard URL: https://admin.heylua.ai`);
|
|
@@ -27124,7 +28228,7 @@ __name(adminCommand, "adminCommand");
|
|
|
27124
28228
|
init_cli();
|
|
27125
28229
|
init_command_utils();
|
|
27126
28230
|
init_analytics();
|
|
27127
|
-
import
|
|
28231
|
+
import open3 from "open";
|
|
27128
28232
|
async function evalsCommand() {
|
|
27129
28233
|
return withErrorHandling(async () => {
|
|
27130
28234
|
writeProgress("Opening Lua Evaluations Dashboard...");
|
|
@@ -27132,7 +28236,7 @@ async function evalsCommand() {
|
|
|
27132
28236
|
showProgress: false
|
|
27133
28237
|
});
|
|
27134
28238
|
const evalsUrl = `https://evals.heylua.ai?apiKey=${apiKey}&agentID=${agentId}`;
|
|
27135
|
-
await
|
|
28239
|
+
await open3(evalsUrl);
|
|
27136
28240
|
writeSuccess("Lua Evaluations Dashboard opened in your browser");
|
|
27137
28241
|
console.log(`
|
|
27138
28242
|
Dashboard URL: https://evals.heylua.ai`);
|
|
@@ -27146,12 +28250,12 @@ __name(evalsCommand, "evalsCommand");
|
|
|
27146
28250
|
// src/commands/docs.ts
|
|
27147
28251
|
init_cli();
|
|
27148
28252
|
init_analytics();
|
|
27149
|
-
import
|
|
28253
|
+
import open4 from "open";
|
|
27150
28254
|
async function docsCommand() {
|
|
27151
28255
|
return withErrorHandling(async () => {
|
|
27152
28256
|
writeProgress("Opening Lua Documentation...");
|
|
27153
28257
|
const docsUrl = "https://docs.heylua.ai";
|
|
27154
|
-
await
|
|
28258
|
+
await open4(docsUrl);
|
|
27155
28259
|
writeSuccess("Lua Documentation opened in your browser");
|
|
27156
28260
|
console.log(`
|
|
27157
28261
|
Documentation: ${docsUrl}
|
|
@@ -27165,7 +28269,7 @@ __name(docsCommand, "docsCommand");
|
|
|
27165
28269
|
init_cli();
|
|
27166
28270
|
init_command_utils();
|
|
27167
28271
|
import inquirer12 from "inquirer";
|
|
27168
|
-
import
|
|
28272
|
+
import open5 from "open";
|
|
27169
28273
|
|
|
27170
28274
|
// src/api/channels.api.service.ts
|
|
27171
28275
|
init_http_client();
|
|
@@ -27734,7 +28838,7 @@ async function openAdminDashboard(apiKey, config) {
|
|
|
27734
28838
|
throw new Error("No orgId found in lua.skill.yaml. Please ensure your configuration is valid.");
|
|
27735
28839
|
}
|
|
27736
28840
|
const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
|
|
27737
|
-
await
|
|
28841
|
+
await open5(adminUrl);
|
|
27738
28842
|
writeSuccess("\u2705 Lua Admin Dashboard opened in your browser");
|
|
27739
28843
|
console.log(`
|
|
27740
28844
|
Dashboard URL: https://admin.heylua.ai`);
|
|
@@ -29865,7 +30969,7 @@ init_auth();
|
|
|
29865
30969
|
init_auth_api_service();
|
|
29866
30970
|
init_files();
|
|
29867
30971
|
init_artifact_loader();
|
|
29868
|
-
import { existsSync as existsSync7, readFileSync as
|
|
30972
|
+
import { existsSync as existsSync7, readFileSync as readFileSync12 } from "fs";
|
|
29869
30973
|
import { join as join6 } from "path";
|
|
29870
30974
|
import { performance } from "perf_hooks";
|
|
29871
30975
|
import * as os from "os";
|
|
@@ -30216,7 +31320,7 @@ function gatherTelemetry() {
|
|
|
30216
31320
|
} else {
|
|
30217
31321
|
try {
|
|
30218
31322
|
if (existsSync7(TELEMETRY_FILE)) {
|
|
30219
|
-
const raw =
|
|
31323
|
+
const raw = readFileSync12(TELEMETRY_FILE, "utf8");
|
|
30220
31324
|
const cfg = JSON.parse(raw);
|
|
30221
31325
|
if (typeof cfg.enabled === "boolean") enabled = cfg.enabled;
|
|
30222
31326
|
}
|
|
@@ -36518,7 +37622,7 @@ init_cli();
|
|
|
36518
37622
|
init_constants();
|
|
36519
37623
|
import http from "http";
|
|
36520
37624
|
import { URL as URL2 } from "url";
|
|
36521
|
-
import
|
|
37625
|
+
import open6 from "open";
|
|
36522
37626
|
init_command_utils();
|
|
36523
37627
|
init_developer_api_service();
|
|
36524
37628
|
|
|
@@ -37541,7 +38645,7 @@ Integration: ${selectedIntegration.name}`);
|
|
|
37541
38645
|
`);
|
|
37542
38646
|
const callbackPromise = startCallbackServer(3e5);
|
|
37543
38647
|
try {
|
|
37544
|
-
await
|
|
38648
|
+
await open6(authUrl);
|
|
37545
38649
|
writeInfo("\u{1F310} Browser opened - please complete the authorization");
|
|
37546
38650
|
} catch (error) {
|
|
37547
38651
|
writeInfo("\u{1F4A1} Could not open browser automatically. Please open the URL above manually.");
|
|
@@ -37972,7 +39076,7 @@ Available scopes for ${selectedIntegration.name}:`);
|
|
|
37972
39076
|
`);
|
|
37973
39077
|
const callbackPromise = startCallbackServer(3e5);
|
|
37974
39078
|
try {
|
|
37975
|
-
await
|
|
39079
|
+
await open6(authUrl);
|
|
37976
39080
|
writeInfo("\u{1F310} Browser opened - please complete the authorization");
|
|
37977
39081
|
} catch (error) {
|
|
37978
39082
|
writeInfo("\u{1F4A1} Could not open browser automatically. Please open the URL above manually.");
|
|
@@ -39397,7 +40501,7 @@ __name(telemetryCommand, "telemetryCommand");
|
|
|
39397
40501
|
init_cli();
|
|
39398
40502
|
init_command_utils();
|
|
39399
40503
|
init_analytics();
|
|
39400
|
-
import { writeFileSync as
|
|
40504
|
+
import { writeFileSync as writeFileSync9, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
|
|
39401
40505
|
import { resolve as resolve4, join as join7 } from "path";
|
|
39402
40506
|
init_artifact_loader();
|
|
39403
40507
|
init_types();
|
|
@@ -39558,7 +40662,7 @@ async function governanceCommand(action) {
|
|
|
39558
40662
|
}
|
|
39559
40663
|
}
|
|
39560
40664
|
const content = generateFile(setup);
|
|
39561
|
-
|
|
40665
|
+
writeFileSync9(filePath, content, "utf-8");
|
|
39562
40666
|
const relativePath = filePath.replace(process.cwd() + "/", "");
|
|
39563
40667
|
writeSuccess(`Created ${relativePath}`);
|
|
39564
40668
|
console.log("");
|
|
@@ -39822,14 +40926,14 @@ init_analytics();
|
|
|
39822
40926
|
init_command_utils();
|
|
39823
40927
|
init_artifact_loader();
|
|
39824
40928
|
init_types();
|
|
39825
|
-
import { spawn as
|
|
40929
|
+
import { spawn as spawn3 } from "child_process";
|
|
39826
40930
|
import { existsSync as existsSync9, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
39827
40931
|
import { join as join8, relative, resolve as resolve5 } from "path";
|
|
39828
40932
|
init_voice_api_service();
|
|
39829
40933
|
init_constants();
|
|
39830
40934
|
|
|
39831
40935
|
// src/commands/voice-terminal.ts
|
|
39832
|
-
import { spawn, spawnSync } from "child_process";
|
|
40936
|
+
import { spawn as spawn2, spawnSync } from "child_process";
|
|
39833
40937
|
import { platform as platform3 } from "os";
|
|
39834
40938
|
import { AudioFrame, AudioSource, AudioStream, LocalAudioTrack, Room, RoomEvent, TrackKind, TrackPublishOptions, TrackSource } from "@livekit/rtc-node";
|
|
39835
40939
|
|
|
@@ -40092,7 +41196,7 @@ function spawnCapture() {
|
|
|
40092
41196
|
String(FRAME_SAMPLES * 2),
|
|
40093
41197
|
"-"
|
|
40094
41198
|
];
|
|
40095
|
-
const proc =
|
|
41199
|
+
const proc = spawn2("sox", args2, {
|
|
40096
41200
|
stdio: [
|
|
40097
41201
|
"ignore",
|
|
40098
41202
|
"pipe",
|
|
@@ -40123,7 +41227,7 @@ function spawnPlayback() {
|
|
|
40123
41227
|
"-",
|
|
40124
41228
|
"-d"
|
|
40125
41229
|
];
|
|
40126
|
-
const proc =
|
|
41230
|
+
const proc = spawn2("sox", args2, {
|
|
40127
41231
|
stdio: [
|
|
40128
41232
|
"pipe",
|
|
40129
41233
|
"ignore",
|
|
@@ -40185,7 +41289,7 @@ __name(pumpRemoteAudioToSpeaker, "pumpRemoteAudioToSpeaker");
|
|
|
40185
41289
|
// src/commands/voice-browser.ts
|
|
40186
41290
|
init_cli();
|
|
40187
41291
|
import http2 from "http";
|
|
40188
|
-
import
|
|
41292
|
+
import open7 from "open";
|
|
40189
41293
|
var SAFETY_TIMEOUT_MS = 60 * 60 * 1e3;
|
|
40190
41294
|
async function runBrowserMode(joinUrl) {
|
|
40191
41295
|
const { wsUrl, token } = parseJoinUrl(joinUrl);
|
|
@@ -40217,7 +41321,7 @@ async function runBrowserMode(joinUrl) {
|
|
|
40217
41321
|
const localUrl = `http://localhost:${port}/`;
|
|
40218
41322
|
writeSuccess(`Voice room ready (opening browser)`);
|
|
40219
41323
|
console.log(` ${localUrl}`);
|
|
40220
|
-
await
|
|
41324
|
+
await open7(localUrl);
|
|
40221
41325
|
await new Promise((resolve6) => {
|
|
40222
41326
|
const safety = setTimeout(() => {
|
|
40223
41327
|
try {
|
|
@@ -40453,7 +41557,7 @@ function buildRunnerArgs(runner, opts) {
|
|
|
40453
41557
|
__name(buildRunnerArgs, "buildRunnerArgs");
|
|
40454
41558
|
async function spawnRunner(runner, args2, cwd) {
|
|
40455
41559
|
return new Promise((resolveExit) => {
|
|
40456
|
-
const proc =
|
|
41560
|
+
const proc = spawn3("npx", [
|
|
40457
41561
|
runner,
|
|
40458
41562
|
...args2
|
|
40459
41563
|
], {
|
|
@@ -40846,6 +41950,12 @@ async function sourceRollbackCommand(opts) {
|
|
|
40846
41950
|
const config = readYamlConfig();
|
|
40847
41951
|
const agentId = config?.agent?.agentId;
|
|
40848
41952
|
if (!agentId) throw new Error("No agentId in lua.skill.yaml. Run `lua init` first.");
|
|
41953
|
+
if (!opts.silent) {
|
|
41954
|
+
const versioning = await getVersioningModeCached(apiKey, agentId);
|
|
41955
|
+
if (versioning.enabled) {
|
|
41956
|
+
writeInfo("\u26A0\uFE0F `lua source rollback` is deprecated when agent versioning is on. Use `lua version promote <version>` \u2014 instant, no re-upload required.");
|
|
41957
|
+
}
|
|
41958
|
+
}
|
|
40849
41959
|
const fetchM = opts.fetchManifest ?? defaultFetchManifest;
|
|
40850
41960
|
const fetchU = opts.fetchUrls ?? defaultFetchUrls;
|
|
40851
41961
|
const dlBlobs = opts.downloadBlobs ?? downloadBlobsParallel;
|
|
@@ -40916,6 +42026,631 @@ function defaultRestore(manifest, blobs, targetDir) {
|
|
|
40916
42026
|
}
|
|
40917
42027
|
__name(defaultRestore, "defaultRestore");
|
|
40918
42028
|
|
|
42029
|
+
// src/commands/version.ts
|
|
42030
|
+
init_cli();
|
|
42031
|
+
init_command_utils();
|
|
42032
|
+
init_analytics();
|
|
42033
|
+
init_files();
|
|
42034
|
+
init_constants();
|
|
42035
|
+
|
|
42036
|
+
// src/utils/parse-version.ts
|
|
42037
|
+
function parseVersion2(arg) {
|
|
42038
|
+
if (!arg) throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
|
|
42039
|
+
const stripped = arg.replace(/^v/i, "");
|
|
42040
|
+
const n = Number.parseInt(stripped, 10);
|
|
42041
|
+
if (!Number.isFinite(n) || n <= 0 || String(n) !== stripped) {
|
|
42042
|
+
throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
|
|
42043
|
+
}
|
|
42044
|
+
return n;
|
|
42045
|
+
}
|
|
42046
|
+
__name(parseVersion2, "parseVersion");
|
|
42047
|
+
|
|
42048
|
+
// src/commands/version.ts
|
|
42049
|
+
async function assertVersioningEnabled(apiKey, agentId) {
|
|
42050
|
+
const { enabled } = await getVersioningModeCached(apiKey, agentId);
|
|
42051
|
+
if (!enabled) {
|
|
42052
|
+
throw new Error("Agent versioning is not enabled for your organization. Contact your admin to enable it, or use `lua push <primitive>` for the legacy flow.");
|
|
42053
|
+
}
|
|
42054
|
+
}
|
|
42055
|
+
__name(assertVersioningEnabled, "assertVersioningEnabled");
|
|
42056
|
+
async function versionCreateCommand(options = {}) {
|
|
42057
|
+
return withErrorHandling(async () => {
|
|
42058
|
+
const { apiKey, agentId } = await initializeCommand();
|
|
42059
|
+
await assertVersioningEnabled(apiKey, agentId);
|
|
42060
|
+
let config = readYamlConfig();
|
|
42061
|
+
let backupVersion = config?.backup?.activeVersion;
|
|
42062
|
+
if (options.autoPush) {
|
|
42063
|
+
await pushAllCommand({
|
|
42064
|
+
force: true,
|
|
42065
|
+
includeSource: true,
|
|
42066
|
+
suppressVersionCreateHint: true
|
|
42067
|
+
});
|
|
42068
|
+
config = readYamlConfig();
|
|
42069
|
+
backupVersion = config?.backup?.activeVersion;
|
|
42070
|
+
if (backupVersion == null) {
|
|
42071
|
+
throw new Error("Backup tracking lost after push. Aborting.");
|
|
42072
|
+
}
|
|
42073
|
+
}
|
|
42074
|
+
if (backupVersion == null) {
|
|
42075
|
+
throw new Error("No backup exists for this agent. Run `lua push` first, or pass `--auto-push` to push and snapshot in one step.");
|
|
42076
|
+
}
|
|
42077
|
+
let message = options.message;
|
|
42078
|
+
const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
42079
|
+
const isCi = isCiModeEnabled();
|
|
42080
|
+
if (!message && isTTY && !isCi) {
|
|
42081
|
+
const answers = await safePrompt([
|
|
42082
|
+
{
|
|
42083
|
+
type: "input",
|
|
42084
|
+
name: "input",
|
|
42085
|
+
message: "Optional description (blank to skip):"
|
|
42086
|
+
}
|
|
42087
|
+
]);
|
|
42088
|
+
message = answers?.input?.trim() || void 0;
|
|
42089
|
+
}
|
|
42090
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
42091
|
+
const result = await api.createVersion({
|
|
42092
|
+
message,
|
|
42093
|
+
sourceManifestVersion: backupVersion,
|
|
42094
|
+
commitHash: options.commitHash
|
|
42095
|
+
});
|
|
42096
|
+
if (!result.success || !result.data) {
|
|
42097
|
+
throw new Error(result.error?.message ?? "Failed to create version");
|
|
42098
|
+
}
|
|
42099
|
+
writeInfo(`\u2713 Created v${result.data.version} (staged). Run \`lua version promote v${result.data.version}\` to deploy.`);
|
|
42100
|
+
try {
|
|
42101
|
+
const gitResult = await tryGitCommit({
|
|
42102
|
+
message: GIT_MESSAGES.createVersion(result.data.version, message),
|
|
42103
|
+
action: "version-create",
|
|
42104
|
+
tag: tagName(result.data.version),
|
|
42105
|
+
allowEmpty: true
|
|
42106
|
+
});
|
|
42107
|
+
if (gitResult.committed && gitResult.sha) {
|
|
42108
|
+
await api.patchCommitHash(result.data.version, gitResult.sha);
|
|
42109
|
+
}
|
|
42110
|
+
} catch {
|
|
42111
|
+
}
|
|
42112
|
+
trackEvent("cli_version_create_completed", {
|
|
42113
|
+
auto_push: Boolean(options.autoPush),
|
|
42114
|
+
has_message: Boolean(message),
|
|
42115
|
+
non_interactive: !isTTY || isCi
|
|
42116
|
+
});
|
|
42117
|
+
}, "version create");
|
|
42118
|
+
}
|
|
42119
|
+
__name(versionCreateCommand, "versionCreateCommand");
|
|
42120
|
+
async function versionListCommand(options = {}) {
|
|
42121
|
+
return withErrorHandling(async () => {
|
|
42122
|
+
const { apiKey, agentId } = await initializeCommand();
|
|
42123
|
+
await assertVersioningEnabled(apiKey, agentId);
|
|
42124
|
+
const query = {};
|
|
42125
|
+
if (options.all) query.all = true;
|
|
42126
|
+
if (options.limit != null) query.limit = options.limit;
|
|
42127
|
+
if (options.status) query.status = options.status;
|
|
42128
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
42129
|
+
const response = await api.listVersions(query);
|
|
42130
|
+
if (!response.success || !response.data) {
|
|
42131
|
+
throw new Error(response.error?.message ?? "Failed to list versions");
|
|
42132
|
+
}
|
|
42133
|
+
const versions = response.data;
|
|
42134
|
+
if (options.json) {
|
|
42135
|
+
console.log(JSON.stringify(versions, null, 2));
|
|
42136
|
+
} else if (versions.length === 0) {
|
|
42137
|
+
writeInfo("(no versions yet \u2014 run `lua version create` to make one)");
|
|
42138
|
+
} else {
|
|
42139
|
+
console.log("VERSION STATUS CREATED BY MESSAGE");
|
|
42140
|
+
for (const v of versions) {
|
|
42141
|
+
const star = v.status === "active" ? "*" : " ";
|
|
42142
|
+
const date = new Date(v.createdAt).toISOString().slice(0, 16).replace("T", " ");
|
|
42143
|
+
const status = v.status.padEnd(11);
|
|
42144
|
+
const by = (v.createdBy || "").slice(0, 10).padEnd(10);
|
|
42145
|
+
const msg = (v.message || "").slice(0, 60);
|
|
42146
|
+
console.log(`v${v.version}${star} ${status} ${date} ${by}${msg}`);
|
|
42147
|
+
}
|
|
42148
|
+
}
|
|
42149
|
+
trackEvent("cli_version_list_completed", {
|
|
42150
|
+
count: versions.length,
|
|
42151
|
+
status_filter: options.status ?? "all"
|
|
42152
|
+
});
|
|
42153
|
+
}, "version list");
|
|
42154
|
+
}
|
|
42155
|
+
__name(versionListCommand, "versionListCommand");
|
|
42156
|
+
async function versionShowCommand(versionArg, options = {}) {
|
|
42157
|
+
return withErrorHandling(async () => {
|
|
42158
|
+
const { apiKey, agentId } = await initializeCommand();
|
|
42159
|
+
await assertVersioningEnabled(apiKey, agentId);
|
|
42160
|
+
const version = parseVersion2(versionArg);
|
|
42161
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
42162
|
+
const response = await api.getVersion(version);
|
|
42163
|
+
if (!response.success || !response.data) {
|
|
42164
|
+
throw new Error(response.error?.message ?? `Version v${version} not found`);
|
|
42165
|
+
}
|
|
42166
|
+
const v = response.data;
|
|
42167
|
+
if (options.json) {
|
|
42168
|
+
console.log(JSON.stringify(v, null, 2));
|
|
42169
|
+
} else {
|
|
42170
|
+
console.log(`Version v${v.version} (${v.status})`);
|
|
42171
|
+
console.log(` Created: ${v.createdAt} by ${v.createdBy}`);
|
|
42172
|
+
if (v.message) console.log(` Message: ${v.message}`);
|
|
42173
|
+
if (v.commitHash) console.log(` Commit: ${v.commitHash}`);
|
|
42174
|
+
console.log(` Model: ${v.snapshot.model}`);
|
|
42175
|
+
console.log(` Snapshot:`);
|
|
42176
|
+
console.log(` Skills: ${v.snapshot.skills.length}`);
|
|
42177
|
+
console.log(` Webhooks: ${v.snapshot.webhooks.length}`);
|
|
42178
|
+
console.log(` Jobs: ${v.snapshot.jobs.length}`);
|
|
42179
|
+
console.log(` Preprocessors: ${v.snapshot.preprocessors.length}`);
|
|
42180
|
+
console.log(` Postprocessors: ${v.snapshot.postprocessors.length}`);
|
|
42181
|
+
console.log(` MCP servers: ${v.snapshot.mcpServers.length}`);
|
|
42182
|
+
console.log(` Persona: ${v.snapshot.persona.versionId || "(none)"}`);
|
|
42183
|
+
}
|
|
42184
|
+
trackEvent("cli_version_show_completed", {
|
|
42185
|
+
version,
|
|
42186
|
+
json: Boolean(options.json)
|
|
42187
|
+
});
|
|
42188
|
+
}, "version show");
|
|
42189
|
+
}
|
|
42190
|
+
__name(versionShowCommand, "versionShowCommand");
|
|
42191
|
+
async function versionDiffCommand(fromArg, toArg, options = {}) {
|
|
42192
|
+
return withErrorHandling(async () => {
|
|
42193
|
+
const { apiKey, agentId } = await initializeCommand();
|
|
42194
|
+
await assertVersioningEnabled(apiKey, agentId);
|
|
42195
|
+
const from = parseVersion2(fromArg);
|
|
42196
|
+
const to = parseVersion2(toArg);
|
|
42197
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
42198
|
+
const response = await api.diffVersions(from, to);
|
|
42199
|
+
if (!response.success || !response.data) {
|
|
42200
|
+
throw new Error(response.error?.message ?? "Failed to diff versions");
|
|
42201
|
+
}
|
|
42202
|
+
const diff = response.data;
|
|
42203
|
+
if (options.json) {
|
|
42204
|
+
console.log(JSON.stringify(diff, null, 2));
|
|
42205
|
+
} else {
|
|
42206
|
+
console.log(`Diff v${from} \u2192 v${to}`);
|
|
42207
|
+
const sections = [
|
|
42208
|
+
{
|
|
42209
|
+
name: "Skills",
|
|
42210
|
+
entry: diff.skills,
|
|
42211
|
+
key: "skillId"
|
|
42212
|
+
},
|
|
42213
|
+
{
|
|
42214
|
+
name: "Webhooks",
|
|
42215
|
+
entry: diff.webhooks,
|
|
42216
|
+
key: "webhookId"
|
|
42217
|
+
},
|
|
42218
|
+
{
|
|
42219
|
+
name: "Jobs",
|
|
42220
|
+
entry: diff.jobs,
|
|
42221
|
+
key: "jobId"
|
|
42222
|
+
},
|
|
42223
|
+
{
|
|
42224
|
+
name: "Preprocessors",
|
|
42225
|
+
entry: diff.preprocessors,
|
|
42226
|
+
key: "id"
|
|
42227
|
+
},
|
|
42228
|
+
{
|
|
42229
|
+
name: "Postprocessors",
|
|
42230
|
+
entry: diff.postprocessors,
|
|
42231
|
+
key: "id"
|
|
42232
|
+
}
|
|
42233
|
+
];
|
|
42234
|
+
for (const { name, entry, key } of sections) {
|
|
42235
|
+
const total = entry.added.length + entry.removed.length + entry.changed.length;
|
|
42236
|
+
if (total === 0) {
|
|
42237
|
+
console.log(`${name}: (no changes)`);
|
|
42238
|
+
continue;
|
|
42239
|
+
}
|
|
42240
|
+
console.log(`${name}:`);
|
|
42241
|
+
for (const a of entry.added) {
|
|
42242
|
+
console.log(` + ${a[key]}@${a.version} (added)`);
|
|
42243
|
+
}
|
|
42244
|
+
for (const r of entry.removed) {
|
|
42245
|
+
console.log(` - ${r[key]}@${r.version} (removed)`);
|
|
42246
|
+
}
|
|
42247
|
+
for (const c of entry.changed) {
|
|
42248
|
+
console.log(` ~ ${c[key]} ${c.from.version} \u2192 ${c.to.version}`);
|
|
42249
|
+
}
|
|
42250
|
+
}
|
|
42251
|
+
const mcp = diff.mcpServers;
|
|
42252
|
+
if (mcp.added.length || mcp.removed.length || mcp.changed.length) {
|
|
42253
|
+
console.log("MCP servers:");
|
|
42254
|
+
mcp.added.forEach((m) => console.log(` + ${m.id} (added)`));
|
|
42255
|
+
mcp.removed.forEach((m) => console.log(` - ${m.id} (removed)`));
|
|
42256
|
+
mcp.changed.forEach((m) => console.log(` ~ ${m.id} (config changed)`));
|
|
42257
|
+
} else {
|
|
42258
|
+
console.log("MCP servers: (no changes)");
|
|
42259
|
+
}
|
|
42260
|
+
console.log(`Persona: ${diff.persona ? `${diff.persona.from} \u2192 ${diff.persona.to}` : "(unchanged)"}`);
|
|
42261
|
+
console.log(`Model: ${diff.model ? `${diff.model.from} \u2192 ${diff.model.to}` : "(unchanged)"}`);
|
|
42262
|
+
}
|
|
42263
|
+
trackEvent("cli_version_diff_completed", {
|
|
42264
|
+
from_to_distance: Math.abs(to - from)
|
|
42265
|
+
});
|
|
42266
|
+
}, "version diff");
|
|
42267
|
+
}
|
|
42268
|
+
__name(versionDiffCommand, "versionDiffCommand");
|
|
42269
|
+
async function versionPromoteCommand(versionArg) {
|
|
42270
|
+
return withErrorHandling(async () => {
|
|
42271
|
+
const { apiKey, agentId } = await initializeCommand();
|
|
42272
|
+
await assertVersioningEnabled(apiKey, agentId);
|
|
42273
|
+
const version = parseVersion2(versionArg);
|
|
42274
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
42275
|
+
const response = await api.promoteVersion(version);
|
|
42276
|
+
if (!response.success) {
|
|
42277
|
+
const errCode = response.error?.code;
|
|
42278
|
+
if (errCode === "VERSION_ALREADY_ACTIVE") {
|
|
42279
|
+
writeInfo(`v${version} is already active. No change.`);
|
|
42280
|
+
return;
|
|
42281
|
+
}
|
|
42282
|
+
throw new Error(response.error?.message ?? `Failed to promote v${version}`);
|
|
42283
|
+
}
|
|
42284
|
+
if (!response.data) {
|
|
42285
|
+
throw new Error(`Promote returned no data`);
|
|
42286
|
+
}
|
|
42287
|
+
const { promoted, previousActive } = response.data;
|
|
42288
|
+
const rollback = previousActive != null && previousActive > promoted;
|
|
42289
|
+
if (previousActive == null) {
|
|
42290
|
+
writeInfo(`\u2713 Promoted v${promoted}. (No previous active version.)`);
|
|
42291
|
+
} else {
|
|
42292
|
+
writeInfo(`\u2713 Promoted v${promoted}. Previous active: v${previousActive}.`);
|
|
42293
|
+
}
|
|
42294
|
+
try {
|
|
42295
|
+
await tryGitCommit({
|
|
42296
|
+
message: GIT_MESSAGES.promote(promoted),
|
|
42297
|
+
action: "version-promote",
|
|
42298
|
+
allowEmpty: true
|
|
42299
|
+
});
|
|
42300
|
+
} catch {
|
|
42301
|
+
}
|
|
42302
|
+
trackEvent("cli_version_promote_completed", {
|
|
42303
|
+
from_version: previousActive,
|
|
42304
|
+
to_version: promoted,
|
|
42305
|
+
rollback
|
|
42306
|
+
});
|
|
42307
|
+
}, "version promote");
|
|
42308
|
+
}
|
|
42309
|
+
__name(versionPromoteCommand, "versionPromoteCommand");
|
|
42310
|
+
async function versionDeleteCommand(versionArg, options = {}) {
|
|
42311
|
+
return withErrorHandling(async () => {
|
|
42312
|
+
const { apiKey, agentId } = await initializeCommand();
|
|
42313
|
+
await assertVersioningEnabled(apiKey, agentId);
|
|
42314
|
+
if (versionArg === "current" || versionArg === "active") {
|
|
42315
|
+
throw new Error(`Cannot delete the active version directly. Use \`lua version promote <other>\` to roll back first, then delete.`);
|
|
42316
|
+
}
|
|
42317
|
+
const version = parseVersion2(versionArg);
|
|
42318
|
+
const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
42319
|
+
const isCi = isCiModeEnabled();
|
|
42320
|
+
if (!options.force && isTTY && !isCi) {
|
|
42321
|
+
const answer = await safePrompt([
|
|
42322
|
+
{
|
|
42323
|
+
type: "confirm",
|
|
42324
|
+
name: "confirm",
|
|
42325
|
+
message: `Delete v${version}?`,
|
|
42326
|
+
default: false
|
|
42327
|
+
}
|
|
42328
|
+
]);
|
|
42329
|
+
if (!answer?.confirm) {
|
|
42330
|
+
writeInfo("Aborted.");
|
|
42331
|
+
return;
|
|
42332
|
+
}
|
|
42333
|
+
}
|
|
42334
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
42335
|
+
const response = await api.deleteVersion(version);
|
|
42336
|
+
if (!response.success) {
|
|
42337
|
+
const errCode = response.error?.code;
|
|
42338
|
+
if (errCode === "CANNOT_DELETE_ACTIVE_VERSION") {
|
|
42339
|
+
throw new Error(`Cannot delete v${version}: it is the active version. Promote a different version first.`);
|
|
42340
|
+
}
|
|
42341
|
+
if (errCode === "CANNOT_DELETE_LAST_VERSION") {
|
|
42342
|
+
throw new Error(`Cannot delete v${version}: it is the only remaining version.`);
|
|
42343
|
+
}
|
|
42344
|
+
throw new Error(response.error?.message ?? `Failed to delete v${version}`);
|
|
42345
|
+
}
|
|
42346
|
+
writeInfo(`\u2713 Deleted v${version}.`);
|
|
42347
|
+
try {
|
|
42348
|
+
await tryGitCommit({
|
|
42349
|
+
message: GIT_MESSAGES.delete(version),
|
|
42350
|
+
action: "version-delete",
|
|
42351
|
+
allowEmpty: true
|
|
42352
|
+
});
|
|
42353
|
+
} catch {
|
|
42354
|
+
}
|
|
42355
|
+
trackEvent("cli_version_delete_completed", {
|
|
42356
|
+
version,
|
|
42357
|
+
force: Boolean(options.force)
|
|
42358
|
+
});
|
|
42359
|
+
}, "version delete");
|
|
42360
|
+
}
|
|
42361
|
+
__name(versionDeleteCommand, "versionDeleteCommand");
|
|
42362
|
+
|
|
42363
|
+
// src/commands/git.ts
|
|
42364
|
+
init_cli();
|
|
42365
|
+
init_files();
|
|
42366
|
+
init_analytics();
|
|
42367
|
+
var NO_YAML_MESSAGE = "No lua.skill.yaml found. Please run this command from a skill directory.";
|
|
42368
|
+
function failConnect(status, banner, errorMessage) {
|
|
42369
|
+
if (banner) writeError(banner);
|
|
42370
|
+
trackEvent("cli_git_connect_completed", {
|
|
42371
|
+
...status,
|
|
42372
|
+
succeeded: false
|
|
42373
|
+
});
|
|
42374
|
+
throw new Error(errorMessage);
|
|
42375
|
+
}
|
|
42376
|
+
__name(failConnect, "failConnect");
|
|
42377
|
+
async function gitConnectCommand() {
|
|
42378
|
+
return withErrorHandling(async () => {
|
|
42379
|
+
const config = readYamlConfig();
|
|
42380
|
+
if (!config) {
|
|
42381
|
+
failConnect({
|
|
42382
|
+
git_available: false,
|
|
42383
|
+
in_repo: false,
|
|
42384
|
+
user_configured: false
|
|
42385
|
+
}, null, NO_YAML_MESSAGE);
|
|
42386
|
+
}
|
|
42387
|
+
if (!await isGitAvailable()) {
|
|
42388
|
+
failConnect({
|
|
42389
|
+
git_available: false,
|
|
42390
|
+
in_repo: false,
|
|
42391
|
+
user_configured: false
|
|
42392
|
+
}, "\u2717 Git binary not found on PATH.\n Fix: install git from https://git-scm.com/downloads, then re-run `lua git connect`.", "git is not installed");
|
|
42393
|
+
}
|
|
42394
|
+
if (!await isInsideGitRepo()) {
|
|
42395
|
+
failConnect({
|
|
42396
|
+
git_available: true,
|
|
42397
|
+
in_repo: false,
|
|
42398
|
+
user_configured: false
|
|
42399
|
+
}, "\u2717 This directory is not inside a git repository.\n Fix: run `git init` in your project root, then re-run `lua git connect`.", "not inside a git repository");
|
|
42400
|
+
}
|
|
42401
|
+
const identity = await gitUserConfigured();
|
|
42402
|
+
if (!identity.email) {
|
|
42403
|
+
failConnect({
|
|
42404
|
+
git_available: true,
|
|
42405
|
+
in_repo: true,
|
|
42406
|
+
user_configured: false
|
|
42407
|
+
}, '\u2717 Git user.email is not configured.\n Fix: git config --global user.email "you@example.com"', "git user.email is not configured");
|
|
42408
|
+
}
|
|
42409
|
+
if (!identity.name) {
|
|
42410
|
+
failConnect({
|
|
42411
|
+
git_available: true,
|
|
42412
|
+
in_repo: true,
|
|
42413
|
+
user_configured: false
|
|
42414
|
+
}, '\u2717 Git user.name is not configured.\n Fix: git config --global user.name "Your Name"', "git user.name is not configured");
|
|
42415
|
+
}
|
|
42416
|
+
config.git = {
|
|
42417
|
+
enabled: true
|
|
42418
|
+
};
|
|
42419
|
+
writeYamlConfig(config);
|
|
42420
|
+
writeSuccess("\u2713 Git integration enabled. Future `lua push`, `lua version *`, and `lua pull` runs will auto-commit.");
|
|
42421
|
+
trackEvent("cli_git_connect_completed", {
|
|
42422
|
+
git_available: true,
|
|
42423
|
+
in_repo: true,
|
|
42424
|
+
user_configured: true,
|
|
42425
|
+
succeeded: true
|
|
42426
|
+
});
|
|
42427
|
+
}, "git connect");
|
|
42428
|
+
}
|
|
42429
|
+
__name(gitConnectCommand, "gitConnectCommand");
|
|
42430
|
+
async function gitDisconnectCommand() {
|
|
42431
|
+
return withErrorHandling(async () => {
|
|
42432
|
+
const config = readYamlConfig();
|
|
42433
|
+
if (!config) {
|
|
42434
|
+
throw new Error(NO_YAML_MESSAGE);
|
|
42435
|
+
}
|
|
42436
|
+
config.git = {
|
|
42437
|
+
enabled: false
|
|
42438
|
+
};
|
|
42439
|
+
writeYamlConfig(config);
|
|
42440
|
+
writeSuccess("\u2713 Git integration disabled. Existing commits and tags are untouched.");
|
|
42441
|
+
trackEvent("cli_git_disconnect_completed", {});
|
|
42442
|
+
}, "git disconnect");
|
|
42443
|
+
}
|
|
42444
|
+
__name(gitDisconnectCommand, "gitDisconnectCommand");
|
|
42445
|
+
async function gitStatusCommand() {
|
|
42446
|
+
return withErrorHandling(async () => {
|
|
42447
|
+
const config = readYamlConfig();
|
|
42448
|
+
const enabled = Boolean(config?.git?.enabled);
|
|
42449
|
+
if (!enabled) {
|
|
42450
|
+
writeInfo("Git integration: disabled (run `lua git connect` to enable)");
|
|
42451
|
+
trackEvent("cli_git_status_completed", {
|
|
42452
|
+
enabled: false
|
|
42453
|
+
});
|
|
42454
|
+
return;
|
|
42455
|
+
}
|
|
42456
|
+
const settled = await Promise.allSettled([
|
|
42457
|
+
runGit([
|
|
42458
|
+
"--version"
|
|
42459
|
+
]),
|
|
42460
|
+
gitUserConfigured(),
|
|
42461
|
+
runGit([
|
|
42462
|
+
"log",
|
|
42463
|
+
"--grep",
|
|
42464
|
+
"^lua: ",
|
|
42465
|
+
"-n",
|
|
42466
|
+
"1",
|
|
42467
|
+
"--format=%h %s"
|
|
42468
|
+
]),
|
|
42469
|
+
// tryGitCommit creates lightweight tags (`git tag <name>`), which have
|
|
42470
|
+
// no taggerdate field. creatordate works for both flavors and returns
|
|
42471
|
+
// the committerdate of the referenced commit — chronological order
|
|
42472
|
+
// matches our pattern of tagging immediately after the commit.
|
|
42473
|
+
runGit([
|
|
42474
|
+
"for-each-ref",
|
|
42475
|
+
"--sort=-creatordate",
|
|
42476
|
+
"refs/tags/lua/",
|
|
42477
|
+
"--count=1",
|
|
42478
|
+
"--format=%(refname:short)"
|
|
42479
|
+
])
|
|
42480
|
+
]);
|
|
42481
|
+
const versionResult = settled[0].status === "fulfilled" ? settled[0].value : {
|
|
42482
|
+
stdout: "",
|
|
42483
|
+
stderr: "",
|
|
42484
|
+
code: 1
|
|
42485
|
+
};
|
|
42486
|
+
const identity = settled[1].status === "fulfilled" ? settled[1].value : {
|
|
42487
|
+
name: void 0,
|
|
42488
|
+
email: void 0
|
|
42489
|
+
};
|
|
42490
|
+
const lastCommitResult = settled[2].status === "fulfilled" ? settled[2].value : {
|
|
42491
|
+
stdout: "",
|
|
42492
|
+
stderr: "",
|
|
42493
|
+
code: 1
|
|
42494
|
+
};
|
|
42495
|
+
const lastTagResult = settled[3].status === "fulfilled" ? settled[3].value : {
|
|
42496
|
+
stdout: "",
|
|
42497
|
+
stderr: "",
|
|
42498
|
+
code: 1
|
|
42499
|
+
};
|
|
42500
|
+
const versionLine = versionResult.code === 0 ? `${versionResult.stdout.trim()} (\u2713)` : "(not detected)";
|
|
42501
|
+
const identityLine = identity.name && identity.email ? `${identity.name} <${identity.email}> (\u2713)` : "(not configured)";
|
|
42502
|
+
const lastCommit = lastCommitResult.code === 0 && lastCommitResult.stdout.trim() ? lastCommitResult.stdout.trim() : "(none yet)";
|
|
42503
|
+
const lastTag = lastTagResult.code === 0 && lastTagResult.stdout.trim() ? lastTagResult.stdout.trim() : "(none yet)";
|
|
42504
|
+
writeInfo(`Git integration: enabled`);
|
|
42505
|
+
writeInfo(`Project repo: ${process.cwd()}`);
|
|
42506
|
+
writeInfo(`Git binary: ${versionLine}`);
|
|
42507
|
+
writeInfo(`User identity: ${identityLine}`);
|
|
42508
|
+
writeInfo(`Last lua commit: ${lastCommit}`);
|
|
42509
|
+
writeInfo(`Last lua tag: ${lastTag}`);
|
|
42510
|
+
trackEvent("cli_git_status_completed", {
|
|
42511
|
+
enabled: true
|
|
42512
|
+
});
|
|
42513
|
+
}, "git status");
|
|
42514
|
+
}
|
|
42515
|
+
__name(gitStatusCommand, "gitStatusCommand");
|
|
42516
|
+
|
|
42517
|
+
// src/commands/git-auth.ts
|
|
42518
|
+
init_cli();
|
|
42519
|
+
init_analytics();
|
|
42520
|
+
function gitAuthGithubCommand(opts) {
|
|
42521
|
+
return withErrorHandling(async () => {
|
|
42522
|
+
const provider = new GitHubProvider();
|
|
42523
|
+
const existing = await provider.status();
|
|
42524
|
+
if (existing.linked && !opts.force) {
|
|
42525
|
+
const ok = await confirmAction(`Already linked to GitHub as @${existing.username}. Re-link?`);
|
|
42526
|
+
if (!ok) {
|
|
42527
|
+
writeInfo("Aborted. Existing link preserved.");
|
|
42528
|
+
return;
|
|
42529
|
+
}
|
|
42530
|
+
}
|
|
42531
|
+
const result = await provider.login({
|
|
42532
|
+
device: opts.device,
|
|
42533
|
+
force: opts.force
|
|
42534
|
+
});
|
|
42535
|
+
writeSuccess(`Logged in to GitHub as @${result.username}.`);
|
|
42536
|
+
trackEvent("cli_git_auth_succeeded", {
|
|
42537
|
+
provider: "github",
|
|
42538
|
+
flow: opts.device ? "device" : "loopback"
|
|
42539
|
+
});
|
|
42540
|
+
}, "git-auth-github");
|
|
42541
|
+
}
|
|
42542
|
+
__name(gitAuthGithubCommand, "gitAuthGithubCommand");
|
|
42543
|
+
function gitAuthStatusCommand() {
|
|
42544
|
+
return withErrorHandling(async () => {
|
|
42545
|
+
const provider = new GitHubProvider();
|
|
42546
|
+
const s = await provider.status();
|
|
42547
|
+
if (!s.linked) {
|
|
42548
|
+
console.log("Not linked. Run `lua git auth github` to link.");
|
|
42549
|
+
} else {
|
|
42550
|
+
console.log(`Linked: github \u2014 @${s.username} (scopes: ${s.scopes.join(", ")}) \u2014 linked ${formatLinkedAt(s.linkedAt)}`);
|
|
42551
|
+
}
|
|
42552
|
+
trackEvent("cli_git_auth_status_completed", {
|
|
42553
|
+
linked: s.linked
|
|
42554
|
+
});
|
|
42555
|
+
}, "git-auth-status");
|
|
42556
|
+
}
|
|
42557
|
+
__name(gitAuthStatusCommand, "gitAuthStatusCommand");
|
|
42558
|
+
function gitAuthDisconnectCommand(provider) {
|
|
42559
|
+
return withErrorHandling(async () => {
|
|
42560
|
+
const name = (provider ?? "github").toLowerCase();
|
|
42561
|
+
if (name !== "github") {
|
|
42562
|
+
writeError(`Unknown provider: ${name}. v1 only supports github.`);
|
|
42563
|
+
return;
|
|
42564
|
+
}
|
|
42565
|
+
const p = new GitHubProvider();
|
|
42566
|
+
const s = await p.status();
|
|
42567
|
+
if (!s.linked) {
|
|
42568
|
+
console.log(`Not linked to ${name}. Nothing to do.`);
|
|
42569
|
+
return;
|
|
42570
|
+
}
|
|
42571
|
+
await p.logout();
|
|
42572
|
+
writeSuccess(`Disconnected from ${name}.`);
|
|
42573
|
+
trackEvent("cli_git_auth_disconnect_completed", {
|
|
42574
|
+
provider: name
|
|
42575
|
+
});
|
|
42576
|
+
}, "git-auth-disconnect");
|
|
42577
|
+
}
|
|
42578
|
+
__name(gitAuthDisconnectCommand, "gitAuthDisconnectCommand");
|
|
42579
|
+
function formatLinkedAt(iso) {
|
|
42580
|
+
try {
|
|
42581
|
+
return new Date(iso).toISOString().replace("T", " ").replace(/\..*$/, " UTC");
|
|
42582
|
+
} catch {
|
|
42583
|
+
return iso;
|
|
42584
|
+
}
|
|
42585
|
+
}
|
|
42586
|
+
__name(formatLinkedAt, "formatLinkedAt");
|
|
42587
|
+
|
|
42588
|
+
// src/commands/pull.ts
|
|
42589
|
+
init_cli();
|
|
42590
|
+
init_command_utils();
|
|
42591
|
+
init_analytics();
|
|
42592
|
+
init_backup_api_service();
|
|
42593
|
+
init_constants();
|
|
42594
|
+
async function pullCommand(options = {}) {
|
|
42595
|
+
return withErrorHandling(async () => {
|
|
42596
|
+
const { apiKey, agentId } = await initializeCommand();
|
|
42597
|
+
const force = Boolean(options.force);
|
|
42598
|
+
if (options.version) {
|
|
42599
|
+
await assertVersioningEnabled(apiKey, agentId);
|
|
42600
|
+
const version = parseVersion2(options.version);
|
|
42601
|
+
const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
|
|
42602
|
+
const response = await api.getVersion(version);
|
|
42603
|
+
if (!response.success || !response.data) {
|
|
42604
|
+
throw new Error(response.error?.message ?? `Version v${version} not found`);
|
|
42605
|
+
}
|
|
42606
|
+
const sourceManifestVersion = response.data.sourceManifestVersion;
|
|
42607
|
+
if (sourceManifestVersion == null) {
|
|
42608
|
+
throw new Error(`Version v${version} has no source backup linked. Cannot pull source for this version.`);
|
|
42609
|
+
}
|
|
42610
|
+
writeInfo(`Pulling source for agent version v${version} (backup v${sourceManifestVersion})\u2026`);
|
|
42611
|
+
await sourceRollbackCommand({
|
|
42612
|
+
version: sourceManifestVersion,
|
|
42613
|
+
force,
|
|
42614
|
+
silent: true
|
|
42615
|
+
});
|
|
42616
|
+
try {
|
|
42617
|
+
await tryGitCommit({
|
|
42618
|
+
message: GIT_MESSAGES.pullVersion(version),
|
|
42619
|
+
action: "pull-version"
|
|
42620
|
+
});
|
|
42621
|
+
} catch {
|
|
42622
|
+
}
|
|
42623
|
+
trackEvent("cli_pull_completed", {
|
|
42624
|
+
has_version_arg: true
|
|
42625
|
+
});
|
|
42626
|
+
return;
|
|
42627
|
+
}
|
|
42628
|
+
const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
|
|
42629
|
+
const backupResp = await backupApi.getBackupVersions();
|
|
42630
|
+
if (!backupResp.success || !backupResp.data?.versions?.length) {
|
|
42631
|
+
throw new Error("No backup versions available to pull.");
|
|
42632
|
+
}
|
|
42633
|
+
const latest = backupResp.data.versions[0].version;
|
|
42634
|
+
writeInfo(`Pulling latest source backup (v${latest})\u2026`);
|
|
42635
|
+
await sourceRollbackCommand({
|
|
42636
|
+
version: latest,
|
|
42637
|
+
force,
|
|
42638
|
+
silent: true
|
|
42639
|
+
});
|
|
42640
|
+
try {
|
|
42641
|
+
await tryGitCommit({
|
|
42642
|
+
message: GIT_MESSAGES.pullLatest,
|
|
42643
|
+
action: "pull-latest"
|
|
42644
|
+
});
|
|
42645
|
+
} catch {
|
|
42646
|
+
}
|
|
42647
|
+
trackEvent("cli_pull_completed", {
|
|
42648
|
+
has_version_arg: false
|
|
42649
|
+
});
|
|
42650
|
+
}, "pull");
|
|
42651
|
+
}
|
|
42652
|
+
__name(pullCommand, "pullCommand");
|
|
42653
|
+
|
|
40919
42654
|
// src/cli/command-definitions.ts
|
|
40920
42655
|
init_cli();
|
|
40921
42656
|
function setupAuthCommands(program2) {
|
|
@@ -41544,6 +43279,59 @@ Examples:
|
|
|
41544
43279
|
$ lua voice test --runner vitest Force vitest
|
|
41545
43280
|
`).action(voiceTestCommand);
|
|
41546
43281
|
voice.command("list").description("List LuaVoice primitives in the compiled manifest").option("--json", "Output as JSON").action(voiceListCommand);
|
|
43282
|
+
const versionGroup = program2.command("version").description("\u{1F3F7}\uFE0F Manage agent versions \u2014 atomic snapshots (requires agentVersioningEnabled for your org)").addHelpText("after", `
|
|
43283
|
+
Note:
|
|
43284
|
+
All \`lua version\` subcommands require the \`agentVersioningEnabled\`
|
|
43285
|
+
feature flag for your org. If a subcommand errors with "Agent versioning
|
|
43286
|
+
is not enabled," contact your admin to enable it.
|
|
43287
|
+
`);
|
|
43288
|
+
versionGroup.command("create").description("Snapshot current staged state into a new version").option("-m, --message <message>", "Optional description for this version").option("--auto-push", "Push first if local changes are not yet staged").option("--commit-hash <hash>", "Optional git commit hash to associate with this version").addHelpText("after", `
|
|
43289
|
+
Examples:
|
|
43290
|
+
$ lua version create Snapshot current state
|
|
43291
|
+
$ lua version create -m "Add FAQ skill" Include a description
|
|
43292
|
+
$ lua version create --auto-push Push then snapshot in one step
|
|
43293
|
+
`).action((opts) => versionCreateCommand(opts));
|
|
43294
|
+
versionGroup.command("list").description("List versions of the current agent").option("--all", "Show all versions (no server-side cap)").option("--limit <n>", "Cap output to this many entries", (v) => parseInt(v, 10)).option("--status <status>", "Filter by status: active | staged | all", "all").option("--json", "Output as JSON").addHelpText("after", `
|
|
43295
|
+
Examples:
|
|
43296
|
+
$ lua version list List all versions (default)
|
|
43297
|
+
$ lua version list --status active Show only active versions
|
|
43298
|
+
$ lua version list --limit 10 Show the 10 most recent
|
|
43299
|
+
$ lua version list --json Machine-readable output
|
|
43300
|
+
`).action((opts) => versionListCommand(opts));
|
|
43301
|
+
versionGroup.command("show <version>").description("Show full snapshot of a specific version").option("--json", "Output as JSON").addHelpText("after", `
|
|
43302
|
+
Examples:
|
|
43303
|
+
$ lua version show 3 Show version 3 details
|
|
43304
|
+
$ lua version show v3 --json JSON output
|
|
43305
|
+
`).action((version, opts) => versionShowCommand(version, opts));
|
|
43306
|
+
versionGroup.command("diff <from> <to>").description("Compare two versions side-by-side").option("--json", "Output diff as JSON").addHelpText("after", `
|
|
43307
|
+
Examples:
|
|
43308
|
+
$ lua version diff 2 3 Compare v2 \u2192 v3
|
|
43309
|
+
$ lua version diff v2 v3 --json Machine-readable diff
|
|
43310
|
+
`).action((from, to, opts) => versionDiffCommand(from, to, opts));
|
|
43311
|
+
versionGroup.command("promote <version>").description("Promote a version to active (atomic, instant)").addHelpText("after", `
|
|
43312
|
+
Examples:
|
|
43313
|
+
$ lua version promote 3 Promote v3 to the active version
|
|
43314
|
+
$ lua version promote v3 Same (v-prefix accepted)
|
|
43315
|
+
`).action((version) => versionPromoteCommand(version));
|
|
43316
|
+
versionGroup.command("delete <version>").description("Soft-delete a version").option("--force", "Skip confirmation prompt").addHelpText("after", `
|
|
43317
|
+
Examples:
|
|
43318
|
+
$ lua version delete 3 Delete v3 (confirms first)
|
|
43319
|
+
$ lua version delete v3 --force Skip confirmation
|
|
43320
|
+
`).action((version, opts) => versionDeleteCommand(version, opts));
|
|
43321
|
+
const gitGroup = program2.command("git").description("Manage opt-in git auto-commits for this project");
|
|
43322
|
+
gitGroup.command("connect").description("Enable git auto-commits (runs sanity checks, writes git.enabled: true)").action(() => gitConnectCommand());
|
|
43323
|
+
gitGroup.command("disconnect").description("Disable git auto-commits (existing commits/tags untouched)").action(() => gitDisconnectCommand());
|
|
43324
|
+
gitGroup.command("status").description("Show git integration config + last lua-issued commit/tag").action(() => gitStatusCommand());
|
|
43325
|
+
const gitAuthGroup = gitGroup.command("auth").description("Manage git remote provider authentication");
|
|
43326
|
+
gitAuthGroup.command("github").description("Link a GitHub account").option("--device", "Use the device flow (paste a code into github.com/login/device)").option("--force", "Overwrite an existing link without prompting").action((opts) => gitAuthGithubCommand(opts));
|
|
43327
|
+
gitAuthGroup.command("status").description("Show the current git auth status").action(() => gitAuthStatusCommand());
|
|
43328
|
+
gitAuthGroup.command("disconnect [provider]").description("Disconnect a git provider (default: github)").action((provider) => gitAuthDisconnectCommand(provider));
|
|
43329
|
+
program2.command("pull").description("\u{1F4E5} Pull the agent's source code locally").option("--version <version>", "Pull source linked to a specific agent version (requires versioning)").option("--force", "Skip confirmation prompt").addHelpText("after", `
|
|
43330
|
+
Examples:
|
|
43331
|
+
$ lua pull Restore the latest source backup
|
|
43332
|
+
$ lua pull --version 3 Restore the source snapshot from agent version 3
|
|
43333
|
+
$ lua pull --version v3 --force Skip the confirmation prompt
|
|
43334
|
+
`).action((opts) => pullCommand(opts));
|
|
41547
43335
|
const sourceCmd = program2.command("source").description("\u{1F5C2}\uFE0F Manage workspace source versions");
|
|
41548
43336
|
sourceCmd.command("list").description("List source versions for the current agent").option("--all", "Show all versions instead of the most recent 50", false).option("--limit <n>", "Number of versions to show", "50").addHelpText("after", `
|
|
41549
43337
|
Examples:
|