free-pi-cli 0.5.2 → 0.6.0
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/README.md +4 -2
- package/dist/index.js +1 -1
- package/dist/{main-43bb5vjt.js → main-98djq99p.js} +771 -158
- package/package.json +1 -1
- package/skills/{security-audit → freepi-security-audit}/SKILL.md +1 -1
- /package/skills/{security-audit → freepi-security-audit}/AI-AND-LLM.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/ATTACK-CLASSES.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/CLIENT-SIDE.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/CLOUD-AND-DEPLOYMENT.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/DATA-ISOLATION-AND-LIFECYCLE.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/DESKTOP-MOBILE-AND-LOCAL-IPC.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/HUNTING.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/LICENSE +0 -0
- /package/skills/{security-audit → freepi-security-audit}/MEMORY-SAFETY-AND-BINARY.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/PROTOCOLS-RPC-AND-MESSAGING.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/RECONNAISSANCE.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/SUPPLY-CHAIN-AND-RELEASE.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/UPSTREAM.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/VALIDATION-AND-REPORTING.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/WEB-PROTOCOL-AND-AUTH.md +0 -0
- /package/skills/{security-audit → freepi-security-audit}/report-schema.json +0 -0
- /package/skills/{security-audit → freepi-security-audit}/validate-coverage-ledger.cjs +0 -0
- /package/skills/{security-audit → freepi-security-audit}/validate-findings.cjs +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
|
|
7
7
|
// src/main.ts
|
|
8
8
|
import { realpathSync as realpathSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
9
|
-
import { dirname as
|
|
9
|
+
import { dirname as dirname10, join as join11 } from "node:path";
|
|
10
10
|
|
|
11
11
|
// src/browser.ts
|
|
12
12
|
import { spawn } from "node:child_process";
|
|
@@ -14408,11 +14408,16 @@ var GuestStartRequestSchema = exports_external.object({
|
|
|
14408
14408
|
signup_src: exports_external.string().optional(),
|
|
14409
14409
|
referral_code: exports_external.string().optional()
|
|
14410
14410
|
});
|
|
14411
|
+
var ExperimentVariantSchema = exports_external.enum(["control", "treatment"]);
|
|
14412
|
+
var ExperimentAssignmentSchema = exports_external.object({
|
|
14413
|
+
guest_handoff: ExperimentVariantSchema.optional()
|
|
14414
|
+
});
|
|
14411
14415
|
var GuestStartResponseSchema = exports_external.object({
|
|
14412
14416
|
token: exports_external.string(),
|
|
14413
14417
|
guest: exports_external.literal(true),
|
|
14414
14418
|
allowance_usd: exports_external.string(),
|
|
14415
|
-
remaining_usd: exports_external.string()
|
|
14419
|
+
remaining_usd: exports_external.string(),
|
|
14420
|
+
experiments: ExperimentAssignmentSchema.optional()
|
|
14416
14421
|
});
|
|
14417
14422
|
var GuestUnavailableResponseSchema = exports_external.object({
|
|
14418
14423
|
code: exports_external.literal("guest_unavailable"),
|
|
@@ -14424,10 +14429,15 @@ var FunnelEventRequestSchema = exports_external.object({
|
|
|
14424
14429
|
session_id: exports_external.string().uuid().optional()
|
|
14425
14430
|
});
|
|
14426
14431
|
var ReturnBonusTelemetrySchema = exports_external.object({
|
|
14427
|
-
type: exports_external.
|
|
14432
|
+
type: exports_external.enum(["card_shown", "credits_back"])
|
|
14433
|
+
});
|
|
14434
|
+
var ExperimentExposureSchema = exports_external.object({
|
|
14435
|
+
experiment: exports_external.enum(["resume_on_launch"]),
|
|
14436
|
+
event: exports_external.enum(["offer_shown", "offer_accepted"])
|
|
14428
14437
|
});
|
|
14429
14438
|
var ReturnBonusSurfaceSchema = exports_external.object({
|
|
14430
14439
|
show_card: exports_external.boolean(),
|
|
14440
|
+
credits_back: exports_external.boolean(),
|
|
14431
14441
|
day2_granted: exports_external.boolean(),
|
|
14432
14442
|
day7_granted: exports_external.boolean()
|
|
14433
14443
|
});
|
|
@@ -14448,9 +14458,15 @@ var MeResponseSchema = exports_external.object({
|
|
|
14448
14458
|
refill_at: exports_external.string().optional(),
|
|
14449
14459
|
lane: UsageLaneSchema.optional(),
|
|
14450
14460
|
bonus_credits: exports_external.number().optional(),
|
|
14461
|
+
bonus_wallet_credits: exports_external.number().optional(),
|
|
14462
|
+
use_bonus: exports_external.string().optional(),
|
|
14451
14463
|
guest: exports_external.boolean().optional(),
|
|
14452
14464
|
guest_prompt_login: exports_external.boolean().optional(),
|
|
14453
|
-
|
|
14465
|
+
guest_requests_remaining: exports_external.number().int().optional(),
|
|
14466
|
+
guest_near_wall: exports_external.boolean().optional(),
|
|
14467
|
+
experiments: ExperimentAssignmentSchema.optional(),
|
|
14468
|
+
return_bonus: ReturnBonusSurfaceSchema.optional(),
|
|
14469
|
+
visible_allowance: exports_external.boolean().optional()
|
|
14454
14470
|
});
|
|
14455
14471
|
var MeStatsResponseSchema = exports_external.object({
|
|
14456
14472
|
user_id: exports_external.string(),
|
|
@@ -14466,6 +14482,8 @@ var MeStatsResponseSchema = exports_external.object({
|
|
|
14466
14482
|
refill_at: exports_external.string(),
|
|
14467
14483
|
lane: UsageLaneSchema,
|
|
14468
14484
|
bonus_credits: exports_external.number(),
|
|
14485
|
+
bonus_wallet_credits: exports_external.number().optional(),
|
|
14486
|
+
use_bonus: exports_external.string().optional(),
|
|
14469
14487
|
request_count_today: exports_external.number(),
|
|
14470
14488
|
prompt_tokens_today: exports_external.number(),
|
|
14471
14489
|
completion_tokens_today: exports_external.number(),
|
|
@@ -14477,6 +14495,9 @@ var MeStatsResponseSchema = exports_external.object({
|
|
|
14477
14495
|
}),
|
|
14478
14496
|
guest: exports_external.boolean().optional(),
|
|
14479
14497
|
guest_prompt_login: exports_external.boolean().optional(),
|
|
14498
|
+
guest_requests_remaining: exports_external.number().int().optional(),
|
|
14499
|
+
guest_near_wall: exports_external.boolean().optional(),
|
|
14500
|
+
experiments: ExperimentAssignmentSchema.optional(),
|
|
14480
14501
|
return_bonus: ReturnBonusSurfaceSchema.optional()
|
|
14481
14502
|
});
|
|
14482
14503
|
var AdSlotSchema = exports_external.enum(["banner", "inline"]);
|
|
@@ -14530,7 +14551,8 @@ var ClientVersionResponseSchema = exports_external.object({
|
|
|
14530
14551
|
model: exports_external.string().optional(),
|
|
14531
14552
|
notice: exports_external.string().optional(),
|
|
14532
14553
|
models: exports_external.array(exports_external.object({ id: exports_external.string(), name: exports_external.string(), section: exports_external.string().optional() })).optional(),
|
|
14533
|
-
fusion: exports_external.object({ planner: exports_external.string(), executor: exports_external.string() }).optional()
|
|
14554
|
+
fusion: exports_external.object({ planner: exports_external.string(), executor: exports_external.string() }).optional(),
|
|
14555
|
+
experiments: exports_external.record(exports_external.string(), exports_external.object({ variant: exports_external.enum(["control", "treatment"]) }).passthrough()).optional()
|
|
14534
14556
|
});
|
|
14535
14557
|
var SessionResetResponseSchema = exports_external.object({
|
|
14536
14558
|
released: exports_external.boolean()
|
|
@@ -14596,6 +14618,12 @@ var ConfigSchema = exports_external.object({
|
|
|
14596
14618
|
providerDataCollection: exports_external.enum(["allow", "deny"]).optional(),
|
|
14597
14619
|
promptCacheEnabled: exports_external.boolean().default(false),
|
|
14598
14620
|
emailCaptureEnabled: exports_external.boolean().default(false),
|
|
14621
|
+
lifecycleEmailExperimentEnabled: exports_external.boolean().default(false),
|
|
14622
|
+
lifecycleEmailEnabled: exports_external.boolean().default(false),
|
|
14623
|
+
lifecycleEmailSend: exports_external.boolean().default(false),
|
|
14624
|
+
emailProviderConfigured: exports_external.boolean().default(false),
|
|
14625
|
+
emailFrom: exports_external.string().optional(),
|
|
14626
|
+
emailProviderUrl: exports_external.string().optional(),
|
|
14599
14627
|
hiveEnabled: exports_external.boolean().default(false),
|
|
14600
14628
|
providerOrder: exports_external.array(exports_external.string()).optional(),
|
|
14601
14629
|
tierNewMaxDays: exports_external.number().int().default(30),
|
|
@@ -14607,6 +14635,7 @@ var ConfigSchema = exports_external.object({
|
|
|
14607
14635
|
fallbackLaneMinHeadroomBps: exports_external.number().int().default(1000),
|
|
14608
14636
|
queueWaitRetryAfterSeconds: exports_external.number().int().default(0),
|
|
14609
14637
|
sessionLeaseTtlMinutes: exports_external.number().int().default(30),
|
|
14638
|
+
signedInMaxCliSessions: exports_external.number().int().min(1).default(4),
|
|
14610
14639
|
minCliVersion: exports_external.string().default("0.0.0"),
|
|
14611
14640
|
latestCliVersion: exports_external.string().default("0.2.0"),
|
|
14612
14641
|
abuseWindowMinutes: exports_external.number().int().default(30),
|
|
@@ -14623,13 +14652,14 @@ var ConfigSchema = exports_external.object({
|
|
|
14623
14652
|
abuseRestrictedHourlyCap: exports_external.number().int().default(5),
|
|
14624
14653
|
abuseNormalHourlyCap: exports_external.number().int().default(30),
|
|
14625
14654
|
abuseSignedInNormalHourlyCap: exports_external.number().int().default(0),
|
|
14655
|
+
abuseDay0SkipHourly: exports_external.boolean().default(true),
|
|
14626
14656
|
abuseFlipAlertThreshold: exports_external.number().int().default(10),
|
|
14627
14657
|
abuseAdBypassMinRequests: exports_external.number().int().default(20),
|
|
14628
14658
|
abuseAdBypassRatio: exports_external.number().int().default(10),
|
|
14629
14659
|
traceMaxInflightWrites: exports_external.number().int().default(32),
|
|
14630
14660
|
catalogCacheTtlSeconds: exports_external.number().int().default(60),
|
|
14631
14661
|
eventMaxInflightWrites: exports_external.number().int().default(128),
|
|
14632
|
-
guestEnabled: exports_external.boolean().default(
|
|
14662
|
+
guestEnabled: exports_external.boolean().default(false),
|
|
14633
14663
|
guestAllowanceMicros: exports_external.bigint().default(30000n),
|
|
14634
14664
|
guestGlobalDailyCapMicros: exports_external.bigint().default(3000000n),
|
|
14635
14665
|
guestIpSessionsPerDay: exports_external.number().int().default(8),
|
|
@@ -14639,10 +14669,12 @@ var ConfigSchema = exports_external.object({
|
|
|
14639
14669
|
guestIpHashRetentionDays: exports_external.number().int().default(7),
|
|
14640
14670
|
returnBonusDay2Credits: exports_external.number().int().default(2000),
|
|
14641
14671
|
returnBonusDay7Credits: exports_external.number().int().default(1000),
|
|
14672
|
+
returnNudgeEmailEnabled: exports_external.boolean().default(false),
|
|
14642
14673
|
referralBonusCreditsPerDay: exports_external.number().int().default(5000),
|
|
14643
14674
|
referralBonusDays: exports_external.number().int().default(30),
|
|
14644
14675
|
referralMaxActivePerReferrer: exports_external.number().int().default(10),
|
|
14645
|
-
referralGithubMinAgeDays: exports_external.number().int().default(30)
|
|
14676
|
+
referralGithubMinAgeDays: exports_external.number().int().default(30),
|
|
14677
|
+
referralCreditGrantCredits: exports_external.number().int().default(0)
|
|
14646
14678
|
});
|
|
14647
14679
|
function envNumber(value) {
|
|
14648
14680
|
if (value === undefined || value === "")
|
|
@@ -14702,6 +14734,12 @@ function loadConfig(env = process.env) {
|
|
|
14702
14734
|
providerDataCollection: env.PROVIDER_DATA_COLLECTION || undefined,
|
|
14703
14735
|
promptCacheEnabled: envBoolean(env.PROMPT_CACHE_ENABLED),
|
|
14704
14736
|
emailCaptureEnabled: envBoolean(env.EMAIL_CAPTURE_ENABLED),
|
|
14737
|
+
lifecycleEmailExperimentEnabled: envBoolean(env.LIFECYCLE_EMAIL_EXPERIMENT),
|
|
14738
|
+
lifecycleEmailEnabled: envBoolean(env.LIFECYCLE_EMAIL_ENABLED),
|
|
14739
|
+
lifecycleEmailSend: envBoolean(env.LIFECYCLE_EMAIL_SEND),
|
|
14740
|
+
emailProviderConfigured: env.EMAIL_PROVIDER_KEY ? env.EMAIL_PROVIDER_KEY.trim().length > 0 : undefined,
|
|
14741
|
+
emailFrom: env.EMAIL_FROM?.trim() || undefined,
|
|
14742
|
+
emailProviderUrl: env.EMAIL_PROVIDER_URL?.trim() || undefined,
|
|
14705
14743
|
hiveEnabled: envBoolean(env.HIVE_ENABLED),
|
|
14706
14744
|
providerOrder: envList(env.PROVIDER_ORDER),
|
|
14707
14745
|
tierNewMaxDays: envNumber(env.TIER_NEW_MAX_DAYS),
|
|
@@ -14713,6 +14751,7 @@ function loadConfig(env = process.env) {
|
|
|
14713
14751
|
fallbackLaneMinHeadroomBps: envNumber(env.FALLBACK_LANE_MIN_HEADROOM_BPS),
|
|
14714
14752
|
queueWaitRetryAfterSeconds: envNumber(env.QUEUE_WAIT_RETRY_AFTER_SECONDS),
|
|
14715
14753
|
sessionLeaseTtlMinutes: envNumber(env.SESSION_LEASE_TTL_MINUTES),
|
|
14754
|
+
signedInMaxCliSessions: envNumber(env.SIGNED_IN_MAX_CLI_SESSIONS),
|
|
14716
14755
|
minCliVersion: env.MIN_CLI_VERSION || undefined,
|
|
14717
14756
|
latestCliVersion: env.LATEST_CLI_VERSION || undefined,
|
|
14718
14757
|
abuseWindowMinutes: envNumber(env.ABUSE_WINDOW_MINUTES),
|
|
@@ -14729,6 +14768,7 @@ function loadConfig(env = process.env) {
|
|
|
14729
14768
|
abuseRestrictedHourlyCap: envNumber(env.ABUSE_RESTRICTED_HOURLY_CAP),
|
|
14730
14769
|
abuseNormalHourlyCap: envNumber(env.ABUSE_NORMAL_HOURLY_CAP),
|
|
14731
14770
|
abuseSignedInNormalHourlyCap: envNumber(env.ABUSE_SIGNED_IN_NORMAL_HOURLY_CAP),
|
|
14771
|
+
abuseDay0SkipHourly: envBoolean(env.ABUSE_DAY0_SKIP_HOURLY),
|
|
14732
14772
|
abuseFlipAlertThreshold: envNumber(env.ABUSE_FLIP_ALERT_THRESHOLD),
|
|
14733
14773
|
abuseAdBypassMinRequests: envNumber(env.ABUSE_AD_BYPASS_MIN_REQUESTS),
|
|
14734
14774
|
abuseAdBypassRatio: envNumber(env.ABUSE_AD_BYPASS_RATIO),
|
|
@@ -14745,10 +14785,12 @@ function loadConfig(env = process.env) {
|
|
|
14745
14785
|
guestIpHashRetentionDays: envStrictInt("GUEST_IP_HASH_RETENTION_DAYS", env.GUEST_IP_HASH_RETENTION_DAYS),
|
|
14746
14786
|
returnBonusDay2Credits: envStrictInt("RETURN_BONUS_DAY2_CREDITS", env.RETURN_BONUS_DAY2_CREDITS),
|
|
14747
14787
|
returnBonusDay7Credits: envStrictInt("RETURN_BONUS_DAY7_CREDITS", env.RETURN_BONUS_DAY7_CREDITS),
|
|
14788
|
+
returnNudgeEmailEnabled: envBoolean(env.RETURN_NUDGE_EMAIL_ENABLED),
|
|
14748
14789
|
referralBonusCreditsPerDay: envStrictInt("REFERRAL_BONUS_CREDITS_PER_DAY", env.REFERRAL_BONUS_CREDITS_PER_DAY),
|
|
14749
14790
|
referralBonusDays: envStrictInt("REFERRAL_BONUS_DAYS", env.REFERRAL_BONUS_DAYS),
|
|
14750
14791
|
referralMaxActivePerReferrer: envStrictInt("REFERRAL_MAX_ACTIVE_PER_REFERRER", env.REFERRAL_MAX_ACTIVE_PER_REFERRER),
|
|
14751
|
-
referralGithubMinAgeDays: envStrictInt("REFERRAL_GITHUB_MIN_AGE_DAYS", env.REFERRAL_GITHUB_MIN_AGE_DAYS)
|
|
14792
|
+
referralGithubMinAgeDays: envStrictInt("REFERRAL_GITHUB_MIN_AGE_DAYS", env.REFERRAL_GITHUB_MIN_AGE_DAYS),
|
|
14793
|
+
referralCreditGrantCredits: envStrictInt("REFERRAL_CREDIT_GRANT_CREDITS", env.REFERRAL_CREDIT_GRANT_CREDITS)
|
|
14752
14794
|
});
|
|
14753
14795
|
}
|
|
14754
14796
|
// ../shared/src/credits.ts
|
|
@@ -14759,6 +14801,43 @@ function usdToCredits(usd) {
|
|
|
14759
14801
|
function nextUtcMidnight(now) {
|
|
14760
14802
|
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
|
|
14761
14803
|
}
|
|
14804
|
+
function formatRefillLocal(date5, timeZone) {
|
|
14805
|
+
const opts = {
|
|
14806
|
+
hour: "numeric",
|
|
14807
|
+
minute: "2-digit",
|
|
14808
|
+
hour12: true,
|
|
14809
|
+
timeZoneName: "short"
|
|
14810
|
+
};
|
|
14811
|
+
if (timeZone)
|
|
14812
|
+
opts.timeZone = timeZone;
|
|
14813
|
+
return new Intl.DateTimeFormat("en-US", opts).format(date5);
|
|
14814
|
+
}
|
|
14815
|
+
function formatVisibleAllowanceLine(input) {
|
|
14816
|
+
const left = input.remaining.toLocaleString("en-US");
|
|
14817
|
+
if (input.guest)
|
|
14818
|
+
return `${left} credits left · sign in for 15,000 a day`;
|
|
14819
|
+
if (input.refillAt && !Number.isNaN(input.refillAt.getTime())) {
|
|
14820
|
+
return `${left} credits left today · resets ${formatRefillLocal(input.refillAt, input.timeZone)}`;
|
|
14821
|
+
}
|
|
14822
|
+
return `${left} credits left today`;
|
|
14823
|
+
}
|
|
14824
|
+
function formatVisibleAllowanceStatus(input) {
|
|
14825
|
+
const left = input.remaining.toLocaleString("en-US");
|
|
14826
|
+
const bought = typeof input.bonusCredits === "number" && input.bonusCredits > 0 ? ` + ${input.bonusCredits.toLocaleString("en-US")}` : "";
|
|
14827
|
+
if (input.guest)
|
|
14828
|
+
return `${left}${bought} credits left`;
|
|
14829
|
+
if (input.refillAt && !Number.isNaN(input.refillAt.getTime())) {
|
|
14830
|
+
return `${left}${bought} credits left today · resets ${formatRefillLocal(input.refillAt, input.timeZone)}`;
|
|
14831
|
+
}
|
|
14832
|
+
return `${left}${bought} credits left today`;
|
|
14833
|
+
}
|
|
14834
|
+
function formatLowAllowanceWarning(input) {
|
|
14835
|
+
const left = input.remaining.toLocaleString("en-US");
|
|
14836
|
+
if (input.guest)
|
|
14837
|
+
return `Low on credits: ${left} left${input.suffix ?? ""}`;
|
|
14838
|
+
const reset = input.refillAt && !Number.isNaN(input.refillAt.getTime()) ? ` · resets ${formatRefillLocal(input.refillAt, input.timeZone)}` : "";
|
|
14839
|
+
return `Low on credits: ${left} left today${reset}${input.suffix ?? ""}`;
|
|
14840
|
+
}
|
|
14762
14841
|
function formatRefillEt(date5, timeZone = "America/New_York") {
|
|
14763
14842
|
const formatted = new Intl.DateTimeFormat("en-US", {
|
|
14764
14843
|
timeZone,
|
|
@@ -14786,11 +14865,13 @@ var LIMIT_NOTICE_LABELS = {
|
|
|
14786
14865
|
};
|
|
14787
14866
|
var EXTRA_CREDITS_URL = "freepi.ai/extra-credits";
|
|
14788
14867
|
var LIMIT_NOTICE_LOCAL_TIME_AFTER_SECONDS = 90 * 60;
|
|
14868
|
+
var REFILL_CLOCK_LIMITS = new Set(["daily_cap", "global_cap", "queue_wait"]);
|
|
14789
14869
|
function formatLimitNotice(fields, opts = {}) {
|
|
14790
14870
|
const slow = fields.lane === "slow" && fields.limit === "hourly_ceiling" && fields.hourly_ceiling != null;
|
|
14791
14871
|
const label = slow ? `You've used today's free allowance, so you're on the slow lane (${fields.hourly_ceiling.toLocaleString("en-US")} requests an hour).` : LIMIT_NOTICE_LABELS[fields.limit] ?? fields.limit.replace(/_/g, " ");
|
|
14792
14872
|
const parts = [label];
|
|
14793
14873
|
const retry = fields.retry_after_seconds;
|
|
14874
|
+
const waitBits = [];
|
|
14794
14875
|
if (retry != null && retry > 0) {
|
|
14795
14876
|
const useClock = Boolean(opts.localTime) && Boolean(fields.clears_at) && retry >= LIMIT_NOTICE_LOCAL_TIME_AFTER_SECONDS;
|
|
14796
14877
|
if (useClock) {
|
|
@@ -14799,13 +14880,21 @@ function formatLimitNotice(fields, opts = {}) {
|
|
|
14799
14880
|
hour: "numeric",
|
|
14800
14881
|
minute: "2-digit"
|
|
14801
14882
|
}).format(new Date(fields.clears_at));
|
|
14802
|
-
|
|
14883
|
+
waitBits.push(`back at ${formatted}`);
|
|
14803
14884
|
} else {
|
|
14804
|
-
|
|
14885
|
+
waitBits.push(`back in ${Math.max(1, Math.ceil(retry / 60))} min`);
|
|
14886
|
+
}
|
|
14887
|
+
}
|
|
14888
|
+
if (opts.copyStyle === "treatment" && REFILL_CLOCK_LIMITS.has(fields.limit) && fields.clears_at) {
|
|
14889
|
+
const clears = new Date(fields.clears_at);
|
|
14890
|
+
if (!Number.isNaN(clears.getTime())) {
|
|
14891
|
+
waitBits.push(`resets ${formatRefillEt(clears)}`);
|
|
14805
14892
|
}
|
|
14806
14893
|
}
|
|
14894
|
+
if (waitBits.length > 0)
|
|
14895
|
+
parts.push(waitBits.join(" · "));
|
|
14807
14896
|
if (slow) {
|
|
14808
|
-
const wait =
|
|
14897
|
+
const wait = waitBits[0] ? ` ${waitBits[0][0].toUpperCase()}${waitBits[0].slice(1)} — or get` : " Get";
|
|
14809
14898
|
return `${label}${wait} credits at ${EXTRA_CREDITS_URL} to keep going at full speed.`;
|
|
14810
14899
|
}
|
|
14811
14900
|
parts.push(`${fields.remaining_credits_today.toLocaleString("en-US")} credits left today`);
|
|
@@ -14940,7 +15029,7 @@ var HiveClassifyResponseSchema = exports_external.object({
|
|
|
14940
15029
|
// package.json
|
|
14941
15030
|
var package_default = {
|
|
14942
15031
|
name: "free-pi-cli",
|
|
14943
|
-
version: "0.
|
|
15032
|
+
version: "0.6.0",
|
|
14944
15033
|
description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
|
|
14945
15034
|
license: "MIT",
|
|
14946
15035
|
type: "module",
|
|
@@ -14981,6 +15070,8 @@ free-pi — free AI coding in your terminal; ads pay for the inference.
|
|
|
14981
15070
|
Usage:
|
|
14982
15071
|
free-pi-cli start the coding agent (first run: a short guest trial, then GitHub)
|
|
14983
15072
|
free-pi-cli --session <id> resume a previous session
|
|
15073
|
+
free-pi-cli --continue resume the last session in this folder
|
|
15074
|
+
free-pi-cli --new start a new session
|
|
14984
15075
|
free-pi-cli --ref <code> first-run referral code (from freepi.ai/?ref=)
|
|
14985
15076
|
free-pi-cli --src <channel> first-run campaign attribution (from freepi.ai/?src=)
|
|
14986
15077
|
free-pi-cli logout remove the stored sign-in token; the next run signs in again
|
|
@@ -15016,6 +15107,10 @@ function parseCliArgs(argv) {
|
|
|
15016
15107
|
let i = 0;
|
|
15017
15108
|
while (i < argv.length) {
|
|
15018
15109
|
const arg = argv[i];
|
|
15110
|
+
if (arg === "--continue" || arg === "--resume" || arg === "--new") {
|
|
15111
|
+
i += 1;
|
|
15112
|
+
continue;
|
|
15113
|
+
}
|
|
15019
15114
|
if (arg === "--session" || arg === "--ref" || arg === "--src") {
|
|
15020
15115
|
i += 1;
|
|
15021
15116
|
if (argv[i] && !String(argv[i]).startsWith("--"))
|
|
@@ -15326,7 +15421,11 @@ async function fetchMe(deps) {
|
|
|
15326
15421
|
const res = await authedFetch(deps, "/me");
|
|
15327
15422
|
if (!res.ok)
|
|
15328
15423
|
return;
|
|
15329
|
-
|
|
15424
|
+
const me = await res.json();
|
|
15425
|
+
try {
|
|
15426
|
+
deps.onMe?.(me);
|
|
15427
|
+
} catch {}
|
|
15428
|
+
return me;
|
|
15330
15429
|
} catch {
|
|
15331
15430
|
return;
|
|
15332
15431
|
}
|
|
@@ -15494,7 +15593,7 @@ function mapErrorCode(code) {
|
|
|
15494
15593
|
case "concurrent":
|
|
15495
15594
|
return "only one request at a time on the free tier — wait for the current one to finish";
|
|
15496
15595
|
case "concurrent_session":
|
|
15497
|
-
return "
|
|
15596
|
+
return "this account is already at its session limit — run /close-other-session or wait for a slot";
|
|
15498
15597
|
case "context_ceiling":
|
|
15499
15598
|
return "this conversation is too large for free-pi's context limit";
|
|
15500
15599
|
case "upstream_error":
|
|
@@ -15506,7 +15605,7 @@ function mapErrorCode(code) {
|
|
|
15506
15605
|
case "upgrade_required":
|
|
15507
15606
|
return "your free-pi-cli is out of date — run `npx free-pi-cli@latest`";
|
|
15508
15607
|
case "hourly_ceiling":
|
|
15509
|
-
return "
|
|
15608
|
+
return "daily allowance still applies — it refills at midnight UTC";
|
|
15510
15609
|
case "account_review":
|
|
15511
15610
|
return "this account is under review — contact support";
|
|
15512
15611
|
case "ads_required":
|
|
@@ -15575,9 +15674,23 @@ async function renderMeter(deps, ctx) {
|
|
|
15575
15674
|
if (!Number.isNaN(refill.getTime()) && me.lane !== "wait") {
|
|
15576
15675
|
const left = me.remaining_credits_today;
|
|
15577
15676
|
const bought = typeof me.bonus_credits === "number" && me.bonus_credits > 0 ? ` + ${me.bonus_credits.toLocaleString("en-US")}` : "";
|
|
15578
|
-
|
|
15677
|
+
const refill2 = new Date(me.refill_at);
|
|
15678
|
+
const treatment = me.visible_allowance === true;
|
|
15679
|
+
const status = treatment ? formatVisibleAllowanceStatus({
|
|
15680
|
+
remaining: left,
|
|
15681
|
+
bonusCredits: typeof me.bonus_credits === "number" ? me.bonus_credits : undefined,
|
|
15682
|
+
refillAt: refill2,
|
|
15683
|
+
guest: me.guest === true
|
|
15684
|
+
}) : `${left.toLocaleString("en-US")}${bought} credits`;
|
|
15685
|
+
ctx.ui.setStatus(METER_STATUS_KEY, theme.fg("muted", status));
|
|
15579
15686
|
const low = left > 0 && left <= me.allowance_credits * LOW_CREDIT_FRACTION;
|
|
15580
|
-
|
|
15687
|
+
const warning = treatment ? formatLowAllowanceWarning({
|
|
15688
|
+
remaining: left,
|
|
15689
|
+
refillAt: refill2,
|
|
15690
|
+
guest: me.guest === true,
|
|
15691
|
+
suffix: buyMore(me.buy_url)
|
|
15692
|
+
}) : `Low on credits: ${left.toLocaleString("en-US")} left today${buyMore(me.buy_url)}`;
|
|
15693
|
+
ctx.ui.setWidget(METER_WIDGET_ID, low ? [renderWarningLine(warning, theme)] : undefined);
|
|
15581
15694
|
return;
|
|
15582
15695
|
}
|
|
15583
15696
|
}
|
|
@@ -15598,7 +15711,8 @@ function createAdsExtension(opts) {
|
|
|
15598
15711
|
getToken: opts.getToken,
|
|
15599
15712
|
sessionId: opts.sessionId,
|
|
15600
15713
|
fetchImpl: opts.fetchImpl,
|
|
15601
|
-
getGravityContext: opts.getGravityContext
|
|
15714
|
+
getGravityContext: opts.getGravityContext,
|
|
15715
|
+
onMe: opts.onMe
|
|
15602
15716
|
};
|
|
15603
15717
|
const config3 = loadConfig();
|
|
15604
15718
|
return {
|
|
@@ -15651,7 +15765,7 @@ function createAdsExtension(opts) {
|
|
|
15651
15765
|
}
|
|
15652
15766
|
|
|
15653
15767
|
// src/pi-launch.ts
|
|
15654
|
-
import { dirname as
|
|
15768
|
+
import { dirname as dirname8, join as join10, resolve as resolve3 } from "node:path";
|
|
15655
15769
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
15656
15770
|
|
|
15657
15771
|
// src/provider.ts
|
|
@@ -15679,7 +15793,8 @@ function buildProviderConfig(baseUrl, jwt2, sessionId, models, name = PROVIDER_N
|
|
|
15679
15793
|
models: models.map((m) => ({
|
|
15680
15794
|
id: m.id,
|
|
15681
15795
|
name: m.name,
|
|
15682
|
-
reasoning:
|
|
15796
|
+
reasoning: true,
|
|
15797
|
+
compat: { supportsDeveloperRole: false },
|
|
15683
15798
|
input: ["text"],
|
|
15684
15799
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
15685
15800
|
contextWindow: 1048576,
|
|
@@ -20328,6 +20443,7 @@ first. Purchased credits cover use beyond it and do not expire.
|
|
|
20328
20443
|
## Commands
|
|
20329
20444
|
- \`/usage\` — spend and remaining budget today
|
|
20330
20445
|
- \`/support\` — visit today's advertiser
|
|
20446
|
+
- \`/feedback <text>\` — send feedback to the free-pi team
|
|
20331
20447
|
- \`/tos\` — open the Terms of Service
|
|
20332
20448
|
- \`/privacy-policy\` — open the Privacy Policy
|
|
20333
20449
|
- \`/buy-credits\` — get more usage
|
|
@@ -20763,14 +20879,20 @@ function parseErrorEnvelope(errorMessage) {
|
|
|
20763
20879
|
parsed.lane = "slow";
|
|
20764
20880
|
if (ceiling !== undefined)
|
|
20765
20881
|
parsed.hourly_ceiling = ceiling;
|
|
20882
|
+
if (body.prompt === "github_signin")
|
|
20883
|
+
parsed.prompt = "github_signin";
|
|
20884
|
+
if (body.copy_variant === "control" || body.copy_variant === "treatment") {
|
|
20885
|
+
parsed.copy_variant = body.copy_variant;
|
|
20886
|
+
}
|
|
20766
20887
|
return parsed;
|
|
20767
20888
|
} catch {
|
|
20768
20889
|
return;
|
|
20769
20890
|
}
|
|
20770
20891
|
}
|
|
20771
20892
|
function formatLimitEnvelope(env, opts) {
|
|
20772
|
-
if (env.code === "concurrent_session" || typeof env.remaining_credits_today !== "number")
|
|
20893
|
+
if (env.code === "concurrent_session" || env.prompt === "github_signin" || env.copy_variant === "treatment" || typeof env.remaining_credits_today !== "number") {
|
|
20773
20894
|
return env.message;
|
|
20895
|
+
}
|
|
20774
20896
|
return formatLimitNotice({
|
|
20775
20897
|
limit: env.limit ?? env.code,
|
|
20776
20898
|
retry_after_seconds: env.retry_after_seconds,
|
|
@@ -20779,7 +20901,7 @@ function formatLimitEnvelope(env, opts) {
|
|
|
20779
20901
|
allowance_credits: env.allowance_credits,
|
|
20780
20902
|
lane: env.lane,
|
|
20781
20903
|
hourly_ceiling: env.hourly_ceiling
|
|
20782
|
-
}, { localTime: true, timeZone: opts?.timeZone });
|
|
20904
|
+
}, { localTime: true, timeZone: opts?.timeZone, copyStyle: env.copy_variant });
|
|
20783
20905
|
}
|
|
20784
20906
|
function createErrorNoticeExtension(opts = {}) {
|
|
20785
20907
|
const lastAt = new Map;
|
|
@@ -20852,21 +20974,36 @@ function createToolGuardExtension(opts) {
|
|
|
20852
20974
|
// src/bug-report.ts
|
|
20853
20975
|
var BUG_USAGE = "Usage: /bug <what went wrong>";
|
|
20854
20976
|
var BUG_DESCRIPTION = "Report a bug to free-pi";
|
|
20977
|
+
var LOGIN_DESCRIPTION = "Sign in with GitHub to unlock daily credits and a comeback bonus.";
|
|
20978
|
+
var FEEDBACK_USAGE = "Usage: /feedback <what you think>";
|
|
20979
|
+
var FEEDBACK_DESCRIPTION = "Send feedback to the free-pi team";
|
|
20980
|
+
var FEEDBACK_PREFIX = "[feedback] ";
|
|
20981
|
+
var FEEDBACK_MAX = 4000 - FEEDBACK_PREFIX.length;
|
|
20982
|
+
function parseFeedbackArgs(args) {
|
|
20983
|
+
const text = args.trim();
|
|
20984
|
+
if (!text)
|
|
20985
|
+
return { kind: "usage" };
|
|
20986
|
+
if (text.length > FEEDBACK_MAX)
|
|
20987
|
+
return { kind: "too_long" };
|
|
20988
|
+
return { kind: "send", description: FEEDBACK_PREFIX + text };
|
|
20989
|
+
}
|
|
20855
20990
|
function buildBugRequest(description, ctx) {
|
|
20856
20991
|
return { description, session_id: ctx.sessionId, cli_version: CLI_VERSION, model: ctx.model };
|
|
20857
20992
|
}
|
|
20858
|
-
function mapBugFailure(status) {
|
|
20993
|
+
function mapBugFailure(status, kind = "bug") {
|
|
20994
|
+
const fb = kind === "feedback";
|
|
20995
|
+
const what = fb ? "feedback" : "the bug report";
|
|
20859
20996
|
if (status === 401)
|
|
20860
|
-
return
|
|
20997
|
+
return `Couldn't send ${what}: you're signed out. Restart free-pi-cli to sign in again.`;
|
|
20861
20998
|
if (status === 429)
|
|
20862
|
-
return
|
|
20999
|
+
return `Couldn't send ${what}: you've reached today's limit of 20 ${fb ? "reports and feedback messages" : "reports"}. Try again tomorrow.`;
|
|
20863
21000
|
if (status === 413)
|
|
20864
|
-
return
|
|
21001
|
+
return `Couldn't send ${what}: it is too large.`;
|
|
20865
21002
|
if (status === 400)
|
|
20866
|
-
return "Couldn't send the bug report: the description is too long (4000 characters max).";
|
|
20867
|
-
return `Couldn't send
|
|
21003
|
+
return fb ? `Couldn't send feedback: it is too long (${FEEDBACK_MAX} characters max).` : "Couldn't send the bug report: the description is too long (4000 characters max).";
|
|
21004
|
+
return `Couldn't send ${what} (the server returned ${status}). Try again in a moment.`;
|
|
20868
21005
|
}
|
|
20869
|
-
async function sendBugReport(opts, body) {
|
|
21006
|
+
async function sendBugReport(opts, body, kind = "bug") {
|
|
20870
21007
|
try {
|
|
20871
21008
|
const res = await (opts.fetchImpl ?? fetch)(`${opts.baseUrl}/bug-reports`, {
|
|
20872
21009
|
method: "POST",
|
|
@@ -20875,13 +21012,13 @@ async function sendBugReport(opts, body) {
|
|
|
20875
21012
|
signal: AbortSignal.timeout(1e4)
|
|
20876
21013
|
});
|
|
20877
21014
|
if (!res.ok)
|
|
20878
|
-
return { ok: false, message: mapBugFailure(res.status) };
|
|
21015
|
+
return { ok: false, message: mapBugFailure(res.status, kind) };
|
|
20879
21016
|
const parsed = BugReportResponseSchema.safeParse(await res.json());
|
|
20880
21017
|
if (!parsed.success)
|
|
20881
|
-
return { ok: false, message: mapBugFailure(res.status) };
|
|
21018
|
+
return { ok: false, message: mapBugFailure(res.status, kind) };
|
|
20882
21019
|
return { ok: true, id: parsed.data.id };
|
|
20883
21020
|
} catch {
|
|
20884
|
-
return { ok: false, message:
|
|
21021
|
+
return { ok: false, message: `Couldn't reach free-pi to send ${kind === "feedback" ? "feedback" : "the bug report"} — check your connection.` };
|
|
20885
21022
|
}
|
|
20886
21023
|
}
|
|
20887
21024
|
function makeBugHandler(mode, opts) {
|
|
@@ -20909,7 +21046,7 @@ function registerBugAutocomplete(pi) {
|
|
|
20909
21046
|
const r = await cur.getSuggestions(...a);
|
|
20910
21047
|
return r && {
|
|
20911
21048
|
...r,
|
|
20912
|
-
items: r.items.map((i) => i.value === "bug"
|
|
21049
|
+
items: r.items.map((i) => i.label === i.value && i.value === "bug" ? { ...i, description: BUG_DESCRIPTION } : i.label === i.value && i.value === "login" ? { ...i, description: LOGIN_DESCRIPTION } : i)
|
|
20913
21050
|
};
|
|
20914
21051
|
}
|
|
20915
21052
|
}));
|
|
@@ -20976,9 +21113,26 @@ function mergeAttribution(a, b) {
|
|
|
20976
21113
|
|
|
20977
21114
|
// src/changelog.ts
|
|
20978
21115
|
var CHANGELOG_HIGHLIGHTS = [
|
|
21116
|
+
{
|
|
21117
|
+
version: "0.6.0",
|
|
21118
|
+
highlights: [
|
|
21119
|
+
"Send feedback with /feedback <what you think>.",
|
|
21120
|
+
"Turn thinking on with the thinking-level control (Shift+Tab). It starts off.",
|
|
21121
|
+
"Run free-pi in more than one terminal at once.",
|
|
21122
|
+
"Pick up your last session in this folder when you start free-pi (some users).",
|
|
21123
|
+
"The Defense models now show their real model names."
|
|
21124
|
+
]
|
|
21125
|
+
},
|
|
21126
|
+
{
|
|
21127
|
+
version: "0.5.3",
|
|
21128
|
+
highlights: [
|
|
21129
|
+
"No more startup warning about /login.",
|
|
21130
|
+
"The bundled audit skill is now freepi-security-audit, so it no longer clashes with Cloudflare's security-audit skill."
|
|
21131
|
+
]
|
|
21132
|
+
},
|
|
20979
21133
|
{
|
|
20980
21134
|
version: "0.5.2",
|
|
20981
|
-
highlights: ["/model now lists Defense
|
|
21135
|
+
highlights: ["/model now lists the Defense models right under Fusion."]
|
|
20982
21136
|
},
|
|
20983
21137
|
{
|
|
20984
21138
|
version: "0.5.1",
|
|
@@ -21259,7 +21413,8 @@ async function startGuestSession(baseUrl, consentVersion, attribution = {}) {
|
|
|
21259
21413
|
return {
|
|
21260
21414
|
token: parsed.token,
|
|
21261
21415
|
allowance_usd: parsed.allowance_usd,
|
|
21262
|
-
remaining_usd: parsed.remaining_usd
|
|
21416
|
+
remaining_usd: parsed.remaining_usd,
|
|
21417
|
+
guestHandoffVariant: parsed.experiments?.guest_handoff
|
|
21263
21418
|
};
|
|
21264
21419
|
}
|
|
21265
21420
|
async function reportFunnelEvent(baseUrl, event, opts = {}) {
|
|
@@ -21290,7 +21445,15 @@ async function pollForToken(baseUrl, sessionId, interval, sleep, opts = {}) {
|
|
|
21290
21445
|
}
|
|
21291
21446
|
let res;
|
|
21292
21447
|
try {
|
|
21293
|
-
|
|
21448
|
+
const headers = { "content-type": "application/json" };
|
|
21449
|
+
if (opts.jwt)
|
|
21450
|
+
headers.authorization = `Bearer ${opts.jwt}`;
|
|
21451
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
21452
|
+
res = await doFetch(new URL("/auth/token", baseUrl), {
|
|
21453
|
+
method: "POST",
|
|
21454
|
+
headers,
|
|
21455
|
+
body: JSON.stringify({ session_id: sessionId })
|
|
21456
|
+
});
|
|
21294
21457
|
consecutiveNetworkFailures = 0;
|
|
21295
21458
|
} catch (err) {
|
|
21296
21459
|
consecutiveNetworkFailures++;
|
|
@@ -21359,10 +21522,22 @@ async function submitUserConsent(baseUrl, jwt2, consentVersion) {
|
|
|
21359
21522
|
throw new Error(`consent submission failed: HTTP ${res.status}`);
|
|
21360
21523
|
}
|
|
21361
21524
|
|
|
21525
|
+
// src/guest-handoff.ts
|
|
21526
|
+
var LOGIN_VALUE_LINE = "sign in free with GitHub: full daily allowance, FREE abliterated DeepSeek, history saved";
|
|
21527
|
+
var GUEST_NEAR_WALL_LINE = `A few guest requests left. ${LOGIN_VALUE_LINE}`;
|
|
21528
|
+
var SIGNED_IN_CONTINUE_LINE = "Signed in. Your conversation continues — full daily allowance is unlocked.";
|
|
21529
|
+
var HEADLESS_FALLBACK_LINE = "No browser or clipboard here — open the URL and enter the code above.";
|
|
21530
|
+
function isHandoffTreatment(variant) {
|
|
21531
|
+
return variant === "treatment";
|
|
21532
|
+
}
|
|
21533
|
+
|
|
21362
21534
|
// src/clipboard.ts
|
|
21363
21535
|
import { spawn as spawn2 } from "node:child_process";
|
|
21364
|
-
|
|
21536
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
21537
|
+
function copyToClipboard(text, platform = process.platform, spawnImpl = spawn2, env = process.env) {
|
|
21365
21538
|
try {
|
|
21539
|
+
if (!clipboardAvailable(platform, env))
|
|
21540
|
+
return false;
|
|
21366
21541
|
const cmd = clipboardCommand(platform);
|
|
21367
21542
|
if (!cmd)
|
|
21368
21543
|
return false;
|
|
@@ -21375,6 +21550,24 @@ function copyToClipboard(text, platform = process.platform, spawnImpl = spawn2)
|
|
|
21375
21550
|
return false;
|
|
21376
21551
|
}
|
|
21377
21552
|
}
|
|
21553
|
+
function clipboardAvailable(platform = process.platform, env = process.env) {
|
|
21554
|
+
if (platform === "darwin" || platform === "win32")
|
|
21555
|
+
return commandOnPath(clipboardCommand(platform)?.command, env);
|
|
21556
|
+
if (platform !== "linux")
|
|
21557
|
+
return false;
|
|
21558
|
+
if (!env.DISPLAY && !env.WAYLAND_DISPLAY)
|
|
21559
|
+
return false;
|
|
21560
|
+
const cmd = clipboardCommand(platform);
|
|
21561
|
+
return commandOnPath(cmd?.command, env);
|
|
21562
|
+
}
|
|
21563
|
+
function commandOnPath(command, env) {
|
|
21564
|
+
if (!command)
|
|
21565
|
+
return false;
|
|
21566
|
+
if (command.includes("/"))
|
|
21567
|
+
return existsSync2(command);
|
|
21568
|
+
const path = env.PATH ?? "";
|
|
21569
|
+
return path.split(":").some((dir) => dir.length > 0 && existsSync2(`${dir}/${command}`));
|
|
21570
|
+
}
|
|
21378
21571
|
function clipboardCommand(platform) {
|
|
21379
21572
|
if (platform === "darwin")
|
|
21380
21573
|
return { command: "pbcopy", args: [] };
|
|
@@ -21436,7 +21629,7 @@ function allowanceIsLow(remaining, allowance) {
|
|
|
21436
21629
|
return false;
|
|
21437
21630
|
return remaining * LOW_ALLOWANCE_DENOMINATOR <= allowance * LOW_ALLOWANCE_NUMERATOR;
|
|
21438
21631
|
}
|
|
21439
|
-
function
|
|
21632
|
+
function formatRefillLocal2(refillAt, timeZone) {
|
|
21440
21633
|
const opts = {
|
|
21441
21634
|
hour: "numeric",
|
|
21442
21635
|
minute: "2-digit",
|
|
@@ -21456,7 +21649,7 @@ function refillAtFromIso(iso, now = new Date) {
|
|
|
21456
21649
|
return nextUtcMidnight(now);
|
|
21457
21650
|
}
|
|
21458
21651
|
function formatRefillCard(input) {
|
|
21459
|
-
const refill =
|
|
21652
|
+
const refill = formatRefillLocal2(input.refillAt, input.timeZone);
|
|
21460
21653
|
const lines = [
|
|
21461
21654
|
`${input.allowance.toLocaleString("en-US")} free credits refill at ${refill}.`
|
|
21462
21655
|
];
|
|
@@ -21485,6 +21678,7 @@ async function fetchRefillStats(baseUrl, jwt2, fetchImpl = fetch) {
|
|
|
21485
21678
|
allowanceCredits: body.allowance_credits,
|
|
21486
21679
|
refillAt: typeof body.refill_at === "string" ? body.refill_at : undefined,
|
|
21487
21680
|
showCard: body.return_bonus?.show_card === true,
|
|
21681
|
+
creditsBack: body.return_bonus?.credits_back === true,
|
|
21488
21682
|
guest: body.guest === true
|
|
21489
21683
|
};
|
|
21490
21684
|
} catch {
|
|
@@ -21515,6 +21709,12 @@ function sessionFromStats(stats) {
|
|
|
21515
21709
|
shown: false
|
|
21516
21710
|
};
|
|
21517
21711
|
}
|
|
21712
|
+
function formatCreditsBackNudge(stats, timeZone) {
|
|
21713
|
+
if (stats.guest || !stats.creditsBack)
|
|
21714
|
+
return;
|
|
21715
|
+
const refill = formatRefillLocal2(refillAtFromIso(stats.refillAt), timeZone);
|
|
21716
|
+
return `Your credits are back. ${stats.allowanceCredits.toLocaleString("en-US")} free credits refill at ${refill}.`;
|
|
21717
|
+
}
|
|
21518
21718
|
function formatLowAllowanceCard(session, remaining, timeZone) {
|
|
21519
21719
|
if (!session.show || session.shown || session.allowance === undefined)
|
|
21520
21720
|
return;
|
|
@@ -21543,14 +21743,48 @@ function formatExitRefillCard(session, end, timeZone) {
|
|
|
21543
21743
|
});
|
|
21544
21744
|
}
|
|
21545
21745
|
|
|
21746
|
+
// src/pending-login.ts
|
|
21747
|
+
import { mkdir as mkdir4, readFile as readFile3, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
21748
|
+
import { dirname as dirname5, join as join6 } from "node:path";
|
|
21749
|
+
var FINISH_SIGNIN_LINE = "one step to finish signing in";
|
|
21750
|
+
function pendingLoginPath(credentialsPath) {
|
|
21751
|
+
return join6(dirname5(credentialsPath), "pending-login.json");
|
|
21752
|
+
}
|
|
21753
|
+
async function loadPendingLogin(credentialsPath) {
|
|
21754
|
+
try {
|
|
21755
|
+
const raw = await readFile3(pendingLoginPath(credentialsPath), "utf8");
|
|
21756
|
+
const parsed = JSON.parse(raw);
|
|
21757
|
+
if (typeof parsed.session_id !== "string" || typeof parsed.user_code !== "string" || typeof parsed.verification_uri !== "string" || typeof parsed.expires_at !== "number") {
|
|
21758
|
+
return null;
|
|
21759
|
+
}
|
|
21760
|
+
return {
|
|
21761
|
+
session_id: parsed.session_id,
|
|
21762
|
+
user_code: parsed.user_code,
|
|
21763
|
+
verification_uri: parsed.verification_uri,
|
|
21764
|
+
expires_at: parsed.expires_at
|
|
21765
|
+
};
|
|
21766
|
+
} catch {
|
|
21767
|
+
return null;
|
|
21768
|
+
}
|
|
21769
|
+
}
|
|
21770
|
+
async function savePendingLogin(credentialsPath, pending) {
|
|
21771
|
+
const path = pendingLoginPath(credentialsPath);
|
|
21772
|
+
await mkdir4(dirname5(path), { recursive: true });
|
|
21773
|
+
await writeFile3(path, JSON.stringify(pending), { mode: 384 });
|
|
21774
|
+
}
|
|
21775
|
+
async function clearPendingLogin(credentialsPath) {
|
|
21776
|
+
await rm4(pendingLoginPath(credentialsPath), { force: true });
|
|
21777
|
+
}
|
|
21778
|
+
|
|
21546
21779
|
// src/login-ux.ts
|
|
21547
21780
|
var LOGIN_REWARD_LINE = "Sign in with GitHub to get 15,000 free credits a day";
|
|
21548
21781
|
var LOGIN_WAITING_LINE = "Waiting for GitHub…";
|
|
21549
21782
|
var LOGIN_TIMEOUT_LINE = "GitHub sign-in timed out. Run /login to try again.";
|
|
21550
|
-
function formatDeviceCodePrompt(device) {
|
|
21783
|
+
function formatDeviceCodePrompt(device, variant) {
|
|
21784
|
+
const value = isHandoffTreatment(variant) ? LOGIN_VALUE_LINE : LOGIN_REWARD_LINE;
|
|
21551
21785
|
return [
|
|
21552
21786
|
"",
|
|
21553
|
-
|
|
21787
|
+
value,
|
|
21554
21788
|
LOGIN_BONUS_UNLOCK_LINE,
|
|
21555
21789
|
"",
|
|
21556
21790
|
` Open ${device.verification_uri}`,
|
|
@@ -21561,7 +21795,15 @@ function formatDeviceCodePrompt(device) {
|
|
|
21561
21795
|
}
|
|
21562
21796
|
async function runDeviceLogin(deps, attribution = {}) {
|
|
21563
21797
|
const device = await startDeviceFlow(deps.baseUrl);
|
|
21564
|
-
deps.log(formatDeviceCodePrompt(device));
|
|
21798
|
+
deps.log(formatDeviceCodePrompt(device, deps.variant));
|
|
21799
|
+
if (deps.credentialsPath) {
|
|
21800
|
+
await savePendingLogin(deps.credentialsPath, {
|
|
21801
|
+
session_id: device.session_id,
|
|
21802
|
+
user_code: device.user_code,
|
|
21803
|
+
verification_uri: device.verification_uri,
|
|
21804
|
+
expires_at: (deps.now ?? Date.now)() + (device.expires_in ?? 900) * 1000
|
|
21805
|
+
});
|
|
21806
|
+
}
|
|
21565
21807
|
const copied = (deps.copy ?? copyToClipboard)(device.user_code);
|
|
21566
21808
|
if (copied)
|
|
21567
21809
|
deps.log(` (code copied to clipboard)
|
|
@@ -21579,12 +21821,66 @@ async function runDeviceLogin(deps, attribution = {}) {
|
|
|
21579
21821
|
jwt: deps.jwt,
|
|
21580
21822
|
fetchImpl: deps.fetchImpl
|
|
21581
21823
|
});
|
|
21824
|
+
} else if (!copied) {
|
|
21825
|
+
deps.log(HEADLESS_FALLBACK_LINE);
|
|
21582
21826
|
}
|
|
21583
21827
|
await submitConsent(deps.baseUrl, device.session_id, CONSENT_VERSION, attribution);
|
|
21584
21828
|
const expiresInMs = (device.expires_in ?? 900) * 1000;
|
|
21585
21829
|
const deadline = (deps.now ?? Date.now)() + expiresInMs;
|
|
21586
|
-
|
|
21587
|
-
|
|
21830
|
+
try {
|
|
21831
|
+
const token = await pollForToken(deps.baseUrl, device.session_id, device.interval, deps.sleep, { deadline, now: deps.now, jwt: deps.jwt, fetchImpl: deps.fetchImpl });
|
|
21832
|
+
if (deps.credentialsPath)
|
|
21833
|
+
await clearPendingLogin(deps.credentialsPath);
|
|
21834
|
+
return token;
|
|
21835
|
+
} catch (err) {
|
|
21836
|
+
throw err;
|
|
21837
|
+
}
|
|
21838
|
+
}
|
|
21839
|
+
|
|
21840
|
+
// src/allowance-counter.ts
|
|
21841
|
+
var cached2;
|
|
21842
|
+
function updateAllowanceCache(input) {
|
|
21843
|
+
if (typeof input.remaining_credits_today !== "number" || typeof input.allowance_credits !== "number") {
|
|
21844
|
+
return cached2;
|
|
21845
|
+
}
|
|
21846
|
+
const refill = typeof input.refill_at === "string" && !Number.isNaN(new Date(input.refill_at).getTime()) ? new Date(input.refill_at) : undefined;
|
|
21847
|
+
cached2 = {
|
|
21848
|
+
remaining: input.remaining_credits_today,
|
|
21849
|
+
allowance: input.allowance_credits,
|
|
21850
|
+
refillAt: refill,
|
|
21851
|
+
bonusCredits: typeof input.bonus_credits === "number" ? input.bonus_credits : undefined,
|
|
21852
|
+
guest: input.guest === true,
|
|
21853
|
+
visible: input.visible_allowance === true
|
|
21854
|
+
};
|
|
21855
|
+
return cached2;
|
|
21856
|
+
}
|
|
21857
|
+
function allowanceLine(snapshot = cached2, timeZone) {
|
|
21858
|
+
if (!snapshot?.visible)
|
|
21859
|
+
return;
|
|
21860
|
+
return formatVisibleAllowanceLine({
|
|
21861
|
+
remaining: snapshot.remaining,
|
|
21862
|
+
refillAt: snapshot.refillAt,
|
|
21863
|
+
guest: snapshot.guest,
|
|
21864
|
+
timeZone
|
|
21865
|
+
});
|
|
21866
|
+
}
|
|
21867
|
+
function installModelAllowanceHandler(mode, getLine = () => allowanceLine()) {
|
|
21868
|
+
const m = mode;
|
|
21869
|
+
if (typeof m.handleModelCommand !== "function" || typeof m.showStatus !== "function")
|
|
21870
|
+
return false;
|
|
21871
|
+
const original = m.handleModelCommand.bind(mode);
|
|
21872
|
+
m.handleModelCommand = () => {
|
|
21873
|
+
const line = getLine();
|
|
21874
|
+
if (line)
|
|
21875
|
+
m.showStatus(line);
|
|
21876
|
+
return original();
|
|
21877
|
+
};
|
|
21878
|
+
return true;
|
|
21879
|
+
}
|
|
21880
|
+
function notifyAllowance(ctx) {
|
|
21881
|
+
const line = allowanceLine();
|
|
21882
|
+
if (line)
|
|
21883
|
+
ctx.ui.notify(line, "info");
|
|
21588
21884
|
}
|
|
21589
21885
|
|
|
21590
21886
|
// src/commands.ts
|
|
@@ -21607,6 +21903,18 @@ async function closeOtherSession(opts, ctx) {
|
|
|
21607
21903
|
ctx.ui.notify("Couldn't reach free-pi to reset your session — check your connection.", "error");
|
|
21608
21904
|
}
|
|
21609
21905
|
}
|
|
21906
|
+
async function sendFeedback(opts, args, ctx) {
|
|
21907
|
+
const parsed = parseFeedbackArgs(args);
|
|
21908
|
+
if (parsed.kind === "usage")
|
|
21909
|
+
return ctx.ui.notify(FEEDBACK_USAGE, "info");
|
|
21910
|
+
if (parsed.kind === "too_long")
|
|
21911
|
+
return ctx.ui.notify(`Couldn't send feedback: it is too long (${FEEDBACK_MAX} characters max).`, "error");
|
|
21912
|
+
const r = await sendBugReport({ baseUrl: opts.baseUrl, getToken: opts.getToken, fetchImpl: opts.fetchImpl }, buildBugRequest(parsed.description, { sessionId: ctx.sessionManager?.getSessionId(), model: ctx.model?.id }), "feedback");
|
|
21913
|
+
if (r.ok)
|
|
21914
|
+
ctx.ui.notify(`Thanks for the feedback (#${r.id}).`, "info");
|
|
21915
|
+
else
|
|
21916
|
+
ctx.ui.notify(r.message, "error");
|
|
21917
|
+
}
|
|
21610
21918
|
function showWhatsNew(ctx) {
|
|
21611
21919
|
const lines = CHANGELOG_HIGHLIGHTS.slice(0, 3).flatMap((e) => [
|
|
21612
21920
|
`free-pi ${e.version}:`,
|
|
@@ -21671,23 +21979,27 @@ async function openPrivacyPolicy(opts, ctx) {
|
|
|
21671
21979
|
(opts.openBrowserImpl ?? openBrowser)(PRIVACY_URL);
|
|
21672
21980
|
ctx.ui.notify(`Opening the Privacy Policy in your browser: ${PRIVACY_URL}`, "info");
|
|
21673
21981
|
}
|
|
21982
|
+
function resolveHandoffVariant(opts) {
|
|
21983
|
+
return typeof opts.variant === "function" ? opts.variant() : opts.variant;
|
|
21984
|
+
}
|
|
21674
21985
|
async function runLogin(opts, ctx) {
|
|
21675
|
-
ctx.ui.notify("Sign in with GitHub to get 15,000 free credits a day.", "info");
|
|
21676
|
-
ctx.ui.notify(LOGIN_BONUS_UNLOCK_LINE, "info");
|
|
21677
21986
|
try {
|
|
21678
21987
|
const attribution = opts.agentDir ? await loadAttribution(getAttributionPath(opts.agentDir)) : {};
|
|
21988
|
+
const variant = resolveHandoffVariant(opts);
|
|
21679
21989
|
const token = await runDeviceLogin({
|
|
21680
21990
|
baseUrl: opts.baseUrl,
|
|
21681
21991
|
openBrowser: opts.openBrowserImpl ?? openBrowser,
|
|
21682
21992
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
21683
21993
|
log: (message) => ctx.ui.notify(message.trim(), "info"),
|
|
21684
21994
|
jwt: await opts.getToken(),
|
|
21685
|
-
fetchImpl: opts.fetchImpl
|
|
21995
|
+
fetchImpl: opts.fetchImpl,
|
|
21996
|
+
variant,
|
|
21997
|
+
credentialsPath: opts.credentialsPath
|
|
21686
21998
|
}, attribution);
|
|
21687
21999
|
if (opts.credentialsPath)
|
|
21688
22000
|
await saveJwt(opts.credentialsPath, token);
|
|
21689
22001
|
opts.onLogin?.(token);
|
|
21690
|
-
ctx.ui.notify("Signed in. Restart free-pi-cli to unlock 15,000 free credits a day and a one-time comeback bonus.", "info");
|
|
22002
|
+
ctx.ui.notify(isHandoffTreatment(variant) ? SIGNED_IN_CONTINUE_LINE : "Signed in. Restart free-pi-cli to unlock 15,000 free credits a day and a one-time comeback bonus.", "info");
|
|
21691
22003
|
} catch (err) {
|
|
21692
22004
|
if (err instanceof LoginTimeoutError) {
|
|
21693
22005
|
ctx.ui.notify(LOGIN_TIMEOUT_LINE, "warning");
|
|
@@ -21696,6 +22008,22 @@ async function runLogin(opts, ctx) {
|
|
|
21696
22008
|
ctx.ui.notify("Couldn't finish GitHub sign-in. Run /login to try again.", "error");
|
|
21697
22009
|
}
|
|
21698
22010
|
}
|
|
22011
|
+
function installLoginHandler(mode, opts) {
|
|
22012
|
+
const m = mode;
|
|
22013
|
+
mode.handleLoginCommand = () => {
|
|
22014
|
+
const lines = [];
|
|
22015
|
+
const notify = (message, type) => {
|
|
22016
|
+
if (type === "error")
|
|
22017
|
+
return m.showError(message);
|
|
22018
|
+
if (type === "warning")
|
|
22019
|
+
return m.showWarning(message);
|
|
22020
|
+
lines.push(message);
|
|
22021
|
+
m.showStatus(lines.join(`
|
|
22022
|
+
`));
|
|
22023
|
+
};
|
|
22024
|
+
return runLogin(opts, { ui: { notify } });
|
|
22025
|
+
};
|
|
22026
|
+
}
|
|
21699
22027
|
async function showUsage(opts, ctx) {
|
|
21700
22028
|
const r = await fetchUsageText(opts);
|
|
21701
22029
|
ctx.ui.notify(r.text, r.ok ? "info" : "error");
|
|
@@ -21785,23 +22113,34 @@ function loadRea(pi, opts, ctx) {
|
|
|
21785
22113
|
}
|
|
21786
22114
|
function createGuestLoginExtension(opts) {
|
|
21787
22115
|
let started = false;
|
|
22116
|
+
let nearWallWarned = false;
|
|
21788
22117
|
const start = (ctx) => {
|
|
21789
22118
|
started = true;
|
|
21790
|
-
|
|
22119
|
+
const variant = resolveHandoffVariant(opts);
|
|
22120
|
+
ctx.ui.notify(isHandoffTreatment(variant) ? LOGIN_VALUE_LINE : LOGIN_REWARD_LINE, "info");
|
|
21791
22121
|
runLogin(opts, ctx);
|
|
21792
22122
|
};
|
|
21793
22123
|
return {
|
|
21794
22124
|
name: "free-pi-guest-login",
|
|
21795
22125
|
factory(pi) {
|
|
21796
22126
|
pi.on("turn_end", (event, ctx) => {
|
|
21797
|
-
if (!opts.isGuest?.()
|
|
22127
|
+
if (!opts.isGuest?.())
|
|
21798
22128
|
return;
|
|
21799
22129
|
const msg = event.message;
|
|
21800
|
-
if (msg?.stopReason
|
|
22130
|
+
if (!started && msg?.stopReason === "error" && parseErrorEnvelope(msg.errorMessage)?.code === "guest_allowance") {
|
|
22131
|
+
start(ctx);
|
|
21801
22132
|
return;
|
|
21802
|
-
|
|
22133
|
+
}
|
|
22134
|
+
if (started || nearWallWarned || !isHandoffTreatment(resolveHandoffVariant(opts)))
|
|
21803
22135
|
return;
|
|
21804
|
-
|
|
22136
|
+
if (msg?.stopReason === "error")
|
|
22137
|
+
return;
|
|
22138
|
+
Promise.resolve(opts.getToken()).then((jwt2) => fetchMe2(opts.baseUrl, jwt2)).then((me) => {
|
|
22139
|
+
if (nearWallWarned || started || !me?.guest_near_wall)
|
|
22140
|
+
return;
|
|
22141
|
+
nearWallWarned = true;
|
|
22142
|
+
ctx.ui.notify(GUEST_NEAR_WALL_LINE, "warning");
|
|
22143
|
+
}).catch(() => {});
|
|
21805
22144
|
});
|
|
21806
22145
|
pi.on("agent_end", (event, ctx) => {
|
|
21807
22146
|
if (!opts.isGuest?.() || started)
|
|
@@ -21831,18 +22170,21 @@ function createFreePiCommandsExtension(opts) {
|
|
|
21831
22170
|
description: "Update free-pi-cli to the latest version.",
|
|
21832
22171
|
handler: (_args, ctx) => runUpdate(ctx)
|
|
21833
22172
|
});
|
|
21834
|
-
pi.registerCommand("login", {
|
|
21835
|
-
description: "Sign in with GitHub to unlock daily credits and a comeback bonus.",
|
|
21836
|
-
handler: (_args, ctx) => runLogin(opts, ctx)
|
|
21837
|
-
});
|
|
21838
22173
|
pi.registerCommand("usage", {
|
|
21839
22174
|
description: "Show credits left today, refill time, and purchased credit.",
|
|
21840
22175
|
handler: (_args, ctx) => showUsage(opts, ctx)
|
|
21841
22176
|
});
|
|
22177
|
+
pi.on("model_select", (_event, ctx) => {
|
|
22178
|
+
notifyAllowance(ctx);
|
|
22179
|
+
});
|
|
21842
22180
|
pi.registerCommand("buy-credits", {
|
|
21843
22181
|
description: "Buy more usage ($5 or $10 packs, card or USDC) — opens the buy page.",
|
|
21844
22182
|
handler: (_args, ctx) => buyCredits(opts, ctx)
|
|
21845
22183
|
});
|
|
22184
|
+
pi.registerCommand("feedback", {
|
|
22185
|
+
description: FEEDBACK_DESCRIPTION,
|
|
22186
|
+
handler: (args, ctx) => sendFeedback(opts, args, ctx)
|
|
22187
|
+
});
|
|
21846
22188
|
pi.registerCommand("support", {
|
|
21847
22189
|
description: "Open today's advertiser in your browser to support free-pi.",
|
|
21848
22190
|
handler: (_args, ctx) => openAdvertiser(opts, ctx)
|
|
@@ -21918,7 +22260,7 @@ function truncate2(text, maxWidth) {
|
|
|
21918
22260
|
return text.slice(0, maxWidth);
|
|
21919
22261
|
return `${text.slice(0, maxWidth - 1)}…`;
|
|
21920
22262
|
}
|
|
21921
|
-
function headerLines(theme, modelName, expanded, width = Number.POSITIVE_INFINITY, updateLatest, guest) {
|
|
22263
|
+
function headerLines(theme, modelName, expanded, width = Number.POSITIVE_INFINITY, updateLatest, guest, allowanceLine2) {
|
|
21922
22264
|
const inner = width - 1;
|
|
21923
22265
|
const fit2 = (plain, styled) => plain.length <= inner ? styled() : theme.fg("dim", truncate2(plain, inner));
|
|
21924
22266
|
const titleRest = ` · ${modelName} · ctrl+o for help`;
|
|
@@ -21934,6 +22276,7 @@ function headerLines(theme, modelName, expanded, width = Number.POSITIVE_INFINIT
|
|
|
21934
22276
|
"",
|
|
21935
22277
|
dim(WELCOME_LINE),
|
|
21936
22278
|
...guest ? [dim(GUEST_LINE)] : [],
|
|
22279
|
+
...allowanceLine2 ? [dim(allowanceLine2)] : [],
|
|
21937
22280
|
"",
|
|
21938
22281
|
dim(CONSENT_LINE_1),
|
|
21939
22282
|
dim(CONSENT_LINE_2),
|
|
@@ -21957,7 +22300,7 @@ function createHeaderExtension(opts) {
|
|
|
21957
22300
|
let expanded = false;
|
|
21958
22301
|
return {
|
|
21959
22302
|
render(width) {
|
|
21960
|
-
return headerLines(theme, opts.modelName, expanded, width, opts.updateLatest, opts.guest);
|
|
22303
|
+
return headerLines(theme, opts.modelName, expanded, width, opts.updateLatest, opts.guest, opts.allowanceLine);
|
|
21961
22304
|
},
|
|
21962
22305
|
invalidate() {},
|
|
21963
22306
|
setExpanded(next) {
|
|
@@ -22181,12 +22524,12 @@ function resolveFreePiScope(modelRuntime, providerName, modelIds, env = process.
|
|
|
22181
22524
|
import { createHash as createHash3 } from "node:crypto";
|
|
22182
22525
|
import { execFileSync } from "node:child_process";
|
|
22183
22526
|
import { lstatSync as lstatSync2, readFileSync as readFileSync3, readlinkSync as readlinkSync2 } from "node:fs";
|
|
22184
|
-
import { join as
|
|
22527
|
+
import { join as join8 } from "node:path";
|
|
22185
22528
|
|
|
22186
22529
|
// src/hive-guard.ts
|
|
22187
|
-
import { existsSync as
|
|
22530
|
+
import { existsSync as existsSync3, lstatSync, readdirSync, readlinkSync, realpathSync } from "node:fs";
|
|
22188
22531
|
import { homedir as homedir3 } from "node:os";
|
|
22189
|
-
import { basename, dirname as
|
|
22532
|
+
import { basename, dirname as dirname6, isAbsolute, join as join7, relative, resolve, sep } from "node:path";
|
|
22190
22533
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
22191
22534
|
var GUARD_INPUT_LIMIT = 1500;
|
|
22192
22535
|
var CLASSIFY_TIMEOUT_MS = 2500;
|
|
@@ -22198,7 +22541,7 @@ var SAFE_READ_TOOLS = ["read", "grep", "find", "ls"];
|
|
|
22198
22541
|
var ALWAYS_ASK = ["codemode", "tool_search", FUSION_PLAN_TOOL, WEB_SEARCH_TOOL_NAME, DOCS_LOOKUP_TOOL_NAME];
|
|
22199
22542
|
var CLASSIFIED = ["bash", "write", "edit"];
|
|
22200
22543
|
var SECRET_BASENAME = [/^\.env(\..*)?$/, /\.pem$/, /^id_(rsa|dsa|ecdsa|ed25519)/];
|
|
22201
|
-
var SECRET_HOME_DIRS = [".ssh", ".aws",
|
|
22544
|
+
var SECRET_HOME_DIRS = [".ssh", ".aws", join7(".config", "freepi"), ".free-pi"];
|
|
22202
22545
|
var SECRET_IN_TEXT = /(?:^|[\s"'=:/])\.env(?:\.[\w.-]+)?(?=$|[\s"';|&)])|\.pem\b|\bid_(?:rsa|dsa|ecdsa|ed25519)|\.ssh\b|\.aws\b|\.config\/freepi|\.free-pi\b/;
|
|
22203
22546
|
var BASH_DENY = [
|
|
22204
22547
|
[/\brm\s+(?:-\S+\s+)*(?:\/|~|\$HOME)\*?(?:\s|$)/, "destructive command"],
|
|
@@ -22221,7 +22564,7 @@ function protectedLinkTargets(root, limit) {
|
|
|
22221
22564
|
const note = (link) => {
|
|
22222
22565
|
let target2;
|
|
22223
22566
|
try {
|
|
22224
|
-
target2 = resolve(
|
|
22567
|
+
target2 = resolve(dirname6(link), readlinkSync(link));
|
|
22225
22568
|
} catch {
|
|
22226
22569
|
return;
|
|
22227
22570
|
}
|
|
@@ -22231,7 +22574,7 @@ function protectedLinkTargets(root, limit) {
|
|
|
22231
22574
|
out.push(real);
|
|
22232
22575
|
};
|
|
22233
22576
|
for (const entry of [".git/hooks", ".git/config"]) {
|
|
22234
|
-
const link =
|
|
22577
|
+
const link = join7(root, entry);
|
|
22235
22578
|
try {
|
|
22236
22579
|
if (lstatSync(link).isSymbolicLink())
|
|
22237
22580
|
note(link);
|
|
@@ -22250,7 +22593,7 @@ function protectedLinkTargets(root, limit) {
|
|
|
22250
22593
|
for (const entry of entries) {
|
|
22251
22594
|
if (++seen > limit)
|
|
22252
22595
|
return null;
|
|
22253
|
-
const path =
|
|
22596
|
+
const path = join7(dir, entry.name);
|
|
22254
22597
|
if (entry.isSymbolicLink()) {
|
|
22255
22598
|
if (protectedByName(relative(root, path)))
|
|
22256
22599
|
note(path);
|
|
@@ -22300,7 +22643,7 @@ function resolveLikePi(raw, cwd, home, platform) {
|
|
|
22300
22643
|
if (p === "~")
|
|
22301
22644
|
p = home;
|
|
22302
22645
|
else if (p.startsWith("~/") || platform === "win32" && p.startsWith("~\\"))
|
|
22303
|
-
p =
|
|
22646
|
+
p = join7(home, p.slice(2));
|
|
22304
22647
|
if (/^file:\/\//.test(p))
|
|
22305
22648
|
p = fileURLToPath3(p);
|
|
22306
22649
|
return resolve(cwd, p);
|
|
@@ -22310,14 +22653,14 @@ function realpathDeepest(p) {
|
|
|
22310
22653
|
const rest3 = [];
|
|
22311
22654
|
for (;; ) {
|
|
22312
22655
|
try {
|
|
22313
|
-
return
|
|
22656
|
+
return join7(realpathSync(current), ...rest3.reverse());
|
|
22314
22657
|
} catch {
|
|
22315
22658
|
try {
|
|
22316
22659
|
lstatSync(current);
|
|
22317
22660
|
return null;
|
|
22318
22661
|
} catch {}
|
|
22319
22662
|
}
|
|
22320
|
-
const parent =
|
|
22663
|
+
const parent = dirname6(current);
|
|
22321
22664
|
if (parent === current)
|
|
22322
22665
|
return null;
|
|
22323
22666
|
rest3.push(basename(current));
|
|
@@ -22332,7 +22675,7 @@ function isSecret(target2, home) {
|
|
|
22332
22675
|
if (SECRET_BASENAME.some((re) => re.test(basename(target2))))
|
|
22333
22676
|
return true;
|
|
22334
22677
|
return SECRET_HOME_DIRS.some((dir) => {
|
|
22335
|
-
const lexical =
|
|
22678
|
+
const lexical = join7(home, dir);
|
|
22336
22679
|
return isInside(lexical, target2) || isInside(realpathDeepest(lexical) ?? lexical, target2);
|
|
22337
22680
|
});
|
|
22338
22681
|
}
|
|
@@ -22353,7 +22696,7 @@ function scanForSecret(dir) {
|
|
|
22353
22696
|
if (SECRET_BASENAME.some((re) => re.test(entry.name)))
|
|
22354
22697
|
return "yes";
|
|
22355
22698
|
if (entry.isDirectory())
|
|
22356
|
-
queue.push(
|
|
22699
|
+
queue.push(join7(current, entry.name));
|
|
22357
22700
|
}
|
|
22358
22701
|
}
|
|
22359
22702
|
return "no";
|
|
@@ -22393,7 +22736,7 @@ function pathVerdict(tool, input, opts, home) {
|
|
|
22393
22736
|
const verdict = checkPath(raw, search, opts, home);
|
|
22394
22737
|
if (verdict)
|
|
22395
22738
|
return verdict;
|
|
22396
|
-
if (tool === "read" && !
|
|
22739
|
+
if (tool === "read" && !existsSync3(resolveLikePi(raw, opts.cwd, home, opts.platform ?? process.platform))) {
|
|
22397
22740
|
return { ask: "file not found; pi may open a similarly named file" };
|
|
22398
22741
|
}
|
|
22399
22742
|
return;
|
|
@@ -22528,7 +22871,7 @@ function worktreeSnapshot(g) {
|
|
|
22528
22871
|
const status = entry.slice(0, 2);
|
|
22529
22872
|
if (status[0] === "R" || status[0] === "C")
|
|
22530
22873
|
i++;
|
|
22531
|
-
files.set(entry.slice(3), `${status}:${fingerprint(
|
|
22874
|
+
files.set(entry.slice(3), `${status}:${fingerprint(join8(root, entry.slice(3)))}`);
|
|
22532
22875
|
}
|
|
22533
22876
|
return { files, head: runGit(g, "rev-parse", "HEAD")?.trim() };
|
|
22534
22877
|
} catch (e) {
|
|
@@ -22981,7 +23324,7 @@ ${decision.reason}`, ACTION_DIALOG_MS);
|
|
|
22981
23324
|
import { spawnSync } from "node:child_process";
|
|
22982
23325
|
import {
|
|
22983
23326
|
chmodSync,
|
|
22984
|
-
existsSync as
|
|
23327
|
+
existsSync as existsSync4,
|
|
22985
23328
|
lstatSync as lstatSync3,
|
|
22986
23329
|
mkdirSync,
|
|
22987
23330
|
mkdtempSync,
|
|
@@ -22992,7 +23335,7 @@ import {
|
|
|
22992
23335
|
writeFileSync
|
|
22993
23336
|
} from "node:fs";
|
|
22994
23337
|
import { homedir as homedir4, tmpdir } from "node:os";
|
|
22995
|
-
import { dirname as
|
|
23338
|
+
import { dirname as dirname7, join as join9 } from "node:path";
|
|
22996
23339
|
var SECRET_NAME_PATTERNS = [
|
|
22997
23340
|
"\\.env(\\..*)?",
|
|
22998
23341
|
"[^/]*\\.pem",
|
|
@@ -23027,8 +23370,8 @@ var TOOLCHAIN_ROOTS = [
|
|
|
23027
23370
|
".local/share/pnpm",
|
|
23028
23371
|
"Library/pnpm"
|
|
23029
23372
|
];
|
|
23030
|
-
function toolchainRoots(home, exists =
|
|
23031
|
-
return TOOLCHAIN_ROOTS.map((r) =>
|
|
23373
|
+
function toolchainRoots(home, exists = existsSync4) {
|
|
23374
|
+
return TOOLCHAIN_ROOTS.map((r) => join9(home, r)).filter((p) => exists(p));
|
|
23032
23375
|
}
|
|
23033
23376
|
var ENV_ALLOW = ["PATH", "LANG", "LC_ALL", "LC_CTYPE", "TERM", "COLORTERM", "TZ", "USER", "LOGNAME", "SHELL", "PI_PROVIDER", "PI_MODEL"];
|
|
23034
23377
|
function under(path, dir) {
|
|
@@ -23046,10 +23389,10 @@ function sandboxEnv(base, o) {
|
|
|
23046
23389
|
for (const k of ENV_ALLOW)
|
|
23047
23390
|
if (base[k] !== undefined)
|
|
23048
23391
|
env[k] = base[k];
|
|
23049
|
-
env.PATH = [...o.developerDir ? [
|
|
23392
|
+
env.PATH = [...o.developerDir ? [join9(o.developerDir, "usr/bin")] : [], ...o.pathPrefix ?? [], ...(base.PATH ?? "/usr/bin:/bin").split(":")].filter((p) => p.startsWith("/") && !under(p, o.project) && !under(realOrSelf(p), o.project)).join(":");
|
|
23050
23393
|
if (o.developerDir)
|
|
23051
23394
|
env.DEVELOPER_DIR = o.developerDir;
|
|
23052
|
-
env.HOME =
|
|
23395
|
+
env.HOME = join9(o.scratch, "home");
|
|
23053
23396
|
env.TMP = env.TEMP = o.scratch;
|
|
23054
23397
|
env.TMPDIR = `${o.scratch}/`;
|
|
23055
23398
|
env.BUN_OPTIONS = "--no-env-file";
|
|
@@ -23059,7 +23402,7 @@ function sandboxEnv(base, o) {
|
|
|
23059
23402
|
env.GIT_CONFIG_COUNT = "1";
|
|
23060
23403
|
env.GIT_CONFIG_KEY_0 = "credential.helper";
|
|
23061
23404
|
env.GIT_CONFIG_VALUE_0 = "";
|
|
23062
|
-
env.npm_config_cache =
|
|
23405
|
+
env.npm_config_cache = join9(o.scratch, "npm-cache");
|
|
23063
23406
|
env.FREEPI_HIVE_CALL = o.callId;
|
|
23064
23407
|
return env;
|
|
23065
23408
|
}
|
|
@@ -23105,7 +23448,7 @@ function scanProject(project, limit = SCAN_LIMIT2) {
|
|
|
23105
23448
|
scan.overflow = true;
|
|
23106
23449
|
break;
|
|
23107
23450
|
}
|
|
23108
|
-
const path =
|
|
23451
|
+
const path = join9(dir, e.name);
|
|
23109
23452
|
if (e.isSymbolicLink())
|
|
23110
23453
|
continue;
|
|
23111
23454
|
if (e.isDirectory()) {
|
|
@@ -23188,7 +23531,7 @@ var MAC_MACH_SERVICES = [
|
|
|
23188
23531
|
["com.apple.system.opendirectoryd.libinfo", "user and group names: id, ls -l, python pwd, git author"]
|
|
23189
23532
|
];
|
|
23190
23533
|
function developerDir(o = {}) {
|
|
23191
|
-
const exists = o.exists ??
|
|
23534
|
+
const exists = o.exists ?? existsSync4;
|
|
23192
23535
|
const realpath = o.realpath ?? realpathSync2;
|
|
23193
23536
|
const run = o.exec ?? defaultExec;
|
|
23194
23537
|
const candidates2 = [];
|
|
@@ -23205,14 +23548,14 @@ function developerDir(o = {}) {
|
|
|
23205
23548
|
continue;
|
|
23206
23549
|
try {
|
|
23207
23550
|
const real = realpath(c);
|
|
23208
|
-
if (exists(
|
|
23551
|
+
if (exists(join9(real, "usr/bin/git")))
|
|
23209
23552
|
return real;
|
|
23210
23553
|
} catch {}
|
|
23211
23554
|
}
|
|
23212
23555
|
return null;
|
|
23213
23556
|
}
|
|
23214
23557
|
function opensslFiles(o = {}) {
|
|
23215
|
-
const exists = o.exists ??
|
|
23558
|
+
const exists = o.exists ?? existsSync4;
|
|
23216
23559
|
const list = o.list ?? ((d) => {
|
|
23217
23560
|
try {
|
|
23218
23561
|
return readdirSync2(d);
|
|
@@ -23223,18 +23566,18 @@ function opensslFiles(o = {}) {
|
|
|
23223
23566
|
const files = [];
|
|
23224
23567
|
const dirs = [];
|
|
23225
23568
|
for (const prefix of o.prefixes ?? ["/opt/homebrew", "/usr/local"]) {
|
|
23226
|
-
const etc =
|
|
23569
|
+
const etc = join9(prefix, "etc");
|
|
23227
23570
|
for (const name of list(etc)) {
|
|
23228
23571
|
if (!/^openssl(@[0-9.]+)?$/.test(name))
|
|
23229
23572
|
continue;
|
|
23230
23573
|
for (const f of ["openssl.cnf", "ct_log_list.cnf", "cert.pem"])
|
|
23231
|
-
if (exists(
|
|
23232
|
-
files.push(
|
|
23233
|
-
if (exists(
|
|
23234
|
-
dirs.push(
|
|
23574
|
+
if (exists(join9(etc, name, f)))
|
|
23575
|
+
files.push(join9(etc, name, f));
|
|
23576
|
+
if (exists(join9(etc, name, "certs")))
|
|
23577
|
+
dirs.push(join9(etc, name, "certs"));
|
|
23235
23578
|
}
|
|
23236
|
-
if (exists(
|
|
23237
|
-
files.push(
|
|
23579
|
+
if (exists(join9(etc, "ca-certificates/cert.pem")))
|
|
23580
|
+
files.push(join9(etc, "ca-certificates/cert.pem"));
|
|
23238
23581
|
}
|
|
23239
23582
|
return { files, dirs };
|
|
23240
23583
|
}
|
|
@@ -23242,7 +23585,7 @@ var MAC_DEV_READ = ["/dev/null", "/dev/zero", "/dev/random", "/dev/urandom", "/d
|
|
|
23242
23585
|
var MAC_DEV_WRITE = ["/dev/null", "/dev/tty", "/dev/zero", "/dev/dtracehelper"];
|
|
23243
23586
|
function ancestors(p) {
|
|
23244
23587
|
const out = [];
|
|
23245
|
-
for (let d =
|
|
23588
|
+
for (let d = dirname7(p);; d = dirname7(d)) {
|
|
23246
23589
|
out.push(d);
|
|
23247
23590
|
if (d === "/")
|
|
23248
23591
|
break;
|
|
@@ -23278,9 +23621,9 @@ function buildMacProfile(i) {
|
|
|
23278
23621
|
L.push(`(deny file-read* file-write* (literal ${sbString(s)}))`);
|
|
23279
23622
|
for (const f of i.links)
|
|
23280
23623
|
L.push(`(deny file-write* (literal ${sbString(f)}))`);
|
|
23281
|
-
const guarded = new Set([i.project,
|
|
23282
|
-
for (const f of [...i.sockets, ...i.links, ...i.nodeModules ?? [],
|
|
23283
|
-
for (let d =
|
|
23624
|
+
const guarded = new Set([i.project, join9(i.project, ".git"), ...i.nodeModules ?? []]);
|
|
23625
|
+
for (const f of [...i.sockets, ...i.links, ...i.nodeModules ?? [], join9(i.project, ".git")]) {
|
|
23626
|
+
for (let d = dirname7(f);d.length >= i.project.length && (d === i.project || d.startsWith(i.project + "/")); d = dirname7(d))
|
|
23284
23627
|
guarded.add(d);
|
|
23285
23628
|
}
|
|
23286
23629
|
for (const d of [...guarded].sort())
|
|
@@ -23333,7 +23676,7 @@ var BWRAP_ETC = [
|
|
|
23333
23676
|
"resolv.conf"
|
|
23334
23677
|
].map((e) => `/etc/${e}`);
|
|
23335
23678
|
function buildBwrapArgs(i) {
|
|
23336
|
-
const exists = i.exists ??
|
|
23679
|
+
const exists = i.exists ?? existsSync4;
|
|
23337
23680
|
const a = ["--unshare-all", "--die-with-parent", "--new-session"];
|
|
23338
23681
|
const ro = (p) => a.push("--ro-bind", p, p);
|
|
23339
23682
|
for (const d of BWRAP_SYSTEM_DIRS)
|
|
@@ -23349,10 +23692,10 @@ function buildBwrapArgs(i) {
|
|
|
23349
23692
|
a.push("--bind", i.scratch, i.scratch);
|
|
23350
23693
|
for (const d of i.nodeModules)
|
|
23351
23694
|
ro(d);
|
|
23352
|
-
if (exists(
|
|
23353
|
-
ro(
|
|
23354
|
-
if (exists(
|
|
23355
|
-
ro(
|
|
23695
|
+
if (exists(join9(i.project, ".git/hooks")))
|
|
23696
|
+
ro(join9(i.project, ".git/hooks"));
|
|
23697
|
+
if (exists(join9(i.project, ".git/config")))
|
|
23698
|
+
ro(join9(i.project, ".git/config"));
|
|
23356
23699
|
for (const f of i.links)
|
|
23357
23700
|
ro(f);
|
|
23358
23701
|
for (const s of i.sockets)
|
|
@@ -23382,7 +23725,7 @@ function removeTree(path) {
|
|
|
23382
23725
|
return;
|
|
23383
23726
|
chmodSync(p, 448);
|
|
23384
23727
|
for (const e of readdirSync2(p))
|
|
23385
|
-
walk(
|
|
23728
|
+
walk(join9(p, e));
|
|
23386
23729
|
} catch {}
|
|
23387
23730
|
};
|
|
23388
23731
|
walk(path);
|
|
@@ -23420,16 +23763,16 @@ function createShellSandbox(deps = {}) {
|
|
|
23420
23763
|
if (deps.python !== undefined)
|
|
23421
23764
|
return deps.python;
|
|
23422
23765
|
const dev = platform === "darwin" ? macHost().developerDir : null;
|
|
23423
|
-
return dev &&
|
|
23766
|
+
return dev && existsSync4(join9(dev, "usr/bin/python3")) ? join9(dev, "usr/bin/python3") : null;
|
|
23424
23767
|
};
|
|
23425
|
-
let
|
|
23768
|
+
let cached3;
|
|
23426
23769
|
function probe() {
|
|
23427
23770
|
if (platform === "win32")
|
|
23428
23771
|
return { supported: false, reason: "Windows has no sandbox" };
|
|
23429
23772
|
if (platform !== "darwin" && platform !== "linux")
|
|
23430
23773
|
return { supported: false, reason: `No shell sandbox for ${platform}` };
|
|
23431
23774
|
const mac3 = platform === "darwin";
|
|
23432
|
-
if (!
|
|
23775
|
+
if (!existsSync4(launcher.sandbox))
|
|
23433
23776
|
return { supported: false, reason: mac3 ? "sandbox-exec is unavailable" : "bubblewrap is not installed" };
|
|
23434
23777
|
if (checkLauncher(launcher.sandbox, undefined, stat) !== null)
|
|
23435
23778
|
return { supported: false, reason: NOT_TRUSTWORTHY };
|
|
@@ -23439,7 +23782,7 @@ function createShellSandbox(deps = {}) {
|
|
|
23439
23782
|
}
|
|
23440
23783
|
const args = ["--unshare-all"];
|
|
23441
23784
|
for (const d of ["/usr", "/bin", "/lib", "/lib64"])
|
|
23442
|
-
if (
|
|
23785
|
+
if (existsSync4(d))
|
|
23443
23786
|
args.push("--ro-bind", d, d);
|
|
23444
23787
|
args.push("--proc", "/proc", "--dev", "/dev", "/bin/true");
|
|
23445
23788
|
const r = exec(launcher.sandbox, args, { timeout: 5000 });
|
|
@@ -23514,12 +23857,12 @@ function createShellSandbox(deps = {}) {
|
|
|
23514
23857
|
return {
|
|
23515
23858
|
enabled: false,
|
|
23516
23859
|
capability() {
|
|
23517
|
-
return
|
|
23860
|
+
return cached3 ??= probe();
|
|
23518
23861
|
},
|
|
23519
23862
|
prepare(toolCallId, cwd) {
|
|
23520
23863
|
dispose(toolCallId);
|
|
23521
23864
|
decisions.delete(toolCallId);
|
|
23522
|
-
const cap =
|
|
23865
|
+
const cap = cached3 ??= probe();
|
|
23523
23866
|
if (!cap.supported)
|
|
23524
23867
|
return { ready: false, reason: cap.reason };
|
|
23525
23868
|
let project;
|
|
@@ -23538,12 +23881,12 @@ function createShellSandbox(deps = {}) {
|
|
|
23538
23881
|
return { ready: false, reason: "a folder in the project cannot be read, so it cannot be checked for sockets and hard links" };
|
|
23539
23882
|
let scratch;
|
|
23540
23883
|
try {
|
|
23541
|
-
scratch = mkdtempSync(
|
|
23884
|
+
scratch = mkdtempSync(join9(realpathSync2(tmpdir()), "freepi-hive-"));
|
|
23542
23885
|
const dir = scratch;
|
|
23543
|
-
mkdirSync(
|
|
23544
|
-
mkdirSync(
|
|
23545
|
-
const emptyFile =
|
|
23546
|
-
const marker =
|
|
23886
|
+
mkdirSync(join9(dir, "home"));
|
|
23887
|
+
mkdirSync(join9(dir, "npm-cache"));
|
|
23888
|
+
const emptyFile = join9(dir, "empty");
|
|
23889
|
+
const marker = join9(dir, "call.fd");
|
|
23547
23890
|
writeFileSync(emptyFile, "", { mode: 292 });
|
|
23548
23891
|
const roots = toolchainRoots(home);
|
|
23549
23892
|
const mac3 = platform === "darwin";
|
|
@@ -23704,6 +24047,20 @@ function projectTrustForCwd(cwd, launch, agentDir, hasTrustRequiringResources =
|
|
|
23704
24047
|
return new ProjectTrustStore(agentDir).get(cwd) === true;
|
|
23705
24048
|
}
|
|
23706
24049
|
|
|
24050
|
+
// src/session-auth.ts
|
|
24051
|
+
function createSessionAuth(initial) {
|
|
24052
|
+
let jwt2 = initial.jwt;
|
|
24053
|
+
let guest = initial.guest;
|
|
24054
|
+
return {
|
|
24055
|
+
getJwt: () => jwt2,
|
|
24056
|
+
isGuest: () => guest,
|
|
24057
|
+
becomeSignedIn(token) {
|
|
24058
|
+
jwt2 = token;
|
|
24059
|
+
guest = false;
|
|
24060
|
+
}
|
|
24061
|
+
};
|
|
24062
|
+
}
|
|
24063
|
+
|
|
23707
24064
|
// src/pi-launch.ts
|
|
23708
24065
|
function catalogModelsFor(opts) {
|
|
23709
24066
|
const main = splitCatalogModels(opts.models ?? []).main;
|
|
@@ -23735,7 +24092,7 @@ var SDK_TOOLS = [
|
|
|
23735
24092
|
"list_mcp_resource_templates",
|
|
23736
24093
|
"read_mcp_resource"
|
|
23737
24094
|
];
|
|
23738
|
-
var SKILLS_DIR = resolve3(
|
|
24095
|
+
var SKILLS_DIR = resolve3(dirname8(fileURLToPath4(import.meta.url)), "..", "skills");
|
|
23739
24096
|
function resolveModelName(opts) {
|
|
23740
24097
|
return opts.models?.[0]?.name || opts.model || MODEL_ID;
|
|
23741
24098
|
}
|
|
@@ -23746,6 +24103,7 @@ function buildSettingsManager(opts, models, projectTrusted) {
|
|
|
23746
24103
|
defaultTools: [...SAFE_TOOLS, TODO_TOOL_NAME],
|
|
23747
24104
|
packages: [],
|
|
23748
24105
|
quietStartup: true,
|
|
24106
|
+
defaultThinkingLevel: "off",
|
|
23749
24107
|
hideThinkingBlock: true,
|
|
23750
24108
|
tuiMode: "regular",
|
|
23751
24109
|
theme: "light/dark",
|
|
@@ -23758,42 +24116,50 @@ function settingsForRuntime(opts, rtCwd, launch) {
|
|
|
23758
24116
|
function buildRuntimeOptions(opts, sessionId) {
|
|
23759
24117
|
process.env.PI_CODING_AGENT_DIR = opts.agentDir;
|
|
23760
24118
|
const models = catalogModelsFor(opts);
|
|
24119
|
+
const auth = opts.sessionAuth ?? createSessionAuth({ jwt: opts.jwt, guest: Boolean(opts.guest) });
|
|
24120
|
+
opts.sessionAuth = auth;
|
|
23761
24121
|
const providerExtension = {
|
|
23762
24122
|
name: "free-pi-provider",
|
|
23763
24123
|
factory: (pi) => {
|
|
23764
|
-
pi.registerProvider(PROVIDER_NAME, buildProviderConfig(opts.baseUrl,
|
|
24124
|
+
pi.registerProvider(PROVIDER_NAME, buildProviderConfig(opts.baseUrl, auth.getJwt(), sessionId, models));
|
|
23765
24125
|
const defense = defenseModelsFor(opts);
|
|
23766
24126
|
if (defense.length > 0) {
|
|
23767
|
-
pi.registerProvider(DEFENSE_PROVIDER_NAME, buildProviderConfig(opts.baseUrl,
|
|
24127
|
+
pi.registerProvider(DEFENSE_PROVIDER_NAME, buildProviderConfig(opts.baseUrl, auth.getJwt(), sessionId, defense, DEFENSE_PROVIDER_NAME));
|
|
23768
24128
|
}
|
|
24129
|
+
pi.on("before_provider_headers", (event) => {
|
|
24130
|
+
event.headers.Authorization = `Bearer ${auth.getJwt()}`;
|
|
24131
|
+
});
|
|
23769
24132
|
}
|
|
23770
24133
|
};
|
|
23771
24134
|
let advertiserUrl;
|
|
23772
24135
|
const adsExtension = createAdsExtension({
|
|
23773
24136
|
baseUrl: opts.baseUrl,
|
|
23774
|
-
getToken: () =>
|
|
24137
|
+
getToken: () => auth.getJwt(),
|
|
23775
24138
|
sessionId,
|
|
23776
24139
|
getGravityContext: opts.getGravityContext,
|
|
23777
24140
|
onBannerAd: (u) => {
|
|
23778
24141
|
advertiserUrl = u;
|
|
24142
|
+
},
|
|
24143
|
+
onMe: (me) => {
|
|
24144
|
+
updateAllowanceCache(me);
|
|
23779
24145
|
}
|
|
23780
24146
|
});
|
|
23781
24147
|
const usageToolExtension = createUsageToolExtension({
|
|
23782
24148
|
baseUrl: opts.baseUrl,
|
|
23783
|
-
getToken: () =>
|
|
24149
|
+
getToken: () => auth.getJwt()
|
|
23784
24150
|
});
|
|
23785
24151
|
const buyToolExtension = createBuyToolExtension({
|
|
23786
24152
|
baseUrl: opts.baseUrl,
|
|
23787
|
-
getToken: () =>
|
|
24153
|
+
getToken: () => auth.getJwt()
|
|
23788
24154
|
});
|
|
23789
24155
|
const docsToolExtension = createDocsToolExtension();
|
|
23790
24156
|
const webToolsExtension = createWebToolsExtension({
|
|
23791
24157
|
baseUrl: opts.baseUrl,
|
|
23792
|
-
getToken: () =>
|
|
24158
|
+
getToken: () => auth.getJwt()
|
|
23793
24159
|
});
|
|
23794
24160
|
const imageToolExtension = createImageToolExtension({
|
|
23795
24161
|
baseUrl: opts.baseUrl,
|
|
23796
|
-
getToken: () =>
|
|
24162
|
+
getToken: () => auth.getJwt()
|
|
23797
24163
|
});
|
|
23798
24164
|
const errorNoticeExtension = createErrorNoticeExtension({
|
|
23799
24165
|
extraNotice: (env) => {
|
|
@@ -23804,34 +24170,39 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
23804
24170
|
if (!card)
|
|
23805
24171
|
return;
|
|
23806
24172
|
session.shown = true;
|
|
23807
|
-
reportRefillCardShown({ baseUrl: opts.baseUrl, jwt:
|
|
24173
|
+
reportRefillCardShown({ baseUrl: opts.baseUrl, jwt: auth.getJwt() });
|
|
23808
24174
|
return card;
|
|
23809
24175
|
}
|
|
23810
24176
|
});
|
|
23811
24177
|
const toolGuardExtension = createToolGuardExtension({
|
|
23812
24178
|
baseUrl: opts.baseUrl,
|
|
23813
|
-
getToken: () =>
|
|
24179
|
+
getToken: () => auth.getJwt(),
|
|
23814
24180
|
allowedTools: ALLOWED_TOOL_NAMES
|
|
23815
24181
|
});
|
|
23816
24182
|
const commandsExtension = createFreePiCommandsExtension({
|
|
23817
24183
|
baseUrl: opts.baseUrl,
|
|
23818
|
-
getToken: () =>
|
|
24184
|
+
getToken: () => auth.getJwt(),
|
|
23819
24185
|
getAdvertiserUrl: () => advertiserUrl,
|
|
23820
24186
|
credentialsPath: opts.credentialsPath,
|
|
23821
24187
|
agentDir: opts.agentDir,
|
|
23822
|
-
isGuest: () =>
|
|
24188
|
+
isGuest: () => auth.isGuest(),
|
|
24189
|
+
onLogin: (token) => auth.becomeSignedIn(token),
|
|
24190
|
+
variant: () => opts.guestHandoffVariant
|
|
23823
24191
|
});
|
|
23824
24192
|
const guestLoginExtension = opts.guest ? createGuestLoginExtension({
|
|
23825
24193
|
baseUrl: opts.baseUrl,
|
|
23826
|
-
getToken: () =>
|
|
24194
|
+
getToken: () => auth.getJwt(),
|
|
23827
24195
|
credentialsPath: opts.credentialsPath,
|
|
23828
24196
|
agentDir: opts.agentDir,
|
|
23829
|
-
isGuest: () =>
|
|
24197
|
+
isGuest: () => auth.isGuest(),
|
|
24198
|
+
onLogin: (token) => auth.becomeSignedIn(token),
|
|
24199
|
+
variant: () => opts.guestHandoffVariant
|
|
23830
24200
|
}) : undefined;
|
|
23831
24201
|
const headerExtension = createHeaderExtension({
|
|
23832
24202
|
modelName: resolveModelName(opts),
|
|
23833
24203
|
updateLatest: opts.updateLatest,
|
|
23834
|
-
guest: opts.guest
|
|
24204
|
+
guest: opts.guest,
|
|
24205
|
+
allowanceLine: opts.allowanceLine
|
|
23835
24206
|
});
|
|
23836
24207
|
const todoToolExtension = createTodoToolExtension();
|
|
23837
24208
|
const shellSandbox = opts.shellSandbox ?? createShellSandbox();
|
|
@@ -23841,7 +24212,7 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
23841
24212
|
const toolSearchExtension = { name: "pi-tool-search", factory: createToolSearchExtension() };
|
|
23842
24213
|
const hiveExtension = createHiveExtension({
|
|
23843
24214
|
baseUrl: opts.baseUrl,
|
|
23844
|
-
getToken: () =>
|
|
24215
|
+
getToken: () => auth.getJwt(),
|
|
23845
24216
|
knownTools: ALLOWED_TOOL_NAMES,
|
|
23846
24217
|
sandbox: shellSandbox
|
|
23847
24218
|
});
|
|
@@ -23881,7 +24252,20 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
23881
24252
|
function getSessionDir(cwd, agentDir) {
|
|
23882
24253
|
const resolvedCwd = resolve3(cwd);
|
|
23883
24254
|
const safePath = `--${resolvedCwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
23884
|
-
return
|
|
24255
|
+
return join10(resolve3(agentDir), "sessions", safePath);
|
|
24256
|
+
}
|
|
24257
|
+
async function findLatestSession(cwd, sessionDir, api3 = SessionManager) {
|
|
24258
|
+
const infos = await api3.list(cwd, sessionDir);
|
|
24259
|
+
if (infos.length === 0)
|
|
24260
|
+
return;
|
|
24261
|
+
const latest = [...infos].sort((a, b) => +new Date(b.modified) - +new Date(a.modified))[0];
|
|
24262
|
+
if (!latest)
|
|
24263
|
+
return;
|
|
24264
|
+
return {
|
|
24265
|
+
id: latest.id,
|
|
24266
|
+
modified: latest.modified instanceof Date ? latest.modified : new Date(latest.modified),
|
|
24267
|
+
firstMessage: latest.firstMessage ?? ""
|
|
24268
|
+
};
|
|
23885
24269
|
}
|
|
23886
24270
|
async function createSessionManager(cwd, sessionDir, sessionId, api3 = SessionManager) {
|
|
23887
24271
|
if (sessionId) {
|
|
@@ -23907,9 +24291,11 @@ async function launchPi(opts) {
|
|
|
23907
24291
|
const cwd = process.cwd();
|
|
23908
24292
|
const sessionDir = getSessionDir(cwd, opts.agentDir);
|
|
23909
24293
|
const sm = await createSessionManager(cwd, sessionDir, opts.session);
|
|
24294
|
+
const auth = opts.sessionAuth ?? createSessionAuth({ jwt: opts.jwt, guest: Boolean(opts.guest) });
|
|
24295
|
+
const launchOpts = { ...opts, sessionAuth: auth };
|
|
23910
24296
|
const sessionId = sm.getSessionId();
|
|
23911
24297
|
const projectTrusted = await decideProjectTrustInteractive(cwd, opts.agentDir);
|
|
23912
|
-
const { resourceLoaderOptions, tools } = buildRuntimeOptions({ ...
|
|
24298
|
+
const { resourceLoaderOptions, tools } = buildRuntimeOptions({ ...launchOpts, projectTrusted }, sessionId);
|
|
23913
24299
|
const launchTrust = { cwd, trusted: projectTrusted };
|
|
23914
24300
|
const createRuntime = async ({
|
|
23915
24301
|
cwd: rtCwd,
|
|
@@ -23944,7 +24330,17 @@ async function launchPi(opts) {
|
|
|
23944
24330
|
initialImages: [],
|
|
23945
24331
|
initialMessages: []
|
|
23946
24332
|
});
|
|
23947
|
-
installBugHandler(mode, { baseUrl: opts.baseUrl, getToken: () =>
|
|
24333
|
+
installBugHandler(mode, { baseUrl: opts.baseUrl, getToken: () => auth.getJwt() });
|
|
24334
|
+
installLoginHandler(mode, {
|
|
24335
|
+
baseUrl: opts.baseUrl,
|
|
24336
|
+
getToken: () => auth.getJwt(),
|
|
24337
|
+
credentialsPath: opts.credentialsPath,
|
|
24338
|
+
agentDir: opts.agentDir,
|
|
24339
|
+
isGuest: () => auth.isGuest(),
|
|
24340
|
+
onLogin: (token) => auth.becomeSignedIn(token),
|
|
24341
|
+
variant: () => opts.guestHandoffVariant
|
|
24342
|
+
});
|
|
24343
|
+
installModelAllowanceHandler(mode);
|
|
23948
24344
|
installFooterRightAlign(mode);
|
|
23949
24345
|
await mode.run();
|
|
23950
24346
|
return sm.getSessionId();
|
|
@@ -23994,6 +24390,154 @@ function createRealSpawn(io = { platform: process.platform, spawnSyncImpl: spawn
|
|
|
23994
24390
|
// src/run.ts
|
|
23995
24391
|
import { SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
|
|
23996
24392
|
|
|
24393
|
+
// src/resume-on-launch.ts
|
|
24394
|
+
var RESUME_ON_LAUNCH_EXPERIMENT = "resume_on_launch";
|
|
24395
|
+
var DEFAULT_RESUME_RECENT_HOURS = 48;
|
|
24396
|
+
var RESUME_PROMPT_SUFFIX = " [Y/n] ";
|
|
24397
|
+
function userArgv(argv) {
|
|
24398
|
+
if (/(^|[\\/])(node|bun)(\.exe)?$/i.test(argv[0] ?? ""))
|
|
24399
|
+
return argv.slice(2);
|
|
24400
|
+
return argv;
|
|
24401
|
+
}
|
|
24402
|
+
function parseResumeFlags(argv) {
|
|
24403
|
+
const args = userArgv(argv);
|
|
24404
|
+
let session;
|
|
24405
|
+
let sessionError;
|
|
24406
|
+
let continueLast = false;
|
|
24407
|
+
let startNew = false;
|
|
24408
|
+
let hasPrompt = false;
|
|
24409
|
+
for (let i = 0;i < args.length; i++) {
|
|
24410
|
+
const arg = args[i];
|
|
24411
|
+
if (arg === "--session") {
|
|
24412
|
+
const value = args[i + 1];
|
|
24413
|
+
if (!value || value.startsWith("--")) {
|
|
24414
|
+
sessionError = "Missing value for --session";
|
|
24415
|
+
} else {
|
|
24416
|
+
session = value;
|
|
24417
|
+
i += 1;
|
|
24418
|
+
}
|
|
24419
|
+
continue;
|
|
24420
|
+
}
|
|
24421
|
+
if (arg.startsWith("--session=")) {
|
|
24422
|
+
const value = arg.slice("--session=".length);
|
|
24423
|
+
if (!value)
|
|
24424
|
+
sessionError = "Missing value for --session";
|
|
24425
|
+
else
|
|
24426
|
+
session = value;
|
|
24427
|
+
continue;
|
|
24428
|
+
}
|
|
24429
|
+
if (arg === "--continue" || arg === "--resume") {
|
|
24430
|
+
continueLast = true;
|
|
24431
|
+
continue;
|
|
24432
|
+
}
|
|
24433
|
+
if (arg === "--new") {
|
|
24434
|
+
startNew = true;
|
|
24435
|
+
continue;
|
|
24436
|
+
}
|
|
24437
|
+
if (arg === "--ref" || arg === "--src") {
|
|
24438
|
+
if (args[i + 1] && !String(args[i + 1]).startsWith("--"))
|
|
24439
|
+
i += 1;
|
|
24440
|
+
continue;
|
|
24441
|
+
}
|
|
24442
|
+
if (arg.startsWith("--ref=") || arg.startsWith("--src="))
|
|
24443
|
+
continue;
|
|
24444
|
+
if (!arg.startsWith("-"))
|
|
24445
|
+
hasPrompt = true;
|
|
24446
|
+
}
|
|
24447
|
+
return { session, sessionError, continueLast, startNew, hasPrompt };
|
|
24448
|
+
}
|
|
24449
|
+
function isRecentSession(modified, recentHours, now) {
|
|
24450
|
+
const ageMs = now.getTime() - modified.getTime();
|
|
24451
|
+
return ageMs >= 0 && ageMs <= recentHours * 3600000;
|
|
24452
|
+
}
|
|
24453
|
+
function recentHoursFromEnv(env = process.env, serverHours) {
|
|
24454
|
+
const raw = env.FREEPI_RESUME_RECENT_HOURS;
|
|
24455
|
+
if (raw !== undefined && raw !== "") {
|
|
24456
|
+
const n = Number(raw);
|
|
24457
|
+
if (Number.isInteger(n) && n >= 1)
|
|
24458
|
+
return n;
|
|
24459
|
+
}
|
|
24460
|
+
if (typeof serverHours === "number" && Number.isInteger(serverHours) && serverHours >= 1) {
|
|
24461
|
+
return serverHours;
|
|
24462
|
+
}
|
|
24463
|
+
return DEFAULT_RESUME_RECENT_HOURS;
|
|
24464
|
+
}
|
|
24465
|
+
function formatResumeAge(modified, now) {
|
|
24466
|
+
const deltaSec = Math.max(0, Math.floor((now.getTime() - modified.getTime()) / 1000));
|
|
24467
|
+
if (deltaSec < 60)
|
|
24468
|
+
return "just now";
|
|
24469
|
+
const minutes = Math.floor(deltaSec / 60);
|
|
24470
|
+
if (minutes < 60)
|
|
24471
|
+
return minutes === 1 ? "1 minute ago" : `${minutes} minutes ago`;
|
|
24472
|
+
const hours = Math.floor(minutes / 60);
|
|
24473
|
+
if (hours < 24)
|
|
24474
|
+
return hours === 1 ? "1 hour ago" : `${hours} hours ago`;
|
|
24475
|
+
const days = Math.floor(hours / 24);
|
|
24476
|
+
if (days === 1)
|
|
24477
|
+
return "yesterday";
|
|
24478
|
+
return `${days} days ago`;
|
|
24479
|
+
}
|
|
24480
|
+
function previewText(firstMessage) {
|
|
24481
|
+
if (!firstMessage)
|
|
24482
|
+
return;
|
|
24483
|
+
const oneLine = firstMessage.replace(/\s+/g, " ").trim();
|
|
24484
|
+
if (!oneLine)
|
|
24485
|
+
return;
|
|
24486
|
+
return oneLine.length > 60 ? `${oneLine.slice(0, 57)}...` : oneLine;
|
|
24487
|
+
}
|
|
24488
|
+
function formatResumePrompt(session, now = new Date) {
|
|
24489
|
+
const age = formatResumeAge(session.modified, now);
|
|
24490
|
+
const preview = previewText(session.firstMessage);
|
|
24491
|
+
const body = preview ? `Continue last session from ${age}: ${preview}?` : `Continue last session from ${age}?`;
|
|
24492
|
+
return `${body}${RESUME_PROMPT_SUFFIX}`;
|
|
24493
|
+
}
|
|
24494
|
+
function parseResumeKeypress(raw) {
|
|
24495
|
+
if (raw === "\x03")
|
|
24496
|
+
return null;
|
|
24497
|
+
const ch = raw.slice(0, 1);
|
|
24498
|
+
if (ch === "n" || ch === "N")
|
|
24499
|
+
return false;
|
|
24500
|
+
return true;
|
|
24501
|
+
}
|
|
24502
|
+
async function promptResume(session, deps) {
|
|
24503
|
+
if (!deps.stdinIsTTY)
|
|
24504
|
+
return false;
|
|
24505
|
+
const prompt = formatResumePrompt(session, deps.now ?? new Date);
|
|
24506
|
+
const raw = await deps.askKey(prompt);
|
|
24507
|
+
const parsed = parseResumeKeypress(raw);
|
|
24508
|
+
if (parsed === null)
|
|
24509
|
+
return "aborted";
|
|
24510
|
+
deps.write(`
|
|
24511
|
+
`);
|
|
24512
|
+
return parsed;
|
|
24513
|
+
}
|
|
24514
|
+
async function readOneKeypress(prompt, stdin = process.stdin, stdout = process.stdout) {
|
|
24515
|
+
stdout.write(prompt);
|
|
24516
|
+
const wasRaw = stdin.isRaw;
|
|
24517
|
+
if (typeof stdin.setRawMode === "function")
|
|
24518
|
+
stdin.setRawMode(true);
|
|
24519
|
+
stdin.resume();
|
|
24520
|
+
return await new Promise((resolve4) => {
|
|
24521
|
+
const onData = (chunk) => {
|
|
24522
|
+
stdin.off("data", onData);
|
|
24523
|
+
if (typeof stdin.setRawMode === "function")
|
|
24524
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
24525
|
+
stdin.pause();
|
|
24526
|
+
resolve4(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
24527
|
+
};
|
|
24528
|
+
stdin.once("data", onData);
|
|
24529
|
+
});
|
|
24530
|
+
}
|
|
24531
|
+
async function promptResumeInteractive(session) {
|
|
24532
|
+
return promptResume(session, {
|
|
24533
|
+
stdinIsTTY: process.stdin.isTTY === true,
|
|
24534
|
+
write: (text) => {
|
|
24535
|
+
process.stdout.write(text);
|
|
24536
|
+
},
|
|
24537
|
+
askKey: (prompt) => readOneKeypress(prompt)
|
|
24538
|
+
});
|
|
24539
|
+
}
|
|
24540
|
+
|
|
23997
24541
|
// src/onboarding.ts
|
|
23998
24542
|
var INTRO_ENV_SKIP = "FREEPI_NO_INTRO";
|
|
23999
24543
|
var INTRO_TEXT = `
|
|
@@ -24021,12 +24565,12 @@ ${INTRO_TEXT}
|
|
|
24021
24565
|
}
|
|
24022
24566
|
|
|
24023
24567
|
// src/whats-new.ts
|
|
24024
|
-
import { existsSync as
|
|
24025
|
-
import { dirname as
|
|
24568
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
24569
|
+
import { dirname as dirname9 } from "node:path";
|
|
24026
24570
|
function maybeWhatsNew(cliVersion, agentDir) {
|
|
24027
24571
|
try {
|
|
24028
24572
|
const path = getLastSeenVersionPath(agentDir);
|
|
24029
|
-
const lastSeen =
|
|
24573
|
+
const lastSeen = existsSync5(path) ? readFileSync4(path, "utf-8").trim() : undefined;
|
|
24030
24574
|
writeLastSeen(path, cliVersion);
|
|
24031
24575
|
if (lastSeen === undefined)
|
|
24032
24576
|
return;
|
|
@@ -24042,7 +24586,7 @@ function maybeWhatsNew(cliVersion, agentDir) {
|
|
|
24042
24586
|
}
|
|
24043
24587
|
}
|
|
24044
24588
|
function writeLastSeen(path, version3) {
|
|
24045
|
-
mkdirSync2(
|
|
24589
|
+
mkdirSync2(dirname9(path), { recursive: true });
|
|
24046
24590
|
writeFileSync2(path, version3, "utf-8");
|
|
24047
24591
|
}
|
|
24048
24592
|
|
|
@@ -24068,7 +24612,8 @@ async function loginFlow(deps, argv = []) {
|
|
|
24068
24612
|
openBrowser: deps.openBrowser,
|
|
24069
24613
|
sleep: deps.sleep,
|
|
24070
24614
|
log: deps.log,
|
|
24071
|
-
jwt: await loadJwt(deps.credentialsPath)
|
|
24615
|
+
jwt: await loadJwt(deps.credentialsPath),
|
|
24616
|
+
credentialsPath: deps.credentialsPath
|
|
24072
24617
|
}, resolved.attribution ?? {});
|
|
24073
24618
|
await saveJwt(deps.credentialsPath, token);
|
|
24074
24619
|
return token;
|
|
@@ -24079,28 +24624,52 @@ async function loginFlow(deps, argv = []) {
|
|
|
24079
24624
|
throw err;
|
|
24080
24625
|
}
|
|
24081
24626
|
}
|
|
24082
|
-
function
|
|
24083
|
-
const flagIndex = argv.indexOf("--session");
|
|
24084
|
-
if (flagIndex === -1)
|
|
24085
|
-
return {};
|
|
24086
|
-
const value = argv[flagIndex + 1];
|
|
24087
|
-
if (!value || value.startsWith("--")) {
|
|
24088
|
-
return { error: "Missing value for --session" };
|
|
24089
|
-
}
|
|
24627
|
+
function validateSessionId(value) {
|
|
24090
24628
|
try {
|
|
24091
24629
|
SessionManager2.inMemory(process.cwd(), { id: value });
|
|
24630
|
+
return;
|
|
24092
24631
|
} catch (err) {
|
|
24093
|
-
return
|
|
24632
|
+
return `Invalid --session value: ${err instanceof Error ? err.message : String(err)}`;
|
|
24094
24633
|
}
|
|
24095
|
-
|
|
24634
|
+
}
|
|
24635
|
+
function reportResumeExposure(deps, jwt2, event) {
|
|
24636
|
+
if (deps.reportResumeExposure) {
|
|
24637
|
+
deps.reportResumeExposure(event);
|
|
24638
|
+
return;
|
|
24639
|
+
}
|
|
24640
|
+
fetch(new URL("/telemetry/experiment", deps.baseUrl), {
|
|
24641
|
+
method: "POST",
|
|
24642
|
+
headers: {
|
|
24643
|
+
authorization: `Bearer ${jwt2}`,
|
|
24644
|
+
"content-type": "application/json"
|
|
24645
|
+
},
|
|
24646
|
+
body: JSON.stringify({ experiment: RESUME_ON_LAUNCH_EXPERIMENT, event }),
|
|
24647
|
+
signal: AbortSignal.timeout(3000)
|
|
24648
|
+
}).catch(() => {});
|
|
24649
|
+
}
|
|
24650
|
+
async function defaultFindLatestSession(cwd, agentDir) {
|
|
24651
|
+
return findLatestSession(cwd, getSessionDir(cwd, agentDir));
|
|
24096
24652
|
}
|
|
24097
24653
|
async function run(deps, argv = process.argv) {
|
|
24098
|
-
const
|
|
24099
|
-
if (
|
|
24100
|
-
deps.log(
|
|
24654
|
+
const resumeFlags = parseResumeFlags(argv);
|
|
24655
|
+
if (resumeFlags.sessionError) {
|
|
24656
|
+
deps.log(resumeFlags.sessionError);
|
|
24101
24657
|
return 1;
|
|
24102
24658
|
}
|
|
24659
|
+
if (resumeFlags.session) {
|
|
24660
|
+
const invalid = validateSessionId(resumeFlags.session);
|
|
24661
|
+
if (invalid) {
|
|
24662
|
+
deps.log(invalid);
|
|
24663
|
+
return 1;
|
|
24664
|
+
}
|
|
24665
|
+
}
|
|
24666
|
+
const pending = await loadPendingLogin(deps.credentialsPath);
|
|
24667
|
+
if (pending)
|
|
24668
|
+
deps.log(`
|
|
24669
|
+
${FINISH_SIGNIN_LINE}
|
|
24670
|
+
`);
|
|
24103
24671
|
let jwt2 = await loadJwt(deps.credentialsPath);
|
|
24672
|
+
let guestHandoffVariant;
|
|
24104
24673
|
let consentCurrent = false;
|
|
24105
24674
|
if (jwt2) {
|
|
24106
24675
|
try {
|
|
@@ -24143,9 +24712,12 @@ async function run(deps, argv = process.argv) {
|
|
|
24143
24712
|
const guest = await startGuestSession(deps.baseUrl, CONSENT_VERSION, attribution);
|
|
24144
24713
|
await saveJwt(deps.credentialsPath, guest.token);
|
|
24145
24714
|
jwt2 = guest.token;
|
|
24146
|
-
deps.log(`
|
|
24715
|
+
deps.log(isHandoffTreatment(guest.guestHandoffVariant) ? `
|
|
24716
|
+
You're on a short guest trial — ads pay for it. ${LOGIN_VALUE_LINE}
|
|
24717
|
+
` : `
|
|
24147
24718
|
You're on a short guest trial — ads pay for it. Sign in with GitHub anytime (/login) to get 15,000 free credits a day and unlock a one-time comeback bonus.
|
|
24148
24719
|
`);
|
|
24720
|
+
guestHandoffVariant = guest.guestHandoffVariant;
|
|
24149
24721
|
} catch (err) {
|
|
24150
24722
|
if (!(err instanceof GuestUnavailableError))
|
|
24151
24723
|
throw err;
|
|
@@ -24156,7 +24728,7 @@ Guest trial is not available right now.
|
|
|
24156
24728
|
}
|
|
24157
24729
|
consentCurrent = true;
|
|
24158
24730
|
}
|
|
24159
|
-
const versionCheck = await deps.checkClientVersion(deps.baseUrl, CLI_VERSION);
|
|
24731
|
+
const versionCheck = await deps.checkClientVersion(deps.baseUrl, CLI_VERSION, jwt2);
|
|
24160
24732
|
let updateLatest;
|
|
24161
24733
|
if (versionCheck.action !== "ok") {
|
|
24162
24734
|
const outcome = await deps.maybeSelfUpdate(versionCheck.action, versionCheck.latest);
|
|
@@ -24182,23 +24754,61 @@ Guest trial is not available right now.
|
|
|
24182
24754
|
} catch {}
|
|
24183
24755
|
}
|
|
24184
24756
|
const me = await fetchMe2(deps.baseUrl, jwt2).catch(() => null);
|
|
24757
|
+
if (me)
|
|
24758
|
+
updateAllowanceCache(me);
|
|
24759
|
+
guestHandoffVariant = me?.experiments?.guest_handoff ?? guestHandoffVariant;
|
|
24185
24760
|
const startRefill = await fetchRefillStats(deps.baseUrl, jwt2).catch(() => {
|
|
24186
24761
|
return;
|
|
24187
24762
|
});
|
|
24188
24763
|
const refillSession = sessionFromStats(startRefill);
|
|
24764
|
+
const creditsBack = startRefill ? formatCreditsBackNudge(startRefill) : undefined;
|
|
24765
|
+
if (creditsBack) {
|
|
24766
|
+
deps.log(`
|
|
24767
|
+
${creditsBack}`);
|
|
24768
|
+
reportRefillCardShown({ baseUrl: deps.baseUrl, jwt: jwt2 });
|
|
24769
|
+
}
|
|
24770
|
+
let session = resumeFlags.session;
|
|
24771
|
+
const lookupLatest = deps.findLatestSession ?? defaultFindLatestSession;
|
|
24772
|
+
if (!session && resumeFlags.continueLast && !resumeFlags.startNew) {
|
|
24773
|
+
session = (await lookupLatest(process.cwd(), deps.agentDir))?.id;
|
|
24774
|
+
if (!session)
|
|
24775
|
+
deps.log("No earlier session in this folder; starting a new one.");
|
|
24776
|
+
}
|
|
24777
|
+
const experiment = versionCheck.experiments?.[RESUME_ON_LAUNCH_EXPERIMENT];
|
|
24778
|
+
const variant = experiment?.variant === "treatment" ? "treatment" : "control";
|
|
24779
|
+
const recentHours = recentHoursFromEnv(process.env, experiment?.recent_hours);
|
|
24780
|
+
const interactive = deps.stdinIsTTY ?? process.stdin.isTTY === true;
|
|
24781
|
+
if (!session && !resumeFlags.startNew && !resumeFlags.hasPrompt && variant === "treatment" && interactive) {
|
|
24782
|
+
const latest = await lookupLatest(process.cwd(), deps.agentDir);
|
|
24783
|
+
const now = deps.now?.() ?? new Date;
|
|
24784
|
+
if (latest && isRecentSession(latest.modified, recentHours, now)) {
|
|
24785
|
+
reportResumeExposure(deps, jwt2, "offer_shown");
|
|
24786
|
+
const answer = await (deps.promptResume ?? promptResumeInteractive)(latest);
|
|
24787
|
+
if (answer === "aborted") {
|
|
24788
|
+
deps.log("Aborted.");
|
|
24789
|
+
return 1;
|
|
24790
|
+
}
|
|
24791
|
+
if (answer) {
|
|
24792
|
+
reportResumeExposure(deps, jwt2, "offer_accepted");
|
|
24793
|
+
session = latest.id;
|
|
24794
|
+
}
|
|
24795
|
+
}
|
|
24796
|
+
}
|
|
24189
24797
|
const finalSessionId = await deps.launchPi({
|
|
24190
24798
|
baseUrl: deps.baseUrl,
|
|
24191
24799
|
jwt: jwt2,
|
|
24192
24800
|
agentDir: deps.agentDir,
|
|
24193
24801
|
credentialsPath: deps.credentialsPath,
|
|
24194
|
-
session
|
|
24802
|
+
session,
|
|
24195
24803
|
model: versionCheck.model,
|
|
24196
24804
|
models: versionCheck.models,
|
|
24197
24805
|
fusion: versionCheck.fusion,
|
|
24198
24806
|
updateLatest,
|
|
24199
24807
|
getGravityContext,
|
|
24200
24808
|
guest: Boolean(me?.guest),
|
|
24201
|
-
refillCard: refillSession
|
|
24809
|
+
refillCard: refillSession,
|
|
24810
|
+
allowanceLine: allowanceLine(),
|
|
24811
|
+
guestHandoffVariant
|
|
24202
24812
|
});
|
|
24203
24813
|
const endRefill = await fetchRefillStats(deps.baseUrl, jwt2).catch(() => {
|
|
24204
24814
|
return;
|
|
@@ -24253,17 +24863,18 @@ async function maybeSelfUpdate(action2, latest, deps) {
|
|
|
24253
24863
|
}
|
|
24254
24864
|
|
|
24255
24865
|
// src/update-check.ts
|
|
24256
|
-
async function checkClientVersion(baseUrl, cliVersion, fetchImpl = fetch) {
|
|
24866
|
+
async function checkClientVersion(baseUrl, cliVersion, fetchImpl = fetch, jwt2) {
|
|
24257
24867
|
try {
|
|
24258
24868
|
const res = await fetchImpl(new URL("/client-version", baseUrl), {
|
|
24259
|
-
signal: AbortSignal.timeout(3000)
|
|
24869
|
+
signal: AbortSignal.timeout(3000),
|
|
24870
|
+
headers: jwt2 ? { authorization: `Bearer ${jwt2}` } : undefined
|
|
24260
24871
|
});
|
|
24261
24872
|
if (!res.ok)
|
|
24262
24873
|
return { action: "ok" };
|
|
24263
24874
|
const parsed = ClientVersionResponseSchema.safeParse(await res.json());
|
|
24264
24875
|
if (!parsed.success)
|
|
24265
24876
|
return { action: "ok" };
|
|
24266
|
-
const { min, latest, model, models, notice, fusion } = parsed.data;
|
|
24877
|
+
const { min, latest, model, models, notice, fusion, experiments } = parsed.data;
|
|
24267
24878
|
const extra = {};
|
|
24268
24879
|
if (model !== undefined)
|
|
24269
24880
|
extra.model = model;
|
|
@@ -24273,6 +24884,8 @@ async function checkClientVersion(baseUrl, cliVersion, fetchImpl = fetch) {
|
|
|
24273
24884
|
extra.notice = notice;
|
|
24274
24885
|
if (fusion !== undefined)
|
|
24275
24886
|
extra.fusion = fusion;
|
|
24887
|
+
if (experiments !== undefined)
|
|
24888
|
+
extra.experiments = experiments;
|
|
24276
24889
|
if (compareVersions(cliVersion, min) === -1)
|
|
24277
24890
|
return { action: "block", latest, ...extra };
|
|
24278
24891
|
if (compareVersions(cliVersion, latest) === -1)
|
|
@@ -24340,8 +24953,8 @@ ${HELP_TEXT}`);
|
|
|
24340
24953
|
sleep: (ms) => new Promise((resolve4) => setTimeout(resolve4, ms)),
|
|
24341
24954
|
launchPi,
|
|
24342
24955
|
log: (message) => console.log(message),
|
|
24343
|
-
checkClientVersion,
|
|
24344
|
-
startGravityPixel: (jwt2) => startGravityPixel(jwt2,
|
|
24956
|
+
checkClientVersion: (baseUrl, cliVersion, jwt2) => checkClientVersion(baseUrl, cliVersion, fetch, jwt2),
|
|
24957
|
+
startGravityPixel: (jwt2) => startGravityPixel(jwt2, join11(dirname10(agentDir), "gravity")),
|
|
24345
24958
|
maybeSelfUpdate: (action2, latest) => maybeSelfUpdate(action2, latest, {
|
|
24346
24959
|
env: process.env,
|
|
24347
24960
|
autoUpdateEnabled: () => readAutoUpdate(getConfigPath()),
|