@alivault/pico 0.1.5 → 0.1.7

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.
@@ -2,7 +2,7 @@ import { i as __toESM } from "../_runtime.mjs";
2
2
  import { X as require_react, Y as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
3
3
  import { a as createRouter, c as createRootRoute, n as Scripts, r as HeadContent, s as createFileRoute } from "../_libs/@tanstack/react-router+[...].mjs";
4
4
  import { h as OctagonX, i as TriangleAlert, k as CircleCheck, v as Info } from "../_libs/lucide-react.mjs";
5
- import { A as extractToolText, C as buildItemsFromSync, N as mergeAssistantBlocksForStreaming, O as extractMessageImages, S as assistantBlocksFromMessage, T as createCompactionSummaryItem, h as THEME_STORAGE_KEY, k as extractMessageText$1, m as Spinner, nt as toolCategoryFromTool, u as Route$46, y as TooltipProvider } from "./routes-Cvysm_fM.mjs";
5
+ import { A as extractToolText, C as buildItemsFromSync, N as mergeAssistantBlocksForStreaming, O as extractMessageImages, S as assistantBlocksFromMessage, T as createCompactionSummaryItem, h as THEME_STORAGE_KEY, k as extractMessageText$1, m as Spinner, nt as toolCategoryFromTool, u as Route$46, y as TooltipProvider } from "./routes-Di-eYVLe.mjs";
6
6
  import { t as QueryClient } from "../_libs/tanstack__query-core.mjs";
7
7
  import { t as setupRouterSsrQueryIntegration } from "../_libs/@tanstack/react-router-ssr-query+[...].mjs";
8
8
  import { n as HotkeysProvider } from "../_libs/tanstack__react-hotkeys.mjs";
@@ -20,7 +20,7 @@ import os, { homedir } from "node:os";
20
20
  import { performance } from "node:perf_hooks";
21
21
  import { isMainThread } from "node:worker_threads";
22
22
  import { execSync, spawn } from "node:child_process";
23
- //#region node_modules/.nitro/vite/services/ssr/assets/router-ClRUX20Q.js
23
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-CvoH1TXL.js
24
24
  var import_react = /* @__PURE__ */ __toESM(require_react());
25
25
  var import_jsx_runtime = require_jsx_runtime();
26
26
  var Toaster$1 = ({ ...props }) => {
@@ -2727,6 +2727,167 @@ var GitWatchManager = class {
2727
2727
  state.watchers.clear();
2728
2728
  }
2729
2729
  };
2730
+ var PROVIDER_MAP = {
2731
+ anthropic: "claude",
2732
+ "openai-codex": "codex"
2733
+ };
2734
+ async function getOAuthAccessToken(authStorage, provider) {
2735
+ const credential = authStorage?.get(provider);
2736
+ if (credential?.type !== "oauth") return void 0;
2737
+ let token = credential.access;
2738
+ if (typeof authStorage?.getApiKey === "function") try {
2739
+ token = await authStorage.getApiKey(provider, { includeFallback: false }) || token;
2740
+ } catch {}
2741
+ const refreshedCredential = authStorage?.get(provider);
2742
+ return {
2743
+ token,
2744
+ credential: refreshedCredential?.type === "oauth" ? refreshedCredential : credential
2745
+ };
2746
+ }
2747
+ async function getClaudeToken(authStorage) {
2748
+ const result = await getOAuthAccessToken(authStorage, "anthropic");
2749
+ if (result?.token) return result.token;
2750
+ try {
2751
+ const keychainData = execSync("security find-generic-password -s \"Claude Code-credentials\" -w 2>/dev/null", {
2752
+ encoding: "utf-8",
2753
+ stdio: [
2754
+ "pipe",
2755
+ "pipe",
2756
+ "pipe"
2757
+ ]
2758
+ }).trim();
2759
+ if (keychainData) {
2760
+ const parsed = JSON.parse(keychainData);
2761
+ if (parsed.claudeAiOauth?.accessToken) return String(parsed.claudeAiOauth.accessToken);
2762
+ }
2763
+ } catch {}
2764
+ }
2765
+ async function getCodexToken(authStorage) {
2766
+ const result = await getOAuthAccessToken(authStorage, "openai-codex");
2767
+ if (result) return {
2768
+ token: result.token,
2769
+ accountId: typeof result.credential.accountId === "string" ? result.credential.accountId : void 0
2770
+ };
2771
+ const codexPath = join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
2772
+ try {
2773
+ if (existsSync(codexPath)) {
2774
+ const data = JSON.parse(readFileSync(codexPath, "utf-8"));
2775
+ if (data.OPENAI_API_KEY) return { token: String(data.OPENAI_API_KEY) };
2776
+ if (data.tokens?.access_token) return {
2777
+ token: String(data.tokens.access_token),
2778
+ accountId: data.tokens.account_id ? String(data.tokens.account_id) : void 0
2779
+ };
2780
+ }
2781
+ } catch {}
2782
+ }
2783
+ function clampPercent(value) {
2784
+ if (!Number.isFinite(value)) return 0;
2785
+ return Math.max(0, Math.min(100, value));
2786
+ }
2787
+ function normalizePercent(value) {
2788
+ if (!Number.isFinite(value)) return 0;
2789
+ return clampPercent(value <= 1 && value >= 0 ? value * 100 : value);
2790
+ }
2791
+ function formatResetTime(date) {
2792
+ const diffMs = date.getTime() - Date.now();
2793
+ if (diffMs < 0) return "now";
2794
+ const diffMins = Math.floor(diffMs / 6e4);
2795
+ if (diffMins < 60) return `${diffMins}m`;
2796
+ const hours = Math.floor(diffMins / 60);
2797
+ const mins = diffMins % 60;
2798
+ if (hours < 24) return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
2799
+ const days = Math.floor(hours / 24);
2800
+ const remainingHours = hours % 24;
2801
+ return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`;
2802
+ }
2803
+ function getWindowLabel(durationMs, fallback) {
2804
+ if (!durationMs || !Number.isFinite(durationMs) || durationMs <= 0) return fallback;
2805
+ const hourMs = 3600 * 1e3;
2806
+ const weekMs = 168 * hourMs;
2807
+ const isCloseToWeek = Math.abs(durationMs - weekMs) <= hourMs * 2;
2808
+ const isCloseTo5h = Math.abs(durationMs - 5 * hourMs) <= hourMs * 2;
2809
+ if (isCloseToWeek || fallback === "Week") return "Week";
2810
+ if (isCloseTo5h || fallback === "5h") return fallback;
2811
+ const hours = Math.round(durationMs / hourMs);
2812
+ if (hours >= 1 && hours < 48) return `${hours}h`;
2813
+ return fallback;
2814
+ }
2815
+ async function fetchWithTimeout(url, init) {
2816
+ const controller = new AbortController();
2817
+ const timeout = setTimeout(() => controller.abort(), 5e3);
2818
+ try {
2819
+ return await fetch(url, {
2820
+ ...init,
2821
+ signal: controller.signal
2822
+ });
2823
+ } finally {
2824
+ clearTimeout(timeout);
2825
+ }
2826
+ }
2827
+ async function fetchClaudeUsage(authStorage) {
2828
+ const token = await getClaudeToken(authStorage);
2829
+ if (!token) return { windows: [] };
2830
+ try {
2831
+ const res = await fetchWithTimeout("https://api.anthropic.com/api/oauth/usage", { headers: {
2832
+ Authorization: `Bearer ${token}`,
2833
+ "anthropic-beta": "oauth-2025-04-20"
2834
+ } });
2835
+ if (!res.ok) return { windows: [] };
2836
+ const data = await res.json();
2837
+ const windows = [];
2838
+ if (data.five_hour?.utilization !== void 0) windows.push({
2839
+ label: "5h",
2840
+ usedPercent: normalizePercent(data.five_hour.utilization),
2841
+ resetsIn: data.five_hour.resets_at ? formatResetTime(new Date(data.five_hour.resets_at)) : void 0
2842
+ });
2843
+ if (data.seven_day?.utilization !== void 0) windows.push({
2844
+ label: "Week",
2845
+ usedPercent: normalizePercent(data.seven_day.utilization),
2846
+ resetsIn: data.seven_day.resets_at ? formatResetTime(new Date(data.seven_day.resets_at)) : void 0
2847
+ });
2848
+ return { windows };
2849
+ } catch {
2850
+ return { windows: [] };
2851
+ }
2852
+ }
2853
+ async function fetchCodexUsage(authStorage) {
2854
+ const creds = await getCodexToken(authStorage);
2855
+ if (!creds) return { windows: [] };
2856
+ try {
2857
+ const headers = {
2858
+ Authorization: `Bearer ${creds.token}`,
2859
+ "User-Agent": "pico",
2860
+ Accept: "application/json"
2861
+ };
2862
+ if (creds.accountId) headers["ChatGPT-Account-Id"] = creds.accountId;
2863
+ const res = await fetchWithTimeout("https://chatgpt.com/backend-api/wham/usage", { headers });
2864
+ if (!res.ok) return { windows: [] };
2865
+ const data = await res.json();
2866
+ const windows = [];
2867
+ const primary = data.rate_limit?.primary_window;
2868
+ if (primary) windows.push({
2869
+ label: getWindowLabel(typeof primary.limit_window_seconds === "number" ? primary.limit_window_seconds * 1e3 : void 0, "5h"),
2870
+ usedPercent: clampPercent(primary.used_percent || 0),
2871
+ resetsIn: primary.reset_at ? formatResetTime(/* @__PURE__ */ new Date(primary.reset_at * 1e3)) : void 0
2872
+ });
2873
+ const secondary = data.rate_limit?.secondary_window;
2874
+ if (secondary) windows.push({
2875
+ label: getWindowLabel(typeof secondary.limit_window_seconds === "number" ? secondary.limit_window_seconds * 1e3 : void 0, "Week"),
2876
+ usedPercent: clampPercent(secondary.used_percent || 0),
2877
+ resetsIn: secondary.reset_at ? formatResetTime(/* @__PURE__ */ new Date(secondary.reset_at * 1e3)) : void 0
2878
+ });
2879
+ return { windows };
2880
+ } catch {
2881
+ return { windows: [] };
2882
+ }
2883
+ }
2884
+ async function fetchProviderUsage(modelProvider, authStorage) {
2885
+ switch (modelProvider ? PROVIDER_MAP[modelProvider] : void 0) {
2886
+ case "claude": return fetchClaudeUsage(authStorage);
2887
+ case "codex": return fetchCodexUsage(authStorage);
2888
+ default: return { windows: [] };
2889
+ }
2890
+ }
2730
2891
  var VALID_THINKING_LEVELS = new Set([
2731
2892
  "off",
2732
2893
  "minimal",
@@ -5460,6 +5621,10 @@ var PicoRuntime = class {
5460
5621
  this.requireAuthStorage(activeEntry).logout(provider);
5461
5622
  return await this.finishAuthMutation(activeEntry, provider);
5462
5623
  }
5624
+ async getProviderUsage(request, provider) {
5625
+ const { activeEntry } = await this.resolveRequest(request);
5626
+ return await fetchProviderUsage(provider, activeEntry.services.modelRegistry.authStorage);
5627
+ }
5463
5628
  async loginProviderOAuth(request, body) {
5464
5629
  const { activeEntry } = await this.resolveRequest(request);
5465
5630
  const provider = typeof body.provider === "string" ? body.provider.trim() : "";
@@ -5474,14 +5639,28 @@ var PicoRuntime = class {
5474
5639
  createRequestId: () => randomUUID(),
5475
5640
  broadcastToViewers: (sessionKey, payload) => this.broadcastToViewers(sessionKey, payload)
5476
5641
  });
5642
+ const loginAbortController = new AbortController();
5643
+ const loginSignal = loginAbortController.signal;
5477
5644
  const requestInput = async (payload) => {
5478
- const value = await ui.createDialogPromise(void 0, { payload }, (response) => {
5645
+ const value = await ui.createDialogPromise(void 0, {
5646
+ payload,
5647
+ signal: loginSignal
5648
+ }, (response) => {
5479
5649
  if (response.cancelled) return void 0;
5480
5650
  return typeof response.value === "string" ? response.value : void 0;
5481
5651
  });
5482
5652
  if (value === void 0) throw new Error("Login cancelled");
5483
5653
  return value;
5484
5654
  };
5655
+ const requestSelection = async (payload) => {
5656
+ return await ui.createDialogPromise(void 0, {
5657
+ payload,
5658
+ signal: loginSignal
5659
+ }, (response) => {
5660
+ if (response.cancelled) return void 0;
5661
+ return typeof response.value === "string" ? response.value : void 0;
5662
+ });
5663
+ };
5485
5664
  let manualRedirect;
5486
5665
  const getManualRedirect = () => {
5487
5666
  if (!manualRedirect) {
@@ -5504,7 +5683,8 @@ var PicoRuntime = class {
5504
5683
  throw new Error(`Cancelled the existing ${providerInfo.name} login. Try login again.`);
5505
5684
  }
5506
5685
  this.activeOAuthLogins.set(loginKey, () => {
5507
- getManualRedirect().reject(/* @__PURE__ */ new Error("Login cancelled"));
5686
+ loginAbortController.abort();
5687
+ manualRedirect?.reject(/* @__PURE__ */ new Error("Login cancelled"));
5508
5688
  });
5509
5689
  try {
5510
5690
  await authStorage.login(provider, {
@@ -5517,6 +5697,7 @@ var PicoRuntime = class {
5517
5697
  authUrl: info.url,
5518
5698
  authManualAllowed: Boolean(providerInfo.usesCallbackServer)
5519
5699
  },
5700
+ signal: loginSignal,
5520
5701
  timeout: 600 * 1e3
5521
5702
  }, (response) => {
5522
5703
  if (typeof response.value === "string") return {
@@ -5547,7 +5728,19 @@ var PicoRuntime = class {
5547
5728
  },
5548
5729
  onManualCodeInput: async () => {
5549
5730
  return await getManualRedirect().promise;
5550
- }
5731
+ },
5732
+ onSelect: async (prompt) => {
5733
+ return await requestSelection({
5734
+ method: "auth_select",
5735
+ title: `Log in to ${providerInfo.name}`,
5736
+ message: prompt.message,
5737
+ options: prompt.options.map((option) => ({
5738
+ value: option.id,
5739
+ label: option.label
5740
+ }))
5741
+ });
5742
+ },
5743
+ signal: loginSignal
5551
5744
  });
5552
5745
  return await this.finishAuthMutation(activeEntry, provider);
5553
5746
  } finally {
@@ -5696,167 +5889,12 @@ var Route$42 = createFileRoute("/api/slash-command")({ server: { handlers: { POS
5696
5889
  return routeErrorResponse(error, "Failed to run slash command");
5697
5890
  }
5698
5891
  } } } });
5699
- var PROVIDER_MAP = {
5700
- anthropic: "claude",
5701
- "openai-codex": "codex"
5702
- };
5703
- function loadAuthJson() {
5704
- const authPath = join(homedir(), ".pi", "agent", "auth.json");
5705
- try {
5706
- if (existsSync(authPath)) return JSON.parse(readFileSync(authPath, "utf-8"));
5707
- } catch {}
5708
- return {};
5709
- }
5710
- function getClaudeToken() {
5711
- const auth = loadAuthJson();
5712
- if (auth.anthropic?.access) return String(auth.anthropic.access);
5713
- try {
5714
- const keychainData = execSync("security find-generic-password -s \"Claude Code-credentials\" -w 2>/dev/null", {
5715
- encoding: "utf-8",
5716
- stdio: [
5717
- "pipe",
5718
- "pipe",
5719
- "pipe"
5720
- ]
5721
- }).trim();
5722
- if (keychainData) {
5723
- const parsed = JSON.parse(keychainData);
5724
- if (parsed.claudeAiOauth?.accessToken) return String(parsed.claudeAiOauth.accessToken);
5725
- }
5726
- } catch {}
5727
- }
5728
- function getCodexToken() {
5729
- const auth = loadAuthJson();
5730
- if (auth["openai-codex"]?.access) return {
5731
- token: String(auth["openai-codex"].access),
5732
- accountId: auth["openai-codex"]?.accountId ? String(auth["openai-codex"].accountId) : void 0
5733
- };
5734
- const codexPath = join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
5735
- try {
5736
- if (existsSync(codexPath)) {
5737
- const data = JSON.parse(readFileSync(codexPath, "utf-8"));
5738
- if (data.OPENAI_API_KEY) return { token: String(data.OPENAI_API_KEY) };
5739
- if (data.tokens?.access_token) return {
5740
- token: String(data.tokens.access_token),
5741
- accountId: data.tokens.account_id ? String(data.tokens.account_id) : void 0
5742
- };
5743
- }
5744
- } catch {}
5745
- }
5746
- function clampPercent(value) {
5747
- if (!Number.isFinite(value)) return 0;
5748
- return Math.max(0, Math.min(100, value));
5749
- }
5750
- function normalizePercent(value) {
5751
- if (!Number.isFinite(value)) return 0;
5752
- return clampPercent(value <= 1 && value >= 0 ? value * 100 : value);
5753
- }
5754
- function formatResetTime(date) {
5755
- const diffMs = date.getTime() - Date.now();
5756
- if (diffMs < 0) return "now";
5757
- const diffMins = Math.floor(diffMs / 6e4);
5758
- if (diffMins < 60) return `${diffMins}m`;
5759
- const hours = Math.floor(diffMins / 60);
5760
- const mins = diffMins % 60;
5761
- if (hours < 24) return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
5762
- const days = Math.floor(hours / 24);
5763
- const remainingHours = hours % 24;
5764
- return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`;
5765
- }
5766
- function getWindowLabel(durationMs, fallback) {
5767
- if (!durationMs || !Number.isFinite(durationMs) || durationMs <= 0) return fallback;
5768
- const hourMs = 3600 * 1e3;
5769
- const weekMs = 168 * hourMs;
5770
- const isCloseToWeek = Math.abs(durationMs - weekMs) <= hourMs * 2;
5771
- const isCloseTo5h = Math.abs(durationMs - 5 * hourMs) <= hourMs * 2;
5772
- if (isCloseToWeek || fallback === "Week") return "Week";
5773
- if (isCloseTo5h || fallback === "5h") return fallback;
5774
- const hours = Math.round(durationMs / hourMs);
5775
- if (hours >= 1 && hours < 48) return `${hours}h`;
5776
- return fallback;
5777
- }
5778
- async function fetchWithTimeout(url, init) {
5779
- const controller = new AbortController();
5780
- const timeout = setTimeout(() => controller.abort(), 5e3);
5781
- try {
5782
- return await fetch(url, {
5783
- ...init,
5784
- signal: controller.signal
5785
- });
5786
- } finally {
5787
- clearTimeout(timeout);
5788
- }
5789
- }
5790
- async function fetchClaudeUsage() {
5791
- const token = getClaudeToken();
5792
- if (!token) return { windows: [] };
5793
- try {
5794
- const res = await fetchWithTimeout("https://api.anthropic.com/api/oauth/usage", { headers: {
5795
- Authorization: `Bearer ${token}`,
5796
- "anthropic-beta": "oauth-2025-04-20"
5797
- } });
5798
- if (!res.ok) return { windows: [] };
5799
- const data = await res.json();
5800
- const windows = [];
5801
- if (data.five_hour?.utilization !== void 0) windows.push({
5802
- label: "5h",
5803
- usedPercent: normalizePercent(data.five_hour.utilization),
5804
- resetsIn: data.five_hour.resets_at ? formatResetTime(new Date(data.five_hour.resets_at)) : void 0
5805
- });
5806
- if (data.seven_day?.utilization !== void 0) windows.push({
5807
- label: "Week",
5808
- usedPercent: normalizePercent(data.seven_day.utilization),
5809
- resetsIn: data.seven_day.resets_at ? formatResetTime(new Date(data.seven_day.resets_at)) : void 0
5810
- });
5811
- return { windows };
5812
- } catch {
5813
- return { windows: [] };
5814
- }
5815
- }
5816
- async function fetchCodexUsage() {
5817
- const creds = getCodexToken();
5818
- if (!creds) return { windows: [] };
5819
- try {
5820
- const headers = {
5821
- Authorization: `Bearer ${creds.token}`,
5822
- "User-Agent": "pico",
5823
- Accept: "application/json"
5824
- };
5825
- if (creds.accountId) headers["ChatGPT-Account-Id"] = creds.accountId;
5826
- const res = await fetchWithTimeout("https://chatgpt.com/backend-api/wham/usage", { headers });
5827
- if (!res.ok) return { windows: [] };
5828
- const data = await res.json();
5829
- const windows = [];
5830
- const primary = data.rate_limit?.primary_window;
5831
- if (primary) windows.push({
5832
- label: getWindowLabel(typeof primary.limit_window_seconds === "number" ? primary.limit_window_seconds * 1e3 : void 0, "5h"),
5833
- usedPercent: clampPercent(primary.used_percent || 0),
5834
- resetsIn: primary.reset_at ? formatResetTime(/* @__PURE__ */ new Date(primary.reset_at * 1e3)) : void 0
5835
- });
5836
- const secondary = data.rate_limit?.secondary_window;
5837
- if (secondary) windows.push({
5838
- label: getWindowLabel(typeof secondary.limit_window_seconds === "number" ? secondary.limit_window_seconds * 1e3 : void 0, "Week"),
5839
- usedPercent: clampPercent(secondary.used_percent || 0),
5840
- resetsIn: secondary.reset_at ? formatResetTime(/* @__PURE__ */ new Date(secondary.reset_at * 1e3)) : void 0
5841
- });
5842
- return { windows };
5843
- } catch {
5844
- return { windows: [] };
5845
- }
5846
- }
5847
- async function fetchProviderUsage(modelProvider) {
5848
- switch (modelProvider ? PROVIDER_MAP[modelProvider] : void 0) {
5849
- case "claude": return fetchClaudeUsage();
5850
- case "codex": return fetchCodexUsage();
5851
- default: return { windows: [] };
5852
- }
5853
- }
5854
5892
  var Route$41 = createFileRoute("/api/provider-usage")({ server: { handlers: { GET: async ({ request }) => {
5855
5893
  const provider = new URL(request.url).searchParams.get("provider") || void 0;
5856
5894
  try {
5857
5895
  return jsonResponse({
5858
5896
  ok: true,
5859
- usage: await fetchProviderUsage(provider)
5897
+ usage: await getPicoRuntime().getProviderUsage(request, provider)
5860
5898
  });
5861
5899
  } catch (error) {
5862
5900
  return routeErrorResponse(error, "Failed to read provider usage");