@my-life-buddies/cli-windowscompat 0.0.0-stage → 0.29.0-dev.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 +19 -2
- package/dist/bin/buddy.js +542 -0
- package/dist/bin/buddy.js.map +7 -0
- package/dist/bin/command-runner.js +529 -0
- package/dist/bin/command-runner.js.map +7 -0
- package/dist/bin/core.js +9939 -0
- package/dist/bin/core.js.map +7 -0
- package/dist/bin/platform.js +543 -0
- package/dist/bin/platform.js.map +7 -0
- package/dist/bin/preview.js +2924 -0
- package/dist/bin/preview.js.map +7 -0
- package/dist/bin/resources/widget-document.js +41 -0
- package/package.json +54 -4
- package/resources/README.md +10 -0
- package/resources/agent-template/buddy-dev-control.mjs +49 -0
- package/resources/agent-template/package-lock.json +23 -0
- package/resources/agent-template/package.json +14 -0
- package/resources/brand/README.md +11 -0
- package/resources/brand/login-result.css +25 -0
- package/resources/brand/logo.png +0 -0
- package/resources/brand/tokens.css +100 -0
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
// <define:__BUDDY_CLI_LANE_BUILD__>
|
|
2
|
+
var define_BUDDY_CLI_LANE_BUILD_default = { schemaVersion: 1, id: "windowscompat", origin: "https://buddies-windowscompat.devops.sl.sit.xiaohongshu.com", command: "buddy-cli-windowscompat", runtimePackage: "@my-life-buddies/buddy-runtime-windowscompat", runtimeTag: "dev", runtimePrerelease: "dev", sourceVersion: "0.29.0-dev.0", distribution: "npm", packageName: "@my-life-buddies/cli-windowscompat", version: "0.29.0-dev.0", validationScope: "mac-config-only" };
|
|
3
|
+
|
|
4
|
+
// packages/cli-core/src/build-target.ts
|
|
5
|
+
var LANE_BUILD = typeof define_BUDDY_CLI_LANE_BUILD_default === "undefined" ? void 0 : Object.freeze(define_BUDDY_CLI_LANE_BUILD_default);
|
|
6
|
+
var CLI_COMMAND_NAME = LANE_BUILD?.command ?? "buddy-cli-sit";
|
|
7
|
+
var CLI_PACKAGE_NAME = LANE_BUILD?.packageName ?? "@my-life-buddies/cli";
|
|
8
|
+
var PLATFORM_ORIGIN = LANE_BUILD?.origin ?? "https://buddies.sit.dots.chat";
|
|
9
|
+
var RUNTIME_PACKAGE_NAME = LANE_BUILD?.runtimePackage ?? "@my-life-buddies/buddy-runtime";
|
|
10
|
+
var RUNTIME_DIST_TAG = LANE_BUILD?.runtimeTag ?? "dev";
|
|
11
|
+
var RUNTIME_SOURCE_LABEL = LANE_BUILD ? `${RUNTIME_PACKAGE_NAME} \u7684 npm ${RUNTIME_DIST_TAG}` : "npm dev";
|
|
12
|
+
var CLI_UPDATE_GUIDANCE = LANE_BUILD ? LANE_BUILD.distribution === "npm" ? `\u6CF3\u9053 npm \u6D4B\u8BD5\u5305\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0\u3001update \u548C setup\u3002\u624B\u52A8\u66F4\u65B0\u8BF7\u5728\u539F\u5B89\u88C5\u76EE\u5F55\u6267\u884C npm install --global --prefix <\u539F\u5B89\u88C5\u524D\u7F00> ${CLI_PACKAGE_NAME}@dev --registry=https://registry.npmjs.org/ --@my-life-buddies:registry=https://registry.npmjs.org/\uFF1B\u4E0D\u4F1A\u5207\u56DE\u539F CLI \u5305\u3002\u5F53\u524D\u4EC5\u652F\u6301 macOS\u3002` : "\u672C\u5730\u6CF3\u9053\u6D4B\u8BD5\u5236\u54C1\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0\u3001update \u548C setup\uFF1B\u66F4\u6362\u7248\u672C\u987B\u91CD\u65B0\u5B89\u88C5\u540C\u6CF3\u9053\u6D4B\u8BD5\u5236\u54C1\u3002" : "\u6B63\u5E38\u547D\u4EE4\u5148\u8054\u7F51\u81EA\u68C0\uFF0C\u65E7\u7248\u81EA\u52A8\u66F4\u65B0\u540E\u7EE7\u7EED\u6267\u884C\uFF1B\u5931\u8D25\u65F6\u505C\u6B62\u3002\u5E2E\u52A9\u3001--version \u548C dev stop \u79BB\u7EBF\u53EF\u7528\u3002";
|
|
13
|
+
function assertBuildManifest(manifest, platform = process.platform) {
|
|
14
|
+
if (!LANE_BUILD) {
|
|
15
|
+
if (manifest.buddyCliLane !== void 0) throw new Error("\u6CF3\u9053\u5236\u54C1\u4E0E\u6267\u884C\u5165\u53E3\u4E0D\u5339\u914D\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5\u5B8C\u6574\u6D4B\u8BD5\u5236\u54C1\u3002");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const actual = manifest.buddyCliLane;
|
|
19
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual) || Object.keys(actual).length !== Object.keys(LANE_BUILD).length || Object.entries(LANE_BUILD).some(([key, value]) => actual[key] !== value) || manifest.name !== CLI_PACKAGE_NAME || manifest.version !== LANE_BUILD.version || (LANE_BUILD.distribution === "local" ? manifest.private !== true : manifest.private !== void 0) || !manifest.bin || typeof manifest.bin !== "object" || Object.keys(manifest.bin).length !== 1 || manifest.bin[LANE_BUILD.command] !== "dist/bin/buddy.js") {
|
|
20
|
+
throw new Error("\u6CF3\u9053\u5236\u54C1\u6807\u8BC6\u3001\u7248\u672C\u6216\u5165\u53E3\u4E0D\u5339\u914D\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5\u5B8C\u6574\u6D4B\u8BD5\u5236\u54C1\uFF1B\u4E0D\u4F1A\u5207\u56DE\u516C\u5171 SIT\u3002");
|
|
21
|
+
}
|
|
22
|
+
if (LANE_BUILD.validationScope === "mac-config-only" && platform !== "darwin") {
|
|
23
|
+
throw new Error("\u6B64\u6CF3\u9053\u5236\u54C1\u4EC5\u652F\u6301 macOS\uFF08mac-config-only\uFF09\uFF1B\u7F3A\u5C11 Windows \u539F\u751F\u5236\u54C1\uFF0C\u5F3A\u5236\u5B89\u88C5\u4E5F\u4E0D\u80FD\u4F5C\u4E3A Windows \u5019\u9009\u8FD0\u884C\u3002");
|
|
24
|
+
}
|
|
25
|
+
if (LANE_BUILD.distribution === "npm" && LANE_BUILD.validationScope === "mac-config-only" && JSON.stringify(manifest.os) !== JSON.stringify(["darwin"])) throw new Error("macOS \u6CF3\u9053 npm \u5236\u54C1\u7684 os \u5FC5\u987B\u4EC5\u4E3A darwin\u3002");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// packages/cli-core/src/platform/npm.ts
|
|
29
|
+
import { execFile } from "node:child_process";
|
|
30
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
31
|
+
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
32
|
+
import { promisify } from "node:util";
|
|
33
|
+
|
|
34
|
+
// packages/cli-core/src/npm-registry.ts
|
|
35
|
+
var REGISTRY = "https://registry.npmjs.org/";
|
|
36
|
+
async function npmPackageManifest(name, selector, options = {}) {
|
|
37
|
+
options.signal?.throwIfAborted();
|
|
38
|
+
const timeout = AbortSignal.timeout(options.timeoutMs ?? 1e4);
|
|
39
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
40
|
+
const response = await (options.fetch ?? globalThis.fetch)(`${REGISTRY}${encodeURIComponent(name)}`, {
|
|
41
|
+
signal,
|
|
42
|
+
headers: { Accept: "application/json" },
|
|
43
|
+
redirect: "error"
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) throw new Error(`npm HTTP ${response.status}`);
|
|
46
|
+
const text = await response.text();
|
|
47
|
+
if (text.length > 8 * 1024 * 1024) throw new Error("npm \u5143\u6570\u636E\u8FC7\u5927");
|
|
48
|
+
const metadata = JSON.parse(text);
|
|
49
|
+
if (metadata?.name !== name) throw new Error("npm \u8FD4\u56DE\u7684\u5305\u540D\u4E0D\u5339\u914D");
|
|
50
|
+
const version2 = Object.hasOwn(metadata["dist-tags"] ?? {}, selector) ? metadata["dist-tags"][selector] : selector;
|
|
51
|
+
const manifest = typeof version2 === "string" && Object.hasOwn(metadata.versions ?? {}, version2) ? metadata.versions[version2] : void 0;
|
|
52
|
+
if (!manifest || manifest.name !== name || manifest.version !== version2) {
|
|
53
|
+
throw new Error(`npm ${selector} \u672A\u8FD4\u56DE\u6B64\u5305\u7684\u51C6\u786E\u7248\u672C`);
|
|
54
|
+
}
|
|
55
|
+
return manifest;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// packages/cli-core/src/platform/npm.ts
|
|
59
|
+
var execute = promisify(execFile);
|
|
60
|
+
async function resolveNpmEntry(options = {}) {
|
|
61
|
+
const node = options.execPath ?? process.execPath;
|
|
62
|
+
const env = options.env ?? process.env;
|
|
63
|
+
const platform = options.platform ?? process.platform;
|
|
64
|
+
const realNode = await realpath(node);
|
|
65
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
66
|
+
if (env.npm_execpath && env.npm_node_execpath && isAbsolute(env.npm_execpath)) {
|
|
67
|
+
try {
|
|
68
|
+
if (await realpath(env.npm_node_execpath) === realNode) {
|
|
69
|
+
candidates.add(join(dirname(await realpath(env.npm_execpath)), "npm-cli.js"));
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const bin of /* @__PURE__ */ new Set([dirname(node), dirname(realNode)])) {
|
|
75
|
+
candidates.add(join(bin, "node_modules", "npm", "bin", "npm-cli.js"));
|
|
76
|
+
candidates.add(resolve(bin, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"));
|
|
77
|
+
}
|
|
78
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path");
|
|
79
|
+
const paths = (env[pathKey ?? "PATH"] ?? "").split(platform === "win32" ? ";" : delimiter);
|
|
80
|
+
for (const directory of paths) {
|
|
81
|
+
if (!directory || !isAbsolute(directory)) continue;
|
|
82
|
+
try {
|
|
83
|
+
if (await realpath(join(directory, platform === "win32" ? "node.exe" : "node")) !== realNode) continue;
|
|
84
|
+
if (platform === "win32") candidates.add(join(directory, "node_modules", "npm", "bin", "npm-cli.js"));
|
|
85
|
+
else candidates.add(await realpath(join(directory, "npm")));
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const candidate of candidates) {
|
|
90
|
+
try {
|
|
91
|
+
const entry = await realpath(candidate);
|
|
92
|
+
const root = dirname(dirname(entry));
|
|
93
|
+
const manifest = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
|
|
94
|
+
if (manifest.name !== "npm" || manifest.bin?.npm !== "bin/npm-cli.js") continue;
|
|
95
|
+
if (await realpath(join(root, manifest.bin.npm)) !== entry || !(await stat(entry)).isFile()) continue;
|
|
96
|
+
return { node, entry };
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw new Error("\u627E\u4E0D\u5230\u4E0E\u5F53\u524D Node.js \u5B89\u88C5\u5339\u914D\u7684 npm \u5165\u53E3\u3002\u8BF7\u4FEE\u590D\u6216\u91CD\u65B0\u5B89\u88C5\u5F53\u524D Node.js\uFF0C\u518D\u91CD\u8BD5\uFF1B\u672A\u8C03\u7528\u5176\u4ED6 Node \u7684 npm\u3002");
|
|
101
|
+
}
|
|
102
|
+
async function runNpm(args, options) {
|
|
103
|
+
options.signal?.throwIfAborted();
|
|
104
|
+
const npm = await resolveNpmEntry({ env: options.env });
|
|
105
|
+
return execute(npm.node, [npm.entry, ...args], {
|
|
106
|
+
...options,
|
|
107
|
+
encoding: "utf8",
|
|
108
|
+
maxBuffer: options.maxBuffer ?? 4 * 1024 * 1024,
|
|
109
|
+
shell: false,
|
|
110
|
+
windowsHide: true
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// packages/cli-core/src/platform/support.ts
|
|
115
|
+
import { machine, release, version } from "node:os";
|
|
116
|
+
var UnsupportedDeveloperPlatformError = class extends Error {
|
|
117
|
+
constructor(platform, message) {
|
|
118
|
+
super(message ?? `\u642D\u642D CLI \u652F\u6301 macOS\u3001Windows 10 22H2 x64 \u548C Windows 11 x64/ARM64\uFF0C\u5F53\u524D\u7CFB\u7EDF\u662F ${platform}\u3002`);
|
|
119
|
+
this.platform = platform;
|
|
120
|
+
this.name = "UnsupportedDeveloperPlatformError";
|
|
121
|
+
}
|
|
122
|
+
platform;
|
|
123
|
+
};
|
|
124
|
+
function assertSupportedDeveloperPlatform(platform = process.platform, info = {}) {
|
|
125
|
+
const arch = info.arch ?? process.arch;
|
|
126
|
+
if (!["darwin", "win32"].includes(platform)) throw new UnsupportedDeveloperPlatformError(platform);
|
|
127
|
+
if (!["x64", "arm64"].includes(arch)) {
|
|
128
|
+
throw new UnsupportedDeveloperPlatformError(platform, `\u642D\u642D CLI \u9700\u8981 x64 \u6216 ARM64 Node.js\uFF0C\u5F53\u524D\u8FDB\u7A0B\u67B6\u6784\u662F ${arch}\u3002`);
|
|
129
|
+
}
|
|
130
|
+
const node = info.nodeVersion ?? process.versions.node;
|
|
131
|
+
if (!/^\d+\./u.test(node) || Number(node.split(".")[0]) < 24) {
|
|
132
|
+
throw new UnsupportedDeveloperPlatformError(platform, "\u642D\u642D CLI \u9700\u8981 Node.js 24 \u6216\u66F4\u9AD8\u7248\u672C\u3002");
|
|
133
|
+
}
|
|
134
|
+
if (platform === "darwin") return;
|
|
135
|
+
const osRelease = info.osRelease ?? release();
|
|
136
|
+
const osVersion = info.osVersion ?? version();
|
|
137
|
+
const [major, minor, build] = osRelease.split(".").map(Number);
|
|
138
|
+
if (major !== 10 || minor !== 0 || !Number.isSafeInteger(build) || build < 19045 || /Server|LTSC|LTSB/iu.test(osVersion)) {
|
|
139
|
+
throw new UnsupportedDeveloperPlatformError(platform, `\u642D\u642D CLI \u9700\u8981 Windows 10 22H2\uFF0819045\uFF09\u6216 Windows 11 \u684C\u9762\u7CFB\u7EDF\uFF0C\u5F53\u524D\u7248\u672C\u662F ${osRelease}\u3002`);
|
|
140
|
+
}
|
|
141
|
+
const host = (info.hostMachine ?? machine()).toLowerCase();
|
|
142
|
+
if (arch === "arm64" && build < 22e3 || /^(arm64|aarch64)$/u.test(host) && arch !== "arm64") {
|
|
143
|
+
throw new UnsupportedDeveloperPlatformError(platform, "Windows ARM64 \u9700\u8981 Windows 11 \u548C\u539F\u751F ARM64 Node.js\uFF0C\u8BF7\u5B89\u88C5\u5339\u914D\u7684 Node.js \u7248\u672C\u3002");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// packages/cli-core/src/platform/open.ts
|
|
148
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
149
|
+
import { posix, win32 as win322 } from "node:path";
|
|
150
|
+
|
|
151
|
+
// packages/cli-core/src/platform/windows.ts
|
|
152
|
+
import { spawn } from "node:child_process";
|
|
153
|
+
import { win32 } from "node:path";
|
|
154
|
+
var SCRIPT = String.raw`
|
|
155
|
+
$ErrorActionPreference = 'Stop'
|
|
156
|
+
[Console]::InputEncoding = New-Object System.Text.UTF8Encoding($false)
|
|
157
|
+
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
|
158
|
+
$sid = [Security.Principal.WindowsIdentity]::GetCurrent().User
|
|
159
|
+
function PathAttributes([string]$path) {
|
|
160
|
+
try { return [IO.File]::GetAttributes($path) }
|
|
161
|
+
catch [IO.FileNotFoundException] { return $null }
|
|
162
|
+
catch [IO.DirectoryNotFoundException] { return $null }
|
|
163
|
+
}
|
|
164
|
+
function SafePath([string]$path) {
|
|
165
|
+
if ($path -notmatch '^[A-Za-z]:[\\/]' -or $path -match '[\x00-\x1f\x7f]' -or $path.Substring(2).Contains(':')) { throw 'INVALID_PATH' }
|
|
166
|
+
$full = [IO.Path]::GetFullPath($path)
|
|
167
|
+
$drive = New-Object IO.DriveInfo([IO.Path]::GetPathRoot($full))
|
|
168
|
+
if ($drive.DriveType -eq [IO.DriveType]::Network) { throw 'NETWORK_PATH' }
|
|
169
|
+
$part = $full
|
|
170
|
+
while ($part) {
|
|
171
|
+
$attributes = PathAttributes $part
|
|
172
|
+
if ($null -ne $attributes -and ($attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw 'LINK_PATH' }
|
|
173
|
+
if ($part -eq [IO.Path]::GetPathRoot($part)) { break }
|
|
174
|
+
$parent = [IO.Path]::GetDirectoryName($part.TrimEnd('\'))
|
|
175
|
+
if ($parent -eq $part) { break }; $part = $parent
|
|
176
|
+
}
|
|
177
|
+
return $full
|
|
178
|
+
}
|
|
179
|
+
function PrivateAcl([bool]$directory) {
|
|
180
|
+
if ($directory) { $acl = New-Object Security.AccessControl.DirectorySecurity }
|
|
181
|
+
else { $acl = New-Object Security.AccessControl.FileSecurity }
|
|
182
|
+
$acl.SetOwner($sid); $acl.SetAccessRuleProtection($true, $false)
|
|
183
|
+
if ($directory) {
|
|
184
|
+
$rule = New-Object Security.AccessControl.FileSystemAccessRule($sid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
|
|
185
|
+
} else { $rule = New-Object Security.AccessControl.FileSystemAccessRule($sid, 'FullControl', 'Allow') }
|
|
186
|
+
$acl.AddAccessRule($rule); return $acl
|
|
187
|
+
}
|
|
188
|
+
function CheckOwner([string]$path) {
|
|
189
|
+
$acl = Get-Acl -LiteralPath $path
|
|
190
|
+
if ($acl.GetOwner([Security.Principal.SecurityIdentifier]).Value -ne $sid.Value) { throw 'FOREIGN_OWNER' }
|
|
191
|
+
return $acl
|
|
192
|
+
}
|
|
193
|
+
function CheckFileLinks([string]$path) {
|
|
194
|
+
$attributes = PathAttributes $path
|
|
195
|
+
if ($null -eq $attributes -or ($attributes -band [IO.FileAttributes]::Directory) -ne 0) { return }
|
|
196
|
+
if (!('BuddyPrivateFileInfo' -as [type])) {
|
|
197
|
+
Add-Type -TypeDefinition @'
|
|
198
|
+
using System;
|
|
199
|
+
using System.IO;
|
|
200
|
+
using System.Runtime.InteropServices;
|
|
201
|
+
using Microsoft.Win32.SafeHandles;
|
|
202
|
+
public static class BuddyPrivateFileInfo {
|
|
203
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
204
|
+
private struct Info {
|
|
205
|
+
public uint Attributes;
|
|
206
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME Created, Accessed, Written;
|
|
207
|
+
public uint Volume, SizeHigh, SizeLow, Links, IndexHigh, IndexLow;
|
|
208
|
+
}
|
|
209
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
210
|
+
private static extern bool GetFileInformationByHandle(SafeFileHandle file, out Info info);
|
|
211
|
+
public static uint Links(string path) {
|
|
212
|
+
using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) {
|
|
213
|
+
Info info;
|
|
214
|
+
if (!GetFileInformationByHandle(file.SafeFileHandle, out info)) throw new IOException("FILE_INFO_FAILED");
|
|
215
|
+
return info.Links;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
'@
|
|
220
|
+
}
|
|
221
|
+
if ([BuddyPrivateFileInfo]::Links($path) -ne 1) { throw 'LINK_PATH' }
|
|
222
|
+
}
|
|
223
|
+
function CheckPrivate([string]$path) {
|
|
224
|
+
$path = SafePath $path
|
|
225
|
+
if ($null -eq (PathAttributes $path)) { throw 'ENOENT' }
|
|
226
|
+
CheckFileLinks $path
|
|
227
|
+
$acl = CheckOwner $path
|
|
228
|
+
if (!$acl.AreAccessRulesProtected) { throw 'UNSAFE_ACL' }
|
|
229
|
+
$allowed = $false
|
|
230
|
+
foreach ($rule in $acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier])) {
|
|
231
|
+
if ($rule.AccessControlType -eq 'Allow') {
|
|
232
|
+
if ($rule.IdentityReference.Value -ne $sid.Value) { throw 'UNSAFE_ACL' }
|
|
233
|
+
if (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl) { $allowed = $true }
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (!$allowed) { throw 'UNSAFE_ACL' }
|
|
237
|
+
}
|
|
238
|
+
function PrivateDirectory([string]$path) {
|
|
239
|
+
$path = SafePath $path
|
|
240
|
+
$attributes = PathAttributes $path
|
|
241
|
+
if ($null -ne $attributes) {
|
|
242
|
+
if (($attributes -band [IO.FileAttributes]::Directory) -eq 0) { throw 'INVALID_PATH' }
|
|
243
|
+
$null = CheckOwner $path
|
|
244
|
+
[IO.Directory]::SetAccessControl($path, (PrivateAcl $true))
|
|
245
|
+
} else {
|
|
246
|
+
$parent = [IO.Path]::GetDirectoryName($path)
|
|
247
|
+
if ($null -eq (PathAttributes $parent)) { PrivateDirectory $parent }
|
|
248
|
+
$null = [IO.Directory]::CreateDirectory($path, (PrivateAcl $true))
|
|
249
|
+
}
|
|
250
|
+
CheckPrivate $path
|
|
251
|
+
}
|
|
252
|
+
function AtomicPrivateWrite([string]$path, [byte[]]$bytes) {
|
|
253
|
+
$path = SafePath $path
|
|
254
|
+
$attributes = PathAttributes $path
|
|
255
|
+
if ($null -ne $attributes) {
|
|
256
|
+
if (($attributes -band [IO.FileAttributes]::Directory) -ne 0) { throw 'INVALID_PATH' }
|
|
257
|
+
$null = CheckOwner $path; CheckFileLinks $path
|
|
258
|
+
}
|
|
259
|
+
$temporary = $path + '.' + [Guid]::NewGuid().ToString('N') + '.tmp'
|
|
260
|
+
$stream = $null
|
|
261
|
+
try {
|
|
262
|
+
$stream = New-Object IO.FileStream($temporary, [IO.FileMode]::CreateNew, [Security.AccessControl.FileSystemRights]::Write, [IO.FileShare]::None, 4096, [IO.FileOptions]::WriteThrough, (PrivateAcl $false))
|
|
263
|
+
$stream.Write($bytes, 0, $bytes.Length); $stream.Flush($true); $stream.Dispose(); $stream = $null
|
|
264
|
+
$null = SafePath $path
|
|
265
|
+
if ($null -ne (PathAttributes $path)) {
|
|
266
|
+
$null = CheckOwner $path; CheckFileLinks $path
|
|
267
|
+
# Replace preserves the destination ACL, so secure it before replacement.
|
|
268
|
+
[IO.File]::SetAccessControl($path, (PrivateAcl $false))
|
|
269
|
+
[IO.File]::Replace($temporary, $path, $null)
|
|
270
|
+
} else { [IO.File]::Move($temporary, $path) }
|
|
271
|
+
CheckPrivate $path
|
|
272
|
+
} finally {
|
|
273
|
+
if ($stream) { $stream.Dispose() }
|
|
274
|
+
[IO.File]::Delete($temporary)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function Credential($request) {
|
|
278
|
+
if ([string]$request.scope -cnotmatch '^[a-f0-9]{64}$') { throw 'INVALID_SCOPE' }
|
|
279
|
+
$directory = SafePath $request.directory
|
|
280
|
+
$application = [IO.Path]::GetDirectoryName($directory)
|
|
281
|
+
$path = Join-Path $directory ($request.scope + '.json')
|
|
282
|
+
$null = SafePath $path
|
|
283
|
+
if ($request.operation -eq 'credential-save') { PrivateDirectory $application; PrivateDirectory $directory }
|
|
284
|
+
elseif ($null -eq (PathAttributes $directory)) { return $null }
|
|
285
|
+
if (((PathAttributes $directory) -band [IO.FileAttributes]::Directory) -eq 0) { throw 'INVALID_PATH' }
|
|
286
|
+
CheckPrivate $directory
|
|
287
|
+
CheckPrivate $application
|
|
288
|
+
$attributes = PathAttributes $path
|
|
289
|
+
if ($null -ne $attributes -and ($attributes -band [IO.FileAttributes]::Directory) -ne 0) { throw 'INVALID_PATH' }
|
|
290
|
+
if ($request.operation -eq 'credential-clear') {
|
|
291
|
+
if ($null -ne $attributes) { CheckPrivate $path; [IO.File]::Delete($path) }
|
|
292
|
+
return $null
|
|
293
|
+
}
|
|
294
|
+
Add-Type -AssemblyName System.Security
|
|
295
|
+
$entropy = [Convert]::FromBase64String($request.entropy)
|
|
296
|
+
if ($request.operation -eq 'credential-save') {
|
|
297
|
+
$plain = [Convert]::FromBase64String($request.data)
|
|
298
|
+
if ($plain.Length -eq 0 -or $plain.Length -gt 32768) { throw 'CREDENTIAL_CORRUPT' }
|
|
299
|
+
$encrypted = [Security.Cryptography.ProtectedData]::Protect($plain, $entropy, [Security.Cryptography.DataProtectionScope]::CurrentUser)
|
|
300
|
+
$record = @{version=1; scope=$request.scope; ciphertext=[Convert]::ToBase64String($encrypted)} | ConvertTo-Json -Compress
|
|
301
|
+
AtomicPrivateWrite $path ([Text.Encoding]::UTF8.GetBytes($record))
|
|
302
|
+
return $null
|
|
303
|
+
}
|
|
304
|
+
if ($null -eq $attributes) { return $null }
|
|
305
|
+
CheckPrivate $path
|
|
306
|
+
if ((Get-Item -LiteralPath $path).Length -gt 65536) { throw 'CREDENTIAL_CORRUPT' }
|
|
307
|
+
try {
|
|
308
|
+
$record = (New-Object Text.UTF8Encoding($false, $true)).GetString([IO.File]::ReadAllBytes($path)) | ConvertFrom-Json
|
|
309
|
+
if ($record.version -ne 1 -or $record.scope -cne $request.scope -or !($record.ciphertext -is [string])) { throw 'CREDENTIAL_CORRUPT' }
|
|
310
|
+
$plain = [Security.Cryptography.ProtectedData]::Unprotect([Convert]::FromBase64String($record.ciphertext), $entropy, [Security.Cryptography.DataProtectionScope]::CurrentUser)
|
|
311
|
+
if ($plain.Length -eq 0 -or $plain.Length -gt 32768) { throw 'CREDENTIAL_CORRUPT' }
|
|
312
|
+
return [Convert]::ToBase64String($plain)
|
|
313
|
+
} catch { throw 'CREDENTIAL_CORRUPT' }
|
|
314
|
+
}
|
|
315
|
+
function PrependUserPath([string]$old, [string]$path) {
|
|
316
|
+
$target = $path.TrimEnd('\')
|
|
317
|
+
$entries = @($old -split ';' | Where-Object { $_ -and $_.Trim().Trim('"').TrimEnd('\', '/').Replace('/', '\') -ine $target })
|
|
318
|
+
return ((@($path) + $entries) -join ';')
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
$request = [Console]::In.ReadToEnd() | ConvertFrom-Json
|
|
322
|
+
$value = $null
|
|
323
|
+
switch ($request.operation) {
|
|
324
|
+
'private-directory' { PrivateDirectory $request.path }
|
|
325
|
+
'private-check' { CheckPrivate $request.path }
|
|
326
|
+
'safe-path' { $null = SafePath $request.path }
|
|
327
|
+
'private-write' { AtomicPrivateWrite $request.path ([Convert]::FromBase64String($request.data)) }
|
|
328
|
+
'credential-save' { $value = Credential $request }
|
|
329
|
+
'credential-load' { $value = Credential $request }
|
|
330
|
+
'credential-clear' { $value = Credential $request }
|
|
331
|
+
'open' {
|
|
332
|
+
$start = New-Object Diagnostics.ProcessStartInfo
|
|
333
|
+
$start.FileName = [string]$request.target; $start.UseShellExecute = $true
|
|
334
|
+
$null = [Diagnostics.Process]::Start($start)
|
|
335
|
+
}
|
|
336
|
+
'user-path' {
|
|
337
|
+
$path = SafePath $request.path
|
|
338
|
+
if ($path.Contains(';')) { throw 'INVALID_PATH' }
|
|
339
|
+
$old = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
340
|
+
[Environment]::SetEnvironmentVariable('Path', (PrependUserPath $old $path), 'User')
|
|
341
|
+
try {
|
|
342
|
+
Add-Type -TypeDefinition @'
|
|
343
|
+
using System;
|
|
344
|
+
using System.Runtime.InteropServices;
|
|
345
|
+
public static class BuddyEnvironmentNotify {
|
|
346
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
347
|
+
public static extern IntPtr SendMessageTimeout(IntPtr window, uint message, UIntPtr wParam, string lParam, uint flags, uint timeout, out UIntPtr result);
|
|
348
|
+
}
|
|
349
|
+
'@
|
|
350
|
+
$broadcastResult = [UIntPtr]::Zero
|
|
351
|
+
$notified = [BuddyEnvironmentNotify]::SendMessageTimeout([IntPtr]65535, 26, [UIntPtr]::Zero, 'Environment', 34, 1000, [ref]$broadcastResult)
|
|
352
|
+
if ($notified -eq [IntPtr]::Zero) { throw 'ENVIRONMENT_BROADCAST_FAILED' }
|
|
353
|
+
} catch { throw 'ENVIRONMENT_BROADCAST_FAILED' }
|
|
354
|
+
}
|
|
355
|
+
default { throw 'INVALID_OPERATION' }
|
|
356
|
+
}
|
|
357
|
+
[Console]::Out.Write((@{ok=$true; value=$value} | ConvertTo-Json -Compress))
|
|
358
|
+
} catch {
|
|
359
|
+
$code = if ($_.Exception.Message -eq 'ENOENT') { 'ENOENT' } elseif ($_.Exception.Message -eq 'CREDENTIAL_CORRUPT') { 'EBADMSG' } elseif ($_.Exception.Message -eq 'ENVIRONMENT_BROADCAST_FAILED') { 'ERELOGIN' } else { 'EPERM' }
|
|
360
|
+
# Exception text can contain input. Never send it to logs or the terminal.
|
|
361
|
+
[Console]::Out.Write((@{ok=$false; code=$code} | ConvertTo-Json -Compress)); exit 1
|
|
362
|
+
}
|
|
363
|
+
`;
|
|
364
|
+
function windowsCommand(request, env = process.env) {
|
|
365
|
+
if (request.operation === "user-path" && (typeof request.path !== "string" || request.path.includes(";"))) {
|
|
366
|
+
throw new Error("Windows PATH \u76EE\u5F55\u4E0D\u80FD\u5305\u542B\u5206\u53F7\uFF0C\u8BF7\u9009\u62E9\u6709\u6548\u7684\u672C\u5730\u5B89\u88C5\u76EE\u5F55\u3002");
|
|
367
|
+
}
|
|
368
|
+
const systemRoot = env.SystemRoot ?? env.SYSTEMROOT;
|
|
369
|
+
if (!systemRoot || !/^[A-Za-z]:[\\/]/u.test(systemRoot)) throw new Error("\u65E0\u6CD5\u5B9A\u4F4D Windows PowerShell\uFF0C\u8BF7\u68C0\u67E5 SystemRoot\u3002");
|
|
370
|
+
const command = {
|
|
371
|
+
command: win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
|
|
372
|
+
args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(SCRIPT, "utf16le").toString("base64")],
|
|
373
|
+
input: JSON.stringify(request)
|
|
374
|
+
};
|
|
375
|
+
const commandLineBound = 1 + [command.command, ...command.args].reduce((size, part) => size + part.length + (part.match(/[\\"]/gu)?.length ?? 0) + 3, 0);
|
|
376
|
+
if (commandLineBound > 3e4) throw new Error("Windows \u7CFB\u7EDF\u64CD\u4F5C\u547D\u4EE4\u8D85\u51FA\u5B89\u5168\u957F\u5EA6\uFF0C\u8BF7\u66F4\u65B0\u6216\u4FEE\u590D CLI \u5B89\u88C5\u3002");
|
|
377
|
+
return command;
|
|
378
|
+
}
|
|
379
|
+
async function runWindowsCommand(command) {
|
|
380
|
+
return await new Promise((resolve2, reject) => {
|
|
381
|
+
const child = spawn(command.command, [...command.args], { shell: false, windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
382
|
+
const output = [];
|
|
383
|
+
let size = 0;
|
|
384
|
+
let failure;
|
|
385
|
+
const timer = setTimeout(() => {
|
|
386
|
+
failure = new Error("Windows \u7CFB\u7EDF\u64CD\u4F5C\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5 PowerShell \u662F\u5426\u88AB\u7CFB\u7EDF\u7B56\u7565\u963B\u6B62\u3002");
|
|
387
|
+
child.kill();
|
|
388
|
+
}, 15e3);
|
|
389
|
+
child.stdout.on("data", (chunk) => {
|
|
390
|
+
size += chunk.length;
|
|
391
|
+
if (size > 2 * 1024 * 1024) {
|
|
392
|
+
failure = new Error("Windows \u7CFB\u7EDF\u64CD\u4F5C\u8FD4\u56DE\u8FC7\u591A\u6570\u636E\u3002");
|
|
393
|
+
child.kill();
|
|
394
|
+
} else output.push(chunk);
|
|
395
|
+
});
|
|
396
|
+
child.stderr.resume();
|
|
397
|
+
child.stdin.on("error", () => void 0);
|
|
398
|
+
child.once("error", () => {
|
|
399
|
+
clearTimeout(timer);
|
|
400
|
+
reject(new Error("\u65E0\u6CD5\u8FD0\u884C Windows PowerShell\uFF0C\u8BF7\u68C0\u67E5\u7CFB\u7EDF\u7B56\u7565\u3002"));
|
|
401
|
+
});
|
|
402
|
+
child.once("close", (code) => {
|
|
403
|
+
clearTimeout(timer);
|
|
404
|
+
if (failure) reject(failure);
|
|
405
|
+
else resolve2({ code: code ?? 1, stdout: Buffer.concat(output).toString("utf8"), stderr: "" });
|
|
406
|
+
});
|
|
407
|
+
child.stdin.end(command.input, "utf8");
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
async function windowsOperation(request, options = {}) {
|
|
411
|
+
const command = windowsCommand(request, options.env);
|
|
412
|
+
let result;
|
|
413
|
+
try {
|
|
414
|
+
result = await (options.runner ?? runWindowsCommand)(command);
|
|
415
|
+
} catch (cause) {
|
|
416
|
+
if (request.operation === "user-path") throw new Error("\u65E0\u6CD5\u786E\u8BA4\u7528\u6237 PATH \u66F4\u65B0\u4E0E\u684C\u9762\u901A\u77E5\u662F\u5426\u5B8C\u6210\uFF1B\u8BF7\u91CD\u65B0\u767B\u5F55 Windows \u540E\u68C0\u67E5 CLI\uFF0C\u4ECD\u4E0D\u53EF\u7528\u65F6\u91CD\u65B0\u8FD0\u884C setup\u3002");
|
|
417
|
+
throw cause;
|
|
418
|
+
}
|
|
419
|
+
let response;
|
|
420
|
+
try {
|
|
421
|
+
response = JSON.parse(result.stdout.replace(/^\uFEFF/u, ""));
|
|
422
|
+
} catch {
|
|
423
|
+
response = {};
|
|
424
|
+
}
|
|
425
|
+
if (result.code !== 0 || response.ok !== true) {
|
|
426
|
+
if (response.code === "ERELOGIN") throw Object.assign(new Error("\u7528\u6237 PATH \u5DF2\u4FDD\u5B58\uFF0C\u4F46\u672A\u80FD\u901A\u77E5 Windows \u684C\u9762\uFF1B\u8BF7\u91CD\u65B0\u767B\u5F55 Windows \u540E\u4F7F\u7528 CLI\u3002"), { code: "ERELOGIN" });
|
|
427
|
+
throw Object.assign(new Error("Windows \u5B89\u5168\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D\u7528\u6237\u7684\u6587\u4EF6\u6743\u9650\u548C PowerShell \u7CFB\u7EDF\u7B56\u7565\u3002"), { code: response.code === "ENOENT" || response.code === "EBADMSG" ? response.code : "EPERM" });
|
|
428
|
+
}
|
|
429
|
+
return response.value;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// packages/cli-core/src/platform/open.ts
|
|
433
|
+
function externalTarget(target, platform) {
|
|
434
|
+
const value = target instanceof URL ? target.href : target;
|
|
435
|
+
if (!value || /[\u0000-\u001f\u007f]/u.test(value)) throw new TypeError("\u53EA\u80FD\u6253\u5F00 HTTP(S) \u5730\u5740\u6216\u672C\u5730\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\u3002");
|
|
436
|
+
if (platform === "win32" ? /^[A-Za-z]:[\\/]/u.test(value) && win322.isAbsolute(value) : value.startsWith("/") && !value.startsWith("//") && posix.isAbsolute(value)) return value;
|
|
437
|
+
let url;
|
|
438
|
+
try {
|
|
439
|
+
url = new URL(value);
|
|
440
|
+
} catch {
|
|
441
|
+
throw new TypeError("\u53EA\u80FD\u6253\u5F00 HTTP(S) \u5730\u5740\u6216\u672C\u5730\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\u3002");
|
|
442
|
+
}
|
|
443
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new TypeError("\u53EA\u80FD\u6253\u5F00 HTTP(S) \u5730\u5740\u6216\u672C\u5730\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\u3002");
|
|
444
|
+
return url.href;
|
|
445
|
+
}
|
|
446
|
+
async function runMacOpen(command) {
|
|
447
|
+
return await new Promise((resolve2, reject) => {
|
|
448
|
+
const child = spawn2(command.command, [...command.args], { shell: false, stdio: "ignore" });
|
|
449
|
+
child.once("error", reject);
|
|
450
|
+
child.once("close", (code) => resolve2({ code: code ?? 1, stdout: "", stderr: "" }));
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
async function openExternalTarget(target, options = {}) {
|
|
454
|
+
const platform = options.platform ?? process.platform;
|
|
455
|
+
if (platform !== "darwin" && platform !== "win32") throw new Error("\u6253\u5F00\u6D4F\u89C8\u5668\u6216\u6587\u4EF6\u4EC5\u652F\u6301 macOS \u548C Windows\u3002");
|
|
456
|
+
const value = externalTarget(target, platform);
|
|
457
|
+
if (platform === "win32") {
|
|
458
|
+
await windowsOperation({ operation: "open", target: value }, { runner: options.runner, env: options.env });
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const result = await (options.runner ?? runMacOpen)({ command: "open", args: [value] });
|
|
462
|
+
if (result.code !== 0) throw new Error("\u65E0\u6CD5\u6253\u5F00\u6D4F\u89C8\u5668\u6216\u6587\u4EF6\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u7EC8\u7AEF\u4E2D\u663E\u793A\u7684\u5730\u5740\u6216\u8DEF\u5F84\u3002");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// packages/cli-core/src/platform/private-files.ts
|
|
466
|
+
import { constants } from "node:fs";
|
|
467
|
+
import { chmod, lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
468
|
+
import { randomUUID } from "node:crypto";
|
|
469
|
+
import { isAbsolute as isAbsolute2 } from "node:path";
|
|
470
|
+
async function assertUnlinkedPath(path) {
|
|
471
|
+
if (!isAbsolute2(path)) throw new Error("\u79C1\u5BC6\u6587\u4EF6\u5FC5\u987B\u4F7F\u7528\u7EDD\u5BF9\u8DEF\u5F84\u3002");
|
|
472
|
+
if (process.platform === "win32") await windowsOperation({ operation: "safe-path", path });
|
|
473
|
+
const stat2 = await lstat(path).catch((cause) => {
|
|
474
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
475
|
+
return void 0;
|
|
476
|
+
});
|
|
477
|
+
if (stat2 && (stat2.isSymbolicLink() || stat2.isFile() && stat2.nlink !== 1)) throw new Error("\u79C1\u5BC6\u6587\u4EF6\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5\u3001\u76EE\u5F55\u8054\u63A5\u6216\u786C\u94FE\u63A5\u3002");
|
|
478
|
+
}
|
|
479
|
+
async function assertPrivatePath(path) {
|
|
480
|
+
await assertUnlinkedPath(path);
|
|
481
|
+
const stat2 = await lstat(path);
|
|
482
|
+
if (!stat2.isFile() && !stat2.isDirectory()) throw new Error("\u79C1\u5BC6\u8DEF\u5F84\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\u6216\u76EE\u5F55\u3002");
|
|
483
|
+
if (process.platform === "win32") await windowsOperation({ operation: "private-check", path });
|
|
484
|
+
else if (stat2.uid !== process.getuid?.() || (stat2.mode & 63) !== 0) throw new Error("\u79C1\u5BC6\u8DEF\u5F84\u5FC5\u987B\u5F52\u5F53\u524D\u7528\u6237\u6240\u6709\uFF0C\u4E14\u4EC5\u5F53\u524D\u7528\u6237\u53EF\u8BBF\u95EE\u3002");
|
|
485
|
+
}
|
|
486
|
+
async function ensurePrivateDirectory(path) {
|
|
487
|
+
await assertUnlinkedPath(path);
|
|
488
|
+
if (process.platform === "win32") await windowsOperation({ operation: "private-directory", path });
|
|
489
|
+
else {
|
|
490
|
+
await mkdir(path, { recursive: true, mode: 448 });
|
|
491
|
+
const stat2 = await lstat(path);
|
|
492
|
+
if (!stat2.isDirectory() || stat2.uid !== process.getuid?.()) throw new Error("\u79C1\u5BC6\u76EE\u5F55\u4E0D\u5C5E\u4E8E\u5F53\u524D\u7528\u6237\u3002");
|
|
493
|
+
await chmod(path, 448);
|
|
494
|
+
}
|
|
495
|
+
await assertPrivatePath(path);
|
|
496
|
+
}
|
|
497
|
+
async function writePrivateFile(path, data) {
|
|
498
|
+
await assertUnlinkedPath(path);
|
|
499
|
+
if (process.platform === "win32") {
|
|
500
|
+
await windowsOperation({ operation: "private-write", path, data: Buffer.from(data).toString("base64") });
|
|
501
|
+
} else {
|
|
502
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
503
|
+
let file;
|
|
504
|
+
try {
|
|
505
|
+
file = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
|
|
506
|
+
await file.writeFile(data);
|
|
507
|
+
await file.sync();
|
|
508
|
+
await file.close();
|
|
509
|
+
file = void 0;
|
|
510
|
+
await assertUnlinkedPath(path);
|
|
511
|
+
await rename(temporary, path);
|
|
512
|
+
} finally {
|
|
513
|
+
await file?.close();
|
|
514
|
+
await unlink(temporary).catch((cause) => {
|
|
515
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
await assertPrivatePath(path);
|
|
520
|
+
}
|
|
521
|
+
export {
|
|
522
|
+
CLI_COMMAND_NAME,
|
|
523
|
+
CLI_PACKAGE_NAME,
|
|
524
|
+
CLI_UPDATE_GUIDANCE,
|
|
525
|
+
LANE_BUILD,
|
|
526
|
+
PLATFORM_ORIGIN,
|
|
527
|
+
RUNTIME_DIST_TAG,
|
|
528
|
+
RUNTIME_PACKAGE_NAME,
|
|
529
|
+
RUNTIME_SOURCE_LABEL,
|
|
530
|
+
UnsupportedDeveloperPlatformError,
|
|
531
|
+
assertBuildManifest,
|
|
532
|
+
assertPrivatePath,
|
|
533
|
+
assertSupportedDeveloperPlatform,
|
|
534
|
+
assertUnlinkedPath,
|
|
535
|
+
ensurePrivateDirectory,
|
|
536
|
+
npmPackageManifest,
|
|
537
|
+
openExternalTarget,
|
|
538
|
+
resolveNpmEntry,
|
|
539
|
+
runNpm,
|
|
540
|
+
windowsOperation,
|
|
541
|
+
writePrivateFile
|
|
542
|
+
};
|
|
543
|
+
//# sourceMappingURL=platform.js.map
|