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.
- package/dist/perch.mjs +664 -629
- 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
|
-
|
|
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
|
|
214769
|
-
import
|
|
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 =
|
|
214817
|
-
const instruments = JSON.parse(
|
|
214324
|
+
const instrumentsPath = path10.join(dir, "instruments.json");
|
|
214325
|
+
const instruments = JSON.parse(fs9.readFileSync(instrumentsPath, "utf8"));
|
|
214818
214326
|
const bars = [];
|
|
214819
|
-
const barsDir =
|
|
214820
|
-
for (const file of
|
|
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 =
|
|
214823
|
-
const rows = JSON.parse(
|
|
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 =
|
|
214828
|
-
if (
|
|
214829
|
-
fundamentals = JSON.parse(
|
|
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:${
|
|
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
|
|
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
|
|
233108
|
-
import
|
|
233109
|
-
import
|
|
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 =
|
|
233167
|
-
const fd =
|
|
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
|
-
|
|
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
|
-
|
|
232663
|
+
fs10.readSync(fd, buf, 0, previewBytes, stat2.size - previewBytes);
|
|
233176
232664
|
return { preview: buf.toString("utf8"), truncated: true };
|
|
233177
232665
|
} finally {
|
|
233178
|
-
|
|
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 ??
|
|
233223
|
-
this.storePath =
|
|
233224
|
-
this.outputDir =
|
|
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
|
-
|
|
233228
|
-
|
|
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 =
|
|
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
|
-
|
|
233247
|
-
|
|
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
|
|
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
|
|
233413
|
-
import
|
|
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
|
-
|
|
233419
|
-
const stream =
|
|
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
|
|
233080
|
+
import fs12 from "node:fs";
|
|
233593
233081
|
import fsp from "node:fs/promises";
|
|
233594
|
-
import
|
|
233595
|
-
import
|
|
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 =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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) =>
|
|
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 =
|
|
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:
|
|
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:
|
|
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" ?
|
|
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(
|
|
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(
|
|
234120
|
-
const tempPath =
|
|
234121
|
-
await fsp.mkdir(
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
234263
|
-
const fullPath =
|
|
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(
|
|
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
|
|
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
|
|
233796
|
+
return path13.join(root2, PERCH_DIR, PROJECT_FILE);
|
|
234309
233797
|
}
|
|
234310
233798
|
function loadCliStoredMeta(root2) {
|
|
234311
233799
|
try {
|
|
234312
|
-
const raw =
|
|
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
|
-
|
|
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 =
|
|
233919
|
+
const dirPath = path13.join(root2, PERCH_DIR, RULES_DIR);
|
|
234432
233920
|
try {
|
|
234433
|
-
return
|
|
234434
|
-
fileName:
|
|
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 =
|
|
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
|
|
234450
|
-
const ext =
|
|
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,
|
|
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:
|
|
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 =
|
|
234468
|
-
const fileName =
|
|
233955
|
+
const fullPath = path13.join(root2, relativePath);
|
|
233956
|
+
const fileName = path13.basename(relativePath);
|
|
234469
233957
|
try {
|
|
234470
|
-
const stat2 =
|
|
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 =
|
|
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
|
|
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
|
|
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 (
|
|
234609
|
-
return
|
|
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
|
|
234617
|
-
if (inputPath.startsWith("~/")) return
|
|
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 =
|
|
234622
|
-
return relative2 === "" || !relative2.startsWith("..") && !
|
|
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 =
|
|
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 =
|
|
234696
|
-
const relativePath =
|
|
234697
|
-
const extension2 =
|
|
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:
|
|
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 =
|
|
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
|
|