lua-cli 3.17.0 → 3.17.2
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 +68 -7
- package/dist/api-exports.js +50 -2
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +606 -32
- 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 +10 -4
- package/template/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -132,7 +132,7 @@ 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, CLI_CACHE_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";
|
|
@@ -154,6 +154,7 @@ var init_constants = __esm({
|
|
|
154
154
|
"base"
|
|
155
155
|
];
|
|
156
156
|
SANDBOX_STORAGE_FILE = join(CLI_CONFIG_DIR, "sandbox.json");
|
|
157
|
+
AUTH_STORAGE_FILE = join(CLI_CONFIG_DIR, "auth.json");
|
|
157
158
|
POSTHOG_API_KEY = "phc_W7Qsquwlflshmdkm2hWSqRpXuxGbVFo7LEX8H9HrSjC";
|
|
158
159
|
POSTHOG_HOST = "https://us.i.posthog.com";
|
|
159
160
|
}
|
|
@@ -1199,7 +1200,14 @@ Feel free to add, remove, or rename sections. Your persona can be a single parag
|
|
|
1199
1200
|
// `Data.get('call:<sessionId>')` to drive post-call analytics, follow-ups,
|
|
1200
1201
|
// or QA workflows. Defaults to false — most calls don't need to keep
|
|
1201
1202
|
// a transcript copy.
|
|
1202
|
-
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()
|
|
1203
1211
|
});
|
|
1204
1212
|
LuaVoiceConfigSchema = LuaVoiceConfigInnerSchema.superRefine((cfg, ctx) => {
|
|
1205
1213
|
const isRealtime = cfg.llm.kind === "realtime";
|
|
@@ -6677,6 +6685,31 @@ var init_skill_plugin = __esm({
|
|
|
6677
6685
|
|
|
6678
6686
|
// src/compiler/plugins/agent.plugin.ts
|
|
6679
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
|
+
}
|
|
6680
6713
|
var AgentPlugin, agentPlugin;
|
|
6681
6714
|
var init_agent_plugin = __esm({
|
|
6682
6715
|
"src/compiler/plugins/agent.plugin.ts"() {
|
|
@@ -6709,7 +6742,8 @@ var init_agent_plugin = __esm({
|
|
|
6709
6742
|
"name",
|
|
6710
6743
|
"description",
|
|
6711
6744
|
"persona",
|
|
6712
|
-
"model"
|
|
6745
|
+
"model",
|
|
6746
|
+
"modelSettings"
|
|
6713
6747
|
]
|
|
6714
6748
|
};
|
|
6715
6749
|
supportsClassDefinition = true;
|
|
@@ -6766,6 +6800,9 @@ var init_agent_plugin = __esm({
|
|
|
6766
6800
|
const governanceHit = findClassMember(classDecl, "governance", "property");
|
|
6767
6801
|
const governanceObj = governanceHit && Node12.isPropertyDeclaration(governanceHit.node) ? evaluateNodeAsObject(governanceHit.node.getInitializer()) : void 0;
|
|
6768
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;
|
|
6769
6806
|
return {
|
|
6770
6807
|
kind: this.kind,
|
|
6771
6808
|
name,
|
|
@@ -6780,6 +6817,7 @@ var init_agent_plugin = __esm({
|
|
|
6780
6817
|
persona,
|
|
6781
6818
|
model,
|
|
6782
6819
|
hasModelResolver,
|
|
6820
|
+
modelSettings,
|
|
6783
6821
|
batching,
|
|
6784
6822
|
governance
|
|
6785
6823
|
}
|
|
@@ -6809,6 +6847,7 @@ var init_agent_plugin = __esm({
|
|
|
6809
6847
|
"text"
|
|
6810
6848
|
]) ?? "";
|
|
6811
6849
|
const { model, hasModelResolver } = this.extractModelInfo(config);
|
|
6850
|
+
const modelSettings = this.extractModelSettings(config);
|
|
6812
6851
|
const batching = this.extractBatchingInfo(config);
|
|
6813
6852
|
const governance = this.extractGovernanceInfo(config);
|
|
6814
6853
|
const { voiceRefNames, voiceRefSourcePaths } = this.extractVoiceRefs(config);
|
|
@@ -6825,6 +6864,7 @@ var init_agent_plugin = __esm({
|
|
|
6825
6864
|
persona,
|
|
6826
6865
|
model,
|
|
6827
6866
|
hasModelResolver,
|
|
6867
|
+
modelSettings,
|
|
6828
6868
|
batching,
|
|
6829
6869
|
governance,
|
|
6830
6870
|
voiceRefNames,
|
|
@@ -6895,6 +6935,17 @@ var init_agent_plugin = __esm({
|
|
|
6895
6935
|
return governance;
|
|
6896
6936
|
}
|
|
6897
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
|
+
/**
|
|
6898
6949
|
* Extract model info from agent config.
|
|
6899
6950
|
* Returns the static model string (if any) and whether a resolver function exists.
|
|
6900
6951
|
*/
|
|
@@ -6976,6 +7027,7 @@ var init_agent_plugin = __esm({
|
|
|
6976
7027
|
persona: agentMeta.persona,
|
|
6977
7028
|
model: agentMeta.model,
|
|
6978
7029
|
hasModelResolver: agentMeta.hasModelResolver || void 0,
|
|
7030
|
+
modelSettings: agentMeta.modelSettings,
|
|
6979
7031
|
batching: agentMeta.batching,
|
|
6980
7032
|
governance: agentMeta.governance,
|
|
6981
7033
|
voiceRefs
|
|
@@ -7037,6 +7089,7 @@ var init_agent_plugin = __esm({
|
|
|
7037
7089
|
return rest;
|
|
7038
7090
|
}
|
|
7039
7091
|
};
|
|
7092
|
+
__name(shapeModelSettings, "shapeModelSettings");
|
|
7040
7093
|
agentPlugin = new AgentPlugin();
|
|
7041
7094
|
}
|
|
7042
7095
|
});
|
|
@@ -8100,6 +8153,7 @@ var init_voice_plugin = __esm({
|
|
|
8100
8153
|
const tts = extractModelField(config, "tts");
|
|
8101
8154
|
const krispEnabled = extractBooleanProperty(config, "krispEnabled");
|
|
8102
8155
|
const persistTranscript = extractBooleanProperty(config, "persistTranscript");
|
|
8156
|
+
const onToolFailureSay = extractStringProperty(config, "onToolFailureSay");
|
|
8103
8157
|
const volume = extractNumberProperty(config, "volume");
|
|
8104
8158
|
const interruption = extractObjectProperty(config, "interruption");
|
|
8105
8159
|
const pronunciationsRaw = extractObjectProperty(config, "pronunciations");
|
|
@@ -8136,6 +8190,7 @@ var init_voice_plugin = __esm({
|
|
|
8136
8190
|
hasTools: hasArrayProperty(config, "tools"),
|
|
8137
8191
|
krispEnabled,
|
|
8138
8192
|
persistTranscript,
|
|
8193
|
+
onToolFailureSay,
|
|
8139
8194
|
volume,
|
|
8140
8195
|
pronunciations,
|
|
8141
8196
|
backgroundAudio,
|
|
@@ -8195,6 +8250,7 @@ var init_voice_plugin = __esm({
|
|
|
8195
8250
|
if (fields.sttLanguage !== void 0) candidate.sttLanguage = fields.sttLanguage;
|
|
8196
8251
|
if (fields.krispEnabled !== void 0) candidate.krispEnabled = fields.krispEnabled;
|
|
8197
8252
|
if (fields.persistTranscript !== void 0) candidate.persistTranscript = fields.persistTranscript;
|
|
8253
|
+
if (fields.onToolFailureSay !== void 0) candidate.onToolFailureSay = fields.onToolFailureSay;
|
|
8198
8254
|
if (fields.volume !== void 0) candidate.volume = fields.volume;
|
|
8199
8255
|
if (fields.pronunciations !== void 0) candidate.pronunciations = fields.pronunciations;
|
|
8200
8256
|
if (fields.backgroundAudio !== void 0) candidate.backgroundAudio = fields.backgroundAudio;
|
|
@@ -8349,6 +8405,7 @@ var init_voice_plugin = __esm({
|
|
|
8349
8405
|
volume: fields.volume,
|
|
8350
8406
|
pronunciations: fields.pronunciations,
|
|
8351
8407
|
persistTranscript: fields.persistTranscript,
|
|
8408
|
+
onToolFailureSay: fields.onToolFailureSay,
|
|
8352
8409
|
interruption: fields.interruption
|
|
8353
8410
|
};
|
|
8354
8411
|
return entry;
|
|
@@ -15766,6 +15823,33 @@ var AgentHandler = class {
|
|
|
15766
15823
|
success: false
|
|
15767
15824
|
};
|
|
15768
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
|
+
}
|
|
15769
15853
|
const batching = agent?.batching ?? null;
|
|
15770
15854
|
writeProgress("\n\u23F1 Pushing batching configuration...");
|
|
15771
15855
|
try {
|
|
@@ -15899,6 +15983,13 @@ var AgentHandler = class {
|
|
|
15899
15983
|
});
|
|
15900
15984
|
return result.success;
|
|
15901
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
|
+
}
|
|
15902
15993
|
async pushBatching(ctx, batchingConfig) {
|
|
15903
15994
|
const agentApi = new AgentApi(BASE_URLS.API, ctx.apiKey);
|
|
15904
15995
|
const result = await agentApi.updateAgent(ctx.agentId, {
|
|
@@ -16932,6 +17023,18 @@ async function safePrompt(questions) {
|
|
|
16932
17023
|
}
|
|
16933
17024
|
}
|
|
16934
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");
|
|
16935
17038
|
|
|
16936
17039
|
// src/commands/sync.ts
|
|
16937
17040
|
init_command_utils();
|
|
@@ -18666,6 +18769,7 @@ var VoiceHandler = class extends BaseVersionedHandler {
|
|
|
18666
18769
|
if (voice.hasTools !== void 0) body.hasTools = voice.hasTools;
|
|
18667
18770
|
if (voice.krispEnabled !== void 0) body.krispEnabled = voice.krispEnabled;
|
|
18668
18771
|
if (voice.persistTranscript !== void 0) body.persistTranscript = voice.persistTranscript;
|
|
18772
|
+
if (voice.onToolFailureSay !== void 0) body.onToolFailureSay = voice.onToolFailureSay;
|
|
18669
18773
|
if (voice.volume !== void 0) body.volume = voice.volume;
|
|
18670
18774
|
if (voice.pronunciations !== void 0) body.pronunciations = voice.pronunciations;
|
|
18671
18775
|
if (voice.backgroundAudio !== void 0) body.backgroundAudio = voice.backgroundAudio;
|
|
@@ -22120,6 +22224,23 @@ async function gitUserConfigured(cwd) {
|
|
|
22120
22224
|
};
|
|
22121
22225
|
}
|
|
22122
22226
|
__name(gitUserConfigured, "gitUserConfigured");
|
|
22227
|
+
async function getRemoteOriginUrl(cwd) {
|
|
22228
|
+
try {
|
|
22229
|
+
const { stdout, code } = await runGit([
|
|
22230
|
+
"config",
|
|
22231
|
+
"--get",
|
|
22232
|
+
"remote.origin.url"
|
|
22233
|
+
], {
|
|
22234
|
+
cwd
|
|
22235
|
+
});
|
|
22236
|
+
if (code !== 0) return null;
|
|
22237
|
+
const trimmed = stdout.trim();
|
|
22238
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
22239
|
+
} catch {
|
|
22240
|
+
return null;
|
|
22241
|
+
}
|
|
22242
|
+
}
|
|
22243
|
+
__name(getRemoteOriginUrl, "getRemoteOriginUrl");
|
|
22123
22244
|
async function getCommitSha(cwd) {
|
|
22124
22245
|
try {
|
|
22125
22246
|
const { stdout, code } = await runGit([
|
|
@@ -22139,6 +22260,351 @@ __name(getCommitSha, "getCommitSha");
|
|
|
22139
22260
|
|
|
22140
22261
|
// src/utils/try-git-commit.ts
|
|
22141
22262
|
init_analytics();
|
|
22263
|
+
|
|
22264
|
+
// src/utils/auto-push-hook.ts
|
|
22265
|
+
init_cli();
|
|
22266
|
+
|
|
22267
|
+
// src/utils/git-auth-store.ts
|
|
22268
|
+
init_constants();
|
|
22269
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
22270
|
+
import { dirname as dirname6 } from "path";
|
|
22271
|
+
function readStore() {
|
|
22272
|
+
try {
|
|
22273
|
+
const raw = readFileSync10(AUTH_STORAGE_FILE, "utf8");
|
|
22274
|
+
const parsed = JSON.parse(raw);
|
|
22275
|
+
return {
|
|
22276
|
+
providers: parsed.providers ?? {}
|
|
22277
|
+
};
|
|
22278
|
+
} catch {
|
|
22279
|
+
return {
|
|
22280
|
+
providers: {}
|
|
22281
|
+
};
|
|
22282
|
+
}
|
|
22283
|
+
}
|
|
22284
|
+
__name(readStore, "readStore");
|
|
22285
|
+
function writeStore(store) {
|
|
22286
|
+
mkdirSync7(dirname6(AUTH_STORAGE_FILE), {
|
|
22287
|
+
recursive: true
|
|
22288
|
+
});
|
|
22289
|
+
writeFileSync7(AUTH_STORAGE_FILE, JSON.stringify(store, null, 2), {
|
|
22290
|
+
mode: 384
|
|
22291
|
+
});
|
|
22292
|
+
}
|
|
22293
|
+
__name(writeStore, "writeStore");
|
|
22294
|
+
async function saveAuth(provider, record) {
|
|
22295
|
+
const store = readStore();
|
|
22296
|
+
store.providers[provider] = record;
|
|
22297
|
+
writeStore(store);
|
|
22298
|
+
}
|
|
22299
|
+
__name(saveAuth, "saveAuth");
|
|
22300
|
+
async function getToken2(provider) {
|
|
22301
|
+
return readStore().providers[provider]?.token ?? null;
|
|
22302
|
+
}
|
|
22303
|
+
__name(getToken2, "getToken");
|
|
22304
|
+
async function getStatus(provider) {
|
|
22305
|
+
const record = readStore().providers[provider];
|
|
22306
|
+
if (!record) return {
|
|
22307
|
+
linked: false
|
|
22308
|
+
};
|
|
22309
|
+
return {
|
|
22310
|
+
linked: true,
|
|
22311
|
+
username: record.username,
|
|
22312
|
+
scopes: record.scopes,
|
|
22313
|
+
linkedAt: record.linkedAt
|
|
22314
|
+
};
|
|
22315
|
+
}
|
|
22316
|
+
__name(getStatus, "getStatus");
|
|
22317
|
+
async function clearAuth(provider) {
|
|
22318
|
+
const store = readStore();
|
|
22319
|
+
delete store.providers[provider];
|
|
22320
|
+
writeStore(store);
|
|
22321
|
+
}
|
|
22322
|
+
__name(clearAuth, "clearAuth");
|
|
22323
|
+
|
|
22324
|
+
// src/utils/git-providers/github-provider.ts
|
|
22325
|
+
import { request } from "undici";
|
|
22326
|
+
|
|
22327
|
+
// src/config/git-providers.constants.ts
|
|
22328
|
+
var GITHUB_CLIENT_ID = "Ov23lipe1jgBlCsoi9OO";
|
|
22329
|
+
var GITHUB_OAUTH_BASE_URL = "https://github.com";
|
|
22330
|
+
var GITHUB_API_BASE_URL = "https://api.github.com";
|
|
22331
|
+
var GITHUB_OAUTH_SCOPE = "repo";
|
|
22332
|
+
var DEVICE_FLOW_DEFAULT_INTERVAL_MS = 5e3;
|
|
22333
|
+
var DEVICE_FLOW_SLOW_DOWN_INCREMENT_MS = 5e3;
|
|
22334
|
+
var PUSH_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
22335
|
+
|
|
22336
|
+
// src/interfaces/git-providers.ts
|
|
22337
|
+
var GitPushError = class extends Error {
|
|
22338
|
+
static {
|
|
22339
|
+
__name(this, "GitPushError");
|
|
22340
|
+
}
|
|
22341
|
+
status;
|
|
22342
|
+
scrubbedStderr;
|
|
22343
|
+
constructor(message, status, scrubbedStderr) {
|
|
22344
|
+
super(message), this.status = status, this.scrubbedStderr = scrubbedStderr;
|
|
22345
|
+
this.name = "GitPushError";
|
|
22346
|
+
}
|
|
22347
|
+
};
|
|
22348
|
+
|
|
22349
|
+
// src/utils/git-providers/github-provider.ts
|
|
22350
|
+
init_cli();
|
|
22351
|
+
|
|
22352
|
+
// src/utils/scrub-token.ts
|
|
22353
|
+
function scrubToken(s, token) {
|
|
22354
|
+
if (!token) return s;
|
|
22355
|
+
return s.split(token).join("<redacted>");
|
|
22356
|
+
}
|
|
22357
|
+
__name(scrubToken, "scrubToken");
|
|
22358
|
+
|
|
22359
|
+
// src/utils/git-providers/github-provider.ts
|
|
22360
|
+
var GitHubProvider = class {
|
|
22361
|
+
static {
|
|
22362
|
+
__name(this, "GitHubProvider");
|
|
22363
|
+
}
|
|
22364
|
+
name = "github";
|
|
22365
|
+
/**
|
|
22366
|
+
* GitHub is authenticated via the OAuth **device flow** only.
|
|
22367
|
+
*
|
|
22368
|
+
* The browser/loopback authorization-code flow is intentionally NOT used:
|
|
22369
|
+
* GitHub requires a `client_secret` to exchange the code (PKCE does not
|
|
22370
|
+
* replace it — the secretless exchange returns `incorrect_client_credentials`),
|
|
22371
|
+
* and a CLI published to npm cannot safely embed a secret. The device flow
|
|
22372
|
+
* needs no secret, which is why it is the only viable flow for a public
|
|
22373
|
+
* client (the same reason GitHub's own `gh` CLI defaults to it).
|
|
22374
|
+
*
|
|
22375
|
+
* `opts.device` is accepted for backwards compatibility but is now a no-op:
|
|
22376
|
+
* the device flow is always used.
|
|
22377
|
+
*/
|
|
22378
|
+
async login(_opts) {
|
|
22379
|
+
const token = await this.deviceFlow();
|
|
22380
|
+
const username = await this.fetchUsername(token);
|
|
22381
|
+
await saveAuth(this.name, {
|
|
22382
|
+
token,
|
|
22383
|
+
username,
|
|
22384
|
+
scopes: [
|
|
22385
|
+
GITHUB_OAUTH_SCOPE
|
|
22386
|
+
],
|
|
22387
|
+
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22388
|
+
});
|
|
22389
|
+
return {
|
|
22390
|
+
username
|
|
22391
|
+
};
|
|
22392
|
+
}
|
|
22393
|
+
async logout() {
|
|
22394
|
+
await clearAuth(this.name);
|
|
22395
|
+
}
|
|
22396
|
+
async status() {
|
|
22397
|
+
return getStatus(this.name);
|
|
22398
|
+
}
|
|
22399
|
+
async push(opts) {
|
|
22400
|
+
const parsed = parseGitHubHttpsUrl(opts.remoteUrl);
|
|
22401
|
+
const authedUrl = `https://x-access-token:${opts.token}@github.com/${parsed.owner}/${parsed.repo}`;
|
|
22402
|
+
let result;
|
|
22403
|
+
try {
|
|
22404
|
+
result = await runGit([
|
|
22405
|
+
"push",
|
|
22406
|
+
authedUrl,
|
|
22407
|
+
opts.branch
|
|
22408
|
+
], {
|
|
22409
|
+
cwd: opts.cwd,
|
|
22410
|
+
timeout: PUSH_TIMEOUT_MS
|
|
22411
|
+
});
|
|
22412
|
+
} catch (err) {
|
|
22413
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
22414
|
+
const scrubbed2 = scrubToken(raw, opts.token);
|
|
22415
|
+
throw new GitPushError(`git push failed: ${scrubbed2}`, 0, scrubbed2);
|
|
22416
|
+
}
|
|
22417
|
+
if (result.code === 0) return;
|
|
22418
|
+
const scrubbed = scrubToken(result.stderr, opts.token);
|
|
22419
|
+
const status = matchPushStatus(scrubbed);
|
|
22420
|
+
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);
|
|
22421
|
+
}
|
|
22422
|
+
async fetchUsername(token) {
|
|
22423
|
+
const res = await request(`${GITHUB_API_BASE_URL}/user`, {
|
|
22424
|
+
method: "GET",
|
|
22425
|
+
headers: {
|
|
22426
|
+
accept: "application/vnd.github+json",
|
|
22427
|
+
authorization: `Bearer ${token}`,
|
|
22428
|
+
"user-agent": "lua-cli"
|
|
22429
|
+
}
|
|
22430
|
+
});
|
|
22431
|
+
const body = await res.body.json();
|
|
22432
|
+
if (!body.login) throw new Error("Could not read GitHub username after auth.");
|
|
22433
|
+
return body.login;
|
|
22434
|
+
}
|
|
22435
|
+
async deviceFlow() {
|
|
22436
|
+
const codeRes = await request(`${GITHUB_OAUTH_BASE_URL}/login/device/code`, {
|
|
22437
|
+
method: "POST",
|
|
22438
|
+
headers: {
|
|
22439
|
+
accept: "application/json",
|
|
22440
|
+
"content-type": "application/json"
|
|
22441
|
+
},
|
|
22442
|
+
body: JSON.stringify({
|
|
22443
|
+
client_id: GITHUB_CLIENT_ID,
|
|
22444
|
+
scope: GITHUB_OAUTH_SCOPE
|
|
22445
|
+
})
|
|
22446
|
+
});
|
|
22447
|
+
const codeBody = await codeRes.body.json();
|
|
22448
|
+
if (!codeBody.device_code || !codeBody.user_code || !codeBody.verification_uri) {
|
|
22449
|
+
throw new Error(`GitHub rejected device-code request: ${codeBody.error ?? "unknown error"}`);
|
|
22450
|
+
}
|
|
22451
|
+
writeInfo(`Open ${codeBody.verification_uri} and enter the code: ${codeBody.user_code}`);
|
|
22452
|
+
let intervalMs = codeBody.interval != null ? codeBody.interval * 1e3 : DEVICE_FLOW_DEFAULT_INTERVAL_MS;
|
|
22453
|
+
const deadline = Date.now() + (codeBody.expires_in ?? 900) * 1e3;
|
|
22454
|
+
while (Date.now() < deadline) {
|
|
22455
|
+
await sleep(intervalMs);
|
|
22456
|
+
if (Date.now() >= deadline) break;
|
|
22457
|
+
const pollRes = await request(`${GITHUB_OAUTH_BASE_URL}/login/oauth/access_token`, {
|
|
22458
|
+
method: "POST",
|
|
22459
|
+
headers: {
|
|
22460
|
+
accept: "application/json",
|
|
22461
|
+
"content-type": "application/json"
|
|
22462
|
+
},
|
|
22463
|
+
body: JSON.stringify({
|
|
22464
|
+
client_id: GITHUB_CLIENT_ID,
|
|
22465
|
+
device_code: codeBody.device_code,
|
|
22466
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
22467
|
+
})
|
|
22468
|
+
});
|
|
22469
|
+
const body = await pollRes.body.json();
|
|
22470
|
+
if (body.access_token) return body.access_token;
|
|
22471
|
+
if (body.error === "authorization_pending") continue;
|
|
22472
|
+
if (body.error === "slow_down") {
|
|
22473
|
+
intervalMs += DEVICE_FLOW_SLOW_DOWN_INCREMENT_MS;
|
|
22474
|
+
continue;
|
|
22475
|
+
}
|
|
22476
|
+
if (body.error === "expired_token") {
|
|
22477
|
+
throw new Error("Device code expired. Re-run `lua git auth github --device`.");
|
|
22478
|
+
}
|
|
22479
|
+
throw new Error(`GitHub device-flow error: ${body.error ?? "unknown"}`);
|
|
22480
|
+
}
|
|
22481
|
+
throw new Error("Device flow timed out. Re-run `lua git auth github --device`.");
|
|
22482
|
+
}
|
|
22483
|
+
};
|
|
22484
|
+
function sleep(ms) {
|
|
22485
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
22486
|
+
}
|
|
22487
|
+
__name(sleep, "sleep");
|
|
22488
|
+
function isGitHubUrl(url) {
|
|
22489
|
+
try {
|
|
22490
|
+
const u = new URL(url);
|
|
22491
|
+
return u.protocol === "https:" && u.hostname === "github.com";
|
|
22492
|
+
} catch {
|
|
22493
|
+
return false;
|
|
22494
|
+
}
|
|
22495
|
+
}
|
|
22496
|
+
__name(isGitHubUrl, "isGitHubUrl");
|
|
22497
|
+
function parseGitHubHttpsUrl(url) {
|
|
22498
|
+
let u;
|
|
22499
|
+
try {
|
|
22500
|
+
u = new URL(url);
|
|
22501
|
+
} catch {
|
|
22502
|
+
throw new Error(`Not a parseable URL: ${url}. v1 only supports https GitHub remotes.`);
|
|
22503
|
+
}
|
|
22504
|
+
if (u.protocol !== "https:" || u.hostname !== "github.com") {
|
|
22505
|
+
throw new Error(`Not a GitHub HTTPS remote: ${url}. v1 only supports https://github.com/...`);
|
|
22506
|
+
}
|
|
22507
|
+
const parts = u.pathname.replace(/^\/+/, "").replace(/\.git$/, "").split("/");
|
|
22508
|
+
if (parts.length < 2 || !parts[0] || !parts[1]) {
|
|
22509
|
+
throw new Error(`Unexpected GitHub URL shape: ${url}`);
|
|
22510
|
+
}
|
|
22511
|
+
return {
|
|
22512
|
+
owner: parts[0],
|
|
22513
|
+
repo: parts[1]
|
|
22514
|
+
};
|
|
22515
|
+
}
|
|
22516
|
+
__name(parseGitHubHttpsUrl, "parseGitHubHttpsUrl");
|
|
22517
|
+
function matchPushStatus(stderr) {
|
|
22518
|
+
const s = stderr.toLowerCase();
|
|
22519
|
+
if (s.includes("401") || s.includes("authentication failed")) return 401;
|
|
22520
|
+
if (s.includes("403") || s.includes("permission to") || s.includes("forbidden")) return 403;
|
|
22521
|
+
return 0;
|
|
22522
|
+
}
|
|
22523
|
+
__name(matchPushStatus, "matchPushStatus");
|
|
22524
|
+
|
|
22525
|
+
// src/utils/auto-push-hook.ts
|
|
22526
|
+
init_analytics();
|
|
22527
|
+
var warned = /* @__PURE__ */ new Set();
|
|
22528
|
+
function warnOnce(key, message) {
|
|
22529
|
+
if (warned.has(key)) return;
|
|
22530
|
+
warned.add(key);
|
|
22531
|
+
writeInfo(`\u26A0\uFE0F ${message}`);
|
|
22532
|
+
}
|
|
22533
|
+
__name(warnOnce, "warnOnce");
|
|
22534
|
+
async function tryGitAutoPush(ctx) {
|
|
22535
|
+
try {
|
|
22536
|
+
if (ctx.config?.git?.autoPush !== true) return;
|
|
22537
|
+
const token = await getToken2("github");
|
|
22538
|
+
if (!token) {
|
|
22539
|
+
warnOnce("missing-github-token", "git.autoPush is enabled but no GitHub auth \u2014 run `lua git auth github`.");
|
|
22540
|
+
return;
|
|
22541
|
+
}
|
|
22542
|
+
const remote = await getRemoteOriginUrl(ctx.cwd);
|
|
22543
|
+
if (!remote) {
|
|
22544
|
+
warnOnce("missing-github-remote", "git.autoPush is enabled but no `remote.origin.url` is set. Skipping push.");
|
|
22545
|
+
return;
|
|
22546
|
+
}
|
|
22547
|
+
if (!isGitHubUrl(remote)) {
|
|
22548
|
+
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.`);
|
|
22549
|
+
return;
|
|
22550
|
+
}
|
|
22551
|
+
const branch = await readCurrentBranch(ctx.cwd);
|
|
22552
|
+
if (!branch) {
|
|
22553
|
+
warnOnce("detached-head", "Cannot auto-push from a detached HEAD. Skipping push.");
|
|
22554
|
+
return;
|
|
22555
|
+
}
|
|
22556
|
+
const provider = new GitHubProvider();
|
|
22557
|
+
try {
|
|
22558
|
+
await provider.push({
|
|
22559
|
+
token,
|
|
22560
|
+
remoteUrl: remote,
|
|
22561
|
+
branch,
|
|
22562
|
+
cwd: ctx.cwd ?? process.cwd()
|
|
22563
|
+
});
|
|
22564
|
+
writeInfo(`\u2713 Pushed ${ctx.commitSha?.slice(0, 7) ?? ""} to ${remote} (${branch})`);
|
|
22565
|
+
trackEvent("cli_auto_push_succeeded", {
|
|
22566
|
+
action: ctx.action
|
|
22567
|
+
});
|
|
22568
|
+
} catch (err) {
|
|
22569
|
+
const status = err instanceof GitPushError ? err.status : 0;
|
|
22570
|
+
const message = err instanceof GitPushError ? err.scrubbedStderr || err.message : err instanceof Error ? err.message : String(err);
|
|
22571
|
+
if (status === 401) {
|
|
22572
|
+
warnOnce("push-revoked", "GitHub auth was revoked \u2014 run `lua git auth github` to re-link. Commit kept locally.");
|
|
22573
|
+
} else if (status === 403) {
|
|
22574
|
+
warnOnce("push-rejected-403", `GitHub rejected the push: ${message}. Commit kept locally.`);
|
|
22575
|
+
} else {
|
|
22576
|
+
warnOnce("push-failed-other", `Auto-push failed: ${message}. Commit kept locally; run \`git push\` to retry.`);
|
|
22577
|
+
}
|
|
22578
|
+
trackEvent("cli_auto_push_failed", {
|
|
22579
|
+
action: ctx.action,
|
|
22580
|
+
status
|
|
22581
|
+
});
|
|
22582
|
+
}
|
|
22583
|
+
} catch {
|
|
22584
|
+
warnOnce("hook-errored", "Auto-push hook errored unexpectedly. Commit kept locally; run `git push` to retry.");
|
|
22585
|
+
trackEvent("cli_auto_push_failed", {
|
|
22586
|
+
action: ctx.action,
|
|
22587
|
+
status: 0
|
|
22588
|
+
});
|
|
22589
|
+
}
|
|
22590
|
+
}
|
|
22591
|
+
__name(tryGitAutoPush, "tryGitAutoPush");
|
|
22592
|
+
async function readCurrentBranch(cwd) {
|
|
22593
|
+
const res = await runGit([
|
|
22594
|
+
"rev-parse",
|
|
22595
|
+
"--abbrev-ref",
|
|
22596
|
+
"HEAD"
|
|
22597
|
+
], {
|
|
22598
|
+
cwd
|
|
22599
|
+
});
|
|
22600
|
+
if (res.code !== 0) return null;
|
|
22601
|
+
const trimmed = res.stdout.trim();
|
|
22602
|
+
if (!trimmed || trimmed === "HEAD") return null;
|
|
22603
|
+
return trimmed;
|
|
22604
|
+
}
|
|
22605
|
+
__name(readCurrentBranch, "readCurrentBranch");
|
|
22606
|
+
|
|
22607
|
+
// src/utils/try-git-commit.ts
|
|
22142
22608
|
async function tryGitCommit(opts) {
|
|
22143
22609
|
const config = readYamlConfig();
|
|
22144
22610
|
if (!config?.git?.enabled) {
|
|
@@ -22221,6 +22687,12 @@ async function tryGitCommit(opts) {
|
|
|
22221
22687
|
has_sha: Boolean(sha),
|
|
22222
22688
|
has_tag: tagged === true
|
|
22223
22689
|
});
|
|
22690
|
+
await tryGitAutoPush({
|
|
22691
|
+
config,
|
|
22692
|
+
cwd: opts.cwd,
|
|
22693
|
+
commitSha: sha,
|
|
22694
|
+
action: opts.action
|
|
22695
|
+
});
|
|
22224
22696
|
return {
|
|
22225
22697
|
committed: true,
|
|
22226
22698
|
sha,
|
|
@@ -24154,11 +24626,11 @@ init_cli();
|
|
|
24154
24626
|
|
|
24155
24627
|
// src/utils/sandbox-storage.ts
|
|
24156
24628
|
init_constants();
|
|
24157
|
-
import { readFileSync as
|
|
24158
|
-
import { dirname as
|
|
24159
|
-
function
|
|
24629
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
|
|
24630
|
+
import { dirname as dirname7 } from "path";
|
|
24631
|
+
function readStore2() {
|
|
24160
24632
|
try {
|
|
24161
|
-
const raw =
|
|
24633
|
+
const raw = readFileSync11(SANDBOX_STORAGE_FILE, "utf8");
|
|
24162
24634
|
const parsed = JSON.parse(raw);
|
|
24163
24635
|
return {
|
|
24164
24636
|
skills: parsed.skills ?? {},
|
|
@@ -24175,28 +24647,28 @@ function readStore() {
|
|
|
24175
24647
|
};
|
|
24176
24648
|
}
|
|
24177
24649
|
}
|
|
24178
|
-
__name(
|
|
24179
|
-
function
|
|
24650
|
+
__name(readStore2, "readStore");
|
|
24651
|
+
function writeStore2(store) {
|
|
24180
24652
|
try {
|
|
24181
|
-
|
|
24653
|
+
mkdirSync8(dirname7(SANDBOX_STORAGE_FILE), {
|
|
24182
24654
|
recursive: true
|
|
24183
24655
|
});
|
|
24184
|
-
|
|
24656
|
+
writeFileSync8(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
|
|
24185
24657
|
} catch {
|
|
24186
24658
|
}
|
|
24187
24659
|
}
|
|
24188
|
-
__name(
|
|
24660
|
+
__name(writeStore2, "writeStore");
|
|
24189
24661
|
async function getSandboxSkillId(skillName) {
|
|
24190
|
-
const store =
|
|
24662
|
+
const store = readStore2();
|
|
24191
24663
|
const key = skillName ?? "__default__";
|
|
24192
24664
|
return store.skills[key] ?? null;
|
|
24193
24665
|
}
|
|
24194
24666
|
__name(getSandboxSkillId, "getSandboxSkillId");
|
|
24195
24667
|
async function setSandboxSkillId(sandboxId, skillName) {
|
|
24196
|
-
const store =
|
|
24668
|
+
const store = readStore2();
|
|
24197
24669
|
const key = skillName ?? "__default__";
|
|
24198
24670
|
store.skills[key] = sandboxId;
|
|
24199
|
-
|
|
24671
|
+
writeStore2(store);
|
|
24200
24672
|
}
|
|
24201
24673
|
__name(setSandboxSkillId, "setSandboxSkillId");
|
|
24202
24674
|
async function getAllSandboxSkillIds(yamlConfig) {
|
|
@@ -24222,14 +24694,14 @@ async function getAllSandboxSkillIds(yamlConfig) {
|
|
|
24222
24694
|
}
|
|
24223
24695
|
__name(getAllSandboxSkillIds, "getAllSandboxSkillIds");
|
|
24224
24696
|
async function getSandboxPreProcessorId(preprocessorName) {
|
|
24225
|
-
const store =
|
|
24697
|
+
const store = readStore2();
|
|
24226
24698
|
return store.preprocessors[preprocessorName] ?? null;
|
|
24227
24699
|
}
|
|
24228
24700
|
__name(getSandboxPreProcessorId, "getSandboxPreProcessorId");
|
|
24229
24701
|
async function setSandboxPreProcessorId(sandboxId, preprocessorName) {
|
|
24230
|
-
const store =
|
|
24702
|
+
const store = readStore2();
|
|
24231
24703
|
store.preprocessors[preprocessorName] = sandboxId;
|
|
24232
|
-
|
|
24704
|
+
writeStore2(store);
|
|
24233
24705
|
}
|
|
24234
24706
|
__name(setSandboxPreProcessorId, "setSandboxPreProcessorId");
|
|
24235
24707
|
async function getAllSandboxPreProcessorIds(config) {
|
|
@@ -24254,14 +24726,14 @@ async function getAllSandboxPreProcessorIds(config) {
|
|
|
24254
24726
|
}
|
|
24255
24727
|
__name(getAllSandboxPreProcessorIds, "getAllSandboxPreProcessorIds");
|
|
24256
24728
|
async function getSandboxPostProcessorId(postprocessorName) {
|
|
24257
|
-
const store =
|
|
24729
|
+
const store = readStore2();
|
|
24258
24730
|
return store.postprocessors[postprocessorName] ?? null;
|
|
24259
24731
|
}
|
|
24260
24732
|
__name(getSandboxPostProcessorId, "getSandboxPostProcessorId");
|
|
24261
24733
|
async function setSandboxPostProcessorId(sandboxId, postprocessorName) {
|
|
24262
|
-
const store =
|
|
24734
|
+
const store = readStore2();
|
|
24263
24735
|
store.postprocessors[postprocessorName] = sandboxId;
|
|
24264
|
-
|
|
24736
|
+
writeStore2(store);
|
|
24265
24737
|
}
|
|
24266
24738
|
__name(setSandboxPostProcessorId, "setSandboxPostProcessorId");
|
|
24267
24739
|
async function getAllSandboxPostProcessorIds(config) {
|
|
@@ -30358,7 +30830,7 @@ init_auth();
|
|
|
30358
30830
|
init_auth_api_service();
|
|
30359
30831
|
init_files();
|
|
30360
30832
|
init_artifact_loader();
|
|
30361
|
-
import { existsSync as existsSync7, readFileSync as
|
|
30833
|
+
import { existsSync as existsSync7, readFileSync as readFileSync12 } from "fs";
|
|
30362
30834
|
import { join as join6 } from "path";
|
|
30363
30835
|
import { performance } from "perf_hooks";
|
|
30364
30836
|
import * as os from "os";
|
|
@@ -30709,7 +31181,7 @@ function gatherTelemetry() {
|
|
|
30709
31181
|
} else {
|
|
30710
31182
|
try {
|
|
30711
31183
|
if (existsSync7(TELEMETRY_FILE)) {
|
|
30712
|
-
const raw =
|
|
31184
|
+
const raw = readFileSync12(TELEMETRY_FILE, "utf8");
|
|
30713
31185
|
const cfg = JSON.parse(raw);
|
|
30714
31186
|
if (typeof cfg.enabled === "boolean") enabled = cfg.enabled;
|
|
30715
31187
|
}
|
|
@@ -39890,7 +40362,7 @@ __name(telemetryCommand, "telemetryCommand");
|
|
|
39890
40362
|
init_cli();
|
|
39891
40363
|
init_command_utils();
|
|
39892
40364
|
init_analytics();
|
|
39893
|
-
import { writeFileSync as
|
|
40365
|
+
import { writeFileSync as writeFileSync9, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
|
|
39894
40366
|
import { resolve as resolve4, join as join7 } from "path";
|
|
39895
40367
|
init_artifact_loader();
|
|
39896
40368
|
init_types();
|
|
@@ -40051,7 +40523,7 @@ async function governanceCommand(action) {
|
|
|
40051
40523
|
}
|
|
40052
40524
|
}
|
|
40053
40525
|
const content = generateFile(setup);
|
|
40054
|
-
|
|
40526
|
+
writeFileSync9(filePath, content, "utf-8");
|
|
40055
40527
|
const relativePath = filePath.replace(process.cwd() + "/", "");
|
|
40056
40528
|
writeSuccess(`Created ${relativePath}`);
|
|
40057
40529
|
console.log("");
|
|
@@ -41763,7 +42235,7 @@ function failConnect(status, banner, errorMessage) {
|
|
|
41763
42235
|
throw new Error(errorMessage);
|
|
41764
42236
|
}
|
|
41765
42237
|
__name(failConnect, "failConnect");
|
|
41766
|
-
async function gitConnectCommand() {
|
|
42238
|
+
async function gitConnectCommand(opts = {}) {
|
|
41767
42239
|
return withErrorHandling(async () => {
|
|
41768
42240
|
const config = readYamlConfig();
|
|
41769
42241
|
if (!config) {
|
|
@@ -41802,15 +42274,35 @@ async function gitConnectCommand() {
|
|
|
41802
42274
|
user_configured: false
|
|
41803
42275
|
}, '\u2717 Git user.name is not configured.\n Fix: git config --global user.name "Your Name"', "git user.name is not configured");
|
|
41804
42276
|
}
|
|
42277
|
+
const passedStatus = {
|
|
42278
|
+
git_available: true,
|
|
42279
|
+
in_repo: true,
|
|
42280
|
+
user_configured: true
|
|
42281
|
+
};
|
|
42282
|
+
if (opts.autoPush) {
|
|
42283
|
+
const token = await getToken2("github");
|
|
42284
|
+
if (!token) {
|
|
42285
|
+
failConnect(passedStatus, "\u2717 --auto-push needs a linked GitHub account.\n Fix: run `lua git auth github` to link your GitHub account, then re-run `lua git connect --auto-push`.", "no GitHub account linked");
|
|
42286
|
+
}
|
|
42287
|
+
const remote = await getRemoteOriginUrl();
|
|
42288
|
+
if (!remote) {
|
|
42289
|
+
failConnect(passedStatus, "\u2717 --auto-push needs a GitHub remote, but `remote.origin.url` is not set.\n Fix: git remote add origin https://github.com/<owner>/<repo>.git", "no origin remote configured");
|
|
42290
|
+
}
|
|
42291
|
+
if (!isGitHubUrl(remote)) {
|
|
42292
|
+
failConnect(passedStatus, `\u2717 --auto-push only supports GitHub HTTPS remotes, but origin is ${remote}.
|
|
42293
|
+
Fix: git remote set-url origin https://github.com/<owner>/<repo>.git`, "origin is not a GitHub HTTPS remote");
|
|
42294
|
+
}
|
|
42295
|
+
}
|
|
41805
42296
|
config.git = {
|
|
42297
|
+
...config.git,
|
|
41806
42298
|
enabled: true
|
|
41807
42299
|
};
|
|
42300
|
+
if (opts.autoPush) config.git.autoPush = true;
|
|
41808
42301
|
writeYamlConfig(config);
|
|
41809
|
-
writeSuccess("\u2713 Git integration enabled. Future `lua push`, `lua version *`, and `lua pull` runs will auto-commit.");
|
|
42302
|
+
writeSuccess(opts.autoPush ? "\u2713 Git integration enabled with auto-push. Future `lua push`, `lua version *`, and `lua pull` runs will auto-commit and push to GitHub." : "\u2713 Git integration enabled. Future `lua push`, `lua version *`, and `lua pull` runs will auto-commit.");
|
|
41810
42303
|
trackEvent("cli_git_connect_completed", {
|
|
41811
|
-
|
|
41812
|
-
|
|
41813
|
-
user_configured: true,
|
|
42304
|
+
...passedStatus,
|
|
42305
|
+
auto_push: Boolean(opts.autoPush),
|
|
41814
42306
|
succeeded: true
|
|
41815
42307
|
});
|
|
41816
42308
|
}, "git connect");
|
|
@@ -41903,6 +42395,77 @@ async function gitStatusCommand() {
|
|
|
41903
42395
|
}
|
|
41904
42396
|
__name(gitStatusCommand, "gitStatusCommand");
|
|
41905
42397
|
|
|
42398
|
+
// src/commands/git-auth.ts
|
|
42399
|
+
init_cli();
|
|
42400
|
+
init_analytics();
|
|
42401
|
+
function gitAuthGithubCommand(opts) {
|
|
42402
|
+
return withErrorHandling(async () => {
|
|
42403
|
+
const provider = new GitHubProvider();
|
|
42404
|
+
const existing = await provider.status();
|
|
42405
|
+
if (existing.linked && !opts.force) {
|
|
42406
|
+
const ok = await confirmAction(`Already linked to GitHub as @${existing.username}. Re-link?`);
|
|
42407
|
+
if (!ok) {
|
|
42408
|
+
writeInfo("Aborted. Existing link preserved.");
|
|
42409
|
+
return;
|
|
42410
|
+
}
|
|
42411
|
+
}
|
|
42412
|
+
const result = await provider.login({
|
|
42413
|
+
device: opts.device,
|
|
42414
|
+
force: opts.force
|
|
42415
|
+
});
|
|
42416
|
+
writeSuccess(`Logged in to GitHub as @${result.username}.`);
|
|
42417
|
+
trackEvent("cli_git_auth_succeeded", {
|
|
42418
|
+
provider: "github",
|
|
42419
|
+
flow: "device"
|
|
42420
|
+
});
|
|
42421
|
+
}, "git-auth-github");
|
|
42422
|
+
}
|
|
42423
|
+
__name(gitAuthGithubCommand, "gitAuthGithubCommand");
|
|
42424
|
+
function gitAuthStatusCommand() {
|
|
42425
|
+
return withErrorHandling(async () => {
|
|
42426
|
+
const provider = new GitHubProvider();
|
|
42427
|
+
const s = await provider.status();
|
|
42428
|
+
if (!s.linked) {
|
|
42429
|
+
console.log("Not linked. Run `lua git auth github` to link.");
|
|
42430
|
+
} else {
|
|
42431
|
+
console.log(`Linked: github \u2014 @${s.username} (scopes: ${s.scopes.join(", ")}) \u2014 linked ${formatLinkedAt(s.linkedAt)}`);
|
|
42432
|
+
}
|
|
42433
|
+
trackEvent("cli_git_auth_status_completed", {
|
|
42434
|
+
linked: s.linked
|
|
42435
|
+
});
|
|
42436
|
+
}, "git-auth-status");
|
|
42437
|
+
}
|
|
42438
|
+
__name(gitAuthStatusCommand, "gitAuthStatusCommand");
|
|
42439
|
+
function gitAuthDisconnectCommand(provider) {
|
|
42440
|
+
return withErrorHandling(async () => {
|
|
42441
|
+
const name = (provider ?? "github").toLowerCase();
|
|
42442
|
+
if (name !== "github") {
|
|
42443
|
+
writeError(`Unknown provider: ${name}. v1 only supports github.`);
|
|
42444
|
+
return;
|
|
42445
|
+
}
|
|
42446
|
+
const p = new GitHubProvider();
|
|
42447
|
+
const s = await p.status();
|
|
42448
|
+
if (!s.linked) {
|
|
42449
|
+
console.log(`Not linked to ${name}. Nothing to do.`);
|
|
42450
|
+
return;
|
|
42451
|
+
}
|
|
42452
|
+
await p.logout();
|
|
42453
|
+
writeSuccess(`Disconnected from ${name}.`);
|
|
42454
|
+
trackEvent("cli_git_auth_disconnect_completed", {
|
|
42455
|
+
provider: name
|
|
42456
|
+
});
|
|
42457
|
+
}, "git-auth-disconnect");
|
|
42458
|
+
}
|
|
42459
|
+
__name(gitAuthDisconnectCommand, "gitAuthDisconnectCommand");
|
|
42460
|
+
function formatLinkedAt(iso) {
|
|
42461
|
+
try {
|
|
42462
|
+
return new Date(iso).toISOString().replace("T", " ").replace(/\..*$/, " UTC");
|
|
42463
|
+
} catch {
|
|
42464
|
+
return iso;
|
|
42465
|
+
}
|
|
42466
|
+
}
|
|
42467
|
+
__name(formatLinkedAt, "formatLinkedAt");
|
|
42468
|
+
|
|
41906
42469
|
// src/commands/pull.ts
|
|
41907
42470
|
init_cli();
|
|
41908
42471
|
init_command_utils();
|
|
@@ -42597,7 +43160,12 @@ Examples:
|
|
|
42597
43160
|
$ lua voice test --runner vitest Force vitest
|
|
42598
43161
|
`).action(voiceTestCommand);
|
|
42599
43162
|
voice.command("list").description("List LuaVoice primitives in the compiled manifest").option("--json", "Output as JSON").action(voiceListCommand);
|
|
42600
|
-
const versionGroup = program2.command("version").description("\u{1F3F7}\uFE0F Manage agent versions
|
|
43163
|
+
const versionGroup = program2.command("version").description("\u{1F3F7}\uFE0F Manage agent versions \u2014 atomic snapshots (requires agentVersioningEnabled for your org)").addHelpText("after", `
|
|
43164
|
+
Note:
|
|
43165
|
+
All \`lua version\` subcommands require the \`agentVersioningEnabled\`
|
|
43166
|
+
feature flag for your org. If a subcommand errors with "Agent versioning
|
|
43167
|
+
is not enabled," contact your admin to enable it.
|
|
43168
|
+
`);
|
|
42601
43169
|
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", `
|
|
42602
43170
|
Examples:
|
|
42603
43171
|
$ lua version create Snapshot current state
|
|
@@ -42632,9 +43200,15 @@ Examples:
|
|
|
42632
43200
|
$ lua version delete v3 --force Skip confirmation
|
|
42633
43201
|
`).action((version, opts) => versionDeleteCommand(version, opts));
|
|
42634
43202
|
const gitGroup = program2.command("git").description("Manage opt-in git auto-commits for this project");
|
|
42635
|
-
gitGroup.command("connect").description("Enable git auto-commits (runs sanity checks, writes git.enabled: true)").action(() => gitConnectCommand(
|
|
43203
|
+
gitGroup.command("connect").description("Enable git auto-commits (runs sanity checks, writes git.enabled: true)").option("--auto-push", "Also enable post-commit auto-push (requires `lua git auth github` + a GitHub HTTPS origin remote)").action((opts) => gitConnectCommand({
|
|
43204
|
+
autoPush: opts.autoPush
|
|
43205
|
+
}));
|
|
42636
43206
|
gitGroup.command("disconnect").description("Disable git auto-commits (existing commits/tags untouched)").action(() => gitDisconnectCommand());
|
|
42637
43207
|
gitGroup.command("status").description("Show git integration config + last lua-issued commit/tag").action(() => gitStatusCommand());
|
|
43208
|
+
const gitAuthGroup = gitGroup.command("auth").description("Manage git remote provider authentication");
|
|
43209
|
+
gitAuthGroup.command("github").description("Link a GitHub account (OAuth device flow)").option("--device", "(deprecated; device flow is always used \u2014 flag kept for compatibility)").option("--force", "Overwrite an existing link without prompting").action((opts) => gitAuthGithubCommand(opts));
|
|
43210
|
+
gitAuthGroup.command("status").description("Show the current git auth status").action(() => gitAuthStatusCommand());
|
|
43211
|
+
gitAuthGroup.command("disconnect [provider]").description("Disconnect a git provider (default: github)").action((provider) => gitAuthDisconnectCommand(provider));
|
|
42638
43212
|
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", `
|
|
42639
43213
|
Examples:
|
|
42640
43214
|
$ lua pull Restore the latest source backup
|