lua-cli 3.17.0 → 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/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;
@@ -22139,6 +22243,507 @@ __name(getCommitSha, "getCommitSha");
22139
22243
 
22140
22244
  // src/utils/try-git-commit.ts
22141
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>&#x2713; 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
22142
22747
  async function tryGitCommit(opts) {
22143
22748
  const config = readYamlConfig();
22144
22749
  if (!config?.git?.enabled) {
@@ -22221,6 +22826,12 @@ async function tryGitCommit(opts) {
22221
22826
  has_sha: Boolean(sha),
22222
22827
  has_tag: tagged === true
22223
22828
  });
22829
+ await tryGitAutoPush({
22830
+ config,
22831
+ cwd: opts.cwd,
22832
+ commitSha: sha,
22833
+ action: opts.action
22834
+ });
22224
22835
  return {
22225
22836
  committed: true,
22226
22837
  sha,
@@ -24154,11 +24765,11 @@ init_cli();
24154
24765
 
24155
24766
  // src/utils/sandbox-storage.ts
24156
24767
  init_constants();
24157
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
24158
- import { dirname as dirname6 } from "path";
24159
- function readStore() {
24768
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
24769
+ import { dirname as dirname7 } from "path";
24770
+ function readStore2() {
24160
24771
  try {
24161
- const raw = readFileSync10(SANDBOX_STORAGE_FILE, "utf8");
24772
+ const raw = readFileSync11(SANDBOX_STORAGE_FILE, "utf8");
24162
24773
  const parsed = JSON.parse(raw);
24163
24774
  return {
24164
24775
  skills: parsed.skills ?? {},
@@ -24175,28 +24786,28 @@ function readStore() {
24175
24786
  };
24176
24787
  }
24177
24788
  }
24178
- __name(readStore, "readStore");
24179
- function writeStore(store) {
24789
+ __name(readStore2, "readStore");
24790
+ function writeStore2(store) {
24180
24791
  try {
24181
- mkdirSync7(dirname6(SANDBOX_STORAGE_FILE), {
24792
+ mkdirSync8(dirname7(SANDBOX_STORAGE_FILE), {
24182
24793
  recursive: true
24183
24794
  });
24184
- writeFileSync7(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
24795
+ writeFileSync8(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
24185
24796
  } catch {
24186
24797
  }
24187
24798
  }
24188
- __name(writeStore, "writeStore");
24799
+ __name(writeStore2, "writeStore");
24189
24800
  async function getSandboxSkillId(skillName) {
24190
- const store = readStore();
24801
+ const store = readStore2();
24191
24802
  const key = skillName ?? "__default__";
24192
24803
  return store.skills[key] ?? null;
24193
24804
  }
24194
24805
  __name(getSandboxSkillId, "getSandboxSkillId");
24195
24806
  async function setSandboxSkillId(sandboxId, skillName) {
24196
- const store = readStore();
24807
+ const store = readStore2();
24197
24808
  const key = skillName ?? "__default__";
24198
24809
  store.skills[key] = sandboxId;
24199
- writeStore(store);
24810
+ writeStore2(store);
24200
24811
  }
24201
24812
  __name(setSandboxSkillId, "setSandboxSkillId");
24202
24813
  async function getAllSandboxSkillIds(yamlConfig) {
@@ -24222,14 +24833,14 @@ async function getAllSandboxSkillIds(yamlConfig) {
24222
24833
  }
24223
24834
  __name(getAllSandboxSkillIds, "getAllSandboxSkillIds");
24224
24835
  async function getSandboxPreProcessorId(preprocessorName) {
24225
- const store = readStore();
24836
+ const store = readStore2();
24226
24837
  return store.preprocessors[preprocessorName] ?? null;
24227
24838
  }
24228
24839
  __name(getSandboxPreProcessorId, "getSandboxPreProcessorId");
24229
24840
  async function setSandboxPreProcessorId(sandboxId, preprocessorName) {
24230
- const store = readStore();
24841
+ const store = readStore2();
24231
24842
  store.preprocessors[preprocessorName] = sandboxId;
24232
- writeStore(store);
24843
+ writeStore2(store);
24233
24844
  }
24234
24845
  __name(setSandboxPreProcessorId, "setSandboxPreProcessorId");
24235
24846
  async function getAllSandboxPreProcessorIds(config) {
@@ -24254,14 +24865,14 @@ async function getAllSandboxPreProcessorIds(config) {
24254
24865
  }
24255
24866
  __name(getAllSandboxPreProcessorIds, "getAllSandboxPreProcessorIds");
24256
24867
  async function getSandboxPostProcessorId(postprocessorName) {
24257
- const store = readStore();
24868
+ const store = readStore2();
24258
24869
  return store.postprocessors[postprocessorName] ?? null;
24259
24870
  }
24260
24871
  __name(getSandboxPostProcessorId, "getSandboxPostProcessorId");
24261
24872
  async function setSandboxPostProcessorId(sandboxId, postprocessorName) {
24262
- const store = readStore();
24873
+ const store = readStore2();
24263
24874
  store.postprocessors[postprocessorName] = sandboxId;
24264
- writeStore(store);
24875
+ writeStore2(store);
24265
24876
  }
24266
24877
  __name(setSandboxPostProcessorId, "setSandboxPostProcessorId");
24267
24878
  async function getAllSandboxPostProcessorIds(config) {
@@ -27593,7 +28204,7 @@ __name(viewResourceInteractive, "viewResourceInteractive");
27593
28204
  init_cli();
27594
28205
  init_command_utils();
27595
28206
  init_analytics();
27596
- import open from "open";
28207
+ import open2 from "open";
27597
28208
  async function adminCommand() {
27598
28209
  return withErrorHandling(async () => {
27599
28210
  writeProgress("Opening Lua Admin Dashboard...");
@@ -27601,7 +28212,7 @@ async function adminCommand() {
27601
28212
  showProgress: false
27602
28213
  });
27603
28214
  const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
27604
- await open(adminUrl);
28215
+ await open2(adminUrl);
27605
28216
  writeSuccess("Lua Admin Dashboard opened in your browser");
27606
28217
  console.log(`
27607
28218
  Dashboard URL: https://admin.heylua.ai`);
@@ -27617,7 +28228,7 @@ __name(adminCommand, "adminCommand");
27617
28228
  init_cli();
27618
28229
  init_command_utils();
27619
28230
  init_analytics();
27620
- import open2 from "open";
28231
+ import open3 from "open";
27621
28232
  async function evalsCommand() {
27622
28233
  return withErrorHandling(async () => {
27623
28234
  writeProgress("Opening Lua Evaluations Dashboard...");
@@ -27625,7 +28236,7 @@ async function evalsCommand() {
27625
28236
  showProgress: false
27626
28237
  });
27627
28238
  const evalsUrl = `https://evals.heylua.ai?apiKey=${apiKey}&agentID=${agentId}`;
27628
- await open2(evalsUrl);
28239
+ await open3(evalsUrl);
27629
28240
  writeSuccess("Lua Evaluations Dashboard opened in your browser");
27630
28241
  console.log(`
27631
28242
  Dashboard URL: https://evals.heylua.ai`);
@@ -27639,12 +28250,12 @@ __name(evalsCommand, "evalsCommand");
27639
28250
  // src/commands/docs.ts
27640
28251
  init_cli();
27641
28252
  init_analytics();
27642
- import open3 from "open";
28253
+ import open4 from "open";
27643
28254
  async function docsCommand() {
27644
28255
  return withErrorHandling(async () => {
27645
28256
  writeProgress("Opening Lua Documentation...");
27646
28257
  const docsUrl = "https://docs.heylua.ai";
27647
- await open3(docsUrl);
28258
+ await open4(docsUrl);
27648
28259
  writeSuccess("Lua Documentation opened in your browser");
27649
28260
  console.log(`
27650
28261
  Documentation: ${docsUrl}
@@ -27658,7 +28269,7 @@ __name(docsCommand, "docsCommand");
27658
28269
  init_cli();
27659
28270
  init_command_utils();
27660
28271
  import inquirer12 from "inquirer";
27661
- import open4 from "open";
28272
+ import open5 from "open";
27662
28273
 
27663
28274
  // src/api/channels.api.service.ts
27664
28275
  init_http_client();
@@ -28227,7 +28838,7 @@ async function openAdminDashboard(apiKey, config) {
28227
28838
  throw new Error("No orgId found in lua.skill.yaml. Please ensure your configuration is valid.");
28228
28839
  }
28229
28840
  const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
28230
- await open4(adminUrl);
28841
+ await open5(adminUrl);
28231
28842
  writeSuccess("\u2705 Lua Admin Dashboard opened in your browser");
28232
28843
  console.log(`
28233
28844
  Dashboard URL: https://admin.heylua.ai`);
@@ -30358,7 +30969,7 @@ init_auth();
30358
30969
  init_auth_api_service();
30359
30970
  init_files();
30360
30971
  init_artifact_loader();
30361
- import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
30972
+ import { existsSync as existsSync7, readFileSync as readFileSync12 } from "fs";
30362
30973
  import { join as join6 } from "path";
30363
30974
  import { performance } from "perf_hooks";
30364
30975
  import * as os from "os";
@@ -30709,7 +31320,7 @@ function gatherTelemetry() {
30709
31320
  } else {
30710
31321
  try {
30711
31322
  if (existsSync7(TELEMETRY_FILE)) {
30712
- const raw = readFileSync11(TELEMETRY_FILE, "utf8");
31323
+ const raw = readFileSync12(TELEMETRY_FILE, "utf8");
30713
31324
  const cfg = JSON.parse(raw);
30714
31325
  if (typeof cfg.enabled === "boolean") enabled = cfg.enabled;
30715
31326
  }
@@ -37011,7 +37622,7 @@ init_cli();
37011
37622
  init_constants();
37012
37623
  import http from "http";
37013
37624
  import { URL as URL2 } from "url";
37014
- import open5 from "open";
37625
+ import open6 from "open";
37015
37626
  init_command_utils();
37016
37627
  init_developer_api_service();
37017
37628
 
@@ -38034,7 +38645,7 @@ Integration: ${selectedIntegration.name}`);
38034
38645
  `);
38035
38646
  const callbackPromise = startCallbackServer(3e5);
38036
38647
  try {
38037
- await open5(authUrl);
38648
+ await open6(authUrl);
38038
38649
  writeInfo("\u{1F310} Browser opened - please complete the authorization");
38039
38650
  } catch (error) {
38040
38651
  writeInfo("\u{1F4A1} Could not open browser automatically. Please open the URL above manually.");
@@ -38465,7 +39076,7 @@ Available scopes for ${selectedIntegration.name}:`);
38465
39076
  `);
38466
39077
  const callbackPromise = startCallbackServer(3e5);
38467
39078
  try {
38468
- await open5(authUrl);
39079
+ await open6(authUrl);
38469
39080
  writeInfo("\u{1F310} Browser opened - please complete the authorization");
38470
39081
  } catch (error) {
38471
39082
  writeInfo("\u{1F4A1} Could not open browser automatically. Please open the URL above manually.");
@@ -39890,7 +40501,7 @@ __name(telemetryCommand, "telemetryCommand");
39890
40501
  init_cli();
39891
40502
  init_command_utils();
39892
40503
  init_analytics();
39893
- import { writeFileSync as writeFileSync8, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
40504
+ import { writeFileSync as writeFileSync9, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
39894
40505
  import { resolve as resolve4, join as join7 } from "path";
39895
40506
  init_artifact_loader();
39896
40507
  init_types();
@@ -40051,7 +40662,7 @@ async function governanceCommand(action) {
40051
40662
  }
40052
40663
  }
40053
40664
  const content = generateFile(setup);
40054
- writeFileSync8(filePath, content, "utf-8");
40665
+ writeFileSync9(filePath, content, "utf-8");
40055
40666
  const relativePath = filePath.replace(process.cwd() + "/", "");
40056
40667
  writeSuccess(`Created ${relativePath}`);
40057
40668
  console.log("");
@@ -40678,7 +41289,7 @@ __name(pumpRemoteAudioToSpeaker, "pumpRemoteAudioToSpeaker");
40678
41289
  // src/commands/voice-browser.ts
40679
41290
  init_cli();
40680
41291
  import http2 from "http";
40681
- import open6 from "open";
41292
+ import open7 from "open";
40682
41293
  var SAFETY_TIMEOUT_MS = 60 * 60 * 1e3;
40683
41294
  async function runBrowserMode(joinUrl) {
40684
41295
  const { wsUrl, token } = parseJoinUrl(joinUrl);
@@ -40710,7 +41321,7 @@ async function runBrowserMode(joinUrl) {
40710
41321
  const localUrl = `http://localhost:${port}/`;
40711
41322
  writeSuccess(`Voice room ready (opening browser)`);
40712
41323
  console.log(` ${localUrl}`);
40713
- await open6(localUrl);
41324
+ await open7(localUrl);
40714
41325
  await new Promise((resolve6) => {
40715
41326
  const safety = setTimeout(() => {
40716
41327
  try {
@@ -41903,6 +42514,77 @@ async function gitStatusCommand() {
41903
42514
  }
41904
42515
  __name(gitStatusCommand, "gitStatusCommand");
41905
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
+
41906
42588
  // src/commands/pull.ts
41907
42589
  init_cli();
41908
42590
  init_command_utils();
@@ -42597,7 +43279,12 @@ Examples:
42597
43279
  $ lua voice test --runner vitest Force vitest
42598
43280
  `).action(voiceTestCommand);
42599
43281
  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 (atomic snapshots of agent state)");
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
+ `);
42601
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", `
42602
43289
  Examples:
42603
43290
  $ lua version create Snapshot current state
@@ -42635,6 +43322,10 @@ Examples:
42635
43322
  gitGroup.command("connect").description("Enable git auto-commits (runs sanity checks, writes git.enabled: true)").action(() => gitConnectCommand());
42636
43323
  gitGroup.command("disconnect").description("Disable git auto-commits (existing commits/tags untouched)").action(() => gitDisconnectCommand());
42637
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));
42638
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", `
42639
43330
  Examples:
42640
43331
  $ lua pull Restore the latest source backup