pi2dsh 0.7.0 → 0.8.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 +1 -1
- package/README.zh.md +1 -1
- package/dist/analyzer-CKTsWVzn.mjs +628 -0
- package/dist/analyzer-CKTsWVzn.mjs.map +1 -0
- package/dist/cli.mjs +3 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/compat/pi-ai.d.mts +1 -11
- package/dist/compat/pi-ai.d.mts.map +1 -1
- package/dist/compat/pi-ai.mjs +2 -2
- package/dist/credentials-oauth.mjs +1 -1
- package/dist/generator-eyViezvs.mjs +300 -0
- package/dist/generator-eyViezvs.mjs.map +1 -0
- package/dist/host.d.mts +2 -0
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +2 -1
- package/dist/host.mjs.map +1 -1
- package/dist/index.d.mts +28 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +27 -4
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-config-Dlou-ZA-.mjs +707 -0
- package/dist/mcp-config-Dlou-ZA-.mjs.map +1 -0
- package/dist/{pi-ai-B4hucafU.mjs → pi-ai-C6Xag6xt.mjs} +2 -74
- package/dist/pi-ai-C6Xag6xt.mjs.map +1 -0
- package/dist/{runtime-k3IlCgjQ.mjs → runtime-B-Mun-jX.mjs} +44 -947
- package/dist/runtime-B-Mun-jX.mjs.map +1 -0
- package/dist/runtime.d.mts +11 -6
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/package.json +6 -3
- package/dist/all-BChhwMVE.mjs +0 -28512
- package/dist/all-BChhwMVE.mjs.map +0 -1
- package/dist/anthropic-messages-Dlw4T0S4.mjs +0 -6438
- package/dist/anthropic-messages-Dlw4T0S4.mjs.map +0 -1
- package/dist/azure-openai-responses-vGUNukZ7.mjs +0 -209
- package/dist/azure-openai-responses-vGUNukZ7.mjs.map +0 -1
- package/dist/deferred-tools-BD4g5QUx.mjs +0 -32
- package/dist/deferred-tools-BD4g5QUx.mjs.map +0 -1
- package/dist/diagnostics-CJye1UFh.mjs +0 -99
- package/dist/diagnostics-CJye1UFh.mjs.map +0 -1
- package/dist/dist-DY2GeEQ6.mjs +0 -1089
- package/dist/dist-DY2GeEQ6.mjs.map +0 -1
- package/dist/dist-DYfTHu0C.mjs +0 -1246
- package/dist/dist-DYfTHu0C.mjs.map +0 -1
- package/dist/error-body-DKA3lknR.mjs +0 -104
- package/dist/error-body-DKA3lknR.mjs.map +0 -1
- package/dist/github-copilot-headers-CZo2QHrt.mjs +0 -25
- package/dist/github-copilot-headers-CZo2QHrt.mjs.map +0 -1
- package/dist/google-generative-ai-C30gRjRi.mjs +0 -358
- package/dist/google-generative-ai-C30gRjRi.mjs.map +0 -1
- package/dist/google-shared-W8g3Esep.mjs +0 -29042
- package/dist/google-shared-W8g3Esep.mjs.map +0 -1
- package/dist/google-vertex--O_gyVYA.mjs +0 -402
- package/dist/google-vertex--O_gyVYA.mjs.map +0 -1
- package/dist/hash-Dbggamac.mjs +0 -19
- package/dist/hash-Dbggamac.mjs.map +0 -1
- package/dist/headers-CPzZhJQn.mjs +0 -17
- package/dist/headers-CPzZhJQn.mjs.map +0 -1
- package/dist/images-models-DJtHo9Rp.mjs +0 -1068
- package/dist/images-models-DJtHo9Rp.mjs.map +0 -1
- package/dist/json-parse-C-vG4_qc.mjs +0 -384
- package/dist/json-parse-C-vG4_qc.mjs.map +0 -1
- package/dist/mcp-config-Cful6uUS.mjs +0 -1618
- package/dist/mcp-config-Cful6uUS.mjs.map +0 -1
- package/dist/mistral-conversations-LSDG9sjq.mjs +0 -42007
- package/dist/mistral-conversations-LSDG9sjq.mjs.map +0 -1
- package/dist/multipart-parser-CoM3rwav.mjs +0 -301
- package/dist/multipart-parser-CoM3rwav.mjs.map +0 -1
- package/dist/openai-codex-responses-JxII_OUX.mjs +0 -1169
- package/dist/openai-codex-responses-JxII_OUX.mjs.map +0 -1
- package/dist/openai-completions-Dp-VwLzJ.mjs +0 -947
- package/dist/openai-completions-Dp-VwLzJ.mjs.map +0 -1
- package/dist/openai-completions-lite-BrM7gJzs.mjs +0 -316
- package/dist/openai-completions-lite-BrM7gJzs.mjs.map +0 -1
- package/dist/openai-prompt-cache-BDWtZRKh.mjs +0 -11
- package/dist/openai-prompt-cache-BDWtZRKh.mjs.map +0 -1
- package/dist/openai-responses-CWmNks8K.mjs +0 -247
- package/dist/openai-responses-CWmNks8K.mjs.map +0 -1
- package/dist/openai-responses-shared-CYgIDTf3.mjs +0 -572
- package/dist/openai-responses-shared-CYgIDTf3.mjs.map +0 -1
- package/dist/openrouter-images-DfAULV7l.mjs +0 -127
- package/dist/openrouter-images-DfAULV7l.mjs.map +0 -1
- package/dist/otel-Boafyju5.mjs +0 -24935
- package/dist/otel-Boafyju5.mjs.map +0 -1
- package/dist/pi-ai-B4hucafU.mjs.map +0 -1
- package/dist/pi-messages-HzGtvc5_.mjs +0 -306
- package/dist/pi-messages-HzGtvc5_.mjs.map +0 -1
- package/dist/provider-env-C9gOBFJi.mjs +0 -40
- package/dist/provider-env-C9gOBFJi.mjs.map +0 -1
- package/dist/provider-retry-Cw6YJ4cr.mjs +0 -80
- package/dist/provider-retry-Cw6YJ4cr.mjs.map +0 -1
- package/dist/runtime-k3IlCgjQ.mjs.map +0 -1
- package/dist/sanitize-unicode-wLA11KvZ.mjs +0 -29
- package/dist/sanitize-unicode-wLA11KvZ.mjs.map +0 -1
- package/dist/src-D0GRWCE8.mjs +0 -5031
- package/dist/src-D0GRWCE8.mjs.map +0 -1
- package/dist/transform-messages-DGq9mvhA.mjs +0 -379
- package/dist/transform-messages-DGq9mvhA.mjs.map +0 -1
- package/dist/uuid-DtFT7tOE.mjs +0 -65
- package/dist/uuid-DtFT7tOE.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -136,7 +136,7 @@ is aspirational.
|
|
|
136
136
|
| Example | What you get |
|
|
137
137
|
|---|---|
|
|
138
138
|
| [`examples/vision-bridge`](examples/vision-bridge/) | A text-only model answers questions about images: mention an image path, a configured vision model reads it, the analysis is injected into the conversation (works in CLI and the DSH web app; probe images included) |
|
|
139
|
-
| [`examples/custom-
|
|
139
|
+
| [`examples/custom-gateways`](examples/custom-gateways/) | Add any OpenAI-compatible gateway the official DSH way (the `llm-pi-ai:` section of DSH settings) — it appears in the DSH model picker, works as the main model, and every Pi plugin sees it through the bridge's registry projection; the bridge owns zero model configuration |
|
|
140
140
|
|
|
141
141
|
More verified capabilities (approval guardian, cross-session memory,
|
|
142
142
|
interactive OAuth, MCP config conversion, host mode) get their examples as
|
package/README.zh.md
CHANGED
|
@@ -124,7 +124,7 @@ npx pi2dsh mcp-config # Pi mcpServers → DSH patc
|
|
|
124
124
|
| 示例 | 你得到什么 |
|
|
125
125
|
|---|---|
|
|
126
126
|
| [`examples/vision-bridge`](examples/vision-bridge/) | 纯文本模型回答图片问题:消息里带图片路径,配置的视觉模型识图,分析结果注入对话(CLI 与 DSH Web 都能用;附探针测试图) |
|
|
127
|
-
| [`examples/custom-
|
|
127
|
+
| [`examples/custom-gateways`](examples/custom-gateways/) | 用 DSH 官方方式(DSH settings 的 `llm-pi-ai:` 段)接入任意 OpenAI 兼容网关——出现在 DSH 模型选择器、可当主模型、所有 Pi 插件经桥的 registry 投影看到同一路由;桥自身零模型配置 |
|
|
128
128
|
|
|
129
129
|
更多已验证能力(审批 guardian、跨会话记忆、交互式 OAuth、MCP 配置转换、host 模式)的示例,将按同一标准逐个重新端到端验证后补齐。
|
|
130
130
|
|
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
|
|
2
|
+
import { R as __exportAll } from "./pi-ai-C6Xag6xt.mjs";
|
|
3
|
+
import { c as PI_AI_PACKAGES, f as ruleForApi, g as ruleForUiContextProperty, h as ruleForHostImport, l as PI_CODING_AGENT_PACKAGES, m as ruleForEvent, p as ruleForContextProperty, u as PI_TUI_PACKAGES } from "./mcp-config-Dlou-ZA-.mjs";
|
|
4
|
+
import { builtinModules } from "node:module";
|
|
5
|
+
import { lstat, readFile, readdir, stat } from "node:fs/promises";
|
|
6
|
+
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
7
|
+
import ts from "typescript";
|
|
8
|
+
//#region src/module-graph.ts
|
|
9
|
+
const SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
10
|
+
".ts",
|
|
11
|
+
".tsx",
|
|
12
|
+
".mts",
|
|
13
|
+
".cts",
|
|
14
|
+
".js",
|
|
15
|
+
".jsx",
|
|
16
|
+
".mjs",
|
|
17
|
+
".cjs"
|
|
18
|
+
]);
|
|
19
|
+
const MODULE_EXTENSIONS = [
|
|
20
|
+
".ts",
|
|
21
|
+
".tsx",
|
|
22
|
+
".mts",
|
|
23
|
+
".cts",
|
|
24
|
+
".js",
|
|
25
|
+
".jsx",
|
|
26
|
+
".mjs",
|
|
27
|
+
".cjs",
|
|
28
|
+
".json"
|
|
29
|
+
];
|
|
30
|
+
function sourceKind(path) {
|
|
31
|
+
if (path.endsWith(".js") || path.endsWith(".mjs") || path.endsWith(".cjs")) return ts.ScriptKind.JS;
|
|
32
|
+
if (path.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
33
|
+
if (path.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
34
|
+
return ts.ScriptKind.TS;
|
|
35
|
+
}
|
|
36
|
+
function literalModule(node) {
|
|
37
|
+
return node !== void 0 && (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) ? node.text : void 0;
|
|
38
|
+
}
|
|
39
|
+
function isImportMetaUrl(node) {
|
|
40
|
+
return node !== void 0 && ts.isPropertyAccessExpression(node) && node.name.text === "url" && ts.isMetaProperty(node.expression) && node.expression.keywordToken === ts.SyntaxKind.ImportKeyword;
|
|
41
|
+
}
|
|
42
|
+
function localReferences(path, text) {
|
|
43
|
+
const source = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, sourceKind(path));
|
|
44
|
+
const values = /* @__PURE__ */ new Map();
|
|
45
|
+
const createRequireNames = /* @__PURE__ */ new Set(["createRequire"]);
|
|
46
|
+
const requireNames = /* @__PURE__ */ new Set(["require"]);
|
|
47
|
+
const add = (kind, specifier, lazy) => {
|
|
48
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("#")) return;
|
|
49
|
+
const key = `${kind}:${specifier}`;
|
|
50
|
+
const existing = values.get(key);
|
|
51
|
+
if (existing === void 0) values.set(key, {
|
|
52
|
+
kind,
|
|
53
|
+
specifier,
|
|
54
|
+
lazy
|
|
55
|
+
});
|
|
56
|
+
else if (existing.lazy && !lazy) existing.lazy = false;
|
|
57
|
+
};
|
|
58
|
+
function collectRequireAliases(node) {
|
|
59
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && (node.moduleSpecifier.text === "node:module" || node.moduleSpecifier.text === "module") && node.importClause?.namedBindings !== void 0 && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
60
|
+
for (const element of node.importClause.namedBindings.elements) if ((element.propertyName?.text ?? element.name.text) === "createRequire") createRequireNames.add(element.name.text);
|
|
61
|
+
} else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer !== void 0 && ts.isCallExpression(node.initializer) && ts.isIdentifier(node.initializer.expression) && createRequireNames.has(node.initializer.expression.text)) requireNames.add(node.name.text);
|
|
62
|
+
ts.forEachChild(node, collectRequireAliases);
|
|
63
|
+
}
|
|
64
|
+
collectRequireAliases(source);
|
|
65
|
+
function visit(node) {
|
|
66
|
+
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts.isStringLiteral(node.moduleSpecifier)) add("module", node.moduleSpecifier.text, false);
|
|
67
|
+
else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) add("module", node.moduleReference.expression.text, false);
|
|
68
|
+
else if (ts.isCallExpression(node) && node.arguments.length > 0 && (node.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(node.expression) && requireNames.has(node.expression.text))) {
|
|
69
|
+
const specifier = literalModule(node.arguments[0]);
|
|
70
|
+
if (specifier !== void 0) add("module", specifier, insideFunctionBody(node));
|
|
71
|
+
} else if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "URL" && isImportMetaUrl(node.arguments?.[1])) {
|
|
72
|
+
const specifier = literalModule(node.arguments?.[0]);
|
|
73
|
+
if (specifier !== void 0) add("asset", specifier, false);
|
|
74
|
+
}
|
|
75
|
+
ts.forEachChild(node, visit);
|
|
76
|
+
}
|
|
77
|
+
visit(source);
|
|
78
|
+
return [...values.values()];
|
|
79
|
+
}
|
|
80
|
+
function externalPackage(specifier) {
|
|
81
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("#") || specifier.startsWith("node:") || specifier.startsWith("bun:") || builtinModules.includes(specifier)) return void 0;
|
|
82
|
+
const parts = specifier.split("/");
|
|
83
|
+
return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
84
|
+
}
|
|
85
|
+
function insideTry(node) {
|
|
86
|
+
let current = node.parent;
|
|
87
|
+
while (current !== void 0) {
|
|
88
|
+
if (ts.isTryStatement(current)) return true;
|
|
89
|
+
if (ts.isFunctionLike(current)) return false;
|
|
90
|
+
current = current.parent;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
function insideFunctionBody(node) {
|
|
95
|
+
let current = node.parent;
|
|
96
|
+
while (current !== void 0) {
|
|
97
|
+
if (ts.isFunctionLike(current)) return true;
|
|
98
|
+
current = current.parent;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
function runtimeExternalPackages(path, text) {
|
|
103
|
+
const source = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, sourceKind(path));
|
|
104
|
+
const packages = /* @__PURE__ */ new Map();
|
|
105
|
+
const createRequireNames = /* @__PURE__ */ new Set(["createRequire"]);
|
|
106
|
+
const requireNames = /* @__PURE__ */ new Set(["require"]);
|
|
107
|
+
const add = (specifier, lazy) => {
|
|
108
|
+
const name = externalPackage(specifier);
|
|
109
|
+
if (name === void 0 || name.length === 0) return;
|
|
110
|
+
const existing = packages.get(name);
|
|
111
|
+
if (existing === void 0) packages.set(name, {
|
|
112
|
+
name,
|
|
113
|
+
lazy
|
|
114
|
+
});
|
|
115
|
+
else if (existing.lazy && !lazy) existing.lazy = false;
|
|
116
|
+
};
|
|
117
|
+
function collectRequireAliases(node) {
|
|
118
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && (node.moduleSpecifier.text === "node:module" || node.moduleSpecifier.text === "module") && node.importClause?.namedBindings !== void 0 && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
119
|
+
for (const element of node.importClause.namedBindings.elements) if ((element.propertyName?.text ?? element.name.text) === "createRequire") createRequireNames.add(element.name.text);
|
|
120
|
+
} else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer !== void 0 && ts.isCallExpression(node.initializer) && ts.isIdentifier(node.initializer.expression) && createRequireNames.has(node.initializer.expression.text)) requireNames.add(node.name.text);
|
|
121
|
+
ts.forEachChild(node, collectRequireAliases);
|
|
122
|
+
}
|
|
123
|
+
collectRequireAliases(source);
|
|
124
|
+
function visit(node) {
|
|
125
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
126
|
+
const clause = node.importClause;
|
|
127
|
+
const named = clause?.namedBindings;
|
|
128
|
+
const namedImportsAreTypeOnly = named !== void 0 && ts.isNamedImports(named) && named.elements.length > 0 && named.elements.every((element) => element.isTypeOnly);
|
|
129
|
+
if (clause === void 0 || !clause.isTypeOnly && (clause.name !== void 0 || !namedImportsAreTypeOnly)) add(node.moduleSpecifier.text, false);
|
|
130
|
+
} else if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== void 0 && ts.isStringLiteral(node.moduleSpecifier)) add(node.moduleSpecifier.text, false);
|
|
131
|
+
else if (ts.isImportEqualsDeclaration(node) && !node.isTypeOnly && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) add(node.moduleReference.expression.text, false);
|
|
132
|
+
else if (ts.isCallExpression(node) && node.arguments.length > 0 && (node.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(node.expression) && requireNames.has(node.expression.text))) {
|
|
133
|
+
if (!insideTry(node)) {
|
|
134
|
+
const specifier = literalModule(node.arguments[0]);
|
|
135
|
+
if (specifier !== void 0) add(specifier, insideFunctionBody(node));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
ts.forEachChild(node, visit);
|
|
139
|
+
}
|
|
140
|
+
visit(source);
|
|
141
|
+
return [...packages.values()];
|
|
142
|
+
}
|
|
143
|
+
function inside(rootDir, path) {
|
|
144
|
+
const pathRelative = relative(rootDir, path);
|
|
145
|
+
return pathRelative === "" || pathRelative !== ".." && !pathRelative.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
|
|
146
|
+
}
|
|
147
|
+
function sourceAlternates(base) {
|
|
148
|
+
const extension = extname(base);
|
|
149
|
+
const stem = extension.length > 0 ? base.slice(0, -extension.length) : base;
|
|
150
|
+
if (extension === ".js") return [`${stem}.ts`, `${stem}.tsx`];
|
|
151
|
+
if (extension === ".mjs") return [`${stem}.mts`, `${stem}.ts`];
|
|
152
|
+
if (extension === ".cjs") return [`${stem}.cts`, `${stem}.ts`];
|
|
153
|
+
if (extension === ".jsx") return [`${stem}.tsx`, `${stem}.ts`];
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
async function isFile(path) {
|
|
157
|
+
try {
|
|
158
|
+
return (await stat(path)).isFile();
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (error.code === "ENOENT") return false;
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function subpathImportTarget(rootDir, specifier, importsMap) {
|
|
165
|
+
const conditionValue = (value) => {
|
|
166
|
+
if (typeof value === "string") return value;
|
|
167
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
168
|
+
const record = value;
|
|
169
|
+
for (const condition of [
|
|
170
|
+
"import",
|
|
171
|
+
"node",
|
|
172
|
+
"default"
|
|
173
|
+
]) {
|
|
174
|
+
const candidate = conditionValue(record[condition]);
|
|
175
|
+
if (candidate !== void 0) return candidate;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const direct = conditionValue(importsMap[specifier]);
|
|
179
|
+
if (direct !== void 0) return resolve(rootDir, direct);
|
|
180
|
+
for (const [pattern, value] of Object.entries(importsMap)) {
|
|
181
|
+
const star = pattern.indexOf("*");
|
|
182
|
+
if (star === -1) continue;
|
|
183
|
+
const prefix = pattern.slice(0, star);
|
|
184
|
+
const suffix = pattern.slice(star + 1);
|
|
185
|
+
if (!specifier.startsWith(prefix) || !specifier.endsWith(suffix)) continue;
|
|
186
|
+
const wildcard = specifier.slice(prefix.length, specifier.length - suffix.length);
|
|
187
|
+
const target = conditionValue(value);
|
|
188
|
+
if (target !== void 0) return resolve(rootDir, target.replace("*", wildcard));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function packageImportsMap(rootDir) {
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(await readFile(join(rootDir, "package.json"), "utf8"));
|
|
194
|
+
return typeof parsed.imports === "object" && parsed.imports !== null ? parsed.imports : {};
|
|
195
|
+
} catch {
|
|
196
|
+
return {};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function resolveModule(fromFile, specifier, rootDir, importsMap) {
|
|
200
|
+
if (specifier.startsWith("#")) {
|
|
201
|
+
const target = subpathImportTarget(rootDir, specifier, importsMap);
|
|
202
|
+
if (target === void 0) throw new Error(`cannot resolve subpath import ${JSON.stringify(specifier)} through the package "imports" map`);
|
|
203
|
+
return resolveModule(fromFile, relative(dirname(fromFile), target).startsWith(".") ? relative(dirname(fromFile), target) : `./${relative(dirname(fromFile), target)}`, rootDir, importsMap);
|
|
204
|
+
}
|
|
205
|
+
const base = resolve(dirname(fromFile), specifier);
|
|
206
|
+
const candidates = [
|
|
207
|
+
base,
|
|
208
|
+
...sourceAlternates(base),
|
|
209
|
+
...extname(base) === "" ? MODULE_EXTENSIONS.map((extension) => `${base}${extension}`) : [],
|
|
210
|
+
...MODULE_EXTENSIONS.map((extension) => join(base, `index${extension}`))
|
|
211
|
+
];
|
|
212
|
+
for (const candidate of [...new Set(candidates)]) {
|
|
213
|
+
if (!inside(rootDir, candidate)) throw new Error(`extension import escapes the Pi package: ${specifier} from ${fromFile}`);
|
|
214
|
+
if (await isFile(candidate)) return candidate;
|
|
215
|
+
}
|
|
216
|
+
throw new Error(`cannot resolve local extension import ${JSON.stringify(specifier)} from ${fromFile}`);
|
|
217
|
+
}
|
|
218
|
+
async function expandAsset(path, rootDir) {
|
|
219
|
+
if (!inside(rootDir, path)) throw new Error(`extension asset escapes the Pi package: ${path}`);
|
|
220
|
+
let info;
|
|
221
|
+
try {
|
|
222
|
+
info = await lstat(path);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (error.code !== "ENOENT") throw error;
|
|
225
|
+
for (const alternate of sourceAlternates(path)) if (await isFile(alternate)) return [alternate];
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
if (info.isSymbolicLink()) throw new Error(`refusing to copy symbolic link from Pi package: ${path}`);
|
|
229
|
+
if (info.isFile()) return [path];
|
|
230
|
+
if (!info.isDirectory()) return [];
|
|
231
|
+
const output = [];
|
|
232
|
+
for (const entry of await readdir(path)) output.push(...await expandAsset(join(path, entry), rootDir));
|
|
233
|
+
return output;
|
|
234
|
+
}
|
|
235
|
+
async function collectLocalClosure(rootDir, entries) {
|
|
236
|
+
const importsMap = await packageImportsMap(rootDir);
|
|
237
|
+
const graph = /* @__PURE__ */ new Map();
|
|
238
|
+
const issues = [];
|
|
239
|
+
const queue = entries.map((path) => resolve(path));
|
|
240
|
+
while (queue.length > 0) {
|
|
241
|
+
const source = queue.shift();
|
|
242
|
+
if (graph.has(source)) continue;
|
|
243
|
+
if (!inside(rootDir, source)) throw new Error(`extension source escapes the Pi package: ${source}`);
|
|
244
|
+
const info = await lstat(source);
|
|
245
|
+
if (info.isSymbolicLink()) throw new Error(`refusing to copy symbolic link from Pi package: ${source}`);
|
|
246
|
+
if (!info.isFile()) throw new Error(`extension closure contains a non-file path: ${source}`);
|
|
247
|
+
const edges = [];
|
|
248
|
+
graph.set(source, edges);
|
|
249
|
+
if (!SCRIPT_EXTENSIONS.has(extname(source))) continue;
|
|
250
|
+
const text = await readFile(source, "utf8");
|
|
251
|
+
for (const reference of localReferences(source, text)) {
|
|
252
|
+
const lazy = reference.lazy || reference.kind === "asset";
|
|
253
|
+
try {
|
|
254
|
+
const targets = reference.kind === "module" ? [await resolveModule(source, reference.specifier, rootDir, importsMap)] : await expandAsset(resolve(dirname(source), reference.specifier), rootDir);
|
|
255
|
+
edges.push({
|
|
256
|
+
lazy,
|
|
257
|
+
targets
|
|
258
|
+
});
|
|
259
|
+
queue.push(...targets);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
issues.push({
|
|
262
|
+
file: source,
|
|
263
|
+
kind: reference.kind,
|
|
264
|
+
specifier: reference.specifier,
|
|
265
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
266
|
+
lazy
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const loadTimeFiles = /* @__PURE__ */ new Set();
|
|
272
|
+
const loadQueue = entries.map((path) => resolve(path)).filter((path) => graph.has(path));
|
|
273
|
+
while (loadQueue.length > 0) {
|
|
274
|
+
const source = loadQueue.shift();
|
|
275
|
+
if (loadTimeFiles.has(source)) continue;
|
|
276
|
+
loadTimeFiles.add(source);
|
|
277
|
+
for (const edge of graph.get(source) ?? []) {
|
|
278
|
+
if (edge.lazy) continue;
|
|
279
|
+
for (const target of edge.targets) if (!loadTimeFiles.has(target)) loadQueue.push(target);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
for (const issue of issues) if (!loadTimeFiles.has(issue.file)) issue.lazy = true;
|
|
283
|
+
return {
|
|
284
|
+
files: [...graph.keys()].sort(),
|
|
285
|
+
loadTimeFiles,
|
|
286
|
+
issues
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region src/analyzer.ts
|
|
291
|
+
var analyzer_exports = /* @__PURE__ */ __exportAll({ analyzePackage: () => analyzePackage });
|
|
292
|
+
function literalText(node) {
|
|
293
|
+
return node !== void 0 && (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) ? node.text : void 0;
|
|
294
|
+
}
|
|
295
|
+
function hasModifier(node, kind) {
|
|
296
|
+
return ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((modifier) => modifier.kind === kind) === true;
|
|
297
|
+
}
|
|
298
|
+
function parameterIdentifier(node) {
|
|
299
|
+
const parameter = node.parameters[0];
|
|
300
|
+
return parameter !== void 0 && ts.isIdentifier(parameter.name) ? parameter.name.text : void 0;
|
|
301
|
+
}
|
|
302
|
+
function extensionApiReceivers(source) {
|
|
303
|
+
const receivers = /* @__PURE__ */ new Set();
|
|
304
|
+
const functions = /* @__PURE__ */ new Map();
|
|
305
|
+
function index(node) {
|
|
306
|
+
if (ts.isFunctionDeclaration(node) && node.name !== void 0) functions.set(node.name.text, node);
|
|
307
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer !== void 0 && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) functions.set(node.name.text, node.initializer);
|
|
308
|
+
if (ts.isParameter(node) && node.type !== void 0 && /(?:^|\.)ExtensionAPI\b/u.test(node.type.getText(source)) && ts.isIdentifier(node.name)) receivers.add(node.name.text);
|
|
309
|
+
if (ts.isParameter(node) && ts.isIdentifier(node.name) && /^(?:pi|extensionApi)$/iu.test(node.name.text)) receivers.add(node.name.text);
|
|
310
|
+
ts.forEachChild(node, index);
|
|
311
|
+
}
|
|
312
|
+
index(source);
|
|
313
|
+
for (const statement of source.statements) {
|
|
314
|
+
if (ts.isFunctionDeclaration(statement) && hasModifier(statement, ts.SyntaxKind.ExportKeyword) && hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
|
|
315
|
+
const name = parameterIdentifier(statement);
|
|
316
|
+
if (name !== void 0) receivers.add(name);
|
|
317
|
+
}
|
|
318
|
+
if (ts.isExportAssignment(statement)) {
|
|
319
|
+
const expression = statement.expression;
|
|
320
|
+
if (ts.isArrowFunction(expression) || ts.isFunctionExpression(expression)) {
|
|
321
|
+
const name = parameterIdentifier(expression);
|
|
322
|
+
if (name !== void 0) receivers.add(name);
|
|
323
|
+
} else if (ts.isIdentifier(expression)) {
|
|
324
|
+
const candidate = functions.get(expression.text);
|
|
325
|
+
if (candidate !== void 0) {
|
|
326
|
+
const name = parameterIdentifier(candidate);
|
|
327
|
+
if (name !== void 0) receivers.add(name);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (ts.isExportDeclaration(statement) && statement.moduleSpecifier === void 0 && statement.exportClause !== void 0 && ts.isNamedExports(statement.exportClause)) for (const element of statement.exportClause.elements) {
|
|
332
|
+
if (element.name.text !== "default" || element.propertyName === void 0 || !ts.isIdentifier(element.propertyName)) continue;
|
|
333
|
+
const candidate = functions.get(element.propertyName.text);
|
|
334
|
+
if (candidate !== void 0) {
|
|
335
|
+
const name = parameterIdentifier(candidate);
|
|
336
|
+
if (name !== void 0) receivers.add(name);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return receivers;
|
|
341
|
+
}
|
|
342
|
+
function extensionApiProperties(source, receivers) {
|
|
343
|
+
const properties = /* @__PURE__ */ new Set();
|
|
344
|
+
function visit(node) {
|
|
345
|
+
if ((ts.isPropertyDeclaration(node) || ts.isParameter(node)) && ts.isIdentifier(node.name)) {
|
|
346
|
+
if (node.type !== void 0 && /(?:^|\.)(?:Pi)?ExtensionAPI\b/u.test(node.type.getText(source)) || /^(?:pi|extensionApi)$/iu.test(node.name.text)) properties.add(node.name.text);
|
|
347
|
+
} else if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken && ts.isPropertyAccessExpression(node.left) && node.left.expression.kind === ts.SyntaxKind.ThisKeyword && ts.isIdentifier(node.right) && receivers.has(node.right.text)) properties.add(node.left.name.text);
|
|
348
|
+
ts.forEachChild(node, visit);
|
|
349
|
+
}
|
|
350
|
+
visit(source);
|
|
351
|
+
return properties;
|
|
352
|
+
}
|
|
353
|
+
function enclosingFunctionName(node) {
|
|
354
|
+
const parent = node.parent;
|
|
355
|
+
if (ts.isMethodDeclaration(parent) && parent.name !== void 0) return parent.name.getText();
|
|
356
|
+
if ((ts.isArrowFunction(parent) || ts.isFunctionExpression(parent)) && ts.isPropertyAssignment(parent.parent)) return parent.parent.name.getText();
|
|
357
|
+
}
|
|
358
|
+
function extensionContextReceivers(source) {
|
|
359
|
+
const receivers = /* @__PURE__ */ new Set();
|
|
360
|
+
function visit(node) {
|
|
361
|
+
if (ts.isParameter(node) && ts.isIdentifier(node.name)) {
|
|
362
|
+
const typedContext = node.type !== void 0 && /(?:^|\.)(?:Extension|ExtensionCommand|ToolExecution)Context\b/u.test(node.type.getText(source));
|
|
363
|
+
const conventionalHandlerContext = /^(?:ctx|context)$/iu.test(node.name.text) && /^(?:execute|handler)$/u.test(enclosingFunctionName(node) ?? "");
|
|
364
|
+
if (typedContext || conventionalHandlerContext) receivers.add(node.name.text);
|
|
365
|
+
}
|
|
366
|
+
ts.forEachChild(node, visit);
|
|
367
|
+
}
|
|
368
|
+
visit(source);
|
|
369
|
+
return receivers;
|
|
370
|
+
}
|
|
371
|
+
const PI_SHIMMED_PACKAGES = /* @__PURE__ */ new Set([
|
|
372
|
+
...PI_CODING_AGENT_PACKAGES,
|
|
373
|
+
...PI_TUI_PACKAGES,
|
|
374
|
+
...PI_AI_PACKAGES
|
|
375
|
+
]);
|
|
376
|
+
const PI_HOST_PACKAGES = /* @__PURE__ */ new Set([
|
|
377
|
+
...PI_SHIMMED_PACKAGES,
|
|
378
|
+
"typebox",
|
|
379
|
+
"@sinclair/typebox"
|
|
380
|
+
]);
|
|
381
|
+
function dependencyNames(packageJson) {
|
|
382
|
+
const names = /* @__PURE__ */ new Set();
|
|
383
|
+
for (const field of [
|
|
384
|
+
"dependencies",
|
|
385
|
+
"optionalDependencies",
|
|
386
|
+
"peerDependencies"
|
|
387
|
+
]) {
|
|
388
|
+
const value = packageJson[field];
|
|
389
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
|
|
390
|
+
for (const [name, specifier] of Object.entries(value)) if (typeof specifier === "string") names.add(name);
|
|
391
|
+
}
|
|
392
|
+
return names;
|
|
393
|
+
}
|
|
394
|
+
function pushFinding(findings, rootDir, file, source, node, capability, level, detail) {
|
|
395
|
+
const position = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
396
|
+
findings.push({
|
|
397
|
+
capability,
|
|
398
|
+
level,
|
|
399
|
+
file: relative(rootDir, file).replaceAll("\\", "/"),
|
|
400
|
+
line: position.line + 1,
|
|
401
|
+
detail
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
async function analyzeExtension(rootDir, file) {
|
|
405
|
+
const text = await readFile(file, "utf8");
|
|
406
|
+
const source = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, sourceKind(file));
|
|
407
|
+
const findings = [];
|
|
408
|
+
const receivers = extensionApiReceivers(source);
|
|
409
|
+
const apiProperties = extensionApiProperties(source, receivers);
|
|
410
|
+
const contextReceivers = extensionContextReceivers(source);
|
|
411
|
+
const methodAliases = /* @__PURE__ */ new Map();
|
|
412
|
+
const eventBusAliases = /* @__PURE__ */ new Set();
|
|
413
|
+
const uiAliases = /* @__PURE__ */ new Set();
|
|
414
|
+
function reportHostImport(packageName, importedName, node) {
|
|
415
|
+
const matched = ruleForHostImport(packageName, importedName);
|
|
416
|
+
if (matched === void 0) {
|
|
417
|
+
pushFinding(findings, rootDir, file, source, node, `host-import(${packageName}:${importedName})`, "unsupported", `The pi2dsh host shim does not export ${JSON.stringify(importedName)} from ${JSON.stringify(packageName)}.`);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
pushFinding(findings, rootDir, file, source, node, `host-import(${packageName}:${importedName})`, matched.level, matched.detail);
|
|
421
|
+
}
|
|
422
|
+
for (const statement of source.statements) if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier) && PI_SHIMMED_PACKAGES.has(statement.moduleSpecifier.text)) {
|
|
423
|
+
const packageName = statement.moduleSpecifier.text;
|
|
424
|
+
const clause = statement.importClause;
|
|
425
|
+
if (clause === void 0) {
|
|
426
|
+
reportHostImport(packageName, "<side-effect>", statement);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (clause.isTypeOnly) continue;
|
|
430
|
+
if (clause.name !== void 0) reportHostImport(packageName, "default", clause.name);
|
|
431
|
+
if (clause.namedBindings !== void 0 && ts.isNamespaceImport(clause.namedBindings)) reportHostImport(packageName, "*", clause.namedBindings);
|
|
432
|
+
else if (clause.namedBindings !== void 0) {
|
|
433
|
+
for (const element of clause.namedBindings.elements) if (!element.isTypeOnly) reportHostImport(packageName, element.propertyName?.text ?? element.name.text, element);
|
|
434
|
+
}
|
|
435
|
+
} else if (ts.isExportDeclaration(statement) && !statement.isTypeOnly && statement.moduleSpecifier !== void 0 && ts.isStringLiteral(statement.moduleSpecifier) && PI_HOST_PACKAGES.has(statement.moduleSpecifier.text)) {
|
|
436
|
+
const packageName = statement.moduleSpecifier.text;
|
|
437
|
+
if (statement.exportClause === void 0 || ts.isNamespaceExport(statement.exportClause)) reportHostImport(packageName, "*", statement);
|
|
438
|
+
else for (const element of statement.exportClause.elements) if (!element.isTypeOnly) reportHostImport(packageName, element.propertyName?.text ?? element.name.text, element);
|
|
439
|
+
} else if (ts.isImportEqualsDeclaration(statement) && !statement.isTypeOnly && ts.isExternalModuleReference(statement.moduleReference) && ts.isStringLiteral(statement.moduleReference.expression) && PI_HOST_PACKAGES.has(statement.moduleReference.expression.text)) reportHostImport(statement.moduleReference.expression.text, "*", statement);
|
|
440
|
+
const declarations = [];
|
|
441
|
+
const isApiReceiver = (node) => ts.isIdentifier(node) && receivers.has(node.text) || ts.isPropertyAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword && apiProperties.has(node.name.text);
|
|
442
|
+
function collectDeclarations(node) {
|
|
443
|
+
if (ts.isVariableDeclaration(node)) declarations.push(node);
|
|
444
|
+
ts.forEachChild(node, collectDeclarations);
|
|
445
|
+
}
|
|
446
|
+
collectDeclarations(source);
|
|
447
|
+
for (let pass = 0; pass < declarations.length + 1; pass += 1) {
|
|
448
|
+
let changed = false;
|
|
449
|
+
for (const declaration of declarations) {
|
|
450
|
+
const initializer = declaration.initializer;
|
|
451
|
+
if (initializer === void 0) continue;
|
|
452
|
+
if (ts.isIdentifier(declaration.name) && isApiReceiver(initializer)) {
|
|
453
|
+
if (!receivers.has(declaration.name.text)) {
|
|
454
|
+
receivers.add(declaration.name.text);
|
|
455
|
+
changed = true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (ts.isIdentifier(declaration.name) && ts.isIdentifier(initializer) && contextReceivers.has(initializer.text)) {
|
|
459
|
+
if (!contextReceivers.has(declaration.name.text)) {
|
|
460
|
+
contextReceivers.add(declaration.name.text);
|
|
461
|
+
changed = true;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (ts.isIdentifier(declaration.name) && ts.isPropertyAccessExpression(initializer) && initializer.name.text === "ui" && ts.isIdentifier(initializer.expression) && contextReceivers.has(initializer.expression.text) && !uiAliases.has(declaration.name.text)) {
|
|
465
|
+
uiAliases.add(declaration.name.text);
|
|
466
|
+
changed = true;
|
|
467
|
+
}
|
|
468
|
+
if (ts.isIdentifier(declaration.name) && ts.isPropertyAccessExpression(initializer) && isApiReceiver(initializer.expression)) {
|
|
469
|
+
if (initializer.name.text === "events") {
|
|
470
|
+
if (!eventBusAliases.has(declaration.name.text)) {
|
|
471
|
+
eventBusAliases.add(declaration.name.text);
|
|
472
|
+
changed = true;
|
|
473
|
+
}
|
|
474
|
+
} else if (!methodAliases.has(declaration.name.text)) {
|
|
475
|
+
methodAliases.set(declaration.name.text, initializer.name.text);
|
|
476
|
+
changed = true;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (ts.isObjectBindingPattern(declaration.name) && isApiReceiver(initializer)) for (const element of declaration.name.elements) {
|
|
480
|
+
if (!ts.isIdentifier(element.name)) continue;
|
|
481
|
+
const method = element.propertyName !== void 0 && ts.isIdentifier(element.propertyName) ? element.propertyName.text : element.name.text;
|
|
482
|
+
if (method === "events") {
|
|
483
|
+
if (!eventBusAliases.has(element.name.text)) {
|
|
484
|
+
eventBusAliases.add(element.name.text);
|
|
485
|
+
changed = true;
|
|
486
|
+
}
|
|
487
|
+
} else if (!methodAliases.has(element.name.text)) {
|
|
488
|
+
methodAliases.set(element.name.text, method);
|
|
489
|
+
changed = true;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (!changed) break;
|
|
494
|
+
}
|
|
495
|
+
function reportMethod(method, args, node) {
|
|
496
|
+
if (method === "on") {
|
|
497
|
+
const event = literalText(args[0]);
|
|
498
|
+
if (event === void 0) pushFinding(findings, rootDir, file, source, node, "on(<dynamic>)", "unsupported", "Dynamic event names cannot be audited or mapped safely.");
|
|
499
|
+
else {
|
|
500
|
+
const rule = ruleForEvent(event);
|
|
501
|
+
pushFinding(findings, rootDir, file, source, node, `on(${event})`, rule.level, rule.detail);
|
|
502
|
+
}
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const rule = ruleForApi(method);
|
|
506
|
+
if (rule === void 0) {
|
|
507
|
+
pushFinding(findings, rootDir, file, source, node, method, "unsupported", `Unknown ExtensionAPI method ${JSON.stringify(method)} cannot be audited or mapped safely.`);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
pushFinding(findings, rootDir, file, source, node, method, rule.level, rule.detail);
|
|
511
|
+
}
|
|
512
|
+
function reportEventBus(method, node) {
|
|
513
|
+
const rule = ruleForApi("events");
|
|
514
|
+
if ((method === "on" || method === "emit") && rule !== void 0) pushFinding(findings, rootDir, file, source, node, `events.${method}`, rule.level, rule.detail);
|
|
515
|
+
else pushFinding(findings, rootDir, file, source, node, `events.${method}`, "unsupported", `Unknown Pi event-bus method ${JSON.stringify(method)} cannot be mapped safely.`);
|
|
516
|
+
}
|
|
517
|
+
function reportContext(property, node) {
|
|
518
|
+
const matched = ruleForContextProperty(property);
|
|
519
|
+
if (matched === void 0) pushFinding(findings, rootDir, file, source, node, `ctx.${property}`, "unsupported", `Unknown Pi extension-context property ${JSON.stringify(property)} cannot be audited or mapped safely.`);
|
|
520
|
+
else pushFinding(findings, rootDir, file, source, node, `ctx.${property}`, matched.level, matched.detail);
|
|
521
|
+
}
|
|
522
|
+
function reportUiContext(property, node) {
|
|
523
|
+
const matched = ruleForUiContextProperty(property);
|
|
524
|
+
if (matched === void 0) pushFinding(findings, rootDir, file, source, node, `ctx.ui.${property}`, "unsupported", `Unknown Pi UI-context property ${JSON.stringify(property)} cannot be audited or mapped safely.`);
|
|
525
|
+
else pushFinding(findings, rootDir, file, source, node, `ctx.ui.${property}`, matched.level, matched.detail);
|
|
526
|
+
}
|
|
527
|
+
function visit(node) {
|
|
528
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
529
|
+
const method = methodAliases.get(node.expression.text);
|
|
530
|
+
if (method !== void 0) reportMethod(method, node.arguments, node);
|
|
531
|
+
} else if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
532
|
+
const target = node.expression.expression;
|
|
533
|
+
if (isApiReceiver(target)) reportMethod(node.expression.name.text, node.arguments, node);
|
|
534
|
+
else if (ts.isIdentifier(target) && eventBusAliases.has(target.text)) reportEventBus(node.expression.name.text, node);
|
|
535
|
+
else if (ts.isPropertyAccessExpression(target) && target.name.text === "events" && isApiReceiver(target.expression)) reportEventBus(node.expression.name.text, node);
|
|
536
|
+
} else if (ts.isCallExpression(node) && ts.isElementAccessExpression(node.expression) && isApiReceiver(node.expression.expression)) {
|
|
537
|
+
const method = literalText(node.expression.argumentExpression);
|
|
538
|
+
if (method === void 0) pushFinding(findings, rootDir, file, source, node, "<dynamic-api-method>", "unsupported", "Dynamic ExtensionAPI method access cannot be audited or mapped safely.");
|
|
539
|
+
else reportMethod(method, node.arguments, node);
|
|
540
|
+
}
|
|
541
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
542
|
+
const target = node.expression;
|
|
543
|
+
if (ts.isPropertyAccessExpression(target) && target.name.text === "ui" && ts.isIdentifier(target.expression) && contextReceivers.has(target.expression.text)) reportUiContext(node.name.text, node);
|
|
544
|
+
else if (ts.isIdentifier(target) && uiAliases.has(target.text)) reportUiContext(node.name.text, node);
|
|
545
|
+
else if (ts.isIdentifier(target) && contextReceivers.has(target.text) && node.name.text !== "ui") reportContext(node.name.text, node);
|
|
546
|
+
} else if (ts.isElementAccessExpression(node) && ts.isIdentifier(node.expression) && contextReceivers.has(node.expression.text)) {
|
|
547
|
+
const property = literalText(node.argumentExpression);
|
|
548
|
+
if (property === void 0) pushFinding(findings, rootDir, file, source, node, "ctx.<dynamic>", "unsupported", "Dynamic Pi extension-context access cannot be audited or mapped safely.");
|
|
549
|
+
else if (property !== "ui") reportContext(property, node);
|
|
550
|
+
}
|
|
551
|
+
ts.forEachChild(node, visit);
|
|
552
|
+
}
|
|
553
|
+
visit(source);
|
|
554
|
+
return findings;
|
|
555
|
+
}
|
|
556
|
+
async function analyzePackage(pkg) {
|
|
557
|
+
const extensionClosure = await collectLocalClosure(pkg.rootDir, pkg.resources.extensions);
|
|
558
|
+
const findings = (await Promise.all(extensionClosure.files.filter((file) => SCRIPT_EXTENSIONS.has(extname(file))).map((file) => analyzeExtension(pkg.rootDir, file)))).flat();
|
|
559
|
+
if (pkg.resources.extensions.length > 0 && findings.length === 0) findings.push({
|
|
560
|
+
capability: "static-audit",
|
|
561
|
+
level: "unsupported",
|
|
562
|
+
file: pkg.resources.extensions.map((file) => relative(pkg.rootDir, file).replaceAll("\\", "/")).join(", "),
|
|
563
|
+
line: 1,
|
|
564
|
+
detail: "No ExtensionAPI use was statically proven across the local module closure; conversion fails closed instead of claiming compatibility."
|
|
565
|
+
});
|
|
566
|
+
for (const issue of extensionClosure.issues) {
|
|
567
|
+
const lazyIssue = issue.lazy || issue.kind === "asset";
|
|
568
|
+
findings.push({
|
|
569
|
+
capability: `${issue.kind}(${issue.specifier})`,
|
|
570
|
+
level: lazyIssue ? "partial" : "fatal",
|
|
571
|
+
file: relative(pkg.rootDir, issue.file).replaceAll("\\", "/"),
|
|
572
|
+
line: 1,
|
|
573
|
+
detail: lazyIssue ? `Unresolved reference on a lazy path: ${issue.detail}. Extension load is unaffected; if the feature that evaluates it runs, it fails the same way under Pi (published file layout is preserved).` : `The local extension closure is incomplete: ${issue.detail}`
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
const declaredDependencies = dependencyNames(pkg.packageJson);
|
|
577
|
+
for (const file of extensionClosure.files.filter((candidate) => SCRIPT_EXTENSIONS.has(extname(candidate)))) {
|
|
578
|
+
const text = await readFile(file, "utf8");
|
|
579
|
+
const lazyFile = !extensionClosure.loadTimeFiles.has(file);
|
|
580
|
+
for (const use of runtimeExternalPackages(file, text)) {
|
|
581
|
+
if (PI_HOST_PACKAGES.has(use.name) || declaredDependencies.has(use.name)) continue;
|
|
582
|
+
const lazyUse = use.lazy || lazyFile;
|
|
583
|
+
findings.push({
|
|
584
|
+
capability: lazyUse ? `optional-lazy-dependency(${use.name})` : `undeclared-runtime-dependency(${use.name})`,
|
|
585
|
+
level: lazyUse ? "partial" : "fatal",
|
|
586
|
+
file: relative(pkg.rootDir, file).replaceAll("\\", "/"),
|
|
587
|
+
line: 1,
|
|
588
|
+
detail: lazyUse ? `The extension imports ${JSON.stringify(use.name)} only on a lazily-evaluated path without declaring it; the feature that needs it asks for the module at call time, exactly as under Pi (install ${use.name} to use that feature).` : `The extension imports ${JSON.stringify(use.name)} at load time, but the Pi package does not declare it as a dependency.`
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const resourceFinding = (file, capability, level, detail) => ({
|
|
593
|
+
capability,
|
|
594
|
+
level,
|
|
595
|
+
file: relative(pkg.rootDir, file).replaceAll("\\", "/"),
|
|
596
|
+
line: 1,
|
|
597
|
+
detail
|
|
598
|
+
});
|
|
599
|
+
for (const file of pkg.resources.skills.filter((path) => basename(path) === "SKILL.md" || path.endsWith(".md"))) findings.push(resourceFinding(file, "skill", "full", "Copied as a DSH filesystem skill with its resource directory intact."));
|
|
600
|
+
for (const file of pkg.resources.prompts) findings.push(resourceFinding(file, "prompt", "full", "Registered as a DSH slash command with Pi-compatible argument expansion."));
|
|
601
|
+
for (const file of pkg.resources.themes) findings.push(resourceFinding(file, "theme", "unsupported", "Pi terminal themes have no effect in DSH Web or headless surfaces."));
|
|
602
|
+
findings.sort((left, right) => left.file.localeCompare(right.file) || left.line - right.line || left.capability.localeCompare(right.capability));
|
|
603
|
+
const summary = {
|
|
604
|
+
full: 0,
|
|
605
|
+
partial: 0,
|
|
606
|
+
unsupported: 0,
|
|
607
|
+
fatal: 0
|
|
608
|
+
};
|
|
609
|
+
for (const finding of findings) summary[finding.level] += 1;
|
|
610
|
+
const verdict = summary.fatal > 0 ? "blocked" : summary.partial > 0 || summary.unsupported > 0 ? "review" : "ready";
|
|
611
|
+
return {
|
|
612
|
+
schemaVersion: 1,
|
|
613
|
+
package: pkg.identity,
|
|
614
|
+
verdict,
|
|
615
|
+
summary,
|
|
616
|
+
resources: {
|
|
617
|
+
extensions: pkg.resources.extensions.map((file) => relative(pkg.rootDir, file).replaceAll("\\", "/")),
|
|
618
|
+
skills: pkg.resources.skills.map((file) => relative(pkg.rootDir, file).replaceAll("\\", "/")),
|
|
619
|
+
prompts: pkg.resources.prompts.map((file) => relative(pkg.rootDir, file).replaceAll("\\", "/")),
|
|
620
|
+
themes: pkg.resources.themes.map((file) => relative(pkg.rootDir, file).replaceAll("\\", "/"))
|
|
621
|
+
},
|
|
622
|
+
findings
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
//#endregion
|
|
626
|
+
export { runtimeExternalPackages as a, collectLocalClosure as i, analyzer_exports as n, SCRIPT_EXTENSIONS as r, analyzePackage as t };
|
|
627
|
+
|
|
628
|
+
//# sourceMappingURL=analyzer-CKTsWVzn.mjs.map
|