@psnext/slingcli 2.5.20260602-4 → 2.5.20260607-2
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/node_modules/@aws-sdk/core/dist-cjs/index.js +12 -6
- package/node_modules/@aws-sdk/core/dist-cjs/submodules/httpAuthSchemes/index.js +12 -6
- package/node_modules/@aws-sdk/core/dist-es/submodules/httpAuthSchemes/aws_sdk/AwsSdkSigV4ASigner.js +1 -0
- package/node_modules/@aws-sdk/core/dist-es/submodules/httpAuthSchemes/aws_sdk/AwsSdkSigV4Signer.js +11 -6
- package/node_modules/@aws-sdk/core/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-env/package.json +6 -6
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-cjs/index.js +6 -4
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +7 -6
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +2 -1
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http2.js +2 -0
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-https.js +2 -0
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +2 -2
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +1 -1
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http2.d.ts +2 -0
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-https.d.ts +2 -0
- package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/package.json +1 -1
- package/node_modules/@aws-sdk/credential-provider-http/package.json +8 -8
- package/node_modules/@aws-sdk/credential-provider-ini/package.json +14 -14
- package/node_modules/@aws-sdk/credential-provider-login/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-node/package.json +12 -12
- package/node_modules/@aws-sdk/credential-provider-process/package.json +6 -6
- package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-sso/package.json +8 -8
- package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +7 -7
- package/node_modules/@aws-sdk/eventstream-handler-node/package.json +5 -5
- package/node_modules/@aws-sdk/middleware-eventstream/package.json +5 -5
- package/node_modules/@aws-sdk/middleware-websocket/package.json +8 -8
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +99 -106
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +62 -120
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +55 -86
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +73 -104
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +102 -185
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/auth/httpAuthSchemeProvider.js +4 -5
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/endpoint/bdd.js +21 -57
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/runtimeConfig.browser.js +1 -2
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/runtimeConfig.js +2 -4
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/schemas/schemas_0.js +73 -41
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/auth/httpAuthSchemeProvider.js +4 -5
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/endpoint/bdd.js +20 -52
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/runtimeConfig.browser.js +1 -2
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/runtimeConfig.js +2 -4
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/schemas/schemas_0.js +36 -59
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/auth/httpAuthSchemeProvider.js +4 -5
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/endpoint/bdd.js +17 -45
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/runtimeConfig.browser.js +1 -2
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/runtimeConfig.js +2 -4
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/schemas/schemas_0.js +32 -32
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/auth/httpAuthSchemeProvider.js +4 -5
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/endpoint/bdd.js +17 -45
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/runtimeConfig.browser.js +1 -2
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/runtimeConfig.js +2 -4
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/schemas/schemas_0.js +50 -50
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/auth/httpAuthSchemeProvider.js +4 -5
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/defaultRoleAssumers.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/endpoint/bdd.js +34 -96
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/runtimeConfig.browser.js +1 -2
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/runtimeConfig.js +3 -6
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/schemas/schemas_0.js +63 -80
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-cjs/index.js +6 -4
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +7 -6
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +2 -1
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http2.js +2 -0
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-https.js +2 -0
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +2 -2
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +1 -1
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http2.d.ts +2 -0
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-https.d.ts +2 -0
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/package.json +1 -1
- package/node_modules/@aws-sdk/nested-clients/package.json +9 -9
- package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +5 -5
- package/node_modules/@aws-sdk/types/package.json +3 -3
- package/node_modules/@aws-sdk/util-locate-window/package.json +2 -2
- package/node_modules/@aws-sdk/xml-builder/package.json +3 -3
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +827 -140
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +32 -12
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +13 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +4 -2
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +15 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js +4 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/branch-summarization.js +9 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/export-html/template.js +19 -6
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +3 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +47 -13
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js +72 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-display-names.js +3 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +28 -20
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +2 -30
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +71 -30
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js +130 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +54 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js +7 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +3 -12
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +22 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.js +86 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +40 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +55 -8
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/git.js +54 -22
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/open-browser.js +22 -0
- package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
- package/node_modules/@earendil-works/pi-coding-agent/package.json +5 -4
- package/node_modules/@earendil-works/pi-tui/dist/utils.js +3 -0
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/httpRequest.js +2 -2
- package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/node-http.js +2 -0
- package/node_modules/@smithy/credential-provider-imds/dist-types/remoteProvider/httpRequest.d.ts +1 -1
- package/node_modules/@smithy/credential-provider-imds/dist-types/remoteProvider/node-http.d.ts +2 -0
- package/node_modules/@smithy/credential-provider-imds/package.json +1 -1
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/http2.d.ts +3 -0
- package/node_modules/@types/node/package.json +2 -2
- package/node_modules/gaxios/build/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/node_modules/gaxios/build/esm/tsconfig.tsbuildinfo +1 -1
- package/node_modules/gaxios/package.json +7 -9
- package/node_modules/google-auth-library/build/src/auth/gdchclient.d.ts +47 -0
- package/node_modules/google-auth-library/build/src/auth/gdchclient.js +307 -0
- package/node_modules/google-auth-library/build/src/auth/googleauth.d.ts +5 -4
- package/node_modules/google-auth-library/build/src/auth/googleauth.js +18 -1
- package/node_modules/google-auth-library/build/src/index.d.ts +1 -0
- package/node_modules/google-auth-library/build/src/index.js +4 -1
- package/node_modules/google-auth-library/package.json +2 -4
- package/node_modules/semver/classes/semver.js +19 -2
- package/node_modules/semver/package.json +1 -1
- package/node_modules/undici/lib/global.js +1 -1
- package/node_modules/undici/lib/handler/unwrap-handler.js +6 -0
- package/node_modules/undici/package.json +1 -1
- package/node_modules/undici/types/dispatcher.d.ts +2 -0
- package/package.json +6 -6
- package/slingshot/index.js +275 -273
|
@@ -27,6 +27,7 @@ import { formatMissingSessionCwdPrompt, getMissingSessionCwdIssue, MissingSessio
|
|
|
27
27
|
import { assertValidSessionId, SessionManager } from "./core/session-manager.js";
|
|
28
28
|
import { SettingsManager } from "./core/settings-manager.js";
|
|
29
29
|
import { printTimings, resetTimings, time } from "./core/timings.js";
|
|
30
|
+
import { hasProjectTrustInputs, ProjectTrustStore } from "./core/trust-manager.js";
|
|
30
31
|
import { runMigrations, showDeprecationWarnings } from "./migrations.js";
|
|
31
32
|
import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.js";
|
|
32
33
|
import { ExtensionSelectorComponent } from "./modes/interactive/components/extension-selector.js";
|
|
@@ -339,7 +340,7 @@ function buildSessionOptions(parsed, scopedModels, hasExistingSession, modelRegi
|
|
|
339
340
|
function resolveCliPaths(cwd, paths) {
|
|
340
341
|
return paths?.map((value) => (isLocalPath(value) ? resolvePath(value, cwd) : value));
|
|
341
342
|
}
|
|
342
|
-
async function
|
|
343
|
+
async function showStartupSelector(settingsManager, title, options) {
|
|
343
344
|
initTheme(settingsManager.getTheme());
|
|
344
345
|
setKeybindings(KeybindingsManager.create());
|
|
345
346
|
return new Promise((resolve) => {
|
|
@@ -354,12 +355,49 @@ async function promptForMissingSessionCwd(issue, settingsManager) {
|
|
|
354
355
|
ui.stop();
|
|
355
356
|
resolve(result);
|
|
356
357
|
};
|
|
357
|
-
const selector = new ExtensionSelectorComponent(
|
|
358
|
+
const selector = new ExtensionSelectorComponent(title, options.map((option) => option.label), (option) => finish(options.find((entry) => entry.label === option)?.value), () => finish(undefined), { tui: ui });
|
|
358
359
|
ui.addChild(selector);
|
|
359
360
|
ui.setFocus(selector);
|
|
360
361
|
ui.start();
|
|
361
362
|
});
|
|
362
363
|
}
|
|
364
|
+
async function promptForMissingSessionCwd(issue, settingsManager) {
|
|
365
|
+
return showStartupSelector(settingsManager, formatMissingSessionCwdPrompt(issue), [
|
|
366
|
+
{ label: "Continue", value: issue.fallbackCwd },
|
|
367
|
+
{ label: "Cancel", value: undefined },
|
|
368
|
+
]);
|
|
369
|
+
}
|
|
370
|
+
async function promptForProjectTrust(cwd, settingsManager) {
|
|
371
|
+
return showStartupSelector(settingsManager, `Trust project folder?\n${cwd}\n\nThis allows sling to read project instructions (AGENTS.md/CLAUDE.md), load .sling settings and resources, install missing project packages, and execute project extensions.`, [
|
|
372
|
+
{ label: "Trust", value: { trusted: true, remember: true } },
|
|
373
|
+
{ label: "Trust (this session only)", value: { trusted: true, remember: false } },
|
|
374
|
+
{ label: "Do not trust", value: { trusted: false, remember: true } },
|
|
375
|
+
{ label: "Do not trust (this session only)", value: { trusted: false, remember: false } },
|
|
376
|
+
]);
|
|
377
|
+
}
|
|
378
|
+
async function resolveProjectTrusted(options) {
|
|
379
|
+
if (options.trustOverride !== undefined) {
|
|
380
|
+
return options.trustOverride;
|
|
381
|
+
}
|
|
382
|
+
if (!hasProjectTrustInputs(options.cwd)) {
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
const decision = options.trustStore.get(options.cwd);
|
|
386
|
+
if (decision !== null) {
|
|
387
|
+
return decision;
|
|
388
|
+
}
|
|
389
|
+
if (options.appMode !== "interactive") {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
const selected = await promptForProjectTrust(options.cwd, options.settingsManagerForPrompt);
|
|
393
|
+
if (selected !== undefined) {
|
|
394
|
+
if (selected.remember) {
|
|
395
|
+
options.trustStore.set(options.cwd, selected.trusted);
|
|
396
|
+
}
|
|
397
|
+
return selected.trusted;
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
363
401
|
export async function main(args, options) {
|
|
364
402
|
resetTimings();
|
|
365
403
|
const offlineMode = args.includes("--offline") || isTruthyEnvFlag(process.env.PI_OFFLINE);
|
|
@@ -456,16 +494,30 @@ export async function main(args, options) {
|
|
|
456
494
|
sessionManager.appendSessionInfo(name);
|
|
457
495
|
}
|
|
458
496
|
time("createSessionManager");
|
|
497
|
+
const trustStore = new ProjectTrustStore(agentDir);
|
|
498
|
+
const trustPromptMode = parsed.help || parsed.listModels !== undefined ? "print" : appMode;
|
|
499
|
+
const projectTrustedForSession = await resolveProjectTrusted({
|
|
500
|
+
cwd: sessionManager.getCwd(),
|
|
501
|
+
trustStore,
|
|
502
|
+
trustOverride: parsed.projectTrustOverride,
|
|
503
|
+
appMode: trustPromptMode,
|
|
504
|
+
settingsManagerForPrompt: startupSettingsManager,
|
|
505
|
+
});
|
|
459
506
|
const resolvedExtensionPaths = resolveCliPaths(cwd, parsed.extensions);
|
|
460
507
|
const resolvedSkillPaths = resolveCliPaths(cwd, parsed.skills);
|
|
461
508
|
const resolvedPromptTemplatePaths = resolveCliPaths(cwd, parsed.promptTemplates);
|
|
462
509
|
const resolvedThemePaths = resolveCliPaths(cwd, parsed.themes);
|
|
463
510
|
const authStorage = AuthStorage.create();
|
|
464
511
|
const createRuntime = async ({ cwd, agentDir, sessionManager, sessionStartEvent, }) => {
|
|
512
|
+
const projectTrusted = cwd === sessionManager.getCwd()
|
|
513
|
+
? projectTrustedForSession
|
|
514
|
+
: (parsed.projectTrustOverride ?? (!hasProjectTrustInputs(cwd) || trustStore.get(cwd) === true));
|
|
515
|
+
const runtimeSettingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });
|
|
465
516
|
const services = await createAgentSessionServices({
|
|
466
517
|
cwd,
|
|
467
518
|
agentDir,
|
|
468
519
|
authStorage,
|
|
520
|
+
settingsManager: runtimeSettingsManager,
|
|
469
521
|
extensionFlagValues: parsed.unknownFlags,
|
|
470
522
|
resourceLoaderOptions: {
|
|
471
523
|
additionalExtensionPaths: resolvedExtensionPaths,
|
|
@@ -137,10 +137,10 @@ export class BashExecutionComponent extends Container {
|
|
|
137
137
|
// Show how many lines are hidden (collapsed preview)
|
|
138
138
|
if (hiddenLineCount > 0) {
|
|
139
139
|
if (this.expanded) {
|
|
140
|
-
statusParts.push(
|
|
140
|
+
statusParts.push(`${theme.fg("muted", "(")}${keyHint("app.tools.expand", "to collapse")}${theme.fg("muted", ")")}`);
|
|
141
141
|
}
|
|
142
142
|
else {
|
|
143
|
-
statusParts.push(`${theme.fg("muted", `... ${hiddenLineCount} more lines`)}
|
|
143
|
+
statusParts.push(`${theme.fg("muted", `... ${hiddenLineCount} more lines (`)}${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
if (this.status === "cancelled") {
|
|
@@ -41,7 +41,7 @@ function getGroupLabel(metadata) {
|
|
|
41
41
|
? `User (${formatBaseDir(metadata.baseDir)})`
|
|
42
42
|
: `Project (${formatBaseDir(metadata.baseDir)})`;
|
|
43
43
|
}
|
|
44
|
-
return metadata.scope === "user" ? "User (~/.
|
|
44
|
+
return metadata.scope === "user" ? "User (~/.pi/agent/)" : "Project (.pi/)";
|
|
45
45
|
}
|
|
46
46
|
return metadata.scope === "user" ? "User settings" : "Project settings";
|
|
47
47
|
}
|
package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js
CHANGED
|
@@ -78,6 +78,7 @@ export class FooterComponent {
|
|
|
78
78
|
let totalCacheRead = 0;
|
|
79
79
|
let totalCacheWrite = 0;
|
|
80
80
|
let totalCost = 0;
|
|
81
|
+
let latestCacheHitRate;
|
|
81
82
|
for (const entry of this.session.sessionManager.getEntries()) {
|
|
82
83
|
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
83
84
|
totalInput += entry.message.usage.input;
|
|
@@ -85,6 +86,9 @@ export class FooterComponent {
|
|
|
85
86
|
totalCacheRead += entry.message.usage.cacheRead;
|
|
86
87
|
totalCacheWrite += entry.message.usage.cacheWrite;
|
|
87
88
|
totalCost += entry.message.usage.cost.total;
|
|
89
|
+
const latestPromptTokens = entry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;
|
|
90
|
+
latestCacheHitRate =
|
|
91
|
+
latestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
// Calculate context usage from session (handles compaction correctly).
|
|
@@ -115,6 +119,9 @@ export class FooterComponent {
|
|
|
115
119
|
statsParts.push(`R${formatTokens(totalCacheRead)}`);
|
|
116
120
|
if (totalCacheWrite)
|
|
117
121
|
statsParts.push(`W${formatTokens(totalCacheWrite)}`);
|
|
122
|
+
if ((totalCacheRead > 0 || totalCacheWrite > 0) && latestCacheHitRate !== undefined) {
|
|
123
|
+
statsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);
|
|
124
|
+
}
|
|
118
125
|
// Show cost with "(sub)" indicator if using OAuth subscription
|
|
119
126
|
const usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;
|
|
120
127
|
if (totalCost || usingSubscription) {
|
package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export { ThemeSelectorComponent } from "./theme-selector.js";
|
|
|
27
27
|
export { ThinkingSelectorComponent } from "./thinking-selector.js";
|
|
28
28
|
export { ToolExecutionComponent } from "./tool-execution.js";
|
|
29
29
|
export { TreeSelectorComponent } from "./tree-selector.js";
|
|
30
|
+
export { TrustSelectorComponent } from "./trust-selector.js";
|
|
30
31
|
export { UserMessageComponent } from "./user-message.js";
|
|
31
32
|
export { UserMessageSelectorComponent } from "./user-message-selector.js";
|
|
32
33
|
export { truncateToVisualLines } from "./visual-truncate.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getOAuthProviders } from "@earendil-works/pi-ai/oauth";
|
|
2
2
|
import { Container, getKeybindings, Input, Spacer, Text } from "@earendil-works/pi-tui";
|
|
3
|
-
import {
|
|
3
|
+
import { openBrowser } from "../../../utils/open-browser.js";
|
|
4
4
|
import { theme } from "../theme/theme.js";
|
|
5
5
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
6
6
|
import { keyHint } from "./keybinding-hints.js";
|
|
@@ -80,7 +80,7 @@ export class LoginDialogComponent extends Container {
|
|
|
80
80
|
this.contentContainer.addChild(new Spacer(1));
|
|
81
81
|
this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
openBrowser(url);
|
|
84
84
|
this.tui.requestRender();
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
@@ -96,18 +96,9 @@ export class LoginDialogComponent extends Container {
|
|
|
96
96
|
this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
|
|
97
97
|
this.contentContainer.addChild(new Spacer(1));
|
|
98
98
|
this.contentContainer.addChild(new Text(theme.fg("warning", `Enter code: ${info.userCode}`), 1, 0));
|
|
99
|
-
|
|
99
|
+
openBrowser(info.verificationUri);
|
|
100
100
|
this.tui.requestRender();
|
|
101
101
|
}
|
|
102
|
-
openUrl(url) {
|
|
103
|
-
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
104
|
-
try {
|
|
105
|
-
exec(`${openCmd} "${url}"`, () => { });
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
// Ignore browser launch failures. The URL remains visible for manual opening/copying.
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
102
|
/**
|
|
112
103
|
* Show input for manual code/URL entry (for callback server providers)
|
|
113
104
|
*/
|
|
@@ -178,6 +178,28 @@ export class ToolExecutionComponent extends Container {
|
|
|
178
178
|
if (this.hideComponent) {
|
|
179
179
|
return [];
|
|
180
180
|
}
|
|
181
|
+
if (this.hasRendererDefinition() && this.getRenderShell() === "self") {
|
|
182
|
+
const contentLines = this.selfRenderContainer.render(width);
|
|
183
|
+
if (contentLines.length === 0 && this.imageComponents.length === 0) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
const lines = [];
|
|
187
|
+
if (contentLines.length > 0) {
|
|
188
|
+
lines.push("");
|
|
189
|
+
lines.push(...contentLines);
|
|
190
|
+
}
|
|
191
|
+
for (let i = 0; i < this.imageComponents.length; i++) {
|
|
192
|
+
const spacer = this.imageSpacers[i];
|
|
193
|
+
if (spacer) {
|
|
194
|
+
lines.push(...spacer.render(width));
|
|
195
|
+
}
|
|
196
|
+
const imageComponent = this.imageComponents[i];
|
|
197
|
+
if (imageComponent) {
|
|
198
|
+
lines.push(...imageComponent.render(width));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return lines;
|
|
202
|
+
}
|
|
181
203
|
return super.render(width);
|
|
182
204
|
}
|
|
183
205
|
updateDisplay() {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
|
|
2
|
+
import { theme } from "../theme/theme.js";
|
|
3
|
+
import { DynamicBorder } from "./dynamic-border.js";
|
|
4
|
+
import { keyHint, rawKeyHint } from "./keybinding-hints.js";
|
|
5
|
+
const TRUST_OPTIONS = [
|
|
6
|
+
{ label: "Trust", trusted: true },
|
|
7
|
+
{ label: "Do not trust", trusted: false },
|
|
8
|
+
];
|
|
9
|
+
function formatDecision(decision) {
|
|
10
|
+
if (decision === true) {
|
|
11
|
+
return "trusted";
|
|
12
|
+
}
|
|
13
|
+
if (decision === false) {
|
|
14
|
+
return "untrusted";
|
|
15
|
+
}
|
|
16
|
+
return "none";
|
|
17
|
+
}
|
|
18
|
+
export class TrustSelectorComponent extends Container {
|
|
19
|
+
selectedIndex;
|
|
20
|
+
listContainer;
|
|
21
|
+
savedDecision;
|
|
22
|
+
onSelectCallback;
|
|
23
|
+
onCancelCallback;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
super();
|
|
26
|
+
this.savedDecision = options.savedDecision;
|
|
27
|
+
this.selectedIndex = Math.max(0, TRUST_OPTIONS.findIndex((option) => option.trusted === options.savedDecision));
|
|
28
|
+
this.onSelectCallback = options.onSelect;
|
|
29
|
+
this.onCancelCallback = options.onCancel;
|
|
30
|
+
this.addChild(new DynamicBorder());
|
|
31
|
+
this.addChild(new Spacer(1));
|
|
32
|
+
this.addChild(new Text(theme.fg("accent", theme.bold("Project trust")), 1, 0));
|
|
33
|
+
this.addChild(new Text(theme.fg("muted", options.cwd), 1, 0));
|
|
34
|
+
this.addChild(new Spacer(1));
|
|
35
|
+
this.addChild(new Text(theme.fg("muted", `Saved decision: ${formatDecision(options.savedDecision)}`), 1, 0));
|
|
36
|
+
this.addChild(new Text(theme.fg("muted", `Current session: ${options.projectTrusted ? "trusted" : "untrusted"}`), 1, 0));
|
|
37
|
+
this.addChild(new Spacer(1));
|
|
38
|
+
this.listContainer = new Container();
|
|
39
|
+
this.addChild(this.listContainer);
|
|
40
|
+
this.addChild(new Spacer(1));
|
|
41
|
+
this.addChild(new Text(rawKeyHint("↑↓", "navigate") +
|
|
42
|
+
" " +
|
|
43
|
+
keyHint("tui.select.confirm", "save") +
|
|
44
|
+
" " +
|
|
45
|
+
keyHint("tui.select.cancel", "cancel"), 1, 0));
|
|
46
|
+
this.addChild(new Spacer(1));
|
|
47
|
+
this.addChild(new DynamicBorder());
|
|
48
|
+
this.updateList();
|
|
49
|
+
}
|
|
50
|
+
updateList() {
|
|
51
|
+
this.listContainer.clear();
|
|
52
|
+
for (let i = 0; i < TRUST_OPTIONS.length; i++) {
|
|
53
|
+
const option = TRUST_OPTIONS[i];
|
|
54
|
+
if (!option) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const isSelected = i === this.selectedIndex;
|
|
58
|
+
const isCurrent = option.trusted === this.savedDecision;
|
|
59
|
+
const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
|
|
60
|
+
const prefix = isSelected ? theme.fg("accent", "→ ") : " ";
|
|
61
|
+
const label = isSelected ? theme.fg("accent", option.label) : theme.fg("text", option.label);
|
|
62
|
+
this.listContainer.addChild(new Text(`${prefix}${label}${checkmark}`, 1, 0));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
handleInput(keyData) {
|
|
66
|
+
const kb = getKeybindings();
|
|
67
|
+
if (kb.matches(keyData, "tui.select.up") || keyData === "k") {
|
|
68
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
69
|
+
this.updateList();
|
|
70
|
+
}
|
|
71
|
+
else if (kb.matches(keyData, "tui.select.down") || keyData === "j") {
|
|
72
|
+
this.selectedIndex = Math.min(TRUST_OPTIONS.length - 1, this.selectedIndex + 1);
|
|
73
|
+
this.updateList();
|
|
74
|
+
}
|
|
75
|
+
else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
|
|
76
|
+
const selected = TRUST_OPTIONS[this.selectedIndex];
|
|
77
|
+
if (selected) {
|
|
78
|
+
this.onSelectCallback(selected.trusted);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (kb.matches(keyData, "tui.select.cancel")) {
|
|
82
|
+
this.onCancelCallback();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=trust-selector.js.map
|
package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
CHANGED
|
@@ -24,6 +24,7 @@ import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../cor
|
|
|
24
24
|
import { SessionManager } from "../../core/session-manager.js";
|
|
25
25
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
26
26
|
import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
|
|
27
|
+
import { hasProjectTrustInputs, ProjectTrustStore } from "../../core/trust-manager.js";
|
|
27
28
|
import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
|
|
28
29
|
import { copyToClipboard } from "../../utils/clipboard.js";
|
|
29
30
|
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
|
|
@@ -59,6 +60,7 @@ import { SettingsSelectorComponent } from "./components/settings-selector.js";
|
|
|
59
60
|
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
|
|
60
61
|
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
61
62
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
63
|
+
import { TrustSelectorComponent } from "./components/trust-selector.js";
|
|
62
64
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
63
65
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
64
66
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
@@ -2059,6 +2061,11 @@ export class InteractiveMode {
|
|
|
2059
2061
|
this.editor.setText("");
|
|
2060
2062
|
return;
|
|
2061
2063
|
}
|
|
2064
|
+
if (text === "/trust") {
|
|
2065
|
+
this.showTrustSelector();
|
|
2066
|
+
this.editor.setText("");
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2062
2069
|
if (text === "/login") {
|
|
2063
2070
|
this.showOAuthSelector("login");
|
|
2064
2071
|
this.editor.setText("");
|
|
@@ -2528,6 +2535,7 @@ export class InteractiveMode {
|
|
|
2528
2535
|
this.chatContainer.addChild(component);
|
|
2529
2536
|
// Render user message separately if present
|
|
2530
2537
|
if (skillBlock.userMessage) {
|
|
2538
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2531
2539
|
const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings());
|
|
2532
2540
|
this.chatContainer.addChild(userComponent);
|
|
2533
2541
|
}
|
|
@@ -2627,6 +2635,7 @@ export class InteractiveMode {
|
|
|
2627
2635
|
updateFooter: true,
|
|
2628
2636
|
populateHistory: true,
|
|
2629
2637
|
});
|
|
2638
|
+
this.renderProjectTrustWarningIfNeeded();
|
|
2630
2639
|
// Show compaction info if session was compacted
|
|
2631
2640
|
const allEntries = this.sessionManager.getEntries();
|
|
2632
2641
|
const compactionCount = allEntries.filter((e) => e.type === "compaction").length;
|
|
@@ -2635,6 +2644,15 @@ export class InteractiveMode {
|
|
|
2635
2644
|
this.showStatus(`Session compacted ${times}`);
|
|
2636
2645
|
}
|
|
2637
2646
|
}
|
|
2647
|
+
renderProjectTrustWarningIfNeeded() {
|
|
2648
|
+
if (this.settingsManager.isProjectTrusted() || !hasProjectTrustInputs(this.sessionManager.getCwd())) {
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
if (this.chatContainer.children.length > 0) {
|
|
2652
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2653
|
+
}
|
|
2654
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", "This project is not trusted. Project instructions (AGENTS.md/CLAUDE.md), .pi resources, and project packages are ignored. Use /trust to save a trust decision, then restart pi."), 1, 0));
|
|
2655
|
+
}
|
|
2638
2656
|
async getUserInput() {
|
|
2639
2657
|
const queuedInput = this.pendingUserInputs.shift();
|
|
2640
2658
|
if (queuedInput !== undefined) {
|
|
@@ -3450,6 +3468,28 @@ export class InteractiveMode {
|
|
|
3450
3468
|
// Ignore auth lookup failures for warning-only checks.
|
|
3451
3469
|
}
|
|
3452
3470
|
}
|
|
3471
|
+
showTrustSelector() {
|
|
3472
|
+
const cwd = this.sessionManager.getCwd();
|
|
3473
|
+
const trustStore = new ProjectTrustStore(this.runtimeHost.services.agentDir);
|
|
3474
|
+
const savedDecision = trustStore.get(cwd);
|
|
3475
|
+
this.showSelector((done) => {
|
|
3476
|
+
const selector = new TrustSelectorComponent({
|
|
3477
|
+
cwd,
|
|
3478
|
+
savedDecision,
|
|
3479
|
+
projectTrusted: this.settingsManager.isProjectTrusted(),
|
|
3480
|
+
onSelect: (trusted) => {
|
|
3481
|
+
trustStore.set(cwd, trusted);
|
|
3482
|
+
done();
|
|
3483
|
+
this.showStatus(`Saved trust decision: ${trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
|
|
3484
|
+
},
|
|
3485
|
+
onCancel: () => {
|
|
3486
|
+
done();
|
|
3487
|
+
this.ui.requestRender();
|
|
3488
|
+
},
|
|
3489
|
+
});
|
|
3490
|
+
return { component: selector, focus: selector };
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3453
3493
|
showModelSelector(initialSearchInput) {
|
|
3454
3494
|
this.showSelector((done) => {
|
|
3455
3495
|
const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRegistry, this.session.scopedModels, async (model) => {
|
|
@@ -4,6 +4,7 @@ import { selectConfig } from "./cli/config-selector.js";
|
|
|
4
4
|
import { APP_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, VERSION, } from "./config.js";
|
|
5
5
|
import { DefaultPackageManager } from "./core/package-manager.js";
|
|
6
6
|
import { SettingsManager } from "./core/settings-manager.js";
|
|
7
|
+
import { hasProjectTrustInputs, ProjectTrustStore } from "./core/trust-manager.js";
|
|
7
8
|
import { spawnProcess } from "./utils/child-process.js";
|
|
8
9
|
import { getLatestPiRelease, isNewerPackageVersion } from "./utils/version-check.js";
|
|
9
10
|
import { cleanupWindowsSelfUpdateQuarantine, quarantineWindowsNativeDependencies, } from "./utils/windows-self-update.js";
|
|
@@ -35,13 +36,13 @@ function reportSettingsErrors(settingsManager, context) {
|
|
|
35
36
|
function getPackageCommandUsage(command) {
|
|
36
37
|
switch (command) {
|
|
37
38
|
case "install":
|
|
38
|
-
return `${APP_NAME} install <source> [-l]`;
|
|
39
|
+
return `${APP_NAME} install <source> [-l] [--approve|--no-approve]`;
|
|
39
40
|
case "remove":
|
|
40
|
-
return `${APP_NAME} remove <source> [-l]`;
|
|
41
|
+
return `${APP_NAME} remove <source> [-l] [--approve|--no-approve]`;
|
|
41
42
|
case "update":
|
|
42
|
-
return `${APP_NAME} update [source|self|pi] [--self] [--extensions] [--extension <source>] [--force]`;
|
|
43
|
+
return `${APP_NAME} update [source|self|pi] [--self] [--extensions] [--extension <source>] [--approve|--no-approve] [--force]`;
|
|
43
44
|
case "list":
|
|
44
|
-
return `${APP_NAME} list`;
|
|
45
|
+
return `${APP_NAME} list [--approve|--no-approve]`;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
function printPackageCommandHelp(command) {
|
|
@@ -53,7 +54,9 @@ function printPackageCommandHelp(command) {
|
|
|
53
54
|
Install a package and add it to settings.
|
|
54
55
|
|
|
55
56
|
Options:
|
|
56
|
-
-l, --local
|
|
57
|
+
-l, --local Install project-locally (.pi/settings.json)
|
|
58
|
+
-a, --approve Trust project-local files for this command
|
|
59
|
+
-na, --no-approve Ignore project-local files for this command
|
|
57
60
|
|
|
58
61
|
Examples:
|
|
59
62
|
${APP_NAME} install npm:@foo/bar
|
|
@@ -72,7 +75,9 @@ Remove a package and its source from settings.
|
|
|
72
75
|
Alias: ${APP_NAME} uninstall <source> [-l]
|
|
73
76
|
|
|
74
77
|
Options:
|
|
75
|
-
-l, --local
|
|
78
|
+
-l, --local Remove from project settings (.pi/settings.json)
|
|
79
|
+
-a, --approve Trust project-local files for this command
|
|
80
|
+
-na, --no-approve Ignore project-local files for this command
|
|
76
81
|
|
|
77
82
|
Examples:
|
|
78
83
|
${APP_NAME} remove npm:@foo/bar
|
|
@@ -89,6 +94,8 @@ Options:
|
|
|
89
94
|
--self Update pi only
|
|
90
95
|
--extensions Update installed packages only
|
|
91
96
|
--extension <source> Update one package only
|
|
97
|
+
-a, --approve Trust project-local files for this command
|
|
98
|
+
-na, --no-approve Ignore project-local files for this command
|
|
92
99
|
--force Reinstall pi even if the current version is latest
|
|
93
100
|
|
|
94
101
|
Short forms:
|
|
@@ -102,6 +109,10 @@ Short forms:
|
|
|
102
109
|
${getPackageCommandUsage("list")}
|
|
103
110
|
|
|
104
111
|
List installed packages from user and project settings.
|
|
112
|
+
|
|
113
|
+
Options:
|
|
114
|
+
-a, --approve Trust project-local files for this command
|
|
115
|
+
-na, --no-approve Ignore project-local files for this command
|
|
105
116
|
`);
|
|
106
117
|
return;
|
|
107
118
|
}
|
|
@@ -120,6 +131,7 @@ function parsePackageCommand(args) {
|
|
|
120
131
|
}
|
|
121
132
|
let local = false;
|
|
122
133
|
let force = false;
|
|
134
|
+
let projectTrustOverride;
|
|
123
135
|
let help = false;
|
|
124
136
|
let invalidOption;
|
|
125
137
|
let invalidArgument;
|
|
@@ -162,6 +174,14 @@ function parsePackageCommand(args) {
|
|
|
162
174
|
}
|
|
163
175
|
continue;
|
|
164
176
|
}
|
|
177
|
+
if (arg === "--approve" || arg === "-a") {
|
|
178
|
+
projectTrustOverride = true;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (arg === "--no-approve" || arg === "-na") {
|
|
182
|
+
projectTrustOverride = false;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
165
185
|
if (arg === "--force") {
|
|
166
186
|
if (command === "update") {
|
|
167
187
|
force = true;
|
|
@@ -244,6 +264,7 @@ function parsePackageCommand(args) {
|
|
|
244
264
|
updateTarget,
|
|
245
265
|
local,
|
|
246
266
|
force,
|
|
267
|
+
projectTrustOverride,
|
|
247
268
|
help,
|
|
248
269
|
invalidOption,
|
|
249
270
|
invalidArgument,
|
|
@@ -337,13 +358,32 @@ function prepareWindowsNpmSelfUpdate() {
|
|
|
337
358
|
cleanupWindowsSelfUpdateQuarantine(packageDir);
|
|
338
359
|
quarantineWindowsNativeDependencies(packageDir);
|
|
339
360
|
}
|
|
361
|
+
function parseProjectTrustOverride(args) {
|
|
362
|
+
let trustOverride;
|
|
363
|
+
for (const arg of args) {
|
|
364
|
+
if (arg === "--approve" || arg === "-a") {
|
|
365
|
+
trustOverride = true;
|
|
366
|
+
}
|
|
367
|
+
else if (arg === "--no-approve" || arg === "-na") {
|
|
368
|
+
trustOverride = false;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return trustOverride;
|
|
372
|
+
}
|
|
373
|
+
function resolveProjectTrusted(cwd, agentDir, trustOverride) {
|
|
374
|
+
if (trustOverride !== undefined) {
|
|
375
|
+
return trustOverride;
|
|
376
|
+
}
|
|
377
|
+
return !hasProjectTrustInputs(cwd) || new ProjectTrustStore(agentDir).get(cwd) === true;
|
|
378
|
+
}
|
|
340
379
|
export async function handleConfigCommand(args) {
|
|
341
380
|
if (args[0] !== "config") {
|
|
342
381
|
return false;
|
|
343
382
|
}
|
|
344
383
|
const cwd = process.cwd();
|
|
345
384
|
const agentDir = getAgentDir();
|
|
346
|
-
const
|
|
385
|
+
const projectTrusted = parseProjectTrustOverride(args) ?? true;
|
|
386
|
+
const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });
|
|
347
387
|
reportSettingsErrors(settingsManager, "config command");
|
|
348
388
|
const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
|
|
349
389
|
const resolvedPaths = await packageManager.resolve();
|
|
@@ -397,7 +437,14 @@ export async function handlePackageCommand(args) {
|
|
|
397
437
|
}
|
|
398
438
|
const cwd = process.cwd();
|
|
399
439
|
const agentDir = getAgentDir();
|
|
400
|
-
const
|
|
440
|
+
const writesProjectPackageConfig = (options.command === "install" || options.command === "remove") && options.local;
|
|
441
|
+
const projectTrusted = resolveProjectTrusted(cwd, agentDir, options.projectTrustOverride);
|
|
442
|
+
if (!projectTrusted && writesProjectPackageConfig) {
|
|
443
|
+
console.error(chalk.red("Project is not trusted. Use --approve to modify local package config."));
|
|
444
|
+
process.exitCode = 1;
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });
|
|
401
448
|
reportSettingsErrors(settingsManager, "package command");
|
|
402
449
|
const selfUpdateNpmCommand = settingsManager.getGlobalSettings().npmCommand;
|
|
403
450
|
const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
|
|
@@ -56,6 +56,53 @@ function splitRef(url) {
|
|
|
56
56
|
ref,
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
function decodeForValidation(value) {
|
|
60
|
+
try {
|
|
61
|
+
return decodeURIComponent(value);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function hasUnsafeGitInstallPart(value, allowSlash) {
|
|
68
|
+
const decoded = decodeForValidation(value);
|
|
69
|
+
if (decoded === null) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
const candidates = [value, decoded];
|
|
73
|
+
for (const candidate of candidates) {
|
|
74
|
+
if (candidate.includes("\0") || candidate.includes("\\") || candidate.startsWith("/")) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
if (!allowSlash && candidate.includes("/")) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (candidate.split("/").includes("..")) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
function buildGitSource(args) {
|
|
87
|
+
if (args.path.startsWith("/")) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const normalizedPath = args.path.replace(/\.git$/, "").replace(/^\/+/, "");
|
|
91
|
+
if (!args.host || !normalizedPath || normalizedPath.split("/").length < 2) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (hasUnsafeGitInstallPart(args.host, false) || hasUnsafeGitInstallPart(normalizedPath, true)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
type: "git",
|
|
99
|
+
repo: args.repo,
|
|
100
|
+
host: args.host,
|
|
101
|
+
path: normalizedPath,
|
|
102
|
+
ref: args.ref,
|
|
103
|
+
pinned: Boolean(args.ref),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
59
106
|
function parseGenericGitUrl(url) {
|
|
60
107
|
const { repo: repoWithoutRef, ref } = splitRef(url);
|
|
61
108
|
let repo = repoWithoutRef;
|
|
@@ -91,18 +138,7 @@ function parseGenericGitUrl(url) {
|
|
|
91
138
|
}
|
|
92
139
|
repo = `https://${repoWithoutRef}`;
|
|
93
140
|
}
|
|
94
|
-
|
|
95
|
-
if (!host || !normalizedPath || normalizedPath.split("/").length < 2) {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
return {
|
|
99
|
-
type: "git",
|
|
100
|
-
repo,
|
|
101
|
-
host,
|
|
102
|
-
path: normalizedPath,
|
|
103
|
-
ref,
|
|
104
|
-
pinned: Boolean(ref),
|
|
105
|
-
};
|
|
141
|
+
return buildGitSource({ repo, host, path, ref });
|
|
106
142
|
}
|
|
107
143
|
/**
|
|
108
144
|
* Parse git source into a GitSource.
|
|
@@ -131,14 +167,12 @@ export function parseGitUrl(source) {
|
|
|
131
167
|
!split.repo.startsWith("ssh://") &&
|
|
132
168
|
!split.repo.startsWith("git://") &&
|
|
133
169
|
!split.repo.startsWith("git@");
|
|
134
|
-
return {
|
|
135
|
-
type: "git",
|
|
170
|
+
return buildGitSource({
|
|
136
171
|
repo: useHttpsPrefix ? `https://${split.repo}` : split.repo,
|
|
137
172
|
host: info.domain || "",
|
|
138
|
-
path: `${info.user}/${info.project}
|
|
173
|
+
path: `${info.user}/${info.project}`,
|
|
139
174
|
ref: info.committish || split.ref || undefined,
|
|
140
|
-
|
|
141
|
-
};
|
|
175
|
+
});
|
|
142
176
|
}
|
|
143
177
|
}
|
|
144
178
|
const httpsCandidates = [split.ref ? `https://${split.repo}#${split.ref}` : undefined, `https://${url}`].filter((value) => Boolean(value));
|
|
@@ -148,14 +182,12 @@ export function parseGitUrl(source) {
|
|
|
148
182
|
if (split.ref && info.project?.includes("@")) {
|
|
149
183
|
continue;
|
|
150
184
|
}
|
|
151
|
-
return {
|
|
152
|
-
type: "git",
|
|
185
|
+
return buildGitSource({
|
|
153
186
|
repo: `https://${split.repo}`,
|
|
154
187
|
host: info.domain || "",
|
|
155
|
-
path: `${info.user}/${info.project}
|
|
188
|
+
path: `${info.user}/${info.project}`,
|
|
156
189
|
ref: info.committish || split.ref || undefined,
|
|
157
|
-
|
|
158
|
-
};
|
|
190
|
+
});
|
|
159
191
|
}
|
|
160
192
|
}
|
|
161
193
|
return parseGenericGitUrl(url);
|