perchai-cli 2.4.49 → 2.4.50

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.
Files changed (2) hide show
  1. package/dist/perch.mjs +664 -629
  2. package/package.json +1 -1
package/dist/perch.mjs CHANGED
@@ -75566,7 +75566,6 @@ var init_payroll = __esm({
75566
75566
  // lib/perchBusinessTools/index.ts
75567
75567
  var init_perchBusinessTools = __esm({
75568
75568
  "lib/perchBusinessTools/index.ts"() {
75569
- "use strict";
75570
75569
  init_generateAPAuditPacket();
75571
75570
  init_inventoryFolder();
75572
75571
  init_loadBusinessTables();
@@ -75918,6 +75917,7 @@ function isTurnAbortedError(error) {
75918
75917
  var TURN_STOPPED_BY_USER_MESSAGE;
75919
75918
  var init_turnAbort = __esm({
75920
75919
  "features/perchTerminal/runtime/turnAbort.ts"() {
75920
+ "use strict";
75921
75921
  TURN_STOPPED_BY_USER_MESSAGE = "Turn stopped by user.";
75922
75922
  }
75923
75923
  });
@@ -76223,6 +76223,7 @@ function getToolDisplayName(toolName) {
76223
76223
  var NON_MODULE_TOOL_OWNERS, TOOL_RISK, TOOL_DISPLAY_NAMES;
76224
76224
  var init_catalog = __esm({
76225
76225
  "features/perchTerminal/runtime/toolSystem/catalog.ts"() {
76226
+ "use strict";
76226
76227
  init_toolNames();
76227
76228
  NON_MODULE_TOOL_OWNERS = {
76228
76229
  [TOOL_NAMES.listSources]: "lane",
@@ -91148,6 +91149,7 @@ Final answers lead with findings, name artifacts or delivery status, and give on
91148
91149
  var MARKET_DESK_TOOL_NAMES;
91149
91150
  var init_marketDeskAccess = __esm({
91150
91151
  "features/perchTerminal/runtime/marketDesk/marketDeskAccess.ts"() {
91152
+ "use strict";
91151
91153
  init_toolNames();
91152
91154
  MARKET_DESK_TOOL_NAMES = /* @__PURE__ */ new Set([
91153
91155
  TOOL_NAMES.getMarketSignal,
@@ -91827,6 +91829,7 @@ function listFinancialPlaybooks() {
91827
91829
  var AP_AUDIT_PACKET_DEF, PLAYBOOKS;
91828
91830
  var init_registry2 = __esm({
91829
91831
  "features/perchTerminal/runtime/financialPlaybooks/registry.ts"() {
91832
+ "use strict";
91830
91833
  init_managedWorkflowRegistry2();
91831
91834
  init_toolNames();
91832
91835
  AP_AUDIT_PACKET_DEF = {
@@ -119507,6 +119510,21 @@ var init_browserSession = __esm({
119507
119510
  }
119508
119511
  });
119509
119512
 
119513
+ // features/perchTerminal/runtime/cliAuthRefreshRegistry.ts
119514
+ function setCliModelProxyAuthRefresher(fn) {
119515
+ refresher = fn;
119516
+ }
119517
+ function refreshCliModelProxyAuth() {
119518
+ return refresher ? refresher() : Promise.resolve(null);
119519
+ }
119520
+ var refresher;
119521
+ var init_cliAuthRefreshRegistry = __esm({
119522
+ "features/perchTerminal/runtime/cliAuthRefreshRegistry.ts"() {
119523
+ "use strict";
119524
+ refresher = null;
119525
+ }
119526
+ });
119527
+
119510
119528
  // features/perchTerminal/runtime/roostUserSelectionClient.ts
119511
119529
  function coerceSelection(raw) {
119512
119530
  const choice = normalizeRoostModelChoice(raw?.choice);
@@ -119546,507 +119564,6 @@ var init_roostUserSelectionClient = __esm({
119546
119564
  }
119547
119565
  });
119548
119566
 
119549
- // features/perchTerminal/runtime/cliHost/cliAuthSession.ts
119550
- var cliAuthSession_exports = {};
119551
- __export(cliAuthSession_exports, {
119552
- clearStoredCliAuthSession: () => clearStoredCliAuthSession,
119553
- isStoredCliAuthSessionUsable: () => isStoredCliAuthSessionUsable,
119554
- parseStoredCliAuthSession: () => parseStoredCliAuthSession,
119555
- readStoredCliAuthSession: () => readStoredCliAuthSession,
119556
- writeStoredCliAuthSession: () => writeStoredCliAuthSession
119557
- });
119558
- import { execFile } from "node:child_process";
119559
- import fs9 from "node:fs/promises";
119560
- import os from "node:os";
119561
- import path10 from "node:path";
119562
- import { promisify } from "node:util";
119563
- async function readStoredCliAuthSession() {
119564
- const raw = shouldUseFallbackAuthFile() ? await readFallbackSecret().catch(() => null) : process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
119565
- return parseStoredCliAuthSession(raw);
119566
- }
119567
- async function writeStoredCliAuthSession(session) {
119568
- const raw = JSON.stringify(session);
119569
- if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
119570
- await execFileAsync("security", [
119571
- "add-generic-password",
119572
- "-U",
119573
- "-s",
119574
- KEYCHAIN_SERVICE,
119575
- "-a",
119576
- KEYCHAIN_ACCOUNT,
119577
- "-w",
119578
- raw
119579
- ]);
119580
- return;
119581
- }
119582
- await writeFallbackSecret(raw);
119583
- }
119584
- async function clearStoredCliAuthSession() {
119585
- if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
119586
- await execFileAsync("security", [
119587
- "delete-generic-password",
119588
- "-s",
119589
- KEYCHAIN_SERVICE,
119590
- "-a",
119591
- KEYCHAIN_ACCOUNT
119592
- ]).catch(() => void 0);
119593
- return;
119594
- }
119595
- await fs9.rm(fallbackSessionPath(), { force: true }).catch(() => void 0);
119596
- }
119597
- function shouldUseFallbackAuthFile() {
119598
- return Boolean(process.env.PERCH_CLI_AUTH_DIR?.trim());
119599
- }
119600
- function isStoredCliAuthSessionUsable(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
119601
- if (!session?.accessToken?.trim()) return false;
119602
- if (!session.appUrl?.trim()) return false;
119603
- if (!session.expiresAt) return true;
119604
- return session.expiresAt > nowSeconds + 90;
119605
- }
119606
- function parseStoredCliAuthSession(raw) {
119607
- if (!raw?.trim()) return null;
119608
- try {
119609
- const parsed = JSON.parse(raw);
119610
- if (parsed.version !== 1) return null;
119611
- if (!parsed.appUrl || !parsed.accessToken || !parsed.updatedAt) return null;
119612
- return {
119613
- version: 1,
119614
- appUrl: parsed.appUrl,
119615
- accessToken: parsed.accessToken,
119616
- refreshToken: parsed.refreshToken ?? null,
119617
- expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : null,
119618
- userId: parsed.userId ?? null,
119619
- email: parsed.email ?? null,
119620
- updatedAt: parsed.updatedAt
119621
- };
119622
- } catch {
119623
- return null;
119624
- }
119625
- }
119626
- async function readKeychainSecret() {
119627
- const { stdout } = await execFileAsync("security", [
119628
- "find-generic-password",
119629
- "-s",
119630
- KEYCHAIN_SERVICE,
119631
- "-a",
119632
- KEYCHAIN_ACCOUNT,
119633
- "-w"
119634
- ]);
119635
- return stdout.trim() || null;
119636
- }
119637
- function fallbackSessionPath() {
119638
- const base = process.env.PERCH_CLI_AUTH_DIR?.trim() || path10.join(os.homedir(), ".perch");
119639
- return path10.join(base, "cli-auth-session.json");
119640
- }
119641
- async function readFallbackSecret() {
119642
- return await fs9.readFile(fallbackSessionPath(), "utf8");
119643
- }
119644
- async function writeFallbackSecret(raw) {
119645
- const filePath = fallbackSessionPath();
119646
- await fs9.mkdir(path10.dirname(filePath), { recursive: true, mode: 448 });
119647
- await fs9.writeFile(filePath, raw, { mode: 384 });
119648
- }
119649
- var execFileAsync, KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT;
119650
- var init_cliAuthSession = __esm({
119651
- "features/perchTerminal/runtime/cliHost/cliAuthSession.ts"() {
119652
- "use strict";
119653
- execFileAsync = promisify(execFile);
119654
- KEYCHAIN_SERVICE = "app.perchai.cli-auth";
119655
- KEYCHAIN_ACCOUNT = "default";
119656
- }
119657
- });
119658
-
119659
- // features/perchTerminal/runtime/publicModelDefault.ts
119660
- function parsePublicModelDefaultPayload(value) {
119661
- const raw = value && typeof value === "object" ? value : {};
119662
- const selection = sanitizeFounderModelSelection(
119663
- raw.selection ?? DEFAULT_FOUNDER_MODEL_SELECTION
119664
- );
119665
- return {
119666
- selection,
119667
- updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : null,
119668
- updatedBy: typeof raw.updatedBy === "string" ? raw.updatedBy : null
119669
- };
119670
- }
119671
- var init_publicModelDefault = __esm({
119672
- "features/perchTerminal/runtime/publicModelDefault.ts"() {
119673
- "use strict";
119674
- init_modelRegistry();
119675
- }
119676
- });
119677
-
119678
- // features/perchTerminal/runtime/cliHost/modelConnection.ts
119679
- async function connectCliModelProxy(input = {}) {
119680
- const storedSession = input.storedSession === void 0 ? await readStoredCliAuthSession() : input.storedSession;
119681
- const usableStoredSession = await ensureFreshCliAuthSession({
119682
- session: storedSession,
119683
- fetchImpl: input.fetchImpl
119684
- });
119685
- const appUrl = resolveCliAppUrl(input.appUrl, usableStoredSession?.appUrl ?? null);
119686
- if (!appUrl) return noCliModelConnection();
119687
- if (!usableStoredSession?.accessToken) return noCliModelConnection(appUrl);
119688
- const fetchImpl = input.fetchImpl ?? globalThis.fetch;
119689
- if (typeof fetchImpl !== "function") return noCliModelConnection(appUrl);
119690
- const selection = await fetchPublicModelDefaultSelection(
119691
- appUrl,
119692
- fetchImpl,
119693
- usableStoredSession?.accessToken ?? null
119694
- );
119695
- if (!selection) return noCliModelConnection(appUrl);
119696
- const priorProxy = process.env[MODEL_PROXY_ENV];
119697
- const priorToken = process.env[MODEL_PROXY_TOKEN_ENV];
119698
- process.env[MODEL_PROXY_ENV] = appUrl;
119699
- if (usableStoredSession?.accessToken) {
119700
- process.env[MODEL_PROXY_TOKEN_ENV] = usableStoredSession.accessToken;
119701
- }
119702
- return {
119703
- appUrl,
119704
- authenticated: !!usableStoredSession?.accessToken,
119705
- userId: usableStoredSession?.userId ?? null,
119706
- email: usableStoredSession?.email ?? null,
119707
- founderModelSelection: selection,
119708
- restore: () => {
119709
- if (priorProxy === void 0) {
119710
- delete process.env[MODEL_PROXY_ENV];
119711
- } else {
119712
- process.env[MODEL_PROXY_ENV] = priorProxy;
119713
- }
119714
- if (priorToken === void 0) {
119715
- delete process.env[MODEL_PROXY_TOKEN_ENV];
119716
- } else {
119717
- process.env[MODEL_PROXY_TOKEN_ENV] = priorToken;
119718
- }
119719
- }
119720
- };
119721
- }
119722
- function resolveCliAppUrl(explicit, stored) {
119723
- const raw = explicit?.trim() || process.env[CLI_APP_URL_ENV]?.trim() || process.env[FALLBACK_APP_URL_ENV]?.trim() || stored?.trim() || DEFAULT_CLI_APP_URL;
119724
- if (!raw) return null;
119725
- const withProtocol = /^https?:\/\//i.test(raw) ? raw : `http://${raw}`;
119726
- try {
119727
- const url = new URL(withProtocol);
119728
- url.pathname = url.pathname.replace(/\/+$/, "");
119729
- url.search = "";
119730
- url.hash = "";
119731
- return url.toString().replace(/\/+$/, "");
119732
- } catch {
119733
- return null;
119734
- }
119735
- }
119736
- async function fetchPublicModelDefaultSelection(appUrl, fetchImpl, accessToken) {
119737
- const url = `${appUrl}/api/perch-terminal/public-model-default`;
119738
- const controller = new AbortController();
119739
- const timeout = setTimeout(() => controller.abort(), 3500);
119740
- try {
119741
- const response = await fetchImpl(url, {
119742
- method: "GET",
119743
- headers: {
119744
- Accept: "application/json",
119745
- ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
119746
- },
119747
- signal: controller.signal
119748
- });
119749
- if (!response.ok) return null;
119750
- const body = await response.json();
119751
- if (!body || typeof body !== "object") return null;
119752
- const record = body;
119753
- if (record.ok !== true) return null;
119754
- return parsePublicModelDefaultPayload({ selection: record.selection }).selection;
119755
- } catch {
119756
- return null;
119757
- } finally {
119758
- clearTimeout(timeout);
119759
- }
119760
- }
119761
- function noCliModelConnection(appUrl = null) {
119762
- return {
119763
- appUrl,
119764
- authenticated: false,
119765
- userId: null,
119766
- email: null,
119767
- founderModelSelection: null,
119768
- restore: () => {
119769
- }
119770
- };
119771
- }
119772
- var MODEL_PROXY_ENV, MODEL_PROXY_TOKEN_ENV, CLI_APP_URL_ENV, FALLBACK_APP_URL_ENV, DEFAULT_CLI_APP_URL;
119773
- var init_modelConnection = __esm({
119774
- "features/perchTerminal/runtime/cliHost/modelConnection.ts"() {
119775
- "use strict";
119776
- init_publicModelDefault();
119777
- init_cliAuthSession();
119778
- init_cliAuthRefresh();
119779
- MODEL_PROXY_ENV = "PERCH_MODEL_CALL_PROXY_URL";
119780
- MODEL_PROXY_TOKEN_ENV = "PERCH_MODEL_CALL_PROXY_TOKEN";
119781
- CLI_APP_URL_ENV = "PERCH_CLI_APP_URL";
119782
- FALLBACK_APP_URL_ENV = "PERCH_APP_URL";
119783
- DEFAULT_CLI_APP_URL = "https://app.perchai.app";
119784
- }
119785
- });
119786
-
119787
- // features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts
119788
- import { execFile as execFile2 } from "node:child_process";
119789
- import http from "node:http";
119790
- import { promisify as promisify2 } from "node:util";
119791
- async function runCliStandaloneOAuthLogin(input) {
119792
- const appUrl = resolveCliAppUrl(input.appUrl, null);
119793
- if (!appUrl) {
119794
- return { ok: false, code: "invalid_app_url", error: "Invalid Perch app URL." };
119795
- }
119796
- const fetchImpl = input.fetchImpl ?? globalThis.fetch;
119797
- if (typeof fetchImpl !== "function") {
119798
- return { ok: false, code: "fetch_unavailable", error: "This Node runtime does not expose fetch." };
119799
- }
119800
- const config = await fetchCliAuthConfig(appUrl, fetchImpl);
119801
- if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) {
119802
- return {
119803
- ok: false,
119804
- code: "auth_config_unavailable",
119805
- error: "The Perch app did not return CLI auth configuration."
119806
- };
119807
- }
119808
- const provider = selectOAuthProvider(config.providers ?? [], input.provider);
119809
- if (!provider) {
119810
- return {
119811
- ok: false,
119812
- code: "oauth_provider_unavailable",
119813
- error: "No supported CLI OAuth provider is enabled for this Perch app."
119814
- };
119815
- }
119816
- const callback = await createOAuthCallbackServer({
119817
- host: config.redirectHost?.trim() || "127.0.0.1",
119818
- timeoutMs: input.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS
119819
- });
119820
- try {
119821
- const memoryStorage = createMemoryAuthStorage();
119822
- const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
119823
- auth: {
119824
- flowType: "pkce",
119825
- autoRefreshToken: false,
119826
- detectSessionInUrl: false,
119827
- persistSession: true,
119828
- storage: memoryStorage
119829
- }
119830
- });
119831
- const { data, error } = await supabase.auth.signInWithOAuth({
119832
- provider,
119833
- options: {
119834
- redirectTo: callback.redirectTo,
119835
- skipBrowserRedirect: true,
119836
- data: { signup_source: "cli" }
119837
- }
119838
- });
119839
- if (error || !data.url) {
119840
- return {
119841
- ok: false,
119842
- code: "oauth_start_failed",
119843
- error: error?.message ?? "Supabase did not return an OAuth URL."
119844
- };
119845
- }
119846
- await (input.openExternal ?? openExternal)(data.url);
119847
- const callbackResult = await callback.waitForCode();
119848
- if (!callbackResult.ok) {
119849
- return callbackResult;
119850
- }
119851
- const exchanged = await supabase.auth.exchangeCodeForSession(callbackResult.code);
119852
- if (exchanged.error || !exchanged.data.session) {
119853
- return {
119854
- ok: false,
119855
- code: "oauth_exchange_failed",
119856
- error: exchanged.error?.message ?? "Supabase did not return a CLI session."
119857
- };
119858
- }
119859
- const session = storedSessionFromSupabaseSession({
119860
- appUrl,
119861
- accessToken: exchanged.data.session.access_token,
119862
- refreshToken: exchanged.data.session.refresh_token,
119863
- expiresAt: exchanged.data.session.expires_at ?? null,
119864
- userId: exchanged.data.session.user.id,
119865
- email: exchanged.data.session.user.email ?? null
119866
- });
119867
- await writeStoredCliAuthSession(session);
119868
- return { ok: true, session };
119869
- } finally {
119870
- await callback.close();
119871
- }
119872
- }
119873
- async function fetchCliAuthConfig(appUrl, fetchImpl = globalThis.fetch) {
119874
- const response = await fetchImpl(`${appUrl}/api/perch-terminal/cli-auth/config`, {
119875
- method: "GET",
119876
- headers: { Accept: "application/json" }
119877
- });
119878
- if (!response.ok) return { ok: false };
119879
- return await response.json();
119880
- }
119881
- function selectOAuthProvider(providers, preferred) {
119882
- const available = new Set(providers);
119883
- if (preferred && available.has(preferred)) return preferred;
119884
- if (available.has("google")) return "google";
119885
- if (available.has("github")) return "github";
119886
- return null;
119887
- }
119888
- function storedSessionFromSupabaseSession(input) {
119889
- return {
119890
- version: 1,
119891
- appUrl: input.appUrl,
119892
- accessToken: input.accessToken,
119893
- refreshToken: input.refreshToken ?? null,
119894
- expiresAt: input.expiresAt ?? null,
119895
- userId: input.userId ?? null,
119896
- email: input.email ?? null,
119897
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
119898
- };
119899
- }
119900
- function createMemoryAuthStorage() {
119901
- const values2 = /* @__PURE__ */ new Map();
119902
- return {
119903
- getItem: (key) => values2.get(key) ?? null,
119904
- setItem: (key, value) => {
119905
- values2.set(key, value);
119906
- },
119907
- removeItem: (key) => {
119908
- values2.delete(key);
119909
- }
119910
- };
119911
- }
119912
- async function createOAuthCallbackServer(input) {
119913
- let resolveResult = null;
119914
- const resultPromise = new Promise((resolve5) => {
119915
- resolveResult = resolve5;
119916
- });
119917
- const server = http.createServer((request, response) => {
119918
- const requestUrl = new URL(request.url ?? "/", `http://${input.host}`);
119919
- if (requestUrl.pathname !== CALLBACK_PATH) {
119920
- response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
119921
- response.end("Not found");
119922
- return;
119923
- }
119924
- const code = requestUrl.searchParams.get("code");
119925
- const error = requestUrl.searchParams.get("error_description") ?? requestUrl.searchParams.get("error");
119926
- if (!code) {
119927
- response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
119928
- response.end(renderCallbackHtml(false));
119929
- resolveResult?.({
119930
- ok: false,
119931
- code: "oauth_callback_missing_code",
119932
- error: error ?? "OAuth callback did not include a code."
119933
- });
119934
- resolveResult = null;
119935
- return;
119936
- }
119937
- response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
119938
- response.end(renderCallbackHtml(true));
119939
- resolveResult?.({ ok: true, code });
119940
- resolveResult = null;
119941
- });
119942
- await new Promise((resolve5, reject2) => {
119943
- server.once("error", reject2);
119944
- server.listen(0, input.host, () => {
119945
- server.off("error", reject2);
119946
- resolve5();
119947
- });
119948
- });
119949
- const address = server.address();
119950
- const redirectTo = `http://${input.host}:${address.port}${CALLBACK_PATH}`;
119951
- const timeout = setTimeout(() => {
119952
- resolveResult?.({
119953
- ok: false,
119954
- code: "oauth_timeout",
119955
- error: "Timed out waiting for browser sign-in to complete."
119956
- });
119957
- resolveResult = null;
119958
- }, input.timeoutMs);
119959
- return {
119960
- redirectTo,
119961
- waitForCode: async () => resultPromise,
119962
- close: async () => {
119963
- clearTimeout(timeout);
119964
- await new Promise((resolve5) => server.close(() => resolve5()));
119965
- }
119966
- };
119967
- }
119968
- function renderCallbackHtml(ok) {
119969
- const title = ok ? "Perch CLI signed in" : "Perch CLI sign-in failed";
119970
- const message = ok ? "You can close this tab and return to your terminal." : "Return to your terminal and try again.";
119971
- return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title></head><body style="font-family:system-ui;padding:32px;background:#100d0b;color:#fff8f0"><h1>${title}</h1><p>${message}</p></body></html>`;
119972
- }
119973
- async function openExternal(url) {
119974
- if (process.platform === "darwin") {
119975
- await execFileAsync2("open", [url]);
119976
- } else if (process.platform === "win32") {
119977
- await execFileAsync2("cmd", ["/c", "start", "", url]);
119978
- } else {
119979
- await execFileAsync2("xdg-open", [url]);
119980
- }
119981
- }
119982
- var execFileAsync2, DEFAULT_LOGIN_TIMEOUT_MS, CALLBACK_PATH;
119983
- var init_cliStandaloneOAuth = __esm({
119984
- "features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts"() {
119985
- "use strict";
119986
- init_dist4();
119987
- init_modelConnection();
119988
- init_cliAuthSession();
119989
- execFileAsync2 = promisify2(execFile2);
119990
- DEFAULT_LOGIN_TIMEOUT_MS = 12e4;
119991
- CALLBACK_PATH = "/callback";
119992
- }
119993
- });
119994
-
119995
- // features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts
119996
- var cliAuthRefresh_exports = {};
119997
- __export(cliAuthRefresh_exports, {
119998
- ensureFreshCliAuthSession: () => ensureFreshCliAuthSession
119999
- });
120000
- async function ensureFreshCliAuthSession(input) {
120001
- const session = input.session;
120002
- if (!session?.accessToken?.trim()) return null;
120003
- const nowSeconds = Math.floor(Date.now() / 1e3);
120004
- const stillUsable = !session.expiresAt || session.expiresAt > nowSeconds + 90;
120005
- if (stillUsable && !input.forceRefresh) return session;
120006
- if (!session.refreshToken?.trim()) return null;
120007
- const fetchImpl = input.fetchImpl ?? globalThis.fetch;
120008
- if (typeof fetchImpl !== "function") return null;
120009
- const appUrl = session.appUrl?.trim();
120010
- if (!appUrl) return null;
120011
- const config = await fetchCliAuthConfig(appUrl, fetchImpl).catch(() => ({ ok: false }));
120012
- if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) return null;
120013
- const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
120014
- auth: {
120015
- autoRefreshToken: false,
120016
- detectSessionInUrl: false,
120017
- persistSession: false
120018
- }
120019
- });
120020
- try {
120021
- const { data, error } = await supabase.auth.refreshSession({
120022
- refresh_token: session.refreshToken
120023
- });
120024
- if (error || !data.session) return null;
120025
- const refreshed = {
120026
- version: 1,
120027
- appUrl: session.appUrl,
120028
- accessToken: data.session.access_token,
120029
- refreshToken: data.session.refresh_token ?? session.refreshToken,
120030
- expiresAt: data.session.expires_at ?? null,
120031
- userId: data.session.user?.id ?? session.userId ?? null,
120032
- email: data.session.user?.email ?? session.email ?? null,
120033
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
120034
- };
120035
- await writeStoredCliAuthSession(refreshed);
120036
- return refreshed;
120037
- } catch {
120038
- return null;
120039
- }
120040
- }
120041
- var init_cliAuthRefresh = __esm({
120042
- "features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts"() {
120043
- "use strict";
120044
- init_dist4();
120045
- init_cliStandaloneOAuth();
120046
- init_cliAuthSession();
120047
- }
120048
- });
120049
-
120050
119567
  // features/perchTerminal/runtime/modelRouter.ts
120051
119568
  function roostUserSelectionBodyFields() {
120052
119569
  const selection = getEffectiveRoostUserSelection();
@@ -120344,17 +119861,7 @@ async function syncModelProxyAuthAfter401() {
120344
119861
  await syncBrowserAuthSession();
120345
119862
  return null;
120346
119863
  }
120347
- const { readStoredCliAuthSession: readStoredCliAuthSession2 } = await Promise.resolve().then(() => (init_cliAuthSession(), cliAuthSession_exports));
120348
- const { ensureFreshCliAuthSession: ensureFreshCliAuthSession2 } = await Promise.resolve().then(() => (init_cliAuthRefresh(), cliAuthRefresh_exports));
120349
- const fresh = await ensureFreshCliAuthSession2({
120350
- session: await readStoredCliAuthSession2(),
120351
- forceRefresh: true
120352
- });
120353
- if (!fresh?.accessToken?.trim()) return null;
120354
- if (typeof process !== "undefined") {
120355
- process.env[MODEL_CALL_PROXY_TOKEN_ENV] = fresh.accessToken;
120356
- }
120357
- return fresh.accessToken;
119864
+ return refreshCliModelProxyAuth();
120358
119865
  }
120359
119866
  function withModelProxyAuthHeader(init, token) {
120360
119867
  if (!token?.trim()) return init;
@@ -120515,6 +120022,7 @@ var init_modelRouter = __esm({
120515
120022
  init_contextMeter();
120516
120023
  init_inferenceUsageRecorder();
120517
120024
  init_browserSession();
120025
+ init_cliAuthRefreshRegistry();
120518
120026
  init_roostUserSelectionClient();
120519
120027
  init_providerModelStep();
120520
120028
  init_toolCallNormalizer();
@@ -140010,6 +139518,7 @@ function offSandboxEvent(runId, handler) {
140010
139518
  var LOCAL_WORKSPACE_ACCESS_UNAVAILABLE;
140011
139519
  var init_bridge = __esm({
140012
139520
  "features/perchTerminal/desktop/bridge.ts"() {
139521
+ "use strict";
140013
139522
  LOCAL_WORKSPACE_ACCESS_UNAVAILABLE = "Local workspace access is unavailable. Open Perch Desktop or update the app.";
140014
139523
  }
140015
139524
  });
@@ -198227,7 +197736,6 @@ function recordDispatchBatchResult(tasks, result2, ctx, opts = {}) {
198227
197736
  var lifecycleByRun, MAX_LIFECYCLE_RUNS;
198228
197737
  var init_workerLifecycle = __esm({
198229
197738
  "features/perchTerminal/runtime/workers/workerLifecycle.ts"() {
198230
- "use strict";
198231
197739
  init_workerManifest();
198232
197740
  lifecycleByRun = /* @__PURE__ */ new Map();
198233
197741
  MAX_LIFECYCLE_RUNS = 200;
@@ -214765,8 +214273,8 @@ var init_barSeries = __esm({
214765
214273
  });
214766
214274
 
214767
214275
  // lib/perchMarketDesk/data/fixtureStore.ts
214768
- import fs10 from "node:fs";
214769
- import path11 from "node:path";
214276
+ import fs9 from "node:fs";
214277
+ import path10 from "node:path";
214770
214278
  function createFixtureStore(input) {
214771
214279
  const instruments = [...input.instruments].sort((a, b2) => a.ticker.localeCompare(b2.ticker));
214772
214280
  const seriesByTicker = /* @__PURE__ */ new Map();
@@ -214813,22 +214321,22 @@ function createFixtureStore(input) {
214813
214321
  };
214814
214322
  }
214815
214323
  function loadFixtureStoreFromDir(dir) {
214816
- const instrumentsPath = path11.join(dir, "instruments.json");
214817
- const instruments = JSON.parse(fs10.readFileSync(instrumentsPath, "utf8"));
214324
+ const instrumentsPath = path10.join(dir, "instruments.json");
214325
+ const instruments = JSON.parse(fs9.readFileSync(instrumentsPath, "utf8"));
214818
214326
  const bars = [];
214819
- const barsDir = path11.join(dir, "bars");
214820
- for (const file of fs10.readdirSync(barsDir)) {
214327
+ const barsDir = path10.join(dir, "bars");
214328
+ for (const file of fs9.readdirSync(barsDir)) {
214821
214329
  if (!file.endsWith(".json")) continue;
214822
- const ticker = path11.basename(file, ".json");
214823
- const rows = JSON.parse(fs10.readFileSync(path11.join(barsDir, file), "utf8"));
214330
+ const ticker = path10.basename(file, ".json");
214331
+ const rows = JSON.parse(fs9.readFileSync(path10.join(barsDir, file), "utf8"));
214824
214332
  for (const row of rows) bars.push({ ...row, ticker: row.ticker ?? ticker });
214825
214333
  }
214826
214334
  let fundamentals;
214827
- const fundamentalsPath = path11.join(dir, "fundamentals.json");
214828
- if (fs10.existsSync(fundamentalsPath)) {
214829
- fundamentals = JSON.parse(fs10.readFileSync(fundamentalsPath, "utf8"));
214335
+ const fundamentalsPath = path10.join(dir, "fundamentals.json");
214336
+ if (fs9.existsSync(fundamentalsPath)) {
214337
+ fundamentals = JSON.parse(fs9.readFileSync(fundamentalsPath, "utf8"));
214830
214338
  }
214831
- return createFixtureStore({ instruments, bars, fundamentals, label: `dir:${path11.basename(dir)}` });
214339
+ return createFixtureStore({ instruments, bars, fundamentals, label: `dir:${path10.basename(dir)}` });
214832
214340
  }
214833
214341
  var init_fixtureStore = __esm({
214834
214342
  "lib/perchMarketDesk/data/fixtureStore.ts"() {
@@ -224356,7 +223864,7 @@ async function runFlockTurn(input, deps, options = {}) {
224356
223864
  toolCallsReserved += reservedToolCalls;
224357
223865
  emitWorkerUpdate(emit, plan, worker, "running");
224358
223866
  try {
224359
- turnInput = await refreshFlockCliAuth(turnInput);
223867
+ turnInput = deps.refreshTurnInput ? await deps.refreshTurnInput(turnInput) : turnInput;
224360
223868
  const context = contextOverride ?? buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan);
224361
223869
  const objective = worker.role === "worker" && contextContainsSources(context) ? `${worker.objective}
224362
223870
  ${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
@@ -224761,24 +224269,6 @@ function flockWorkerStatusFromSpawnResult(result2) {
224761
224269
  if (result2.usabilityStatus === "needs_user_input") return "blocked";
224762
224270
  return "done";
224763
224271
  }
224764
- async function refreshFlockCliAuth(input) {
224765
- if (input.cliLocalTools !== true) return input;
224766
- const stored = await readStoredCliAuthSession();
224767
- if (!stored?.accessToken?.trim()) return input;
224768
- const turnToken = input.cliServerAccessToken?.trim();
224769
- if (turnToken && turnToken !== stored.accessToken) return input;
224770
- const fresh = await ensureFreshCliAuthSession({ session: stored });
224771
- if (!fresh?.accessToken?.trim()) return input;
224772
- if (fresh.accessToken === input.cliServerAccessToken) return input;
224773
- if (typeof process !== "undefined") {
224774
- process.env.PERCH_MODEL_CALL_PROXY_TOKEN = fresh.accessToken;
224775
- }
224776
- return {
224777
- ...input,
224778
- cliServerAccessToken: fresh.accessToken,
224779
- marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? fresh.accessToken
224780
- };
224781
- }
224782
224272
  function now11() {
224783
224273
  return (/* @__PURE__ */ new Date()).toISOString();
224784
224274
  }
@@ -224789,8 +224279,6 @@ var init_runFlockTurn = __esm({
224789
224279
  init_operatorStream();
224790
224280
  init_runRegistry();
224791
224281
  init_spawnWorker2();
224792
- init_cliAuthSession();
224793
- init_cliAuthRefresh();
224794
224282
  init_registry();
224795
224283
  init_flockCommand();
224796
224284
  init_flockLimits();
@@ -233104,9 +232592,9 @@ var init_backgroundTaskTypes = __esm({
233104
232592
  });
233105
232593
 
233106
232594
  // features/perchTerminal/runtime/background/backgroundTaskRegistry.ts
233107
- import fs11 from "node:fs";
233108
- import os2 from "node:os";
233109
- import path12 from "node:path";
232595
+ import fs10 from "node:fs";
232596
+ import os from "node:os";
232597
+ import path11 from "node:path";
233110
232598
  function isTerminalStatus(status) {
233111
232599
  return status !== "running";
233112
232600
  }
@@ -233163,19 +232651,19 @@ function runtimeMs(record) {
233163
232651
  function readOutputPreview(outputPath, previewBytes = BACKGROUND_TASK_BOUNDS.previewBytes) {
233164
232652
  if (!outputPath) return { preview: "", truncated: false };
233165
232653
  try {
233166
- const stat2 = fs11.statSync(outputPath);
233167
- const fd = fs11.openSync(outputPath, "r");
232654
+ const stat2 = fs10.statSync(outputPath);
232655
+ const fd = fs10.openSync(outputPath, "r");
233168
232656
  try {
233169
232657
  if (stat2.size <= previewBytes) {
233170
232658
  const buf2 = Buffer.alloc(stat2.size);
233171
- fs11.readSync(fd, buf2, 0, stat2.size, 0);
232659
+ fs10.readSync(fd, buf2, 0, stat2.size, 0);
233172
232660
  return { preview: buf2.toString("utf8"), truncated: false };
233173
232661
  }
233174
232662
  const buf = Buffer.alloc(previewBytes);
233175
- fs11.readSync(fd, buf, 0, previewBytes, stat2.size - previewBytes);
232663
+ fs10.readSync(fd, buf, 0, previewBytes, stat2.size - previewBytes);
233176
232664
  return { preview: buf.toString("utf8"), truncated: true };
233177
232665
  } finally {
233178
- fs11.closeSync(fd);
232666
+ fs10.closeSync(fd);
233179
232667
  }
233180
232668
  } catch {
233181
232669
  return { preview: "", truncated: false };
@@ -233219,19 +232707,19 @@ var init_backgroundTaskRegistry = __esm({
233219
232707
  records = /* @__PURE__ */ new Map();
233220
232708
  loaded = false;
233221
232709
  constructor(options = {}) {
233222
- this.baseDir = options.baseDir ?? path12.join(os2.homedir(), ".perch");
233223
- this.storePath = path12.join(this.baseDir, STORE_FILE);
233224
- this.outputDir = path12.join(this.baseDir, "background-output");
232710
+ this.baseDir = options.baseDir ?? path11.join(os.homedir(), ".perch");
232711
+ this.storePath = path11.join(this.baseDir, STORE_FILE);
232712
+ this.outputDir = path11.join(this.baseDir, "background-output");
233225
232713
  }
233226
232714
  ensureDirs() {
233227
- fs11.mkdirSync(this.baseDir, { recursive: true });
233228
- fs11.mkdirSync(this.outputDir, { recursive: true });
232715
+ fs10.mkdirSync(this.baseDir, { recursive: true });
232716
+ fs10.mkdirSync(this.outputDir, { recursive: true });
233229
232717
  }
233230
232718
  load() {
233231
232719
  if (this.loaded) return;
233232
232720
  this.loaded = true;
233233
232721
  try {
233234
- const raw = fs11.readFileSync(this.storePath, "utf8");
232722
+ const raw = fs10.readFileSync(this.storePath, "utf8");
233235
232723
  const parsed = JSON.parse(raw);
233236
232724
  const tasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
233237
232725
  this.records = new Map(tasks.map((t) => [t.taskId, t]));
@@ -233243,11 +232731,11 @@ var init_backgroundTaskRegistry = __esm({
233243
232731
  this.ensureDirs();
233244
232732
  const tasks = Array.from(this.records.values());
233245
232733
  const tmp = `${this.storePath}.tmp`;
233246
- fs11.writeFileSync(tmp, JSON.stringify({ version: 1, tasks }, null, 2), "utf8");
233247
- fs11.renameSync(tmp, this.storePath);
232734
+ fs10.writeFileSync(tmp, JSON.stringify({ version: 1, tasks }, null, 2), "utf8");
232735
+ fs10.renameSync(tmp, this.storePath);
233248
232736
  }
233249
232737
  outputPathFor(taskId) {
233250
- return path12.join(this.outputDir, `${taskId}.log`);
232738
+ return path11.join(this.outputDir, `${taskId}.log`);
233251
232739
  }
233252
232740
  list() {
233253
232741
  this.load();
@@ -233409,14 +232897,14 @@ var init_backgroundWakeController = __esm({
233409
232897
 
233410
232898
  // features/perchTerminal/runtime/background/runBackgroundCommand.ts
233411
232899
  import { spawn as spawn2 } from "node:child_process";
233412
- import fs12 from "node:fs";
233413
- import path13 from "node:path";
232900
+ import fs11 from "node:fs";
232901
+ import path12 from "node:path";
233414
232902
  function newTaskId() {
233415
232903
  return `bg-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
233416
232904
  }
233417
232905
  function createCappedWriter(filePath, maxBytes) {
233418
- fs12.mkdirSync(path13.dirname(filePath), { recursive: true });
233419
- const stream = fs12.createWriteStream(filePath, { flags: "w" });
232906
+ fs11.mkdirSync(path12.dirname(filePath), { recursive: true });
232907
+ const stream = fs11.createWriteStream(filePath, { flags: "w" });
233420
232908
  let written = 0;
233421
232909
  let truncated = false;
233422
232910
  return {
@@ -233589,10 +233077,10 @@ var init_runBackgroundCommand = __esm({
233589
233077
 
233590
233078
  // features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts
233591
233079
  import { spawn as spawn3 } from "node:child_process";
233592
- import fs13 from "node:fs";
233080
+ import fs12 from "node:fs";
233593
233081
  import fsp from "node:fs/promises";
233594
- import os3 from "node:os";
233595
- import path14 from "node:path";
233082
+ import os2 from "node:os";
233083
+ import path13 from "node:path";
233596
233084
  function installCliNodeLocalBridge(input) {
233597
233085
  const globalRef = globalThis;
233598
233086
  const previous = globalRef.__PERCH_TEST_DESKTOP_BRIDGE__;
@@ -233606,7 +233094,7 @@ function installCliNodeLocalBridge(input) {
233606
233094
  };
233607
233095
  }
233608
233096
  function readCliProjectMemoryState(workspaceRoot) {
233609
- const root2 = path14.resolve(expandHome4(workspaceRoot));
233097
+ const root2 = path13.resolve(expandHome4(workspaceRoot));
233610
233098
  if (!isCliProjectMemoryAvailable(root2)) {
233611
233099
  return {
233612
233100
  available: false,
@@ -233621,7 +233109,7 @@ function readCliProjectMemoryState(workspaceRoot) {
233621
233109
  };
233622
233110
  }
233623
233111
  function createCliNodeLocalBridge(input) {
233624
- const workspaceRoot = path14.resolve(input.workspaceRoot);
233112
+ const workspaceRoot = path13.resolve(input.workspaceRoot);
233625
233113
  const now23 = () => (/* @__PURE__ */ new Date()).toISOString();
233626
233114
  const sandboxHandlers = /* @__PURE__ */ new Map();
233627
233115
  const emitSandboxEvent = (event) => {
@@ -233635,7 +233123,7 @@ function createCliNodeLocalBridge(input) {
233635
233123
  cwd: workspaceRoot,
233636
233124
  projectRoot: workspaceRoot,
233637
233125
  shellPath: process.env.SHELL || "/bin/zsh",
233638
- shellName: path14.basename(process.env.SHELL || "zsh"),
233126
+ shellName: path13.basename(process.env.SHELL || "zsh"),
233639
233127
  path: process.env.PATH || "",
233640
233128
  status: "running",
233641
233129
  backend: "spawn-fallback",
@@ -233662,7 +233150,7 @@ function createCliNodeLocalBridge(input) {
233662
233150
  capabilities: ["local-files", "local-shell"]
233663
233151
  }),
233664
233152
  checkFsAccess: async (request) => {
233665
- const absolutePath = path14.resolve(expandHome4(request.absolutePath));
233153
+ const absolutePath = path13.resolve(expandHome4(request.absolutePath));
233666
233154
  return {
233667
233155
  decision: "allow",
233668
233156
  reason: "Allowed by Perch CLI local filesystem access.",
@@ -233680,7 +233168,7 @@ function createCliNodeLocalBridge(input) {
233680
233168
  ok: true,
233681
233169
  directory: workspaceRoot,
233682
233170
  fileName: request.fileName,
233683
- absolutePath: path14.join(workspaceRoot, request.fileName),
233171
+ absolutePath: path13.join(workspaceRoot, request.fileName),
233684
233172
  source: "approved_root"
233685
233173
  }),
233686
233174
  validateWorkspaceRoot: async () => ({
@@ -233769,7 +233257,7 @@ function createCliNodeLocalBridge(input) {
233769
233257
  writeWorkspaceFile: async (request) => {
233770
233258
  const baseRoot = resolveRequestRoot(workspaceRoot, request.workspaceRoot);
233771
233259
  const target = resolveWritePath(baseRoot, request.relativePath);
233772
- await fsp.mkdir(path14.dirname(target), { recursive: true });
233260
+ await fsp.mkdir(path13.dirname(target), { recursive: true });
233773
233261
  const flag = request.overwrite === true ? "w" : "wx";
233774
233262
  const payload = request.encoding === "base64" && typeof request.contentBase64 === "string" ? Buffer.from(request.contentBase64, "base64") : request.content ?? "";
233775
233263
  try {
@@ -233789,14 +233277,14 @@ function createCliNodeLocalBridge(input) {
233789
233277
  moveLocalFile: async (request) => {
233790
233278
  const src = resolveReadPath(workspaceRoot, request.src);
233791
233279
  const dest = resolveWritePath(workspaceRoot, request.dest);
233792
- await fsp.mkdir(path14.dirname(dest), { recursive: true });
233280
+ await fsp.mkdir(path13.dirname(dest), { recursive: true });
233793
233281
  await fsp.rename(src, dest);
233794
233282
  return { ok: true, fromPath: src, toPath: dest };
233795
233283
  },
233796
233284
  copyLocalFile: async (request) => {
233797
233285
  const src = resolveReadPath(workspaceRoot, request.src);
233798
233286
  const dest = resolveWritePath(workspaceRoot, request.dest);
233799
- await fsp.mkdir(path14.dirname(dest), { recursive: true });
233287
+ await fsp.mkdir(path13.dirname(dest), { recursive: true });
233800
233288
  await fsp.copyFile(src, dest);
233801
233289
  return { ok: true, fromPath: src, toPath: dest };
233802
233290
  },
@@ -233821,7 +233309,7 @@ function createCliNodeLocalBridge(input) {
233821
233309
  const pattern = request.pattern?.trim() || "**/*";
233822
233310
  const regex2 = globToRegex(pattern);
233823
233311
  const files = await collectFiles(root2, { maxVisits: Math.max(maxResults * 20, 1e3) });
233824
- const matches = files.map((file) => path14.relative(root2, file).replace(/\\/g, "/")).filter((relative2) => regex2.test(relative2)).slice(0, maxResults);
233312
+ const matches = files.map((file) => path13.relative(root2, file).replace(/\\/g, "/")).filter((relative2) => regex2.test(relative2)).slice(0, maxResults);
233825
233313
  return {
233826
233314
  ok: true,
233827
233315
  matches,
@@ -233844,7 +233332,7 @@ function createCliNodeLocalBridge(input) {
233844
233332
  const files = await collectFiles(root2, { maxVisits: 5e3 });
233845
233333
  const matches = [];
233846
233334
  for (const file of files) {
233847
- const relative2 = path14.relative(root2, file).replace(/\\/g, "/");
233335
+ const relative2 = path13.relative(root2, file).replace(/\\/g, "/");
233848
233336
  if (includeRegex && !includeRegex.test(relative2)) continue;
233849
233337
  const text = await readTextFileIfReasonable(file);
233850
233338
  if (text === null) continue;
@@ -233883,7 +233371,7 @@ function createCliNodeLocalBridge(input) {
233883
233371
  const stat2 = await safeStat(target);
233884
233372
  return {
233885
233373
  ok: true,
233886
- relativePath: path14.isAbsolute(request.relativePath) || request.relativePath.startsWith("~") ? target : path14.relative(baseRoot, target) || ".",
233374
+ relativePath: path13.isAbsolute(request.relativePath) || request.relativePath.startsWith("~") ? target : path13.relative(baseRoot, target) || ".",
233887
233375
  exists: Boolean(stat2),
233888
233376
  isFile: stat2?.isFile() ?? false,
233889
233377
  isDirectory: stat2?.isDirectory() ?? false,
@@ -233917,7 +233405,7 @@ function createCliNodeLocalBridge(input) {
233917
233405
  return {
233918
233406
  ok: true,
233919
233407
  data: buffer.toString("base64"),
233920
- fileName: path14.relative(workspaceRoot, target) || path14.basename(target),
233408
+ fileName: path13.relative(workspaceRoot, target) || path13.basename(target),
233921
233409
  mimeType: inferMimeType(target),
233922
233410
  sizeBytes: stat2.size,
233923
233411
  encoding: "base64"
@@ -234013,7 +233501,7 @@ async function runCliPrepareAPEvidence(workspaceRoot, request) {
234013
233501
  topCases: (artifact.cases ?? []).slice(0, 12),
234014
233502
  duplicateCases: result2.payload.duplicateCases,
234015
233503
  controlGraph: result2.payload.controlGraph,
234016
- relativeOutputDir: typeof artifact.outputDir === "string" ? path14.basename(artifact.outputDir) : null,
233504
+ relativeOutputDir: typeof artifact.outputDir === "string" ? path13.basename(artifact.outputDir) : null,
234017
233505
  approvedRootMatch: true
234018
233506
  },
234019
233507
  executionHost: "electron_desktop",
@@ -234102,7 +233590,7 @@ async function prepareCliSandboxInputs(workspaceRoot, request) {
234102
233590
  const jobId = request.jobId?.trim() || `cli-sandbox-${Date.now()}`;
234103
233591
  const maxFiles = Math.max(1, Math.min(request.maxFiles ?? 20, 100));
234104
233592
  const maxBytesPerFile = Math.max(1, Math.min(request.maxBytesPerFile ?? 20 * 1024 * 1024, 50 * 1024 * 1024));
234105
- const tempDir = await fsp.mkdtemp(path14.join(os3.tmpdir(), `${jobId}-`));
233593
+ const tempDir = await fsp.mkdtemp(path13.join(os2.tmpdir(), `${jobId}-`));
234106
233594
  const entries = [];
234107
233595
  const skipped = [];
234108
233596
  for (const localSourceId of request.localSourceIds.slice(0, maxFiles)) {
@@ -234116,13 +233604,13 @@ async function prepareCliSandboxInputs(workspaceRoot, request) {
234116
233604
  skipped.push({ localSourceId, reason: `File is too large: ${stat2.size} bytes.` });
234117
233605
  continue;
234118
233606
  }
234119
- const relativePath = safeSandboxRelativePath(path14.isAbsolute(localSourceId) || localSourceId.startsWith("~") ? path14.basename(sourcePath) : path14.relative(workspaceRoot, sourcePath));
234120
- const tempPath = path14.join(tempDir, relativePath);
234121
- await fsp.mkdir(path14.dirname(tempPath), { recursive: true });
233607
+ const relativePath = safeSandboxRelativePath(path13.isAbsolute(localSourceId) || localSourceId.startsWith("~") ? path13.basename(sourcePath) : path13.relative(workspaceRoot, sourcePath));
233608
+ const tempPath = path13.join(tempDir, relativePath);
233609
+ await fsp.mkdir(path13.dirname(tempPath), { recursive: true });
234122
233610
  await fsp.copyFile(sourcePath, tempPath);
234123
233611
  entries.push({
234124
233612
  localSourceId,
234125
- fileName: path14.basename(sourcePath),
233613
+ fileName: path13.basename(sourcePath),
234126
233614
  relativePath,
234127
233615
  tempPath,
234128
233616
  sizeBytes: stat2.size,
@@ -234160,11 +233648,11 @@ async function runCliSandboxCodeJob(workspaceRoot, request, onEvent) {
234160
233648
  getApprovedRoot: (rootId) => rootId === CLI_ROOT_ID ? { id: CLI_ROOT_ID, path: workspaceRoot, approvedAt: (/* @__PURE__ */ new Date()).toISOString() } : null,
234161
233649
  isPathSafe: (_rootPath, targetPath) => !targetPath.split(/[\\/]+/).includes(".."),
234162
233650
  isInsideApprovedRoot: (filePath) => {
234163
- const absolute = path14.resolve(expandHome4(filePath));
233651
+ const absolute = path13.resolve(expandHome4(filePath));
234164
233652
  if (isInside(workspaceRoot, absolute)) {
234165
233653
  return { id: CLI_ROOT_ID, path: workspaceRoot, approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
234166
233654
  }
234167
- return { id: "cli-absolute-root", path: path14.dirname(absolute), approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
233655
+ return { id: "cli-absolute-root", path: path13.dirname(absolute), approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
234168
233656
  },
234169
233657
  shouldIgnore: (fileName) => IGNORED_DIRS.has(fileName),
234170
233658
  guessMimeType: inferMimeType,
@@ -234207,7 +233695,7 @@ function safeSandboxRelativePath(value) {
234207
233695
  return clean || "input";
234208
233696
  }
234209
233697
  function guessCliFileType(filePath) {
234210
- const ext = path14.extname(filePath).toLowerCase();
233698
+ const ext = path13.extname(filePath).toLowerCase();
234211
233699
  if ([".xlsx", ".xls"].includes(ext)) return "spreadsheet";
234212
233700
  if (ext === ".csv" || ext === ".tsv") return "csv";
234213
233701
  if (ext === ".pdf") return "pdf";
@@ -234226,7 +233714,7 @@ async function readCliProjectMemoryBridge(workspaceRoot) {
234226
233714
  return { ok: true, meta: state.meta };
234227
233715
  }
234228
233716
  async function writeCliProjectMemory(workspaceRoot, patch) {
234229
- const root2 = path14.resolve(expandHome4(workspaceRoot));
233717
+ const root2 = path13.resolve(expandHome4(workspaceRoot));
234230
233718
  if (!isCliProjectMemoryAvailable(root2)) {
234231
233719
  return {
234232
233720
  ok: false,
@@ -234242,12 +233730,12 @@ async function writeCliProjectMemory(workspaceRoot, patch) {
234242
233730
  lastOpenedThreadId: patch.lastOpenedThreadId !== void 0 ? patch.lastOpenedThreadId : current.lastOpenedThreadId,
234243
233731
  memorySummary: patch.memorySummary !== void 0 ? patch.memorySummary : current.memorySummary
234244
233732
  };
234245
- await fsp.mkdir(path14.join(root2, PERCH_DIR), { recursive: true });
233733
+ await fsp.mkdir(path13.join(root2, PERCH_DIR), { recursive: true });
234246
233734
  await atomicWriteCliUtf8(getCliProjectFilePath(root2), JSON.stringify(next, null, 2));
234247
233735
  return { ok: true, meta: enrichCliProjectMeta(root2, next) };
234248
233736
  }
234249
233737
  async function writeCliMemoryFile(workspaceRoot, request) {
234250
- const root2 = path14.resolve(expandHome4(workspaceRoot));
233738
+ const root2 = path13.resolve(expandHome4(workspaceRoot));
234251
233739
  if (!isCliProjectMemoryAvailable(root2)) {
234252
233740
  return {
234253
233741
  ok: false,
@@ -234259,8 +233747,8 @@ async function writeCliMemoryFile(workspaceRoot, request) {
234259
233747
  }
234260
233748
  const fileName = normalizeCliMemoryFileName(request.fileName);
234261
233749
  if (!fileName) return { ok: false, error: "Invalid memory file name." };
234262
- const relativePath = path14.join(PERCH_DIR, MEMORY_DIR, fileName);
234263
- const fullPath = path14.join(root2, relativePath);
233750
+ const relativePath = path13.join(PERCH_DIR, MEMORY_DIR, fileName);
233751
+ const fullPath = path13.join(root2, relativePath);
234264
233752
  const existing = await fsp.readFile(fullPath, "utf8").catch(() => "");
234265
233753
  let nextContent;
234266
233754
  try {
@@ -234273,7 +233761,7 @@ async function writeCliMemoryFile(workspaceRoot, request) {
234273
233761
  }
234274
233762
  const capError = assertCliMemoryWithinCap(nextContent);
234275
233763
  if (capError) return { ok: false, error: capError };
234276
- await fsp.mkdir(path14.dirname(fullPath), { recursive: true });
233764
+ await fsp.mkdir(path13.dirname(fullPath), { recursive: true });
234277
233765
  await atomicWriteCliUtf8(fullPath, nextContent);
234278
233766
  const updatedFile = readCliTextMemoryFile(root2, relativePath, MAX_MEMORY_BYTES);
234279
233767
  return {
@@ -234299,17 +233787,17 @@ function defaultCliProjectMeta() {
234299
233787
  }
234300
233788
  function isCliProjectMemoryAvailable(root2) {
234301
233789
  try {
234302
- return fs13.statSync(path14.join(root2, PERCH_DIR)).isDirectory();
233790
+ return fs12.statSync(path13.join(root2, PERCH_DIR)).isDirectory();
234303
233791
  } catch {
234304
233792
  return false;
234305
233793
  }
234306
233794
  }
234307
233795
  function getCliProjectFilePath(root2) {
234308
- return path14.join(root2, PERCH_DIR, PROJECT_FILE);
233796
+ return path13.join(root2, PERCH_DIR, PROJECT_FILE);
234309
233797
  }
234310
233798
  function loadCliStoredMeta(root2) {
234311
233799
  try {
234312
- const raw = fs13.readFileSync(getCliProjectFilePath(root2), "utf8");
233800
+ const raw = fs12.readFileSync(getCliProjectFilePath(root2), "utf8");
234313
233801
  return { ...defaultCliProjectMeta(), ...JSON.parse(raw) };
234314
233802
  } catch {
234315
233803
  return defaultCliProjectMeta();
@@ -234317,7 +233805,7 @@ function loadCliStoredMeta(root2) {
234317
233805
  }
234318
233806
  function enrichCliProjectMeta(root2, base) {
234319
233807
  const perchMd = readCliFirstExisting(root2, [
234320
- path14.join(PERCH_DIR, PERCH_INDEX_FILE),
233808
+ path13.join(PERCH_DIR, PERCH_INDEX_FILE),
234321
233809
  PERCH_INDEX_FILE
234322
233810
  ], MAX_TEXT_BYTES);
234323
233811
  const rules = readCliRules(root2);
@@ -234428,10 +233916,10 @@ function readCliFirstExisting(root2, relativePaths, maxBytes) {
234428
233916
  return null;
234429
233917
  }
234430
233918
  function readCliRules(root2) {
234431
- const dirPath = path14.join(root2, PERCH_DIR, RULES_DIR);
233919
+ const dirPath = path13.join(root2, PERCH_DIR, RULES_DIR);
234432
233920
  try {
234433
- return fs13.readdirSync(dirPath).filter((name) => [".md", ".txt"].includes(path14.extname(name).toLowerCase())).slice(0, 40).map((name) => readCliTextMemoryFile(root2, path14.join(PERCH_DIR, RULES_DIR, name), MAX_TEXT_BYTES)).filter((file) => file.found).map((file) => ({
234434
- fileName: path14.basename(file.relativePath),
233921
+ return fs12.readdirSync(dirPath).filter((name) => [".md", ".txt"].includes(path13.extname(name).toLowerCase())).slice(0, 40).map((name) => readCliTextMemoryFile(root2, path13.join(PERCH_DIR, RULES_DIR, name), MAX_TEXT_BYTES)).filter((file) => file.found).map((file) => ({
233922
+ fileName: path13.basename(file.relativePath),
234435
233923
  relativePath: file.relativePath,
234436
233924
  content: file.content,
234437
233925
  sizeBytes: file.sizeBytes,
@@ -234442,15 +233930,15 @@ function readCliRules(root2) {
234442
233930
  }
234443
233931
  }
234444
233932
  function readCliMemoryFiles(root2) {
234445
- const memoryDir = path14.join(root2, PERCH_DIR, MEMORY_DIR);
233933
+ const memoryDir = path13.join(root2, PERCH_DIR, MEMORY_DIR);
234446
233934
  const discovered = /* @__PURE__ */ new Set();
234447
233935
  const memoryFiles = [];
234448
233936
  try {
234449
- for (const name of fs13.readdirSync(memoryDir).slice(0, MAX_MEMORY_FILES)) {
234450
- const ext = path14.extname(name).toLowerCase();
233937
+ for (const name of fs12.readdirSync(memoryDir).slice(0, MAX_MEMORY_FILES)) {
233938
+ const ext = path13.extname(name).toLowerCase();
234451
233939
  if (ext !== ".md" && ext !== ".txt") continue;
234452
233940
  discovered.add(name);
234453
- memoryFiles.push(readCliTextMemoryFile(root2, path14.join(PERCH_DIR, MEMORY_DIR, name), MAX_MEMORY_BYTES));
233941
+ memoryFiles.push(readCliTextMemoryFile(root2, path13.join(PERCH_DIR, MEMORY_DIR, name), MAX_MEMORY_BYTES));
234454
233942
  }
234455
233943
  } catch {
234456
233944
  }
@@ -234458,20 +233946,20 @@ function readCliMemoryFiles(root2) {
234458
233946
  memoryFiles,
234459
233947
  missingMemoryFiles: EXPECTED_MEMORY_FILES.filter((name) => !discovered.has(name)).map((name) => ({
234460
233948
  fileName: name,
234461
- relativePath: path14.join(PERCH_DIR, MEMORY_DIR, name),
233949
+ relativePath: path13.join(PERCH_DIR, MEMORY_DIR, name),
234462
233950
  reason: "expected project memory file was not found"
234463
233951
  }))
234464
233952
  };
234465
233953
  }
234466
233954
  function readCliTextMemoryFile(root2, relativePath, maxBytes) {
234467
- const fullPath = path14.join(root2, relativePath);
234468
- const fileName = path14.basename(relativePath);
233955
+ const fullPath = path13.join(root2, relativePath);
233956
+ const fileName = path13.basename(relativePath);
234469
233957
  try {
234470
- const stat2 = fs13.statSync(fullPath);
233958
+ const stat2 = fs12.statSync(fullPath);
234471
233959
  if (!stat2.isFile()) {
234472
233960
  return { fileName, relativePath, content: "", found: false, error: "not a regular file" };
234473
233961
  }
234474
- const content = fs13.readFileSync(fullPath, "utf8");
233962
+ const content = fs12.readFileSync(fullPath, "utf8");
234475
233963
  if (stat2.size > maxBytes) {
234476
233964
  return {
234477
233965
  fileName,
@@ -234593,7 +234081,7 @@ function terminateProcessGroup(child, signal) {
234593
234081
  }
234594
234082
  function resolveReadPath(root2, inputPath) {
234595
234083
  const expanded = expandHome4(inputPath || ".");
234596
- return path14.resolve(path14.isAbsolute(expanded) ? expanded : path14.join(root2, expanded));
234084
+ return path13.resolve(path13.isAbsolute(expanded) ? expanded : path13.join(root2, expanded));
234597
234085
  }
234598
234086
  function resolveRequestRoot(defaultRoot, requestRoot) {
234599
234087
  const trimmed = requestRoot?.trim();
@@ -234601,25 +234089,25 @@ function resolveRequestRoot(defaultRoot, requestRoot) {
234601
234089
  }
234602
234090
  function resolveWritePath(root2, inputPath) {
234603
234091
  const expanded = expandHome4(inputPath || ".");
234604
- return path14.resolve(path14.isAbsolute(expanded) ? expanded : path14.join(root2, expanded));
234092
+ return path13.resolve(path13.isAbsolute(expanded) ? expanded : path13.join(root2, expanded));
234605
234093
  }
234606
234094
  function displayPath(root2, target, requestedPath) {
234607
234095
  const expanded = expandHome4(requestedPath || ".");
234608
- if (path14.isAbsolute(expanded) || requestedPath.startsWith("~")) return target;
234609
- return path14.relative(root2, target) || path14.basename(target);
234096
+ if (path13.isAbsolute(expanded) || requestedPath.startsWith("~")) return target;
234097
+ return path13.relative(root2, target) || path13.basename(target);
234610
234098
  }
234611
234099
  function resolveLocalSourceId(root2, localSourceId) {
234612
234100
  const raw = localSourceId.includes("::") ? localSourceId.split("::").slice(1).join("::") : localSourceId;
234613
234101
  return resolveReadPath(root2, raw);
234614
234102
  }
234615
234103
  function expandHome4(inputPath) {
234616
- if (inputPath === "~") return os3.homedir();
234617
- if (inputPath.startsWith("~/")) return path14.join(os3.homedir(), inputPath.slice(2));
234104
+ if (inputPath === "~") return os2.homedir();
234105
+ if (inputPath.startsWith("~/")) return path13.join(os2.homedir(), inputPath.slice(2));
234618
234106
  return inputPath;
234619
234107
  }
234620
234108
  function isInside(root2, candidate) {
234621
- const relative2 = path14.relative(path14.resolve(root2), path14.resolve(candidate));
234622
- return relative2 === "" || !relative2.startsWith("..") && !path14.isAbsolute(relative2);
234109
+ const relative2 = path13.relative(path13.resolve(root2), path13.resolve(candidate));
234110
+ return relative2 === "" || !relative2.startsWith("..") && !path13.isAbsolute(relative2);
234623
234111
  }
234624
234112
  async function safeStat(target) {
234625
234113
  try {
@@ -234636,7 +234124,7 @@ async function collectFiles(root2, options) {
234636
234124
  for (const entry of entries) {
234637
234125
  if (result2.length >= options.maxVisits) return;
234638
234126
  if (entry.name.startsWith(".") && entry.name !== ".env") continue;
234639
- const absolute = path14.join(dir, entry.name);
234127
+ const absolute = path13.join(dir, entry.name);
234640
234128
  if (entry.isDirectory()) {
234641
234129
  if (!IGNORED_DIRS.has(entry.name)) await walk2(absolute);
234642
234130
  } else if (entry.isFile()) {
@@ -234692,14 +234180,14 @@ function sanitizeMaxResults(value) {
234692
234180
  return Math.max(1, Math.min(typeof value === "number" ? Math.floor(value) : DEFAULT_MAX_RESULTS, 1e3));
234693
234181
  }
234694
234182
  function localSourceEntry(root2, file, absoluteId = false) {
234695
- const stat2 = fs13.statSync(file);
234696
- const relativePath = path14.relative(root2, file);
234697
- const extension2 = path14.extname(file).toLowerCase();
234183
+ const stat2 = fs12.statSync(file);
234184
+ const relativePath = path13.relative(root2, file);
234185
+ const extension2 = path13.extname(file).toLowerCase();
234698
234186
  return {
234699
234187
  localSourceId: absoluteId ? file : `${CLI_ROOT_ID}::${relativePath}`,
234700
234188
  rootId: absoluteId ? "cli-absolute-root" : CLI_ROOT_ID,
234701
234189
  relativePath,
234702
- fileName: path14.basename(file),
234190
+ fileName: path13.basename(file),
234703
234191
  extension: extension2,
234704
234192
  sizeBytes: stat2.size,
234705
234193
  modifiedAt: stat2.mtime.toISOString(),
@@ -234708,7 +234196,7 @@ function localSourceEntry(root2, file, absoluteId = false) {
234708
234196
  };
234709
234197
  }
234710
234198
  function inferMimeType(file) {
234711
- const ext = path14.extname(file).toLowerCase();
234199
+ const ext = path13.extname(file).toLowerCase();
234712
234200
  if (ext === ".pdf") return "application/pdf";
234713
234201
  if (ext === ".json") return "application/json";
234714
234202
  if (ext === ".csv") return "text/csv";
@@ -234768,6 +234256,550 @@ var init_nodeLocalBridge = __esm({
234768
234256
  }
234769
234257
  });
234770
234258
 
234259
+ // features/perchTerminal/runtime/cliHost/cliAuthSession.ts
234260
+ import { execFile } from "node:child_process";
234261
+ import fs13 from "node:fs/promises";
234262
+ import os3 from "node:os";
234263
+ import path14 from "node:path";
234264
+ import { promisify } from "node:util";
234265
+ async function readStoredCliAuthSession() {
234266
+ const raw = shouldUseFallbackAuthFile() ? await readFallbackSecret().catch(() => null) : process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
234267
+ return parseStoredCliAuthSession(raw);
234268
+ }
234269
+ async function writeStoredCliAuthSession(session) {
234270
+ const raw = JSON.stringify(session);
234271
+ if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
234272
+ await execFileAsync("security", [
234273
+ "add-generic-password",
234274
+ "-U",
234275
+ "-s",
234276
+ KEYCHAIN_SERVICE,
234277
+ "-a",
234278
+ KEYCHAIN_ACCOUNT,
234279
+ "-w",
234280
+ raw
234281
+ ]);
234282
+ return;
234283
+ }
234284
+ await writeFallbackSecret(raw);
234285
+ }
234286
+ async function clearStoredCliAuthSession() {
234287
+ if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
234288
+ await execFileAsync("security", [
234289
+ "delete-generic-password",
234290
+ "-s",
234291
+ KEYCHAIN_SERVICE,
234292
+ "-a",
234293
+ KEYCHAIN_ACCOUNT
234294
+ ]).catch(() => void 0);
234295
+ return;
234296
+ }
234297
+ await fs13.rm(fallbackSessionPath(), { force: true }).catch(() => void 0);
234298
+ }
234299
+ function shouldUseFallbackAuthFile() {
234300
+ return Boolean(process.env.PERCH_CLI_AUTH_DIR?.trim());
234301
+ }
234302
+ function isStoredCliAuthSessionUsable(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
234303
+ if (!session?.accessToken?.trim()) return false;
234304
+ if (!session.appUrl?.trim()) return false;
234305
+ if (!session.expiresAt) return true;
234306
+ return session.expiresAt > nowSeconds + 90;
234307
+ }
234308
+ function parseStoredCliAuthSession(raw) {
234309
+ if (!raw?.trim()) return null;
234310
+ try {
234311
+ const parsed = JSON.parse(raw);
234312
+ if (parsed.version !== 1) return null;
234313
+ if (!parsed.appUrl || !parsed.accessToken || !parsed.updatedAt) return null;
234314
+ return {
234315
+ version: 1,
234316
+ appUrl: parsed.appUrl,
234317
+ accessToken: parsed.accessToken,
234318
+ refreshToken: parsed.refreshToken ?? null,
234319
+ expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : null,
234320
+ userId: parsed.userId ?? null,
234321
+ email: parsed.email ?? null,
234322
+ updatedAt: parsed.updatedAt
234323
+ };
234324
+ } catch {
234325
+ return null;
234326
+ }
234327
+ }
234328
+ async function readKeychainSecret() {
234329
+ const { stdout } = await execFileAsync("security", [
234330
+ "find-generic-password",
234331
+ "-s",
234332
+ KEYCHAIN_SERVICE,
234333
+ "-a",
234334
+ KEYCHAIN_ACCOUNT,
234335
+ "-w"
234336
+ ]);
234337
+ return stdout.trim() || null;
234338
+ }
234339
+ function fallbackSessionPath() {
234340
+ const base = process.env.PERCH_CLI_AUTH_DIR?.trim() || path14.join(os3.homedir(), ".perch");
234341
+ return path14.join(base, "cli-auth-session.json");
234342
+ }
234343
+ async function readFallbackSecret() {
234344
+ return await fs13.readFile(fallbackSessionPath(), "utf8");
234345
+ }
234346
+ async function writeFallbackSecret(raw) {
234347
+ const filePath = fallbackSessionPath();
234348
+ await fs13.mkdir(path14.dirname(filePath), { recursive: true, mode: 448 });
234349
+ await fs13.writeFile(filePath, raw, { mode: 384 });
234350
+ }
234351
+ var execFileAsync, KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT;
234352
+ var init_cliAuthSession = __esm({
234353
+ "features/perchTerminal/runtime/cliHost/cliAuthSession.ts"() {
234354
+ "use strict";
234355
+ execFileAsync = promisify(execFile);
234356
+ KEYCHAIN_SERVICE = "app.perchai.cli-auth";
234357
+ KEYCHAIN_ACCOUNT = "default";
234358
+ }
234359
+ });
234360
+
234361
+ // features/perchTerminal/runtime/publicModelDefault.ts
234362
+ function parsePublicModelDefaultPayload(value) {
234363
+ const raw = value && typeof value === "object" ? value : {};
234364
+ const selection = sanitizeFounderModelSelection(
234365
+ raw.selection ?? DEFAULT_FOUNDER_MODEL_SELECTION
234366
+ );
234367
+ return {
234368
+ selection,
234369
+ updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : null,
234370
+ updatedBy: typeof raw.updatedBy === "string" ? raw.updatedBy : null
234371
+ };
234372
+ }
234373
+ var init_publicModelDefault = __esm({
234374
+ "features/perchTerminal/runtime/publicModelDefault.ts"() {
234375
+ "use strict";
234376
+ init_modelRegistry();
234377
+ }
234378
+ });
234379
+
234380
+ // features/perchTerminal/runtime/cliHost/installCliModelProxyAuthRefresh.ts
234381
+ function installCliModelProxyAuthRefresh() {
234382
+ setCliModelProxyAuthRefresher(async () => {
234383
+ const fresh = await ensureFreshCliAuthSession({
234384
+ session: await readStoredCliAuthSession(),
234385
+ forceRefresh: true
234386
+ });
234387
+ if (!fresh?.accessToken?.trim()) return null;
234388
+ if (typeof process !== "undefined") {
234389
+ process.env[MODEL_CALL_PROXY_TOKEN_ENV2] = fresh.accessToken;
234390
+ }
234391
+ return fresh.accessToken;
234392
+ });
234393
+ return () => setCliModelProxyAuthRefresher(null);
234394
+ }
234395
+ var MODEL_CALL_PROXY_TOKEN_ENV2;
234396
+ var init_installCliModelProxyAuthRefresh = __esm({
234397
+ "features/perchTerminal/runtime/cliHost/installCliModelProxyAuthRefresh.ts"() {
234398
+ "use strict";
234399
+ init_cliAuthRefreshRegistry();
234400
+ init_cliAuthRefresh();
234401
+ init_cliAuthSession();
234402
+ MODEL_CALL_PROXY_TOKEN_ENV2 = "PERCH_MODEL_CALL_PROXY_TOKEN";
234403
+ }
234404
+ });
234405
+
234406
+ // features/perchTerminal/runtime/cliHost/modelConnection.ts
234407
+ async function connectCliModelProxy(input = {}) {
234408
+ installCliModelProxyAuthRefresh();
234409
+ const storedSession = input.storedSession === void 0 ? await readStoredCliAuthSession() : input.storedSession;
234410
+ const usableStoredSession = await ensureFreshCliAuthSession({
234411
+ session: storedSession,
234412
+ fetchImpl: input.fetchImpl
234413
+ });
234414
+ const appUrl = resolveCliAppUrl(input.appUrl, usableStoredSession?.appUrl ?? null);
234415
+ if (!appUrl) return noCliModelConnection();
234416
+ if (!usableStoredSession?.accessToken) return noCliModelConnection(appUrl);
234417
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
234418
+ if (typeof fetchImpl !== "function") return noCliModelConnection(appUrl);
234419
+ const selection = await fetchPublicModelDefaultSelection(
234420
+ appUrl,
234421
+ fetchImpl,
234422
+ usableStoredSession?.accessToken ?? null
234423
+ );
234424
+ if (!selection) return noCliModelConnection(appUrl);
234425
+ const priorProxy = process.env[MODEL_PROXY_ENV];
234426
+ const priorToken = process.env[MODEL_PROXY_TOKEN_ENV];
234427
+ process.env[MODEL_PROXY_ENV] = appUrl;
234428
+ if (usableStoredSession?.accessToken) {
234429
+ process.env[MODEL_PROXY_TOKEN_ENV] = usableStoredSession.accessToken;
234430
+ }
234431
+ return {
234432
+ appUrl,
234433
+ authenticated: !!usableStoredSession?.accessToken,
234434
+ userId: usableStoredSession?.userId ?? null,
234435
+ email: usableStoredSession?.email ?? null,
234436
+ founderModelSelection: selection,
234437
+ restore: () => {
234438
+ if (priorProxy === void 0) {
234439
+ delete process.env[MODEL_PROXY_ENV];
234440
+ } else {
234441
+ process.env[MODEL_PROXY_ENV] = priorProxy;
234442
+ }
234443
+ if (priorToken === void 0) {
234444
+ delete process.env[MODEL_PROXY_TOKEN_ENV];
234445
+ } else {
234446
+ process.env[MODEL_PROXY_TOKEN_ENV] = priorToken;
234447
+ }
234448
+ }
234449
+ };
234450
+ }
234451
+ function resolveCliAppUrl(explicit, stored) {
234452
+ const raw = explicit?.trim() || process.env[CLI_APP_URL_ENV]?.trim() || process.env[FALLBACK_APP_URL_ENV]?.trim() || stored?.trim() || DEFAULT_CLI_APP_URL;
234453
+ if (!raw) return null;
234454
+ const withProtocol = /^https?:\/\//i.test(raw) ? raw : `http://${raw}`;
234455
+ try {
234456
+ const url = new URL(withProtocol);
234457
+ url.pathname = url.pathname.replace(/\/+$/, "");
234458
+ url.search = "";
234459
+ url.hash = "";
234460
+ return url.toString().replace(/\/+$/, "");
234461
+ } catch {
234462
+ return null;
234463
+ }
234464
+ }
234465
+ async function fetchPublicModelDefaultSelection(appUrl, fetchImpl, accessToken) {
234466
+ const url = `${appUrl}/api/perch-terminal/public-model-default`;
234467
+ const controller = new AbortController();
234468
+ const timeout = setTimeout(() => controller.abort(), 3500);
234469
+ try {
234470
+ const response = await fetchImpl(url, {
234471
+ method: "GET",
234472
+ headers: {
234473
+ Accept: "application/json",
234474
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
234475
+ },
234476
+ signal: controller.signal
234477
+ });
234478
+ if (!response.ok) return null;
234479
+ const body = await response.json();
234480
+ if (!body || typeof body !== "object") return null;
234481
+ const record = body;
234482
+ if (record.ok !== true) return null;
234483
+ return parsePublicModelDefaultPayload({ selection: record.selection }).selection;
234484
+ } catch {
234485
+ return null;
234486
+ } finally {
234487
+ clearTimeout(timeout);
234488
+ }
234489
+ }
234490
+ function noCliModelConnection(appUrl = null) {
234491
+ return {
234492
+ appUrl,
234493
+ authenticated: false,
234494
+ userId: null,
234495
+ email: null,
234496
+ founderModelSelection: null,
234497
+ restore: () => {
234498
+ }
234499
+ };
234500
+ }
234501
+ var MODEL_PROXY_ENV, MODEL_PROXY_TOKEN_ENV, CLI_APP_URL_ENV, FALLBACK_APP_URL_ENV, DEFAULT_CLI_APP_URL;
234502
+ var init_modelConnection = __esm({
234503
+ "features/perchTerminal/runtime/cliHost/modelConnection.ts"() {
234504
+ "use strict";
234505
+ init_publicModelDefault();
234506
+ init_cliAuthSession();
234507
+ init_cliAuthRefresh();
234508
+ init_installCliModelProxyAuthRefresh();
234509
+ MODEL_PROXY_ENV = "PERCH_MODEL_CALL_PROXY_URL";
234510
+ MODEL_PROXY_TOKEN_ENV = "PERCH_MODEL_CALL_PROXY_TOKEN";
234511
+ CLI_APP_URL_ENV = "PERCH_CLI_APP_URL";
234512
+ FALLBACK_APP_URL_ENV = "PERCH_APP_URL";
234513
+ DEFAULT_CLI_APP_URL = "https://app.perchai.app";
234514
+ }
234515
+ });
234516
+
234517
+ // features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts
234518
+ import { execFile as execFile2 } from "node:child_process";
234519
+ import http from "node:http";
234520
+ import { promisify as promisify2 } from "node:util";
234521
+ async function runCliStandaloneOAuthLogin(input) {
234522
+ const appUrl = resolveCliAppUrl(input.appUrl, null);
234523
+ if (!appUrl) {
234524
+ return { ok: false, code: "invalid_app_url", error: "Invalid Perch app URL." };
234525
+ }
234526
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
234527
+ if (typeof fetchImpl !== "function") {
234528
+ return { ok: false, code: "fetch_unavailable", error: "This Node runtime does not expose fetch." };
234529
+ }
234530
+ const config = await fetchCliAuthConfig(appUrl, fetchImpl);
234531
+ if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) {
234532
+ return {
234533
+ ok: false,
234534
+ code: "auth_config_unavailable",
234535
+ error: "The Perch app did not return CLI auth configuration."
234536
+ };
234537
+ }
234538
+ const provider = selectOAuthProvider(config.providers ?? [], input.provider);
234539
+ if (!provider) {
234540
+ return {
234541
+ ok: false,
234542
+ code: "oauth_provider_unavailable",
234543
+ error: "No supported CLI OAuth provider is enabled for this Perch app."
234544
+ };
234545
+ }
234546
+ const callback = await createOAuthCallbackServer({
234547
+ host: config.redirectHost?.trim() || "127.0.0.1",
234548
+ timeoutMs: input.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS
234549
+ });
234550
+ try {
234551
+ const memoryStorage = createMemoryAuthStorage();
234552
+ const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
234553
+ auth: {
234554
+ flowType: "pkce",
234555
+ autoRefreshToken: false,
234556
+ detectSessionInUrl: false,
234557
+ persistSession: true,
234558
+ storage: memoryStorage
234559
+ }
234560
+ });
234561
+ const { data, error } = await supabase.auth.signInWithOAuth({
234562
+ provider,
234563
+ options: {
234564
+ redirectTo: callback.redirectTo,
234565
+ skipBrowserRedirect: true,
234566
+ data: { signup_source: "cli" }
234567
+ }
234568
+ });
234569
+ if (error || !data.url) {
234570
+ return {
234571
+ ok: false,
234572
+ code: "oauth_start_failed",
234573
+ error: error?.message ?? "Supabase did not return an OAuth URL."
234574
+ };
234575
+ }
234576
+ await (input.openExternal ?? openExternal)(data.url);
234577
+ const callbackResult = await callback.waitForCode();
234578
+ if (!callbackResult.ok) {
234579
+ return callbackResult;
234580
+ }
234581
+ const exchanged = await supabase.auth.exchangeCodeForSession(callbackResult.code);
234582
+ if (exchanged.error || !exchanged.data.session) {
234583
+ return {
234584
+ ok: false,
234585
+ code: "oauth_exchange_failed",
234586
+ error: exchanged.error?.message ?? "Supabase did not return a CLI session."
234587
+ };
234588
+ }
234589
+ const session = storedSessionFromSupabaseSession({
234590
+ appUrl,
234591
+ accessToken: exchanged.data.session.access_token,
234592
+ refreshToken: exchanged.data.session.refresh_token,
234593
+ expiresAt: exchanged.data.session.expires_at ?? null,
234594
+ userId: exchanged.data.session.user.id,
234595
+ email: exchanged.data.session.user.email ?? null
234596
+ });
234597
+ await writeStoredCliAuthSession(session);
234598
+ return { ok: true, session };
234599
+ } finally {
234600
+ await callback.close();
234601
+ }
234602
+ }
234603
+ async function fetchCliAuthConfig(appUrl, fetchImpl = globalThis.fetch) {
234604
+ const response = await fetchImpl(`${appUrl}/api/perch-terminal/cli-auth/config`, {
234605
+ method: "GET",
234606
+ headers: { Accept: "application/json" }
234607
+ });
234608
+ if (!response.ok) return { ok: false };
234609
+ return await response.json();
234610
+ }
234611
+ function selectOAuthProvider(providers, preferred) {
234612
+ const available = new Set(providers);
234613
+ if (preferred && available.has(preferred)) return preferred;
234614
+ if (available.has("google")) return "google";
234615
+ if (available.has("github")) return "github";
234616
+ return null;
234617
+ }
234618
+ function storedSessionFromSupabaseSession(input) {
234619
+ return {
234620
+ version: 1,
234621
+ appUrl: input.appUrl,
234622
+ accessToken: input.accessToken,
234623
+ refreshToken: input.refreshToken ?? null,
234624
+ expiresAt: input.expiresAt ?? null,
234625
+ userId: input.userId ?? null,
234626
+ email: input.email ?? null,
234627
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
234628
+ };
234629
+ }
234630
+ function createMemoryAuthStorage() {
234631
+ const values2 = /* @__PURE__ */ new Map();
234632
+ return {
234633
+ getItem: (key) => values2.get(key) ?? null,
234634
+ setItem: (key, value) => {
234635
+ values2.set(key, value);
234636
+ },
234637
+ removeItem: (key) => {
234638
+ values2.delete(key);
234639
+ }
234640
+ };
234641
+ }
234642
+ async function createOAuthCallbackServer(input) {
234643
+ let resolveResult = null;
234644
+ const resultPromise = new Promise((resolve5) => {
234645
+ resolveResult = resolve5;
234646
+ });
234647
+ const server = http.createServer((request, response) => {
234648
+ const requestUrl = new URL(request.url ?? "/", `http://${input.host}`);
234649
+ if (requestUrl.pathname !== CALLBACK_PATH) {
234650
+ response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
234651
+ response.end("Not found");
234652
+ return;
234653
+ }
234654
+ const code = requestUrl.searchParams.get("code");
234655
+ const error = requestUrl.searchParams.get("error_description") ?? requestUrl.searchParams.get("error");
234656
+ if (!code) {
234657
+ response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
234658
+ response.end(renderCallbackHtml(false));
234659
+ resolveResult?.({
234660
+ ok: false,
234661
+ code: "oauth_callback_missing_code",
234662
+ error: error ?? "OAuth callback did not include a code."
234663
+ });
234664
+ resolveResult = null;
234665
+ return;
234666
+ }
234667
+ response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
234668
+ response.end(renderCallbackHtml(true));
234669
+ resolveResult?.({ ok: true, code });
234670
+ resolveResult = null;
234671
+ });
234672
+ await new Promise((resolve5, reject2) => {
234673
+ server.once("error", reject2);
234674
+ server.listen(0, input.host, () => {
234675
+ server.off("error", reject2);
234676
+ resolve5();
234677
+ });
234678
+ });
234679
+ const address = server.address();
234680
+ const redirectTo = `http://${input.host}:${address.port}${CALLBACK_PATH}`;
234681
+ const timeout = setTimeout(() => {
234682
+ resolveResult?.({
234683
+ ok: false,
234684
+ code: "oauth_timeout",
234685
+ error: "Timed out waiting for browser sign-in to complete."
234686
+ });
234687
+ resolveResult = null;
234688
+ }, input.timeoutMs);
234689
+ return {
234690
+ redirectTo,
234691
+ waitForCode: async () => resultPromise,
234692
+ close: async () => {
234693
+ clearTimeout(timeout);
234694
+ await new Promise((resolve5) => server.close(() => resolve5()));
234695
+ }
234696
+ };
234697
+ }
234698
+ function renderCallbackHtml(ok) {
234699
+ const title = ok ? "Perch CLI signed in" : "Perch CLI sign-in failed";
234700
+ const message = ok ? "You can close this tab and return to your terminal." : "Return to your terminal and try again.";
234701
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title></head><body style="font-family:system-ui;padding:32px;background:#100d0b;color:#fff8f0"><h1>${title}</h1><p>${message}</p></body></html>`;
234702
+ }
234703
+ async function openExternal(url) {
234704
+ if (process.platform === "darwin") {
234705
+ await execFileAsync2("open", [url]);
234706
+ } else if (process.platform === "win32") {
234707
+ await execFileAsync2("cmd", ["/c", "start", "", url]);
234708
+ } else {
234709
+ await execFileAsync2("xdg-open", [url]);
234710
+ }
234711
+ }
234712
+ var execFileAsync2, DEFAULT_LOGIN_TIMEOUT_MS, CALLBACK_PATH;
234713
+ var init_cliStandaloneOAuth = __esm({
234714
+ "features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts"() {
234715
+ "use strict";
234716
+ init_dist4();
234717
+ init_modelConnection();
234718
+ init_cliAuthSession();
234719
+ execFileAsync2 = promisify2(execFile2);
234720
+ DEFAULT_LOGIN_TIMEOUT_MS = 12e4;
234721
+ CALLBACK_PATH = "/callback";
234722
+ }
234723
+ });
234724
+
234725
+ // features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts
234726
+ async function ensureFreshCliAuthSession(input) {
234727
+ const session = input.session;
234728
+ if (!session?.accessToken?.trim()) return null;
234729
+ const nowSeconds = Math.floor(Date.now() / 1e3);
234730
+ const stillUsable = !session.expiresAt || session.expiresAt > nowSeconds + 90;
234731
+ if (stillUsable && !input.forceRefresh) return session;
234732
+ if (!session.refreshToken?.trim()) return null;
234733
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
234734
+ if (typeof fetchImpl !== "function") return null;
234735
+ const appUrl = session.appUrl?.trim();
234736
+ if (!appUrl) return null;
234737
+ const config = await fetchCliAuthConfig(appUrl, fetchImpl).catch(() => ({ ok: false }));
234738
+ if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) return null;
234739
+ const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
234740
+ auth: {
234741
+ autoRefreshToken: false,
234742
+ detectSessionInUrl: false,
234743
+ persistSession: false
234744
+ }
234745
+ });
234746
+ try {
234747
+ const { data, error } = await supabase.auth.refreshSession({
234748
+ refresh_token: session.refreshToken
234749
+ });
234750
+ if (error || !data.session) return null;
234751
+ const refreshed = {
234752
+ version: 1,
234753
+ appUrl: session.appUrl,
234754
+ accessToken: data.session.access_token,
234755
+ refreshToken: data.session.refresh_token ?? session.refreshToken,
234756
+ expiresAt: data.session.expires_at ?? null,
234757
+ userId: data.session.user?.id ?? session.userId ?? null,
234758
+ email: data.session.user?.email ?? session.email ?? null,
234759
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
234760
+ };
234761
+ await writeStoredCliAuthSession(refreshed);
234762
+ return refreshed;
234763
+ } catch {
234764
+ return null;
234765
+ }
234766
+ }
234767
+ var init_cliAuthRefresh = __esm({
234768
+ "features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts"() {
234769
+ "use strict";
234770
+ init_dist4();
234771
+ init_cliStandaloneOAuth();
234772
+ init_cliAuthSession();
234773
+ }
234774
+ });
234775
+
234776
+ // features/perchTerminal/runtime/cliHost/refreshCliTurnAuth.ts
234777
+ async function refreshCliTurnAuth(input) {
234778
+ if (input.cliLocalTools !== true) return input;
234779
+ const stored = await readStoredCliAuthSession();
234780
+ if (!stored?.accessToken?.trim()) return input;
234781
+ const turnToken = input.cliServerAccessToken?.trim();
234782
+ if (turnToken && turnToken !== stored.accessToken) return input;
234783
+ const fresh = await ensureFreshCliAuthSession({ session: stored });
234784
+ if (!fresh?.accessToken?.trim()) return input;
234785
+ if (fresh.accessToken === input.cliServerAccessToken) return input;
234786
+ if (typeof process !== "undefined") {
234787
+ process.env.PERCH_MODEL_CALL_PROXY_TOKEN = fresh.accessToken;
234788
+ }
234789
+ return {
234790
+ ...input,
234791
+ cliServerAccessToken: fresh.accessToken,
234792
+ marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? fresh.accessToken
234793
+ };
234794
+ }
234795
+ var init_refreshCliTurnAuth = __esm({
234796
+ "features/perchTerminal/runtime/cliHost/refreshCliTurnAuth.ts"() {
234797
+ "use strict";
234798
+ init_cliAuthSession();
234799
+ init_cliAuthRefresh();
234800
+ }
234801
+ });
234802
+
234771
234803
  // features/perchTerminal/runtime/cliHost/runCliTurn.ts
234772
234804
  import path15 from "node:path";
234773
234805
  async function runPerchCliTurn(input, options = {}) {
@@ -234790,7 +234822,8 @@ async function runPerchCliTurn(input, options = {}) {
234790
234822
  onEvent: input.onEvent,
234791
234823
  onContextSnapshot: (snapshot) => {
234792
234824
  latestContextSnapshot = snapshot;
234793
- }
234825
+ },
234826
+ refreshTurnInput: input.cliLocalTools !== false ? refreshCliTurnAuth : void 0
234794
234827
  });
234795
234828
  const restoreLocalBridge = input.cliLocalTools === false ? null : installCliNodeLocalBridge({ workspaceRoot: cwd2 });
234796
234829
  try {
@@ -234877,6 +234910,7 @@ function buildCliTurnDeps(input) {
234877
234910
  input.onEvent?.(event);
234878
234911
  },
234879
234912
  onContextAssembled: input.onContextSnapshot,
234913
+ ...input.refreshTurnInput ? { refreshTurnInput: input.refreshTurnInput } : {},
234880
234914
  persistUserMessage: async () => null,
234881
234915
  persistAssistantMessage: async () => null,
234882
234916
  createWorkflowRun: async () => null,
@@ -234932,6 +234966,7 @@ var init_runCliTurn = __esm({
234932
234966
  init_runOperatorTurn();
234933
234967
  init_nodeLocalBridge();
234934
234968
  init_nodeLocalBridge();
234969
+ init_refreshCliTurnAuth();
234935
234970
  }
234936
234971
  });
234937
234972