pi-provider-cursor-ask 0.1.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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/README.zh-CN.md +87 -0
- package/UPSTREAM_CHANGELOG.md +368 -0
- package/UPSTREAM_SOURCE.md +23 -0
- package/dist/index.js +54 -0
- package/package.json +97 -0
- package/src/auth/cli-credentials.ts +275 -0
- package/src/auth/consent.ts +25 -0
- package/src/auth/index.ts +23 -0
- package/src/auth/oauth.ts +282 -0
- package/src/auth/refresh-guard.ts +93 -0
- package/src/client/bridge.ts +673 -0
- package/src/client/cursor-wire.ts +213 -0
- package/src/client/h2-unary.ts +142 -0
- package/src/client/index.ts +18 -0
- package/src/config/index.ts +69 -0
- package/src/diagnostics/diagnostics.ts +116 -0
- package/src/diagnostics/index.ts +1 -0
- package/src/extension/auth.ts +99 -0
- package/src/extension/commands.ts +163 -0
- package/src/extension/compaction-guard.ts +86 -0
- package/src/extension/debug-hooks.ts +359 -0
- package/src/extension/index.ts +8 -0
- package/src/extension/provider.ts +277 -0
- package/src/extension/quota-adapter.ts +175 -0
- package/src/extension/report-dashboard.ts +133 -0
- package/src/identity.ts +16 -0
- package/src/index.ts +186 -0
- package/src/models/ask-catalog.ts +384 -0
- package/src/models/catalog.json +1163 -0
- package/src/models/cost.ts +126 -0
- package/src/models/index.ts +6 -0
- package/src/models/limits.ts +36 -0
- package/src/models/parameterized.ts +416 -0
- package/src/models/processing.ts +313 -0
- package/src/proto/agent_pb.ts +14577 -0
- package/src/stream/bridge-session.ts +215 -0
- package/src/stream/client-transcript.ts +51 -0
- package/src/stream/config.ts +5 -0
- package/src/stream/context-normalize.ts +308 -0
- package/src/stream/context-usage.ts +168 -0
- package/src/stream/debug-log.ts +316 -0
- package/src/stream/drift.ts +122 -0
- package/src/stream/images.ts +201 -0
- package/src/stream/index.ts +68 -0
- package/src/stream/interaction-query.ts +369 -0
- package/src/stream/message-parsing.ts +402 -0
- package/src/stream/model-cache.ts +100 -0
- package/src/stream/model-discovery.ts +242 -0
- package/src/stream/model-routing.ts +100 -0
- package/src/stream/native-core.ts +2121 -0
- package/src/stream/pi-adapter.ts +414 -0
- package/src/stream/protocol.ts +63 -0
- package/src/stream/recovery.ts +494 -0
- package/src/stream/request-build.ts +668 -0
- package/src/stream/root-prompt.ts +184 -0
- package/src/stream/run-journal.ts +474 -0
- package/src/stream/run-usage.ts +107 -0
- package/src/stream/server-messages.ts +777 -0
- package/src/stream/session-state.ts +499 -0
- package/src/stream/stream-writer.ts +211 -0
- package/src/stream/thinking-filter.ts +63 -0
- package/src/stream/tool-schema.ts +185 -0
- package/src/stream/transport-errors.ts +150 -0
- package/src/stream/tuning.ts +250 -0
- package/src/stream/types.ts +330 -0
- package/src/types/enums.ts +103 -0
- package/src/types/index.ts +4 -0
- package/src/usage.ts +262 -0
- package/src/utils/cache-dir.ts +39 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/security.ts +68 -0
- package/src/utils/util.ts +43 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-disk cache location for cross-process state (model catalog, refresh
|
|
3
|
+
* back-off). Kept out of the pi config dir: everything here is derived data
|
|
4
|
+
* that can be deleted at any time without losing user configuration.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdirSync } from "node:fs";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { join as pathJoin } from "node:path";
|
|
9
|
+
|
|
10
|
+
let cachedDir: string | undefined;
|
|
11
|
+
|
|
12
|
+
/** Resolve (and create) the cache directory. Returns undefined if unusable. */
|
|
13
|
+
export function getCacheDir(): string | undefined {
|
|
14
|
+
if (cachedDir !== undefined) return cachedDir || undefined;
|
|
15
|
+
const configured = process.env.PI_CURSOR_CACHE_DIR?.trim();
|
|
16
|
+
const base =
|
|
17
|
+
configured ||
|
|
18
|
+
pathJoin(process.env.XDG_CACHE_HOME?.trim() || pathJoin(homedir(), ".cache"), "pi-cursor");
|
|
19
|
+
try {
|
|
20
|
+
mkdirSync(base, { recursive: true, mode: 0o700 });
|
|
21
|
+
cachedDir = base;
|
|
22
|
+
return base;
|
|
23
|
+
} catch {
|
|
24
|
+
// Read-only home / sandbox: callers fall back to in-memory only.
|
|
25
|
+
cachedDir = "";
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Absolute path for a cache file, or undefined when no cache dir is usable. */
|
|
31
|
+
export function cacheFilePath(name: string): string | undefined {
|
|
32
|
+
const dir = getCacheDir();
|
|
33
|
+
return dir ? pathJoin(dir, name) : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Test helper: forget the resolved directory between cases. */
|
|
37
|
+
export function resetCacheDirForTests(): void {
|
|
38
|
+
cachedDir = undefined;
|
|
39
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { cursorEnv } from "./util.js";
|
|
2
|
+
|
|
3
|
+
const ALLOWED_HOST_SUFFIXES = [".cursor.sh", ".cursor.com"];
|
|
4
|
+
const ALLOWED_HOSTS = new Set([
|
|
5
|
+
"cursor.sh",
|
|
6
|
+
"cursor.com",
|
|
7
|
+
"api2.cursor.sh",
|
|
8
|
+
"authenticator.cursor.sh",
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
/** Prevent token exfiltration via poisoned agent URL. */
|
|
12
|
+
export function assertSafeCursorBaseUrl(raw: string): string {
|
|
13
|
+
let url: URL;
|
|
14
|
+
try {
|
|
15
|
+
url = new URL(raw);
|
|
16
|
+
} catch {
|
|
17
|
+
throw new Error(`Invalid Cursor agent URL: ${raw}`);
|
|
18
|
+
}
|
|
19
|
+
if (url.username || url.password) {
|
|
20
|
+
throw new Error("Cursor agent URL must not include credentials");
|
|
21
|
+
}
|
|
22
|
+
const host = url.hostname.toLowerCase();
|
|
23
|
+
const loopback =
|
|
24
|
+
host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
25
|
+
if (url.protocol !== "https:" && !(loopback && url.protocol === "http:")) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Cursor agent URL must use HTTPS; HTTP is allowed only for loopback development endpoints (got ${url.protocol})`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const allowed =
|
|
31
|
+
ALLOWED_HOSTS.has(host) ||
|
|
32
|
+
ALLOWED_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix)) ||
|
|
33
|
+
loopback;
|
|
34
|
+
if (!allowed) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Cursor agent URL host "${host}" is not allowed. Use a *.cursor.sh / *.cursor.com endpoint.`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const path = url.pathname.replace(/\/+$/, "");
|
|
40
|
+
return `${url.origin}${path === "/" ? "" : path}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Redact JWTs, bearer tokens, and common secret keys from diagnostics/errors. */
|
|
44
|
+
export function redactSecrets(text: string): string {
|
|
45
|
+
return text
|
|
46
|
+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted-jwt]")
|
|
47
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [redacted]")
|
|
48
|
+
.replace(
|
|
49
|
+
/("?(?:access_token|refresh_token|accessToken|refreshToken|token|authorization|code_verifier)"?\s*[:=]\s*")[^"]*(")/gi,
|
|
50
|
+
"$1[redacted]$2",
|
|
51
|
+
)
|
|
52
|
+
.replace(
|
|
53
|
+
/("?(?:access_token|refresh_token|accessToken|refreshToken|token|authorization|code_verifier)"?\s*[:=]\s*)[^\s&,}]+/gi,
|
|
54
|
+
"$1[redacted]",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function safeError(error: unknown): string {
|
|
59
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
60
|
+
return redactSecrets(raw);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function debugEnabled(): boolean {
|
|
64
|
+
const raw = (cursorEnv("DEBUG") || process.env.PI_CURSOR_PROVIDER_DEBUG || "")
|
|
65
|
+
.trim()
|
|
66
|
+
.toLowerCase();
|
|
67
|
+
return !!raw && raw !== "0" && raw !== "false" && raw !== "off";
|
|
68
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function cursorEnv(name: string): string | undefined {
|
|
2
|
+
return (
|
|
3
|
+
process.env[`PI_CURSOR_${name}`] ||
|
|
4
|
+
process.env[`CURSOR_${name}`] ||
|
|
5
|
+
process.env[`PI_CURSOR_PROVIDER_${name}`]
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isTruthyEnv(value: string | undefined): boolean {
|
|
10
|
+
if (!value) return false;
|
|
11
|
+
const raw = value.trim().toLowerCase();
|
|
12
|
+
return raw === "1" || raw === "true" || raw === "on" || raw === "allow" || raw === "yes";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isFalsyEnv(value: string | undefined): boolean {
|
|
16
|
+
if (!value) return false;
|
|
17
|
+
const raw = value.trim().toLowerCase();
|
|
18
|
+
return raw === "0" || raw === "false" || raw === "off" || raw === "deny" || raw === "no";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function cursorEnvBoolean(name: string, defaultValue = false): boolean {
|
|
22
|
+
const env = cursorEnv(name);
|
|
23
|
+
if (env === undefined) return defaultValue;
|
|
24
|
+
if (isFalsyEnv(env)) return false;
|
|
25
|
+
if (isTruthyEnv(env)) return true;
|
|
26
|
+
return defaultValue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
30
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function asString(value: unknown): string | undefined {
|
|
34
|
+
return typeof value === "string" && value ? value : undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Replace lone (unpaired) surrogates with U+FFFD; valid surrogate pairs (e.g. emoji) pass through. */
|
|
38
|
+
export function sanitizeText(text: unknown): string {
|
|
39
|
+
return String(text ?? "").replace(
|
|
40
|
+
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,
|
|
41
|
+
"\uFFFD",
|
|
42
|
+
);
|
|
43
|
+
}
|