cortico 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/LICENSE +21 -0
- package/README.md +47 -0
- package/package.json +43 -0
- package/src/boot.ts +46 -0
- package/src/bot.ts +1342 -0
- package/src/config-file.ts +41 -0
- package/src/core/README.md +92 -0
- package/src/core/billing.ts +26 -0
- package/src/core/blobs.ts +106 -0
- package/src/core/bus.ts +311 -0
- package/src/core/config-schema.ts +201 -0
- package/src/core/config.ts +262 -0
- package/src/core/core.ts +699 -0
- package/src/core/cost.ts +244 -0
- package/src/core/event-store.ts +304 -0
- package/src/core/fork.ts +113 -0
- package/src/core/generation.ts +118 -0
- package/src/core/instance-lock.ts +215 -0
- package/src/core/ipc-logger.ts +105 -0
- package/src/core/language.ts +62 -0
- package/src/core/log-context.ts +38 -0
- package/src/core/loop.ts +1858 -0
- package/src/core/markers.ts +48 -0
- package/src/core/prefix.ts +121 -0
- package/src/core/run.ts +95 -0
- package/src/core/secrets.ts +18 -0
- package/src/core/session.ts +56 -0
- package/src/core/sessions.ts +261 -0
- package/src/core/state.ts +104 -0
- package/src/core/template.ts +72 -0
- package/src/core/timers.ts +129 -0
- package/src/core/tool-log.ts +135 -0
- package/src/core/transcript.ts +92 -0
- package/src/core/truncate.ts +121 -0
- package/src/core/types.ts +1140 -0
- package/src/core/usage-log.ts +79 -0
- package/src/core/util.ts +386 -0
- package/src/deploy-listing.ts +160 -0
- package/src/deploy.ts +162 -0
- package/src/extensions/README.md +72 -0
- package/src/extensions/dry-mount.ts +390 -0
- package/src/extensions/manifest.ts +153 -0
- package/src/extensions/runtime.ts +42 -0
- package/src/extensions.ts +519 -0
- package/src/launcher.ts +285 -0
- package/src/paths.ts +155 -0
- package/src/protocol/open-responses/LICENSE +201 -0
- package/src/protocol/open-responses/README.md +14 -0
- package/src/protocol/open-responses/context-helpers.ts +27 -0
- package/src/protocol/open-responses/context-log.ts +59 -0
- package/src/protocol/open-responses/context.ts +89 -0
- package/src/protocol/open-responses/generated.ts +115 -0
- package/src/protocol/open-responses/index.ts +33 -0
- package/src/protocol/open-responses/openapi.json +4230 -0
- package/src/protocol/open-responses/stream.ts +146 -0
- package/src/protocol/open-responses/tokens.ts +31 -0
- package/src/providers/README.md +86 -0
- package/src/providers/base.ts +111 -0
- package/src/providers/configuration.ts +97 -0
- package/src/providers/console/config.ts +21 -0
- package/src/providers/console/settings.ts +443 -0
- package/src/providers/console/strings.ts +74 -0
- package/src/providers/console/types.ts +11 -0
- package/src/providers/llamacpp/archive.ts +148 -0
- package/src/providers/llamacpp/catalog.ts +247 -0
- package/src/providers/llamacpp/config.ts +36 -0
- package/src/providers/llamacpp/console/client.ts +6 -0
- package/src/providers/llamacpp/console/models-panel.ts +120 -0
- package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
- package/src/providers/llamacpp/console/server.ts +153 -0
- package/src/providers/llamacpp/index.ts +95 -0
- package/src/providers/llamacpp/native.ts +51 -0
- package/src/providers/llamacpp/options.ts +117 -0
- package/src/providers/llamacpp/runtime-store.ts +139 -0
- package/src/providers/llamacpp/runtime.ts +214 -0
- package/src/providers/llamacpp/server.ts +286 -0
- package/src/providers/llamacpp/strings.ts +230 -0
- package/src/providers/openai-responses-compat/config.ts +20 -0
- package/src/providers/openai-responses-compat/index.ts +89 -0
- package/src/providers/openai-responses-compat/native.ts +127 -0
- package/src/providers/openai-responses-compat/strings.ts +19 -0
- package/src/providers/pricebook.ts +75 -0
- package/src/providers/registry.ts +137 -0
- package/src/providers/strings.ts +71 -0
- package/src/providers/transport/chat.ts +156 -0
- package/src/providers/transport/errors.ts +48 -0
- package/src/providers/transport/history.ts +68 -0
- package/src/providers/transport/native-input.ts +83 -0
- package/src/providers/transport/native-types.ts +13 -0
- package/src/providers/transport/response-assembly.ts +191 -0
- package/src/providers/transport/response-http.ts +196 -0
- package/src/providers/transport/response-meters.ts +27 -0
- package/src/providers/transport/responses-input.ts +56 -0
- package/src/web/README.md +84 -0
- package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
- package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
- package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
- package/src/web/client/console-pages/builtins.ts +10 -0
- package/src/web/client/console-pages/context.ts +135 -0
- package/src/web/client/console-pages/host.ts +633 -0
- package/src/web/client/console-pages/loader.ts +132 -0
- package/src/web/client/console-pages/strings.ts +90 -0
- package/src/web/client/console-pages/tools/strings.ts +49 -0
- package/src/web/client/console-pages/tools/view.ts +215 -0
- package/src/web/client/core/api.ts +200 -0
- package/src/web/client/core/language.ts +56 -0
- package/src/web/client/core/lifecycle.ts +132 -0
- package/src/web/client/core/router.ts +199 -0
- package/src/web/client/core/stream.ts +157 -0
- package/src/web/client/core/websocket.ts +82 -0
- package/src/web/client/features/appearance/index.ts +333 -0
- package/src/web/client/features/appearance/strings.ts +87 -0
- package/src/web/client/features/config/index.ts +11 -0
- package/src/web/client/features/config/strings.ts +41 -0
- package/src/web/client/features/config/view.ts +428 -0
- package/src/web/client/features/core/events.ts +197 -0
- package/src/web/client/features/core/index.ts +254 -0
- package/src/web/client/features/core/run.ts +56 -0
- package/src/web/client/features/core/runlog.ts +214 -0
- package/src/web/client/features/core/sessions.ts +53 -0
- package/src/web/client/features/core/strings.ts +157 -0
- package/src/web/client/features/extensions/index.ts +422 -0
- package/src/web/client/features/extensions/strings.ts +164 -0
- package/src/web/client/features/feature.ts +93 -0
- package/src/web/client/features/live/context.ts +280 -0
- package/src/web/client/features/live/fork.ts +156 -0
- package/src/web/client/features/live/index.ts +392 -0
- package/src/web/client/features/live/onboarding.ts +113 -0
- package/src/web/client/features/live/protocol.ts +122 -0
- package/src/web/client/features/live/sessions.ts +71 -0
- package/src/web/client/features/live/status.ts +68 -0
- package/src/web/client/features/live/strings.ts +252 -0
- package/src/web/client/features/live/timeline.ts +590 -0
- package/src/web/client/features/prompts/editor.ts +292 -0
- package/src/web/client/features/prompts/index.ts +346 -0
- package/src/web/client/features/prompts/strings.ts +136 -0
- package/src/web/client/features/prompts/view.ts +256 -0
- package/src/web/client/features/providers/index.ts +100 -0
- package/src/web/client/features/providers/strings.ts +23 -0
- package/src/web/client/features/settings/general.ts +28 -0
- package/src/web/client/features/settings/index.ts +82 -0
- package/src/web/client/features/settings/strings.ts +33 -0
- package/src/web/client/features/storage/index.ts +1 -0
- package/src/web/client/features/storage/strings.ts +45 -0
- package/src/web/client/features/storage/view.ts +154 -0
- package/src/web/client/features/usage/chart.ts +486 -0
- package/src/web/client/features/usage/index.ts +407 -0
- package/src/web/client/features/usage/labels.ts +46 -0
- package/src/web/client/features/usage/range.ts +36 -0
- package/src/web/client/features/usage/state.ts +37 -0
- package/src/web/client/features/usage/strings.ts +191 -0
- package/src/web/client/features/usage/tooltip.ts +86 -0
- package/src/web/client/features/usage/types.ts +96 -0
- package/src/web/client/features/worlds/index.ts +424 -0
- package/src/web/client/features/worlds/strings.ts +121 -0
- package/src/web/client/main.ts +273 -0
- package/src/web/client/shell/avatar.ts +233 -0
- package/src/web/client/shell/index.ts +549 -0
- package/src/web/client/shell/strings.ts +89 -0
- package/src/web/client/theme/handoff.ts +43 -0
- package/src/web/client/theme/palette.ts +109 -0
- package/src/web/client/theme/registry.ts +130 -0
- package/src/web/client/theme/storage.ts +72 -0
- package/src/web/client/theme/strings.ts +123 -0
- package/src/web/client/theme/studio.ts +366 -0
- package/src/web/client/ui/actions.ts +109 -0
- package/src/web/client/ui/data.ts +185 -0
- package/src/web/client/ui/dom.ts +23 -0
- package/src/web/client/ui/fields.ts +191 -0
- package/src/web/client/ui/format.ts +75 -0
- package/src/web/client/ui/icons.ts +183 -0
- package/src/web/client/ui/images.ts +91 -0
- package/src/web/client/ui/index.ts +89 -0
- package/src/web/client/ui/lamp.ts +125 -0
- package/src/web/client/ui/log.ts +98 -0
- package/src/web/client/ui/overlay.ts +184 -0
- package/src/web/client/ui/page.ts +8 -0
- package/src/web/client/ui/prompt-input.tsx +311 -0
- package/src/web/client/ui/sheet.ts +131 -0
- package/src/web/client/ui/strings.ts +71 -0
- package/src/web/console-pages.ts +367 -0
- package/src/web/files.ts +107 -0
- package/src/web/path-picker.ts +296 -0
- package/src/web/public/index.html +17 -0
- package/src/web/public/styles.css +1715 -0
- package/src/web/server.ts +1883 -0
- package/src/web/shared/client-panel.ts +603 -0
- package/src/web/shared/console-protocol.ts +560 -0
- package/src/web/shared/css.d.ts +1 -0
- package/src/web/shared/path-picker.ts +23 -0
- package/src/web/shared/theme.ts +256 -0
- package/src/web/theme-store.ts +37 -0
- package/src/world.ts +449 -0
- package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
- package/src/worlds/bilibili/README.md +176 -0
- package/src/worlds/bilibili/audience-admission.ts +995 -0
- package/src/worlds/bilibili/client.ts +528 -0
- package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
- package/src/worlds/bilibili/config.ts +185 -0
- package/src/worlds/bilibili/console/client.ts +202 -0
- package/src/worlds/bilibili/definition.ts +35 -0
- package/src/worlds/bilibili/gift-frame.ts +131 -0
- package/src/worlds/bilibili/normalize.ts +452 -0
- package/src/worlds/bilibili/overlay/announcement.ts +76 -0
- package/src/worlds/bilibili/overlay/assets.ts +82 -0
- package/src/worlds/bilibili/overlay/model.ts +494 -0
- package/src/worlds/bilibili/overlay/project.ts +202 -0
- package/src/worlds/bilibili/overlay/server.ts +286 -0
- package/src/worlds/bilibili/overlay/types.ts +217 -0
- package/src/worlds/bilibili/overlay/web/app.js +690 -0
- package/src/worlds/bilibili/overlay/web/editor.css +297 -0
- package/src/worlds/bilibili/overlay/web/editor.html +102 -0
- package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
- package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
- package/src/worlds/bilibili/overlay/web/styles.css +258 -0
- package/src/worlds/bilibili/protobuf.ts +123 -0
- package/src/worlds/bilibili/wire.ts +94 -0
- package/src/worlds/bilibili/world.ts +1389 -0
- package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
- package/src/worlds/console-fixture/console/client.ts +72 -0
- package/src/worlds/console-fixture/world.ts +73 -0
- package/src/worlds/index.ts +15 -0
- package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
- package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
- package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
- package/src/worlds/minecraft/README.md +917 -0
- package/src/worlds/minecraft/blueprint-plan.ts +915 -0
- package/src/worlds/minecraft/blueprint-registry.ts +566 -0
- package/src/worlds/minecraft/blueprint-repair.ts +236 -0
- package/src/worlds/minecraft/blueprint-resource.ts +272 -0
- package/src/worlds/minecraft/blueprint.ts +492 -0
- package/src/worlds/minecraft/body-lease.ts +340 -0
- package/src/worlds/minecraft/bridge.ts +749 -0
- package/src/worlds/minecraft/check.ts +651 -0
- package/src/worlds/minecraft/chests.ts +292 -0
- package/src/worlds/minecraft/client-launch.ts +272 -0
- package/src/worlds/minecraft/client-options.ts +85 -0
- package/src/worlds/minecraft/client-skins.ts +164 -0
- package/src/worlds/minecraft/client-window.ps1 +91 -0
- package/src/worlds/minecraft/client.ts +423 -0
- package/src/worlds/minecraft/combat-context.ts +109 -0
- package/src/worlds/minecraft/combat.ts +1552 -0
- package/src/worlds/minecraft/config.ts +499 -0
- package/src/worlds/minecraft/console/access.ts +218 -0
- package/src/worlds/minecraft/console/client.ts +244 -0
- package/src/worlds/minecraft/console/log.ts +110 -0
- package/src/worlds/minecraft/console/mount.ts +420 -0
- package/src/worlds/minecraft/console/skin.ts +257 -0
- package/src/worlds/minecraft/console/style.css +154 -0
- package/src/worlds/minecraft/console/world.ts +449 -0
- package/src/worlds/minecraft/deaths.ts +135 -0
- package/src/worlds/minecraft/definition.ts +18 -0
- package/src/worlds/minecraft/engine-child.ts +274 -0
- package/src/worlds/minecraft/engine-ipc.ts +98 -0
- package/src/worlds/minecraft/entity-facts.ts +132 -0
- package/src/worlds/minecraft/escape.ts +407 -0
- package/src/worlds/minecraft/executor.ts +14494 -0
- package/src/worlds/minecraft/explored.ts +156 -0
- package/src/worlds/minecraft/geometry.ts +205 -0
- package/src/worlds/minecraft/goal-plan.ts +647 -0
- package/src/worlds/minecraft/item-break.ts +126 -0
- package/src/worlds/minecraft/item-facts.ts +146 -0
- package/src/worlds/minecraft/item-pick.ts +67 -0
- package/src/worlds/minecraft/level-dat.ts +176 -0
- package/src/worlds/minecraft/log.ts +132 -0
- package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
- package/src/worlds/minecraft/names.ts +377 -0
- package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
- package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
- package/src/worlds/minecraft/piglin.ts +49 -0
- package/src/worlds/minecraft/policy.ts +547 -0
- package/src/worlds/minecraft/precheck.ts +655 -0
- package/src/worlds/minecraft/proxy.ts +445 -0
- package/src/worlds/minecraft/ranged.ts +635 -0
- package/src/worlds/minecraft/readouts.ts +102 -0
- package/src/worlds/minecraft/round.ts +26 -0
- package/src/worlds/minecraft/search-observation.ts +92 -0
- package/src/worlds/minecraft/server-config.ts +467 -0
- package/src/worlds/minecraft/server.ts +658 -0
- package/src/worlds/minecraft/show.ts +84 -0
- package/src/worlds/minecraft/skills.ts +1763 -0
- package/src/worlds/minecraft/terrain.ts +1735 -0
- package/src/worlds/minecraft/window.ts +96 -0
- package/src/worlds/minecraft/works.ts +179 -0
- package/src/worlds/minecraft/world.ts +6438 -0
- package/src/worlds/qq/ENV_PROMPT.md +5 -0
- package/src/worlds/qq/config.ts +108 -0
- package/src/worlds/qq/console/client.ts +126 -0
- package/src/worlds/qq/console/events.ts +155 -0
- package/src/worlds/qq/console/gate.ts +232 -0
- package/src/worlds/qq/console/roster.ts +203 -0
- package/src/worlds/qq/console/style.css +65 -0
- package/src/worlds/qq/conversation.ts +28 -0
- package/src/worlds/qq/definition.ts +62 -0
- package/src/worlds/qq/driver.ts +375 -0
- package/src/worlds/qq/history-tools.ts +209 -0
- package/src/worlds/qq/normalize.ts +323 -0
- package/src/worlds/qq/qface-map.ts +282 -0
- package/src/worlds/qq/vision-prompt.ts +27 -0
- package/src/worlds/qq/vision.ts +642 -0
- package/src/worlds/qq/vlm.ts +142 -0
- package/src/worlds/qq/world.ts +1503 -0
- package/src/worlds/terminal/ENV_PROMPT.md +11 -0
- package/src/worlds/terminal/config.ts +9 -0
- package/src/worlds/terminal/definition.ts +14 -0
- package/src/worlds/terminal/world.ts +700 -0
- package/src/worlds/websearch/ENV_PROMPT.md +1 -0
- package/src/worlds/websearch/brave-client.ts +261 -0
- package/src/worlds/websearch/config.ts +75 -0
- package/src/worlds/websearch/definition.ts +20 -0
- package/src/worlds/websearch/world.ts +171 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
|
+
import { dirname, isAbsolute, resolve, sep } from 'node:path';
|
|
4
|
+
import { platform } from 'node:process';
|
|
5
|
+
import type { PathPicker, PathPickerOptions } from './shared/path-picker.ts';
|
|
6
|
+
|
|
7
|
+
interface ExecFailure extends Error {
|
|
8
|
+
code?: string | number;
|
|
9
|
+
stdout?: string;
|
|
10
|
+
stderr?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class PathPickerRequestError extends Error {
|
|
14
|
+
constructor(message: string) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'PathPickerRequestError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class PathPickerUnavailableError extends Error {
|
|
21
|
+
constructor(message: string) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'PathPickerUnavailableError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function stringOption(value: unknown, name: string): string | undefined {
|
|
28
|
+
if (value == null || value === '') return undefined;
|
|
29
|
+
if (typeof value !== 'string') throw new PathPickerRequestError(`${name} 必须是字符串`);
|
|
30
|
+
if (value.includes('\0')) throw new PathPickerRequestError(`${name} 不能含 NUL 字符`);
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeExtension(value: string): string {
|
|
35
|
+
const extension = value.trim().toLowerCase();
|
|
36
|
+
if (!/^\.?[a-z0-9][a-z0-9._+-]*$/i.test(extension)) {
|
|
37
|
+
throw new PathPickerRequestError(`不合法的文件后缀: ${value}`);
|
|
38
|
+
}
|
|
39
|
+
return extension.startsWith('.') ? extension : `.${extension}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 平台命令只接收经过校验的 JSON 边界值。 */
|
|
43
|
+
export function parsePathPickerOptions(raw: unknown): PathPickerOptions {
|
|
44
|
+
if (!raw || typeof raw !== 'object') throw new PathPickerRequestError('请求体必须是对象');
|
|
45
|
+
const body = raw as Record<string, unknown>;
|
|
46
|
+
if (body.kind !== 'file' && body.kind !== 'directory') {
|
|
47
|
+
throw new PathPickerRequestError('kind 必须是 file 或 directory');
|
|
48
|
+
}
|
|
49
|
+
if (body.extensions != null && !Array.isArray(body.extensions)) {
|
|
50
|
+
throw new PathPickerRequestError('extensions 必须是字符串数组');
|
|
51
|
+
}
|
|
52
|
+
const extensions = body.extensions === undefined
|
|
53
|
+
? undefined
|
|
54
|
+
: [...new Set((body.extensions as unknown[]).map((item) => {
|
|
55
|
+
if (typeof item !== 'string') throw new PathPickerRequestError('extensions 必须是字符串数组');
|
|
56
|
+
return normalizeExtension(item);
|
|
57
|
+
}))];
|
|
58
|
+
const title = stringOption(body.title, 'title');
|
|
59
|
+
const currentPath = stringOption(body.currentPath, 'currentPath');
|
|
60
|
+
const recommendedDir = stringOption(body.recommendedDir, 'recommendedDir');
|
|
61
|
+
return {
|
|
62
|
+
kind: body.kind,
|
|
63
|
+
...(title ? { title } : {}),
|
|
64
|
+
...(currentPath ? { currentPath } : {}),
|
|
65
|
+
...(recommendedDir ? { recommendedDir } : {}),
|
|
66
|
+
...(extensions ? { extensions } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 浏览器只会收到存在、类型匹配的绝对路径。 */
|
|
71
|
+
export async function validatePickedPath(
|
|
72
|
+
selected: string | null,
|
|
73
|
+
options: PathPickerOptions,
|
|
74
|
+
): Promise<string | null> {
|
|
75
|
+
if (selected == null || selected.trim() === '') return null;
|
|
76
|
+
const path = isAbsolute(selected) ? resolve(selected) : resolve(process.cwd(), selected);
|
|
77
|
+
let info;
|
|
78
|
+
try {
|
|
79
|
+
info = await stat(path);
|
|
80
|
+
} catch {
|
|
81
|
+
throw new PathPickerRequestError(`选择的路径不存在: ${path}`);
|
|
82
|
+
}
|
|
83
|
+
if (options.kind === 'file' && !info.isFile()) {
|
|
84
|
+
throw new PathPickerRequestError(`选择的路径不是文件: ${path}`);
|
|
85
|
+
}
|
|
86
|
+
if (options.kind === 'directory' && !info.isDirectory()) {
|
|
87
|
+
throw new PathPickerRequestError(`选择的路径不是目录: ${path}`);
|
|
88
|
+
}
|
|
89
|
+
if (options.kind === 'file' && options.extensions?.length) {
|
|
90
|
+
const lower = path.toLowerCase();
|
|
91
|
+
if (!options.extensions.some((extension) => lower.endsWith(extension.toLowerCase()))) {
|
|
92
|
+
throw new PathPickerRequestError(`文件类型应为 ${options.extensions.join(' / ')}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return path;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function run(command: string, args: string[], env?: NodeJS.ProcessEnv): Promise<string> {
|
|
99
|
+
return new Promise((resolveOutput, reject) => {
|
|
100
|
+
execFile(command, args, {
|
|
101
|
+
encoding: 'utf8',
|
|
102
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
103
|
+
maxBuffer: 64 * 1024,
|
|
104
|
+
windowsHide: true,
|
|
105
|
+
}, (error, stdout, stderr) => {
|
|
106
|
+
if (!error) {
|
|
107
|
+
resolveOutput(stdout);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const failure = error as ExecFailure;
|
|
111
|
+
failure.stdout = stdout;
|
|
112
|
+
failure.stderr = stderr;
|
|
113
|
+
reject(failure);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function commandMissing(error: unknown): boolean {
|
|
119
|
+
return (error as ExecFailure | null)?.code === 'ENOENT';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function cancelled(error: unknown): boolean {
|
|
123
|
+
const failure = error as ExecFailure | null;
|
|
124
|
+
if (failure?.code === 1 && !failure.stderr?.trim()) return true;
|
|
125
|
+
return failure?.code === 1 && /user canceled|-128/i.test(failure.stderr ?? '');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function initialDirectory(options: PathPickerOptions): Promise<string> {
|
|
129
|
+
const requested = options.currentPath?.trim() || options.recommendedDir?.trim();
|
|
130
|
+
if (!requested) return '';
|
|
131
|
+
let candidate = resolve(requested);
|
|
132
|
+
for (;;) {
|
|
133
|
+
try {
|
|
134
|
+
const info = await stat(candidate);
|
|
135
|
+
return info.isDirectory() ? candidate : dirname(candidate);
|
|
136
|
+
} catch {
|
|
137
|
+
const parent = dirname(candidate);
|
|
138
|
+
if (parent === candidate) return '';
|
|
139
|
+
candidate = parent;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function patterns(options: PathPickerOptions): string[] {
|
|
145
|
+
return (options.extensions ?? []).map((extension) => `*${extension}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const WINDOWS_SCRIPT = String.raw`
|
|
149
|
+
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
150
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
151
|
+
$title = $env:CORTICO_PICKER_TITLE
|
|
152
|
+
$initial = $env:CORTICO_PICKER_INITIAL
|
|
153
|
+
if ($env:CORTICO_PICKER_KIND -eq 'directory') {
|
|
154
|
+
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
|
|
155
|
+
$dialog.Description = $title
|
|
156
|
+
$dialog.ShowNewFolderButton = $true
|
|
157
|
+
if ($initial -and [System.IO.Directory]::Exists($initial)) { $dialog.SelectedPath = $initial }
|
|
158
|
+
} else {
|
|
159
|
+
$dialog = New-Object System.Windows.Forms.OpenFileDialog
|
|
160
|
+
$dialog.Title = $title
|
|
161
|
+
$dialog.CheckFileExists = $true
|
|
162
|
+
$dialog.Multiselect = $false
|
|
163
|
+
if ($initial -and [System.IO.Directory]::Exists($initial)) { $dialog.InitialDirectory = $initial }
|
|
164
|
+
$patterns = $env:CORTICO_PICKER_PATTERNS
|
|
165
|
+
if ($patterns) { $dialog.Filter = "支持的文件|$patterns|所有文件|*.*" }
|
|
166
|
+
}
|
|
167
|
+
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
|
|
168
|
+
if ($env:CORTICO_PICKER_KIND -eq 'directory') {
|
|
169
|
+
[Console]::Out.Write($dialog.SelectedPath)
|
|
170
|
+
} else {
|
|
171
|
+
[Console]::Out.Write($dialog.FileName)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
$dialog.Dispose()
|
|
175
|
+
`;
|
|
176
|
+
|
|
177
|
+
async function pickWindows(options: PathPickerOptions, initial: string): Promise<string | null> {
|
|
178
|
+
try {
|
|
179
|
+
const stdout = await run('powershell.exe', [
|
|
180
|
+
'-NoProfile', '-STA', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', WINDOWS_SCRIPT,
|
|
181
|
+
], {
|
|
182
|
+
CORTICO_PICKER_KIND: options.kind,
|
|
183
|
+
CORTICO_PICKER_TITLE: options.title ?? (options.kind === 'file' ? '选择文件' : '选择目录'),
|
|
184
|
+
CORTICO_PICKER_INITIAL: initial,
|
|
185
|
+
CORTICO_PICKER_PATTERNS: patterns(options).join(';'),
|
|
186
|
+
});
|
|
187
|
+
return stdout.trim() || null;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new PathPickerUnavailableError(commandMissing(error)
|
|
190
|
+
? '本机找不到 PowerShell,无法打开路径选择器'
|
|
191
|
+
: `无法打开本机路径选择器: ${String((error as Error).message ?? error)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const MACOS_SCRIPT = String.raw`
|
|
196
|
+
on run argv
|
|
197
|
+
set pickerKind to item 1 of argv
|
|
198
|
+
set dialogTitle to item 2 of argv
|
|
199
|
+
set initialPath to item 3 of argv
|
|
200
|
+
set extensionText to item 4 of argv
|
|
201
|
+
set AppleScript's text item delimiters to ","
|
|
202
|
+
set fileTypes to text items of extensionText
|
|
203
|
+
set AppleScript's text item delimiters to ""
|
|
204
|
+
if pickerKind is "directory" then
|
|
205
|
+
if initialPath is "" then
|
|
206
|
+
set picked to choose folder with prompt dialogTitle
|
|
207
|
+
else
|
|
208
|
+
set picked to choose folder with prompt dialogTitle default location (POSIX file initialPath)
|
|
209
|
+
end if
|
|
210
|
+
else if extensionText is "" then
|
|
211
|
+
if initialPath is "" then
|
|
212
|
+
set picked to choose file with prompt dialogTitle
|
|
213
|
+
else
|
|
214
|
+
set picked to choose file with prompt dialogTitle default location (POSIX file initialPath)
|
|
215
|
+
end if
|
|
216
|
+
else
|
|
217
|
+
if initialPath is "" then
|
|
218
|
+
set picked to choose file with prompt dialogTitle of type fileTypes
|
|
219
|
+
else
|
|
220
|
+
set picked to choose file with prompt dialogTitle of type fileTypes default location (POSIX file initialPath)
|
|
221
|
+
end if
|
|
222
|
+
end if
|
|
223
|
+
return POSIX path of picked
|
|
224
|
+
end run
|
|
225
|
+
`;
|
|
226
|
+
|
|
227
|
+
async function pickMacos(options: PathPickerOptions, initial: string): Promise<string | null> {
|
|
228
|
+
try {
|
|
229
|
+
const extensions = (options.extensions ?? []).map((extension) => extension.slice(1)).join(',');
|
|
230
|
+
const stdout = await run('osascript', [
|
|
231
|
+
'-e', MACOS_SCRIPT, '--', options.kind,
|
|
232
|
+
options.title ?? (options.kind === 'file' ? '选择文件' : '选择目录'),
|
|
233
|
+
initial,
|
|
234
|
+
extensions,
|
|
235
|
+
]);
|
|
236
|
+
return stdout.trim() || null;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (cancelled(error)) return null;
|
|
239
|
+
throw new PathPickerUnavailableError(commandMissing(error)
|
|
240
|
+
? '本机找不到 osascript,无法打开路径选择器'
|
|
241
|
+
: `无法打开本机路径选择器: ${String((error as Error).message ?? error)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function pickZenity(options: PathPickerOptions, initial: string): Promise<string | null> {
|
|
246
|
+
const args = ['--file-selection', `--title=${options.title ?? (options.kind === 'file' ? '选择文件' : '选择目录')}`];
|
|
247
|
+
if (options.kind === 'directory') args.push('--directory');
|
|
248
|
+
if (initial) args.push(`--filename=${initial}${sep}`);
|
|
249
|
+
const filter = patterns(options);
|
|
250
|
+
if (filter.length) args.push(`--file-filter=支持的文件 | ${filter.join(' ')}`);
|
|
251
|
+
const stdout = await run('zenity', args);
|
|
252
|
+
return stdout.trim() || null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function pickKdialog(options: PathPickerOptions, initial: string): Promise<string | null> {
|
|
256
|
+
const title = options.title ?? (options.kind === 'file' ? '选择文件' : '选择目录');
|
|
257
|
+
const filter = patterns(options);
|
|
258
|
+
const args = options.kind === 'directory'
|
|
259
|
+
? ['--getexistingdirectory', initial || resolve('.'), '--title', title]
|
|
260
|
+
: [
|
|
261
|
+
'--getopenfilename', initial || resolve('.'),
|
|
262
|
+
filter.length ? `${filter.join(' ')}|支持的文件` : '*|所有文件',
|
|
263
|
+
'--title', title,
|
|
264
|
+
];
|
|
265
|
+
const stdout = await run('kdialog', args);
|
|
266
|
+
return stdout.trim() || null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function pickLinux(options: PathPickerOptions, initial: string): Promise<string | null> {
|
|
270
|
+
let zenityError: unknown;
|
|
271
|
+
try {
|
|
272
|
+
return await pickZenity(options, initial);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (cancelled(error)) return null;
|
|
275
|
+
zenityError = error;
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
return await pickKdialog(options, initial);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (cancelled(error)) return null;
|
|
281
|
+
const commandsMissing = commandMissing(zenityError) && commandMissing(error);
|
|
282
|
+
throw new PathPickerUnavailableError(commandsMissing
|
|
283
|
+
? '本机没有可用的路径选择器;请安装 zenity 或 kdialog'
|
|
284
|
+
: `无法打开本机路径选择器: ${String((error as Error).message ?? error)}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export async function pickNativePath(options: PathPickerOptions): Promise<string | null> {
|
|
289
|
+
const initial = await initialDirectory(options);
|
|
290
|
+
if (platform === 'win32') return pickWindows(options, initial);
|
|
291
|
+
if (platform === 'darwin') return pickMacos(options, initial);
|
|
292
|
+
if (platform === 'linux') return pickLinux(options, initial);
|
|
293
|
+
throw new PathPickerUnavailableError(`当前平台不支持本机路径选择器: ${platform}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export const nativePathPicker: PathPicker = { pick: pickNativePath };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>控制台 · Cortico</title>
|
|
7
|
+
<meta name="theme-color" content="#f4f5f4">
|
|
8
|
+
<link rel="stylesheet" href="/assets/styles.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<!-- 服务端按构建清单在 body 末尾注入带内容 hash 的内核脚本。 -->
|
|
12
|
+
<noscript>控制台需要 JavaScript。 The console needs JavaScript.</noscript>
|
|
13
|
+
<div id="boot-note">
|
|
14
|
+
控制台正在载入…… Loading the console…
|
|
15
|
+
</div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|