shortcutxl 0.3.57 → 0.3.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/cli.js +189 -91
- package/dist/main.js +6 -0
- package/dist/startup/python-abi.d.ts +3 -0
- package/dist/startup/python-abi.js +32 -0
- package/dist/startup/sync-xll.js +2 -27
- package/dist/startup/update-action.d.ts +7 -3
- package/dist/startup/update-action.js +78 -6
- package/package.json +1 -1
- package/user-docs/dist/shortcutxl-docs.pdf +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.58]
|
|
4
|
+
|
|
5
|
+
- **Improved update reliability** - ShortcutXL now shows clearer update errors when Windows blocks a global package upgrade.
|
|
6
|
+
|
|
3
7
|
## [0.3.57]
|
|
4
8
|
|
|
5
9
|
- **Improved Windows reliability** - Improved ShortcutXL install, upgrade, and uninstall behavior on Windows.
|
package/dist/cli.js
CHANGED
|
@@ -26528,7 +26528,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
26528
26528
|
"../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
26529
26529
|
"use strict";
|
|
26530
26530
|
var { writeFile: writeFile10, readFile: readFile11, mkdir: mkdir11 } = __require("node:fs/promises");
|
|
26531
|
-
var { dirname:
|
|
26531
|
+
var { dirname: dirname38, resolve: resolve22 } = __require("node:path");
|
|
26532
26532
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
|
|
26533
26533
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
26534
26534
|
var { hashId, isUrlExcludedFactory, normalizeHeaders: normalizeHeaders2, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -26759,7 +26759,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
26759
26759
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
26760
26760
|
}
|
|
26761
26761
|
const resolvedPath = resolve22(path21);
|
|
26762
|
-
await mkdir11(
|
|
26762
|
+
await mkdir11(dirname38(resolvedPath), { recursive: true });
|
|
26763
26763
|
const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
|
|
26764
26764
|
hash: hash2,
|
|
26765
26765
|
snapshot
|
|
@@ -474819,13 +474819,46 @@ var init_shell = __esm({
|
|
|
474819
474819
|
}
|
|
474820
474820
|
});
|
|
474821
474821
|
|
|
474822
|
+
// src/startup/python-abi.ts
|
|
474823
|
+
import { existsSync as existsSync42, readdirSync as readdirSync12 } from "fs";
|
|
474824
|
+
function listVersionedPythonRuntimeFiles(pythonDir) {
|
|
474825
|
+
if (!existsSync42(pythonDir)) return /* @__PURE__ */ new Set();
|
|
474826
|
+
try {
|
|
474827
|
+
return new Set(
|
|
474828
|
+
readdirSync12(pythonDir).filter((entry) => VERSIONED_PYTHON_RUNTIME_FILE.test(entry)).map((entry) => entry.toLowerCase())
|
|
474829
|
+
);
|
|
474830
|
+
} catch {
|
|
474831
|
+
return /* @__PURE__ */ new Set();
|
|
474832
|
+
}
|
|
474833
|
+
}
|
|
474834
|
+
function hasVersionedPythonRuntimeFileDrift(sourcePythonDir, destPythonDir) {
|
|
474835
|
+
const sourceFiles = listVersionedPythonRuntimeFiles(sourcePythonDir);
|
|
474836
|
+
if (sourceFiles.size === 0) return false;
|
|
474837
|
+
const destFiles = listVersionedPythonRuntimeFiles(destPythonDir);
|
|
474838
|
+
if (destFiles.size === 0) return false;
|
|
474839
|
+
for (const file2 of destFiles) {
|
|
474840
|
+
if (!sourceFiles.has(file2)) return true;
|
|
474841
|
+
}
|
|
474842
|
+
for (const file2 of sourceFiles) {
|
|
474843
|
+
if (!destFiles.has(file2)) return true;
|
|
474844
|
+
}
|
|
474845
|
+
return false;
|
|
474846
|
+
}
|
|
474847
|
+
var VERSIONED_PYTHON_RUNTIME_FILE;
|
|
474848
|
+
var init_python_abi = __esm({
|
|
474849
|
+
"src/startup/python-abi.ts"() {
|
|
474850
|
+
"use strict";
|
|
474851
|
+
VERSIONED_PYTHON_RUNTIME_FILE = /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i;
|
|
474852
|
+
}
|
|
474853
|
+
});
|
|
474854
|
+
|
|
474822
474855
|
// src/startup/sync-xll.ts
|
|
474823
474856
|
import { createHash as createHash3 } from "crypto";
|
|
474824
474857
|
import {
|
|
474825
474858
|
copyFileSync,
|
|
474826
|
-
existsSync as
|
|
474859
|
+
existsSync as existsSync43,
|
|
474827
474860
|
mkdirSync as mkdirSync20,
|
|
474828
|
-
readdirSync as
|
|
474861
|
+
readdirSync as readdirSync13,
|
|
474829
474862
|
readFileSync as readFileSync33,
|
|
474830
474863
|
rmSync as rmSync4,
|
|
474831
474864
|
statSync as statSync11,
|
|
@@ -474841,7 +474874,7 @@ function stripMotw(filePath) {
|
|
|
474841
474874
|
}
|
|
474842
474875
|
}
|
|
474843
474876
|
function filesMatch(src, dest) {
|
|
474844
|
-
if (!
|
|
474877
|
+
if (!existsSync43(dest)) return false;
|
|
474845
474878
|
const srcStat = statSync11(src);
|
|
474846
474879
|
const destStat = statSync11(dest);
|
|
474847
474880
|
return srcStat.size === destStat.size && srcStat.mtimeMs === destStat.mtimeMs;
|
|
@@ -474851,11 +474884,11 @@ function filesContentMatch(src, dest) {
|
|
|
474851
474884
|
return hash2(src) === hash2(dest);
|
|
474852
474885
|
}
|
|
474853
474886
|
function syncDir(src, dest) {
|
|
474854
|
-
if (!
|
|
474887
|
+
if (!existsSync43(src)) return { updated: 0, staleLocked: false };
|
|
474855
474888
|
mkdirSync20(dest, { recursive: true });
|
|
474856
474889
|
let updated = 0;
|
|
474857
474890
|
let staleLocked = false;
|
|
474858
|
-
for (const entry of
|
|
474891
|
+
for (const entry of readdirSync13(src)) {
|
|
474859
474892
|
const srcPath = join58(src, entry);
|
|
474860
474893
|
const destPath = join58(dest, entry);
|
|
474861
474894
|
const stat8 = statSync11(srcPath);
|
|
@@ -474886,28 +474919,8 @@ function syncDir(src, dest) {
|
|
|
474886
474919
|
}
|
|
474887
474920
|
return { updated, staleLocked };
|
|
474888
474921
|
}
|
|
474889
|
-
function listVersionedPythonRuntimeFiles(pythonDir) {
|
|
474890
|
-
if (!existsSync42(pythonDir)) return /* @__PURE__ */ new Set();
|
|
474891
|
-
try {
|
|
474892
|
-
return new Set(
|
|
474893
|
-
readdirSync12(pythonDir).filter((entry) => /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i.test(entry)).map((entry) => entry.toLowerCase())
|
|
474894
|
-
);
|
|
474895
|
-
} catch {
|
|
474896
|
-
return /* @__PURE__ */ new Set();
|
|
474897
|
-
}
|
|
474898
|
-
}
|
|
474899
474922
|
function hasPythonAbiDrift(shippedDir, stableDir) {
|
|
474900
|
-
|
|
474901
|
-
if (shippedRuntimeFiles.size === 0) return false;
|
|
474902
|
-
const stableRuntimeFiles = listVersionedPythonRuntimeFiles(join58(stableDir, "python"));
|
|
474903
|
-
if (stableRuntimeFiles.size === 0) return false;
|
|
474904
|
-
for (const file2 of stableRuntimeFiles) {
|
|
474905
|
-
if (!shippedRuntimeFiles.has(file2)) return true;
|
|
474906
|
-
}
|
|
474907
|
-
for (const file2 of shippedRuntimeFiles) {
|
|
474908
|
-
if (!stableRuntimeFiles.has(file2)) return true;
|
|
474909
|
-
}
|
|
474910
|
-
return false;
|
|
474923
|
+
return hasVersionedPythonRuntimeFileDrift(join58(shippedDir, "python"), join58(stableDir, "python"));
|
|
474911
474924
|
}
|
|
474912
474925
|
function resetStablePythonRuntime(stableDir) {
|
|
474913
474926
|
try {
|
|
@@ -474937,11 +474950,11 @@ function syncXll() {
|
|
|
474937
474950
|
mkdirSync20(stableDir, { recursive: true });
|
|
474938
474951
|
const pythonRuntimeNeedsReset = hasPythonAbiDrift(shippedDir, stableDir);
|
|
474939
474952
|
try {
|
|
474940
|
-
if (
|
|
474953
|
+
if (existsSync43(versionFile)) {
|
|
474941
474954
|
const stamped = readFileSync33(versionFile, "utf-8").trim();
|
|
474942
474955
|
if (stamped === VERSION) {
|
|
474943
|
-
const xllReady2 =
|
|
474944
|
-
const shippedXllReady =
|
|
474956
|
+
const xllReady2 = existsSync43(stableXll);
|
|
474957
|
+
const shippedXllReady = existsSync43(shippedXll);
|
|
474945
474958
|
if (!pythonRuntimeNeedsReset && (!shippedXllReady || xllReady2 && filesContentMatch(shippedXll, stableXll))) {
|
|
474946
474959
|
return { updated: 0, stableDir, xllReady: xllReady2, skipped: true, staleLocked: false };
|
|
474947
474960
|
}
|
|
@@ -474956,7 +474969,7 @@ function syncXll() {
|
|
|
474956
474969
|
hasStaleLockedFiles = true;
|
|
474957
474970
|
syncFailed = true;
|
|
474958
474971
|
}
|
|
474959
|
-
if (
|
|
474972
|
+
if (existsSync43(shippedDir)) {
|
|
474960
474973
|
try {
|
|
474961
474974
|
const { updated, staleLocked } = syncDir(shippedDir, stableDir);
|
|
474962
474975
|
totalUpdated += updated;
|
|
@@ -474977,7 +474990,7 @@ function syncXll() {
|
|
|
474977
474990
|
} catch {
|
|
474978
474991
|
}
|
|
474979
474992
|
}
|
|
474980
|
-
const xllReady =
|
|
474993
|
+
const xllReady = existsSync43(stableXll);
|
|
474981
474994
|
return {
|
|
474982
474995
|
updated: totalUpdated,
|
|
474983
474996
|
stableDir,
|
|
@@ -474991,13 +475004,14 @@ var init_sync_xll = __esm({
|
|
|
474991
475004
|
"src/startup/sync-xll.ts"() {
|
|
474992
475005
|
"use strict";
|
|
474993
475006
|
init_config();
|
|
475007
|
+
init_python_abi();
|
|
474994
475008
|
SYNC_VERSION_FILE = ".sync-version";
|
|
474995
475009
|
PIP_VERSION_FILE = ".pip-version";
|
|
474996
475010
|
}
|
|
474997
475011
|
});
|
|
474998
475012
|
|
|
474999
475013
|
// src/startup/startup-xll.ts
|
|
475000
|
-
import { existsSync as
|
|
475014
|
+
import { existsSync as existsSync44 } from "fs";
|
|
475001
475015
|
import { join as join59 } from "path";
|
|
475002
475016
|
function getSourceCheckoutRequiredPaths() {
|
|
475003
475017
|
return [
|
|
@@ -475011,7 +475025,7 @@ function resolveStartupXll({
|
|
|
475011
475025
|
needsSetup
|
|
475012
475026
|
}) {
|
|
475013
475027
|
if (sourceCheckout) {
|
|
475014
|
-
const missingPaths = getSourceCheckoutRequiredPaths().filter((p2) => !
|
|
475028
|
+
const missingPaths = getSourceCheckoutRequiredPaths().filter((p2) => !existsSync44(p2));
|
|
475015
475029
|
return missingPaths.length === 0 ? { kind: "ok", updated: 0 } : { kind: "source-missing", missingPaths };
|
|
475016
475030
|
}
|
|
475017
475031
|
const result = syncXll();
|
|
@@ -475713,7 +475727,7 @@ var init_mog_command = __esm({
|
|
|
475713
475727
|
});
|
|
475714
475728
|
|
|
475715
475729
|
// src/app/permissions/permissions-command.ts
|
|
475716
|
-
import { existsSync as
|
|
475730
|
+
import { existsSync as existsSync45, statSync as statSync12 } from "fs";
|
|
475717
475731
|
import { join as join60 } from "path";
|
|
475718
475732
|
function getGlobalSettingsPath() {
|
|
475719
475733
|
return join60(getAgentDir(), "settings.json");
|
|
@@ -475724,7 +475738,7 @@ function normalizeWorkspaceRoot(input, cwd) {
|
|
|
475724
475738
|
return "Workspace path is required";
|
|
475725
475739
|
}
|
|
475726
475740
|
const resolved = resolveToCwd(trimmed, cwd);
|
|
475727
|
-
if (!
|
|
475741
|
+
if (!existsSync45(resolved)) {
|
|
475728
475742
|
return `Path does not exist: ${resolved}`;
|
|
475729
475743
|
}
|
|
475730
475744
|
try {
|
|
@@ -475746,7 +475760,7 @@ function normalizeFilesystemGrantPath(input, cwd, scope) {
|
|
|
475746
475760
|
return "Grant path is required";
|
|
475747
475761
|
}
|
|
475748
475762
|
const resolved = resolveToCwd(trimmed, cwd);
|
|
475749
|
-
if (!
|
|
475763
|
+
if (!existsSync45(resolved)) {
|
|
475750
475764
|
return `Path does not exist: ${resolved}`;
|
|
475751
475765
|
}
|
|
475752
475766
|
try {
|
|
@@ -476527,9 +476541,9 @@ var init_remote_skill_files = __esm({
|
|
|
476527
476541
|
// src/app/sync/skills-download.ts
|
|
476528
476542
|
import { createHash as createHash4 } from "crypto";
|
|
476529
476543
|
import {
|
|
476530
|
-
existsSync as
|
|
476544
|
+
existsSync as existsSync46,
|
|
476531
476545
|
mkdirSync as mkdirSync21,
|
|
476532
|
-
readdirSync as
|
|
476546
|
+
readdirSync as readdirSync14,
|
|
476533
476547
|
readFileSync as readFileSync34,
|
|
476534
476548
|
rmSync as rmSync5,
|
|
476535
476549
|
statSync as statSync13,
|
|
@@ -476543,9 +476557,9 @@ function getSkillsDir() {
|
|
|
476543
476557
|
return join61(getAgentDir(), "skills");
|
|
476544
476558
|
}
|
|
476545
476559
|
function collectLocalFiles(dir, rootDir = dir) {
|
|
476546
|
-
if (!
|
|
476560
|
+
if (!existsSync46(dir)) return [];
|
|
476547
476561
|
const files = [];
|
|
476548
|
-
for (const entry of
|
|
476562
|
+
for (const entry of readdirSync14(dir, { withFileTypes: true })) {
|
|
476549
476563
|
const fullPath = join61(dir, entry.name);
|
|
476550
476564
|
if (entry.isDirectory()) {
|
|
476551
476565
|
files.push(...collectLocalFiles(fullPath, rootDir));
|
|
@@ -476847,7 +476861,7 @@ var init_skills_download = __esm({
|
|
|
476847
476861
|
|
|
476848
476862
|
// src/app/sync/skills-status.ts
|
|
476849
476863
|
import { createHash as createHash5 } from "crypto";
|
|
476850
|
-
import { existsSync as
|
|
476864
|
+
import { existsSync as existsSync47, readdirSync as readdirSync15, readFileSync as readFileSync35 } from "fs";
|
|
476851
476865
|
import { join as join62, relative as relative10 } from "path";
|
|
476852
476866
|
function sha2562(data) {
|
|
476853
476867
|
return createHash5("sha256").update(data).digest("hex");
|
|
@@ -476856,13 +476870,13 @@ function getSkillsDir2() {
|
|
|
476856
476870
|
return join62(getAgentDir(), "skills");
|
|
476857
476871
|
}
|
|
476858
476872
|
function listLocalSkillNames(skillsDir) {
|
|
476859
|
-
if (!
|
|
476860
|
-
return
|
|
476873
|
+
if (!existsSync47(skillsDir)) return [];
|
|
476874
|
+
return readdirSync15(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
|
|
476861
476875
|
}
|
|
476862
476876
|
function collectLocalFiles2(dir, rootDir = dir) {
|
|
476863
|
-
if (!
|
|
476877
|
+
if (!existsSync47(dir)) return [];
|
|
476864
476878
|
const files = [];
|
|
476865
|
-
for (const entry of
|
|
476879
|
+
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
476866
476880
|
const fullPath = join62(dir, entry.name);
|
|
476867
476881
|
if (entry.isDirectory()) {
|
|
476868
476882
|
files.push(...collectLocalFiles2(fullPath, rootDir));
|
|
@@ -477104,7 +477118,7 @@ var init_skills_status = __esm({
|
|
|
477104
477118
|
|
|
477105
477119
|
// src/app/sync/skills-upload.ts
|
|
477106
477120
|
import { createHash as createHash6 } from "crypto";
|
|
477107
|
-
import { existsSync as
|
|
477121
|
+
import { existsSync as existsSync48, readdirSync as readdirSync16, readFileSync as readFileSync36, statSync as statSync14 } from "fs";
|
|
477108
477122
|
import { join as join63, relative as relative11 } from "path";
|
|
477109
477123
|
function sha2563(data) {
|
|
477110
477124
|
return createHash6("sha256").update(data).digest("hex");
|
|
@@ -477113,8 +477127,8 @@ function getSkillsDir3() {
|
|
|
477113
477127
|
return join63(getAgentDir(), "skills");
|
|
477114
477128
|
}
|
|
477115
477129
|
function listLocalSkillNames2(skillsDir) {
|
|
477116
|
-
if (!
|
|
477117
|
-
return
|
|
477130
|
+
if (!existsSync48(skillsDir)) return [];
|
|
477131
|
+
return readdirSync16(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
|
|
477118
477132
|
}
|
|
477119
477133
|
function validateSkillName2(name, parentDirName) {
|
|
477120
477134
|
const errors = [];
|
|
@@ -477137,7 +477151,7 @@ function validateSkillName2(name, parentDirName) {
|
|
|
477137
477151
|
}
|
|
477138
477152
|
function validateLocalSkill(skillName, skillDir) {
|
|
477139
477153
|
const skillPath = join63(skillDir, "SKILL.md");
|
|
477140
|
-
if (!
|
|
477154
|
+
if (!existsSync48(skillPath)) {
|
|
477141
477155
|
return "missing SKILL.md";
|
|
477142
477156
|
}
|
|
477143
477157
|
try {
|
|
@@ -477158,7 +477172,7 @@ function validateLocalSkill(skillName, skillDir) {
|
|
|
477158
477172
|
}
|
|
477159
477173
|
function collectLocalFiles3(dir, rootDir = dir) {
|
|
477160
477174
|
const files = [];
|
|
477161
|
-
for (const entry of
|
|
477175
|
+
for (const entry of readdirSync16(dir, { withFileTypes: true })) {
|
|
477162
477176
|
const fullPath = join63(dir, entry.name);
|
|
477163
477177
|
if (entry.isDirectory()) {
|
|
477164
477178
|
files.push(...collectLocalFiles3(fullPath, rootDir));
|
|
@@ -477409,7 +477423,7 @@ var init_skills_upload = __esm({
|
|
|
477409
477423
|
});
|
|
477410
477424
|
|
|
477411
477425
|
// src/startup/interactive-commands.ts
|
|
477412
|
-
import { existsSync as
|
|
477426
|
+
import { existsSync as existsSync49 } from "fs";
|
|
477413
477427
|
async function fetchAuthorizedSkillTeamIds(accessToken) {
|
|
477414
477428
|
const userInfo = await fetchUserInfo(accessToken);
|
|
477415
477429
|
if (!userInfo) return null;
|
|
@@ -477443,7 +477457,7 @@ function createInteractiveCommandHandler(context) {
|
|
|
477443
477457
|
}
|
|
477444
477458
|
if (command === "/docs") {
|
|
477445
477459
|
const docsFile = getUserDocsPath();
|
|
477446
|
-
if (
|
|
477460
|
+
if (existsSync49(docsFile)) {
|
|
477447
477461
|
void openInEditor(docsFile).catch(
|
|
477448
477462
|
(error2) => showStatus(`Could not open documentation: ${formatOpenError(error2)}`)
|
|
477449
477463
|
);
|
|
@@ -477781,12 +477795,12 @@ var init_install_utils = __esm({
|
|
|
477781
477795
|
});
|
|
477782
477796
|
|
|
477783
477797
|
// src/startup/preflight/shared.ts
|
|
477784
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
477798
|
+
import { chmodSync as chmodSync3, existsSync as existsSync50, mkdirSync as mkdirSync22, readFileSync as readFileSync37, unlinkSync as unlinkSync8, writeFileSync as writeFileSync20 } from "fs";
|
|
477785
477799
|
import { dirname as dirname34, join as join64 } from "path";
|
|
477786
477800
|
async function ensureUv(platformTarget) {
|
|
477787
477801
|
header("Python Toolchain");
|
|
477788
477802
|
const uvPath = getUvBinPath();
|
|
477789
|
-
if (
|
|
477803
|
+
if (existsSync50(uvPath)) {
|
|
477790
477804
|
const check2 = run(`"${uvPath}" --version`, { timeout: 5e3 });
|
|
477791
477805
|
if (check2.ok) {
|
|
477792
477806
|
ok(`uv ${check2.stdout.replace("uv ", "").trim()} installed.`);
|
|
@@ -477842,7 +477856,7 @@ async function ensureUvPython() {
|
|
|
477842
477856
|
const uvPath = getUvBinPath();
|
|
477843
477857
|
const venvDir = getUvVenvDir();
|
|
477844
477858
|
const venvPython = join64(venvDir, "bin", "python3");
|
|
477845
|
-
if (
|
|
477859
|
+
if (existsSync50(venvPython)) {
|
|
477846
477860
|
const check2 = run(`"${venvPython}" --version`, { timeout: 5e3 });
|
|
477847
477861
|
if (check2.ok) {
|
|
477848
477862
|
ok(`${check2.stdout.trim()} ready.`);
|
|
@@ -477877,7 +477891,7 @@ async function ensureUvPackages() {
|
|
|
477877
477891
|
const uvPath = getUvBinPath();
|
|
477878
477892
|
const venvDir = getUvVenvDir();
|
|
477879
477893
|
const venvPython = join64(venvDir, "bin", "python3");
|
|
477880
|
-
if (!
|
|
477894
|
+
if (!existsSync50(venvPython)) {
|
|
477881
477895
|
log2(
|
|
477882
477896
|
"Shortcut agent will install packages after Python is ready.",
|
|
477883
477897
|
"Will finish setup automatically."
|
|
@@ -477887,7 +477901,7 @@ async function ensureUvPackages() {
|
|
|
477887
477901
|
const stampFile = join64(venvDir, ".packages-stamp");
|
|
477888
477902
|
const stampValue = `${VERSION}:${UV_PIP_PACKAGES.join(",")}`;
|
|
477889
477903
|
try {
|
|
477890
|
-
if (
|
|
477904
|
+
if (existsSync50(stampFile) && readFileSync37(stampFile, "utf-8").trim() === stampValue) {
|
|
477891
477905
|
ok("Packages already installed.", "Ready.");
|
|
477892
477906
|
return;
|
|
477893
477907
|
}
|
|
@@ -477971,13 +477985,13 @@ __export(mac_exports, {
|
|
|
477971
477985
|
ensureUvPython: () => ensureUvPython,
|
|
477972
477986
|
smokeTestSheetEngine: () => smokeTestSheetEngine
|
|
477973
477987
|
});
|
|
477974
|
-
import { existsSync as
|
|
477988
|
+
import { existsSync as existsSync51 } from "fs";
|
|
477975
477989
|
async function ensureUv2() {
|
|
477976
477990
|
return ensureUv("apple-darwin");
|
|
477977
477991
|
}
|
|
477978
477992
|
function ensureChromeMac() {
|
|
477979
477993
|
header("Chrome");
|
|
477980
|
-
if (
|
|
477994
|
+
if (existsSync51("/Applications/Google Chrome.app")) {
|
|
477981
477995
|
ok("Chrome found \u2014 SEC filing support ready.");
|
|
477982
477996
|
} else {
|
|
477983
477997
|
log2("Chrome not installed \u2014 Shortcut agent can set this up for SEC filing support.");
|
|
@@ -478001,13 +478015,13 @@ __export(linux_exports, {
|
|
|
478001
478015
|
ensureUvPython: () => ensureUvPython,
|
|
478002
478016
|
smokeTestSheetEngine: () => smokeTestSheetEngine
|
|
478003
478017
|
});
|
|
478004
|
-
import { existsSync as
|
|
478018
|
+
import { existsSync as existsSync52 } from "fs";
|
|
478005
478019
|
async function ensureUv3() {
|
|
478006
478020
|
return ensureUv("unknown-linux-gnu");
|
|
478007
478021
|
}
|
|
478008
478022
|
function ensureChromeLinux() {
|
|
478009
478023
|
header("Chrome");
|
|
478010
|
-
const found = LINUX_CHROME_PATHS.find((p2) =>
|
|
478024
|
+
const found = LINUX_CHROME_PATHS.find((p2) => existsSync52(p2));
|
|
478011
478025
|
if (found) {
|
|
478012
478026
|
ok("Chrome found \u2014 SEC filing support ready.");
|
|
478013
478027
|
} else {
|
|
@@ -478039,12 +478053,12 @@ __export(windows_exports, {
|
|
|
478039
478053
|
ensureXllRegistry: () => ensureXllRegistry,
|
|
478040
478054
|
smokeTestExcel: () => smokeTestExcel
|
|
478041
478055
|
});
|
|
478042
|
-
import { existsSync as
|
|
478056
|
+
import { existsSync as existsSync53, readFileSync as readFileSync38, writeFileSync as writeFileSync21 } from "fs";
|
|
478043
478057
|
import { join as join65, resolve as resolve18 } from "path";
|
|
478044
478058
|
async function ensurePipPackages() {
|
|
478045
478059
|
header("Python Packages", "Calculation Engine");
|
|
478046
478060
|
const pythonExe = getEmbeddedPythonExe();
|
|
478047
|
-
if (!
|
|
478061
|
+
if (!existsSync53(pythonExe)) {
|
|
478048
478062
|
log2("Shortcut agent will set up Python.", "Will finish setup automatically.");
|
|
478049
478063
|
return;
|
|
478050
478064
|
}
|
|
@@ -478063,7 +478077,7 @@ async function ensurePipPackages() {
|
|
|
478063
478077
|
const pipStampFile = join65(getStableXllDir(), ".pip-version");
|
|
478064
478078
|
let pipUpToDate = false;
|
|
478065
478079
|
try {
|
|
478066
|
-
pipUpToDate =
|
|
478080
|
+
pipUpToDate = existsSync53(pipStampFile) && readFileSync38(pipStampFile, "utf-8").trim() === VERSION;
|
|
478067
478081
|
} catch {
|
|
478068
478082
|
}
|
|
478069
478083
|
if (pipUpToDate) {
|
|
@@ -478093,7 +478107,7 @@ async function ensureChromeWindows() {
|
|
|
478093
478107
|
return;
|
|
478094
478108
|
}
|
|
478095
478109
|
const pythonExe = getEmbeddedPythonExe();
|
|
478096
|
-
if (!
|
|
478110
|
+
if (!existsSync53(pythonExe)) {
|
|
478097
478111
|
log2("Shortcut agent will set up Chrome.", "Will finish setup automatically.");
|
|
478098
478112
|
return;
|
|
478099
478113
|
}
|
|
@@ -478177,7 +478191,7 @@ async function ensureGitBash() {
|
|
|
478177
478191
|
function ensureXllRegistry() {
|
|
478178
478192
|
header("Excel Add-in", "Excel Integration");
|
|
478179
478193
|
const xllPath = resolve18(getXllPath()).replace(/\//g, "\\");
|
|
478180
|
-
if (!
|
|
478194
|
+
if (!existsSync53(xllPath)) {
|
|
478181
478195
|
log2("Shortcut agent will set up the Excel add-in.", "Will finish setup automatically.");
|
|
478182
478196
|
return;
|
|
478183
478197
|
}
|
|
@@ -478373,7 +478387,7 @@ var diagnostics_exports = {};
|
|
|
478373
478387
|
__export(diagnostics_exports, {
|
|
478374
478388
|
collectPreflightDiagnostics: () => collectPreflightDiagnostics
|
|
478375
478389
|
});
|
|
478376
|
-
import { closeSync as closeSync2, existsSync as
|
|
478390
|
+
import { closeSync as closeSync2, existsSync as existsSync54, openSync as openSync2, readSync as readSync2, statSync as statSync15 } from "fs";
|
|
478377
478391
|
import { resolve as resolve19 } from "path";
|
|
478378
478392
|
function safeRun(command, timeout = DIAGNOSTIC_TIMEOUT_MS) {
|
|
478379
478393
|
try {
|
|
@@ -478498,7 +478512,7 @@ function collectXllLog() {
|
|
|
478498
478512
|
`${process.env.TEMP ?? ""}\\shortcutxl.log`
|
|
478499
478513
|
];
|
|
478500
478514
|
for (const path21 of logPaths) {
|
|
478501
|
-
if (path21 &&
|
|
478515
|
+
if (path21 && existsSync54(path21)) {
|
|
478502
478516
|
return readLogTail(path21);
|
|
478503
478517
|
}
|
|
478504
478518
|
}
|
|
@@ -478550,7 +478564,7 @@ function collectPreflightDiagnostics() {
|
|
|
478550
478564
|
setCurrentStep("diagnostics");
|
|
478551
478565
|
const xllPath = resolve19(getXllPath()).replace(/\//g, "\\");
|
|
478552
478566
|
const diagnostics = {
|
|
478553
|
-
xllExists:
|
|
478567
|
+
xllExists: existsSync54(xllPath),
|
|
478554
478568
|
xllPath,
|
|
478555
478569
|
motw: collectMotw(xllPath),
|
|
478556
478570
|
xllArch: collectXllArch(xllPath),
|
|
@@ -478634,7 +478648,7 @@ var init_preflight = __esm({
|
|
|
478634
478648
|
});
|
|
478635
478649
|
|
|
478636
478650
|
// src/startup/prune-sessions.ts
|
|
478637
|
-
import { readdirSync as
|
|
478651
|
+
import { readdirSync as readdirSync17, rmSync as rmSync6, statSync as statSync16 } from "node:fs";
|
|
478638
478652
|
import { join as join66 } from "node:path";
|
|
478639
478653
|
function pruneStaleFiles() {
|
|
478640
478654
|
const cutoff = Date.now() - FOURTEEN_DAYS_MS;
|
|
@@ -478643,7 +478657,7 @@ function pruneStaleFiles() {
|
|
|
478643
478657
|
function pruneSessionFiles(cutoff) {
|
|
478644
478658
|
try {
|
|
478645
478659
|
const sessionsRoot = getSessionsDir();
|
|
478646
|
-
for (const projectDir of
|
|
478660
|
+
for (const projectDir of readdirSync17(sessionsRoot)) {
|
|
478647
478661
|
pruneJsonlFilesRecursively(join66(sessionsRoot, projectDir), cutoff);
|
|
478648
478662
|
}
|
|
478649
478663
|
} catch {
|
|
@@ -478651,7 +478665,7 @@ function pruneSessionFiles(cutoff) {
|
|
|
478651
478665
|
}
|
|
478652
478666
|
function pruneJsonlFilesRecursively(dir, cutoff) {
|
|
478653
478667
|
try {
|
|
478654
|
-
for (const entry of
|
|
478668
|
+
for (const entry of readdirSync17(dir, { withFileTypes: true })) {
|
|
478655
478669
|
const entryPath = join66(dir, entry.name);
|
|
478656
478670
|
if (entry.isDirectory()) {
|
|
478657
478671
|
pruneJsonlFilesRecursively(entryPath, cutoff);
|
|
@@ -478751,19 +478765,38 @@ var init_session_selection = __esm({
|
|
|
478751
478765
|
|
|
478752
478766
|
// src/startup/update-action.ts
|
|
478753
478767
|
import { spawn as spawn12 } from "child_process";
|
|
478768
|
+
import { appendFileSync as appendFileSync6, mkdirSync as mkdirSync23 } from "fs";
|
|
478769
|
+
import { dirname as dirname35 } from "path";
|
|
478754
478770
|
async function runForegroundUpdate(request, deps = {}) {
|
|
478755
478771
|
const resolveShell = deps.resolveShell ?? getShellConfig;
|
|
478756
478772
|
const resolveEnv = deps.resolveEnv ?? getShellEnv;
|
|
478757
478773
|
const spawnProcess = deps.spawnProcess ?? spawn12;
|
|
478774
|
+
const resolveLogPath = deps.resolveLogPath ?? getDebugLogPath;
|
|
478758
478775
|
const shellConfig = resolveShell();
|
|
478759
478776
|
return await new Promise((resolve22, reject) => {
|
|
478777
|
+
const output = [];
|
|
478778
|
+
let outputBytes = 0;
|
|
478779
|
+
let outputTruncated = false;
|
|
478760
478780
|
const child = spawnProcess(shellConfig.shell, [...shellConfig.args, request.command], {
|
|
478761
478781
|
cwd: request.cwd ?? process.cwd(),
|
|
478762
478782
|
env: resolveEnv(),
|
|
478763
|
-
stdio: "
|
|
478783
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
478764
478784
|
});
|
|
478785
|
+
const capture = (chunk) => {
|
|
478786
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
|
478787
|
+
const remaining = MAX_CAPTURED_OUTPUT_BYTES - outputBytes;
|
|
478788
|
+
if (remaining > 0) {
|
|
478789
|
+
output.push(buffer.subarray(0, remaining));
|
|
478790
|
+
outputBytes += Math.min(buffer.length, remaining);
|
|
478791
|
+
}
|
|
478792
|
+
if (buffer.length > remaining) {
|
|
478793
|
+
outputTruncated = true;
|
|
478794
|
+
}
|
|
478795
|
+
};
|
|
478796
|
+
child.stdout?.on("data", capture);
|
|
478797
|
+
child.stderr?.on("data", capture);
|
|
478765
478798
|
child.once("error", reject);
|
|
478766
|
-
child.once("
|
|
478799
|
+
child.once("close", (exitCode, signal) => {
|
|
478767
478800
|
if (exitCode === 0) {
|
|
478768
478801
|
resolve22({
|
|
478769
478802
|
kind: "success",
|
|
@@ -478772,24 +478805,83 @@ async function runForegroundUpdate(request, deps = {}) {
|
|
|
478772
478805
|
});
|
|
478773
478806
|
return;
|
|
478774
478807
|
}
|
|
478808
|
+
const capturedOutput = Buffer.concat(output).toString("utf-8");
|
|
478809
|
+
const logPath = writeUpdateFailureLog({
|
|
478810
|
+
command: request.command,
|
|
478811
|
+
exitCode,
|
|
478812
|
+
signal,
|
|
478813
|
+
output: capturedOutput,
|
|
478814
|
+
truncated: outputTruncated,
|
|
478815
|
+
resolveLogPath
|
|
478816
|
+
});
|
|
478775
478817
|
resolve22({
|
|
478776
478818
|
kind: "failed",
|
|
478777
478819
|
exitCode,
|
|
478778
|
-
signal
|
|
478820
|
+
signal,
|
|
478821
|
+
message: summarizeUpdateFailure(capturedOutput),
|
|
478822
|
+
logPath
|
|
478779
478823
|
});
|
|
478780
478824
|
});
|
|
478781
478825
|
});
|
|
478782
478826
|
}
|
|
478827
|
+
function summarizeUpdateFailure(output) {
|
|
478828
|
+
if (/EEXIST[\s\S]*shortcut\.cmd/i.test(output) || /File exists:[\s\S]*shortcut\.cmd/i.test(output)) {
|
|
478829
|
+
return [
|
|
478830
|
+
"ShortcutXL could not update because an existing global `shortcut` command blocked npm from replacing it.",
|
|
478831
|
+
"Close any running ShortcutXL terminals, then run `npm uninstall -g shortcutxl` followed by `npm install -g shortcutxl@latest`."
|
|
478832
|
+
].join(" ");
|
|
478833
|
+
}
|
|
478834
|
+
if (/EPERM|ENOTEMPTY|ENOENT/i.test(output) && /AppData[\\/]+Roaming[\\/]+npm[\\/]+node_modules[\\/]+shortcutxl/i.test(output)) {
|
|
478835
|
+
return [
|
|
478836
|
+
"ShortcutXL could not update because npm could not clean up the previous global install.",
|
|
478837
|
+
"Close ShortcutXL terminals and retry `npm install -g shortcutxl@latest`."
|
|
478838
|
+
].join(" ");
|
|
478839
|
+
}
|
|
478840
|
+
return "ShortcutXL could not update automatically. Retry with `npm install -g shortcutxl@latest`.";
|
|
478841
|
+
}
|
|
478842
|
+
function writeUpdateFailureLog({
|
|
478843
|
+
command,
|
|
478844
|
+
exitCode,
|
|
478845
|
+
signal,
|
|
478846
|
+
output,
|
|
478847
|
+
truncated,
|
|
478848
|
+
resolveLogPath
|
|
478849
|
+
}) {
|
|
478850
|
+
try {
|
|
478851
|
+
const logPath = resolveLogPath();
|
|
478852
|
+
mkdirSync23(dirname35(logPath), { recursive: true });
|
|
478853
|
+
appendFileSync6(
|
|
478854
|
+
logPath,
|
|
478855
|
+
[
|
|
478856
|
+
"",
|
|
478857
|
+
"--- ShortcutXL update failure ---",
|
|
478858
|
+
`time=${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
478859
|
+
`command=${command}`,
|
|
478860
|
+
`exitCode=${String(exitCode)}`,
|
|
478861
|
+
`signal=${String(signal)}`,
|
|
478862
|
+
truncated ? `outputTruncatedAfterBytes=${MAX_CAPTURED_OUTPUT_BYTES}` : void 0,
|
|
478863
|
+
output
|
|
478864
|
+
].filter((line) => line !== void 0).join("\n"),
|
|
478865
|
+
"utf-8"
|
|
478866
|
+
);
|
|
478867
|
+
return logPath;
|
|
478868
|
+
} catch {
|
|
478869
|
+
return void 0;
|
|
478870
|
+
}
|
|
478871
|
+
}
|
|
478872
|
+
var MAX_CAPTURED_OUTPUT_BYTES;
|
|
478783
478873
|
var init_update_action = __esm({
|
|
478784
478874
|
"src/startup/update-action.ts"() {
|
|
478785
478875
|
"use strict";
|
|
478786
478876
|
init_bash_runtime();
|
|
478877
|
+
init_config();
|
|
478878
|
+
MAX_CAPTURED_OUTPUT_BYTES = 1024 * 1024;
|
|
478787
478879
|
}
|
|
478788
478880
|
});
|
|
478789
478881
|
|
|
478790
478882
|
// src/startup/update-manager.ts
|
|
478791
|
-
import { mkdirSync as
|
|
478792
|
-
import { dirname as
|
|
478883
|
+
import { mkdirSync as mkdirSync24, readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "fs";
|
|
478884
|
+
import { dirname as dirname36 } from "path";
|
|
478793
478885
|
async function resolveStartupUpdateStatus(options2 = {}, deps = {}) {
|
|
478794
478886
|
const cachePath = options2.cachePath ?? getUpdateCachePath();
|
|
478795
478887
|
const currentVersion = options2.currentVersion ?? VERSION;
|
|
@@ -478902,7 +478994,7 @@ function readUpdateCache(cachePath) {
|
|
|
478902
478994
|
}
|
|
478903
478995
|
}
|
|
478904
478996
|
function writeUpdateCache(cachePath, cache) {
|
|
478905
|
-
|
|
478997
|
+
mkdirSync24(dirname36(cachePath), { recursive: true });
|
|
478906
478998
|
writeFileSync22(cachePath, `${JSON.stringify(cache, null, 2)}
|
|
478907
478999
|
`, "utf-8");
|
|
478908
479000
|
}
|
|
@@ -480350,7 +480442,7 @@ __export(uninstall_exports, {
|
|
|
480350
480442
|
removeOwnedInstallArtifacts: () => removeOwnedInstallArtifacts,
|
|
480351
480443
|
runUninstall: () => runUninstall
|
|
480352
480444
|
});
|
|
480353
|
-
import { existsSync as
|
|
480445
|
+
import { existsSync as existsSync55, readdirSync as readdirSync18, rmSync as rmSync7, rmdirSync } from "fs";
|
|
480354
480446
|
import { isAbsolute as isAbsolute10, join as join67, relative as relative12, resolve as resolve20 } from "path";
|
|
480355
480447
|
function isWithinDir(parentDir, targetPath) {
|
|
480356
480448
|
const parent = resolve20(parentDir);
|
|
@@ -480360,7 +480452,7 @@ function isWithinDir(parentDir, targetPath) {
|
|
|
480360
480452
|
}
|
|
480361
480453
|
function removeIfInsideAgentDir(agentDir, relativePath) {
|
|
480362
480454
|
const target = join67(agentDir, relativePath);
|
|
480363
|
-
if (!isWithinDir(agentDir, target) || !
|
|
480455
|
+
if (!isWithinDir(agentDir, target) || !existsSync55(target)) {
|
|
480364
480456
|
return false;
|
|
480365
480457
|
}
|
|
480366
480458
|
rmSync7(target, { recursive: true, force: true });
|
|
@@ -480368,10 +480460,10 @@ function removeIfInsideAgentDir(agentDir, relativePath) {
|
|
|
480368
480460
|
}
|
|
480369
480461
|
function removeEmptyDirIfInsideAgentDir(agentDir, relativePath) {
|
|
480370
480462
|
const target = join67(agentDir, relativePath);
|
|
480371
|
-
if (!isWithinDir(agentDir, target) || !
|
|
480463
|
+
if (!isWithinDir(agentDir, target) || !existsSync55(target)) {
|
|
480372
480464
|
return false;
|
|
480373
480465
|
}
|
|
480374
|
-
if (
|
|
480466
|
+
if (readdirSync18(target).length > 0) {
|
|
480375
480467
|
return false;
|
|
480376
480468
|
}
|
|
480377
480469
|
rmdirSync(target);
|
|
@@ -480398,7 +480490,7 @@ function removeOwnedInstallArtifacts(agentDir) {
|
|
|
480398
480490
|
}
|
|
480399
480491
|
}
|
|
480400
480492
|
try {
|
|
480401
|
-
if (
|
|
480493
|
+
if (existsSync55(agentDir) && readdirSync18(agentDir).length === 0) {
|
|
480402
480494
|
rmdirSync(agentDir);
|
|
480403
480495
|
result.removed.push(".");
|
|
480404
480496
|
}
|
|
@@ -480474,7 +480566,7 @@ function runUninstall() {
|
|
|
480474
480566
|
}
|
|
480475
480567
|
header2("Playwright Browsers");
|
|
480476
480568
|
const pythonExe = getEmbeddedPythonExe();
|
|
480477
|
-
if (
|
|
480569
|
+
if (existsSync55(pythonExe)) {
|
|
480478
480570
|
const pwResult = run(`"${pythonExe}" -m playwright uninstall --all`, { timeout: 3e4 });
|
|
480479
480571
|
if (pwResult.ok) {
|
|
480480
480572
|
ok2("Removed Playwright browsers.");
|
|
@@ -480490,7 +480582,7 @@ function runUninstall() {
|
|
|
480490
480582
|
}
|
|
480491
480583
|
header2("Files");
|
|
480492
480584
|
const agentDir = getAgentDir();
|
|
480493
|
-
if (
|
|
480585
|
+
if (existsSync55(agentDir)) {
|
|
480494
480586
|
const cleanup = removeOwnedInstallArtifacts(agentDir);
|
|
480495
480587
|
if (cleanup.removed.length > 0) {
|
|
480496
480588
|
ok2(`Removed generated install files from ${agentDir}`);
|
|
@@ -480543,7 +480635,7 @@ __export(main_exports, {
|
|
|
480543
480635
|
isMogRuntimeEnabledForUser: () => isMogRuntimeEnabledForUser,
|
|
480544
480636
|
main: () => main
|
|
480545
480637
|
});
|
|
480546
|
-
import { existsSync as
|
|
480638
|
+
import { existsSync as existsSync56 } from "fs";
|
|
480547
480639
|
async function resolveShortcutUserInfo(authStorage) {
|
|
480548
480640
|
const accessToken = await authStorage.getApiKey(SHORTCUT_PROVIDER_ID);
|
|
480549
480641
|
return accessToken ? fetchUserInfo(accessToken) : null;
|
|
@@ -480968,7 +481060,7 @@ async function main(args) {
|
|
|
480968
481060
|
if (sandboxMode === "enabled" && sandboxAvailable) {
|
|
480969
481061
|
const sandboxFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
480970
481062
|
let sandboxFrame = 0;
|
|
480971
|
-
const needsDownload = !
|
|
481063
|
+
const needsDownload = !existsSync56(getAlpineRootfsPath());
|
|
480972
481064
|
const spinnerMsg = needsDownload ? "Downloading sandbox (~80 MB)..." : "Checking sandbox...";
|
|
480973
481065
|
const sandboxSpinner = setInterval(() => {
|
|
480974
481066
|
process.stderr.write(
|
|
@@ -481109,6 +481201,12 @@ async function main(args) {
|
|
|
481109
481201
|
if (updateResult.kind === "failed") {
|
|
481110
481202
|
const exitDetail = updateResult.exitCode !== null ? `exit code ${updateResult.exitCode}` : updateResult.signal ? `signal ${updateResult.signal}` : "unknown error";
|
|
481111
481203
|
console.error(source_default.red(`Update failed (${exitDetail}). ShortcutXL will not launch.`));
|
|
481204
|
+
if (updateResult.message) {
|
|
481205
|
+
console.error(source_default.yellow(updateResult.message));
|
|
481206
|
+
}
|
|
481207
|
+
if (updateResult.logPath) {
|
|
481208
|
+
console.error(source_default.dim(`Details were written to ${updateResult.logPath}`));
|
|
481209
|
+
}
|
|
481112
481210
|
process.exit(updateResult.exitCode ?? 1);
|
|
481113
481211
|
}
|
|
481114
481212
|
console.log(source_default.green(`Update to v${updatePromptOutcome.update.latestVersion} succeeded.`));
|
|
@@ -481571,9 +481669,9 @@ ${loop.prompt}`;
|
|
|
481571
481669
|
|
|
481572
481670
|
// src/cli.ts
|
|
481573
481671
|
var import_dotenv = __toESM(require_main(), 1);
|
|
481574
|
-
import { dirname as
|
|
481672
|
+
import { dirname as dirname37, resolve as resolve21 } from "path";
|
|
481575
481673
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
481576
|
-
var __dirname3 =
|
|
481674
|
+
var __dirname3 = dirname37(fileURLToPath4(import.meta.url));
|
|
481577
481675
|
var agentRoot = resolve21(__dirname3, "..");
|
|
481578
481676
|
import_dotenv.default.config({ path: resolve21(agentRoot, ".env.development") });
|
|
481579
481677
|
process.title = "shortcut";
|
package/dist/main.js
CHANGED
|
@@ -669,6 +669,12 @@ export async function main(args) {
|
|
|
669
669
|
? `signal ${updateResult.signal}`
|
|
670
670
|
: 'unknown error';
|
|
671
671
|
console.error(chalk.red(`Update failed (${exitDetail}). ShortcutXL will not launch.`));
|
|
672
|
+
if (updateResult.message) {
|
|
673
|
+
console.error(chalk.yellow(updateResult.message));
|
|
674
|
+
}
|
|
675
|
+
if (updateResult.logPath) {
|
|
676
|
+
console.error(chalk.dim(`Details were written to ${updateResult.logPath}`));
|
|
677
|
+
}
|
|
672
678
|
process.exit(updateResult.exitCode ?? 1);
|
|
673
679
|
}
|
|
674
680
|
console.log(chalk.green(`Update to v${updatePromptOutcome.update.latestVersion} succeeded.`));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'fs';
|
|
2
|
+
const VERSIONED_PYTHON_RUNTIME_FILE = /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i;
|
|
3
|
+
export function listVersionedPythonRuntimeFiles(pythonDir) {
|
|
4
|
+
if (!existsSync(pythonDir))
|
|
5
|
+
return new Set();
|
|
6
|
+
try {
|
|
7
|
+
return new Set(readdirSync(pythonDir)
|
|
8
|
+
.filter((entry) => VERSIONED_PYTHON_RUNTIME_FILE.test(entry))
|
|
9
|
+
.map((entry) => entry.toLowerCase()));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return new Set();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function hasVersionedPythonRuntimeFileDrift(sourcePythonDir, destPythonDir) {
|
|
16
|
+
const sourceFiles = listVersionedPythonRuntimeFiles(sourcePythonDir);
|
|
17
|
+
if (sourceFiles.size === 0)
|
|
18
|
+
return false;
|
|
19
|
+
const destFiles = listVersionedPythonRuntimeFiles(destPythonDir);
|
|
20
|
+
if (destFiles.size === 0)
|
|
21
|
+
return false;
|
|
22
|
+
for (const file of destFiles) {
|
|
23
|
+
if (!sourceFiles.has(file))
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
for (const file of sourceFiles) {
|
|
27
|
+
if (!destFiles.has(file))
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=python-abi.js.map
|
package/dist/startup/sync-xll.js
CHANGED
|
@@ -22,6 +22,7 @@ import { createHash } from 'crypto';
|
|
|
22
22
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, unlinkSync, utimesSync, writeFileSync } from 'fs';
|
|
23
23
|
import { join } from 'path';
|
|
24
24
|
import { getPackageDir, getStableXllDir, VERSION } from '../config.js';
|
|
25
|
+
import { hasVersionedPythonRuntimeFileDrift } from './python-abi.js';
|
|
25
26
|
/** Strip the NTFS Zone.Identifier alternate data stream (Mark of the Web). */
|
|
26
27
|
function stripMotw(filePath) {
|
|
27
28
|
try {
|
|
@@ -101,34 +102,8 @@ function syncDir(src, dest) {
|
|
|
101
102
|
}
|
|
102
103
|
const SYNC_VERSION_FILE = '.sync-version';
|
|
103
104
|
const PIP_VERSION_FILE = '.pip-version';
|
|
104
|
-
function listVersionedPythonRuntimeFiles(pythonDir) {
|
|
105
|
-
if (!existsSync(pythonDir))
|
|
106
|
-
return new Set();
|
|
107
|
-
try {
|
|
108
|
-
return new Set(readdirSync(pythonDir)
|
|
109
|
-
.filter((entry) => /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i.test(entry))
|
|
110
|
-
.map((entry) => entry.toLowerCase()));
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
return new Set();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
105
|
export function hasPythonAbiDrift(shippedDir, stableDir) {
|
|
117
|
-
|
|
118
|
-
if (shippedRuntimeFiles.size === 0)
|
|
119
|
-
return false;
|
|
120
|
-
const stableRuntimeFiles = listVersionedPythonRuntimeFiles(join(stableDir, 'python'));
|
|
121
|
-
if (stableRuntimeFiles.size === 0)
|
|
122
|
-
return false;
|
|
123
|
-
for (const file of stableRuntimeFiles) {
|
|
124
|
-
if (!shippedRuntimeFiles.has(file))
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
for (const file of shippedRuntimeFiles) {
|
|
128
|
-
if (!stableRuntimeFiles.has(file))
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
return false;
|
|
106
|
+
return hasVersionedPythonRuntimeFileDrift(join(shippedDir, 'python'), join(stableDir, 'python'));
|
|
132
107
|
}
|
|
133
108
|
function resetStablePythonRuntime(stableDir) {
|
|
134
109
|
try {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Foreground update execution path.
|
|
3
3
|
*
|
|
4
|
-
* Runs the updater command in the startup flow
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Runs the updater command in the startup flow. Updaters such as npm can emit
|
|
5
|
+
* huge cleanup dumps on Windows when a global install tree is locked or
|
|
6
|
+
* half-deleted, so stdout/stderr are captured and summarized instead of being
|
|
7
|
+
* streamed directly into the user's terminal.
|
|
7
8
|
*/
|
|
8
9
|
import { spawn } from 'child_process';
|
|
9
10
|
export interface UpdateLaunchRequest {
|
|
@@ -14,6 +15,8 @@ export interface ForegroundUpdateResult {
|
|
|
14
15
|
kind: 'success' | 'failed';
|
|
15
16
|
exitCode: number | null;
|
|
16
17
|
signal: NodeJS.Signals | null;
|
|
18
|
+
message?: string;
|
|
19
|
+
logPath?: string;
|
|
17
20
|
}
|
|
18
21
|
interface UpdateActionDeps {
|
|
19
22
|
spawnProcess?: typeof spawn;
|
|
@@ -22,6 +25,7 @@ interface UpdateActionDeps {
|
|
|
22
25
|
args: string[];
|
|
23
26
|
};
|
|
24
27
|
resolveEnv?: () => NodeJS.ProcessEnv;
|
|
28
|
+
resolveLogPath?: () => string;
|
|
25
29
|
}
|
|
26
30
|
export declare function runForegroundUpdate(request: UpdateLaunchRequest, deps?: UpdateActionDeps): Promise<ForegroundUpdateResult>;
|
|
27
31
|
export {};
|
|
@@ -1,25 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Foreground update execution path.
|
|
3
3
|
*
|
|
4
|
-
* Runs the updater command in the startup flow
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Runs the updater command in the startup flow. Updaters such as npm can emit
|
|
5
|
+
* huge cleanup dumps on Windows when a global install tree is locked or
|
|
6
|
+
* half-deleted, so stdout/stderr are captured and summarized instead of being
|
|
7
|
+
* streamed directly into the user's terminal.
|
|
7
8
|
*/
|
|
8
9
|
import { spawn } from 'child_process';
|
|
10
|
+
import { appendFileSync, mkdirSync } from 'fs';
|
|
11
|
+
import { dirname } from 'path';
|
|
9
12
|
import { getShellConfig, getShellEnv } from '../app/tools/bash-runtime.js';
|
|
13
|
+
import { getDebugLogPath } from '../config.js';
|
|
14
|
+
const MAX_CAPTURED_OUTPUT_BYTES = 1024 * 1024;
|
|
10
15
|
export async function runForegroundUpdate(request, deps = {}) {
|
|
11
16
|
const resolveShell = deps.resolveShell ?? getShellConfig;
|
|
12
17
|
const resolveEnv = deps.resolveEnv ?? getShellEnv;
|
|
13
18
|
const spawnProcess = deps.spawnProcess ?? spawn;
|
|
19
|
+
const resolveLogPath = deps.resolveLogPath ?? getDebugLogPath;
|
|
14
20
|
const shellConfig = resolveShell();
|
|
15
21
|
return await new Promise((resolve, reject) => {
|
|
22
|
+
const output = [];
|
|
23
|
+
let outputBytes = 0;
|
|
24
|
+
let outputTruncated = false;
|
|
16
25
|
const child = spawnProcess(shellConfig.shell, [...shellConfig.args, request.command], {
|
|
17
26
|
cwd: request.cwd ?? process.cwd(),
|
|
18
27
|
env: resolveEnv(),
|
|
19
|
-
stdio: '
|
|
28
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
20
29
|
});
|
|
30
|
+
const capture = (chunk) => {
|
|
31
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
|
32
|
+
const remaining = MAX_CAPTURED_OUTPUT_BYTES - outputBytes;
|
|
33
|
+
if (remaining > 0) {
|
|
34
|
+
output.push(buffer.subarray(0, remaining));
|
|
35
|
+
outputBytes += Math.min(buffer.length, remaining);
|
|
36
|
+
}
|
|
37
|
+
if (buffer.length > remaining) {
|
|
38
|
+
outputTruncated = true;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
child.stdout?.on('data', capture);
|
|
42
|
+
child.stderr?.on('data', capture);
|
|
21
43
|
child.once('error', reject);
|
|
22
|
-
child.once('
|
|
44
|
+
child.once('close', (exitCode, signal) => {
|
|
23
45
|
if (exitCode === 0) {
|
|
24
46
|
resolve({
|
|
25
47
|
kind: 'success',
|
|
@@ -28,12 +50,62 @@ export async function runForegroundUpdate(request, deps = {}) {
|
|
|
28
50
|
});
|
|
29
51
|
return;
|
|
30
52
|
}
|
|
53
|
+
const capturedOutput = Buffer.concat(output).toString('utf-8');
|
|
54
|
+
const logPath = writeUpdateFailureLog({
|
|
55
|
+
command: request.command,
|
|
56
|
+
exitCode,
|
|
57
|
+
signal,
|
|
58
|
+
output: capturedOutput,
|
|
59
|
+
truncated: outputTruncated,
|
|
60
|
+
resolveLogPath
|
|
61
|
+
});
|
|
31
62
|
resolve({
|
|
32
63
|
kind: 'failed',
|
|
33
64
|
exitCode,
|
|
34
|
-
signal
|
|
65
|
+
signal,
|
|
66
|
+
message: summarizeUpdateFailure(capturedOutput),
|
|
67
|
+
logPath
|
|
35
68
|
});
|
|
36
69
|
});
|
|
37
70
|
});
|
|
38
71
|
}
|
|
72
|
+
function summarizeUpdateFailure(output) {
|
|
73
|
+
if (/EEXIST[\s\S]*shortcut\.cmd/i.test(output) ||
|
|
74
|
+
/File exists:[\s\S]*shortcut\.cmd/i.test(output)) {
|
|
75
|
+
return [
|
|
76
|
+
'ShortcutXL could not update because an existing global `shortcut` command blocked npm from replacing it.',
|
|
77
|
+
'Close any running ShortcutXL terminals, then run `npm uninstall -g shortcutxl` followed by `npm install -g shortcutxl@latest`.'
|
|
78
|
+
].join(' ');
|
|
79
|
+
}
|
|
80
|
+
if (/EPERM|ENOTEMPTY|ENOENT/i.test(output) &&
|
|
81
|
+
/AppData[\\/]+Roaming[\\/]+npm[\\/]+node_modules[\\/]+shortcutxl/i.test(output)) {
|
|
82
|
+
return [
|
|
83
|
+
'ShortcutXL could not update because npm could not clean up the previous global install.',
|
|
84
|
+
'Close ShortcutXL terminals and retry `npm install -g shortcutxl@latest`.'
|
|
85
|
+
].join(' ');
|
|
86
|
+
}
|
|
87
|
+
return 'ShortcutXL could not update automatically. Retry with `npm install -g shortcutxl@latest`.';
|
|
88
|
+
}
|
|
89
|
+
function writeUpdateFailureLog({ command, exitCode, signal, output, truncated, resolveLogPath }) {
|
|
90
|
+
try {
|
|
91
|
+
const logPath = resolveLogPath();
|
|
92
|
+
mkdirSync(dirname(logPath), { recursive: true });
|
|
93
|
+
appendFileSync(logPath, [
|
|
94
|
+
'',
|
|
95
|
+
'--- ShortcutXL update failure ---',
|
|
96
|
+
`time=${new Date().toISOString()}`,
|
|
97
|
+
`command=${command}`,
|
|
98
|
+
`exitCode=${String(exitCode)}`,
|
|
99
|
+
`signal=${String(signal)}`,
|
|
100
|
+
truncated ? `outputTruncatedAfterBytes=${MAX_CAPTURED_OUTPUT_BYTES}` : undefined,
|
|
101
|
+
output
|
|
102
|
+
]
|
|
103
|
+
.filter((line) => line !== undefined)
|
|
104
|
+
.join('\n'), 'utf-8');
|
|
105
|
+
return logPath;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
39
111
|
//# sourceMappingURL=update-action.js.map
|
package/package.json
CHANGED
|
Binary file
|