Merge remote-tracking branch 'origin/main' into pr-4756-squash-cleanup

# Conflicts:
#	scripts/build-binaries.sh
This commit is contained in:
Mario Zechner
2026-05-23 10:52:28 +02:00
15 changed files with 338 additions and 130 deletions

View File

@@ -1047,14 +1047,15 @@ export class DefaultPackageManager implements PackageManager {
for (const entry of sources) {
const parsed = this.parseSource(entry.source);
if (parsed.type === "local" || parsed.pinned) {
continue;
}
// Pinned npm versions are fixed. Pinned git refs are configured checkout targets,
// so include them to reconcile an existing clone when the configured ref changes.
if (parsed.type === "npm") {
npmCandidates.push({ ...entry, parsed });
continue;
if (!parsed.pinned) {
npmCandidates.push({ ...entry, parsed });
}
} else if (parsed.type === "git") {
gitCandidates.push({ ...entry, parsed });
}
gitCandidates.push({ ...entry, parsed });
}
const npmCheckTasks = npmCandidates.map((entry) => async () => ({
@@ -1766,6 +1767,11 @@ export class DefaultPackageManager implements PackageManager {
return;
}
if (source.ref) {
await this.ensureGitRef(targetDir, ["fetch", "origin", source.ref], "FETCH_HEAD");
return;
}
const target = await this.getLocalGitUpdateTarget(targetDir);
await this.ensureGitRef(targetDir, target.fetchArgs, target.ref);
}
@@ -1778,7 +1784,8 @@ export class DefaultPackageManager implements PackageManager {
cwd: targetDir,
timeoutMs: NETWORK_TIMEOUT_MS,
});
const targetHead = await this.runCommandCapture("git", ["rev-parse", ref], {
const commitRef = `${ref}^{commit}`;
const targetHead = await this.runCommandCapture("git", ["rev-parse", commitRef], {
cwd: targetDir,
timeoutMs: NETWORK_TIMEOUT_MS,
});
@@ -1786,7 +1793,7 @@ export class DefaultPackageManager implements PackageManager {
return;
}
await this.runCommand("git", ["reset", "--hard", ref], { cwd: targetDir });
await this.runCommand("git", ["reset", "--hard", commitRef], { cwd: targetDir });
// Clean untracked files (extensions should be pristine)
await this.runCommand("git", ["clean", "-fdx"], { cwd: targetDir });

View File

@@ -129,7 +129,15 @@ function getDefaultAgentDir(): string {
function getAttributionHeaders(
model: Model<any>,
settingsManager: SettingsManager,
sessionId?: string,
): Record<string, string> | undefined {
if (
sessionId &&
(model.provider === "opencode" || model.provider === "opencode-go" || model.baseUrl.includes("opencode.ai"))
) {
return { "x-opencode-session": sessionId, "x-opencode-client": "pi" };
}
if (!isInstallTelemetryEnabled(settingsManager)) {
return undefined;
}
@@ -332,7 +340,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
throw new Error(auth.error);
}
const providerRetrySettings = settingsManager.getProviderRetrySettings();
const attributionHeaders = getAttributionHeaders(model, settingsManager);
const attributionHeaders = getAttributionHeaders(model, settingsManager, options?.sessionId);
return streamSimple(model, context, {
...options,
apiKey: auth.apiKey,

View File

@@ -1,3 +1,4 @@
import { isAbsolute, relative, resolve, sep } from "node:path";
import { type Component, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
import type { AgentSession } from "../../../core/agent-session.ts";
import type { ReadonlyFooterDataProvider } from "../../../core/footer-data-provider.ts";
@@ -26,6 +27,20 @@ function formatTokens(count: number): string {
return `${Math.round(count / 1000000)}M`;
}
export function formatCwdForFooter(cwd: string, home: string | undefined): string {
if (!home) return cwd;
const resolvedCwd = resolve(cwd);
const resolvedHome = resolve(home);
const relativeToHome = relative(resolvedHome, resolvedCwd);
const isInsideHome =
relativeToHome === "" ||
(relativeToHome !== ".." && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));
if (!isInsideHome) return cwd;
return relativeToHome === "" ? "~" : `~${sep}${relativeToHome}`;
}
/**
* Footer component that shows pwd, token stats, and context usage.
* Computes token/context stats from session, gets git branch and extension statuses from provider.
@@ -92,11 +107,7 @@ export class FooterComponent implements Component {
const contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
// Replace home directory with ~
let pwd = this.session.sessionManager.getCwd();
const home = process.env.HOME || process.env.USERPROFILE;
if (home && pwd.startsWith(home)) {
pwd = `~${pwd.slice(home.length)}`;
}
let pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);
// Add git branch if available
const branch = this.footerData.getGitBranch();

View File

@@ -1,4 +1,6 @@
import { createRequire } from "module";
import { dirname, join } from "path";
import { pathToFileURL } from "url";
export type ClipboardModule = {
setText: (text: string) => Promise<void>;
@@ -6,17 +8,25 @@ export type ClipboardModule = {
getImageBinary: () => Promise<Array<number>>;
};
const require = createRequire(import.meta.url);
let clipboard: ClipboardModule | null = null;
type ClipboardRequire = (id: string) => unknown;
const moduleRequire = createRequire(import.meta.url);
const executableDirRequire = createRequire(pathToFileURL(join(dirname(process.execPath), "package.json")).href);
const hasDisplay = process.platform !== "linux" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
if (!process.env.TERMUX_VERSION && hasDisplay) {
try {
clipboard = require("@mariozechner/clipboard") as ClipboardModule;
} catch {
clipboard = null;
export function loadClipboardNative(
requires: readonly ClipboardRequire[] = [moduleRequire, executableDirRequire],
): ClipboardModule | null {
for (const requireClipboard of requires) {
try {
return requireClipboard("@mariozechner/clipboard") as ClipboardModule;
} catch {
// Try the next resolution root.
}
}
return null;
}
const clipboard = !process.env.TERMUX_VERSION && hasDisplay ? loadClipboardNative() : null;
export { clipboard };