ocx 1.2.2 → 1.3.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/dist/index.js +2812 -409
- package/dist/index.js.map +30 -19
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -3761,6 +3761,333 @@ var require_cli_spinners = __commonJS((exports, module) => {
|
|
|
3761
3761
|
module.exports = spinners;
|
|
3762
3762
|
});
|
|
3763
3763
|
|
|
3764
|
+
// ../../node_modules/.bun/ignore@7.0.5/node_modules/ignore/index.js
|
|
3765
|
+
var require_ignore = __commonJS((exports, module) => {
|
|
3766
|
+
function makeArray(subject) {
|
|
3767
|
+
return Array.isArray(subject) ? subject : [subject];
|
|
3768
|
+
}
|
|
3769
|
+
var UNDEFINED = undefined;
|
|
3770
|
+
var EMPTY = "";
|
|
3771
|
+
var SPACE = " ";
|
|
3772
|
+
var ESCAPE = "\\";
|
|
3773
|
+
var REGEX_TEST_BLANK_LINE = /^\s+$/;
|
|
3774
|
+
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
3775
|
+
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
3776
|
+
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
3777
|
+
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
3778
|
+
var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
|
|
3779
|
+
var REGEX_TEST_TRAILING_SLASH = /\/$/;
|
|
3780
|
+
var SLASH2 = "/";
|
|
3781
|
+
var TMP_KEY_IGNORE = "node-ignore";
|
|
3782
|
+
if (typeof Symbol !== "undefined") {
|
|
3783
|
+
TMP_KEY_IGNORE = Symbol.for("node-ignore");
|
|
3784
|
+
}
|
|
3785
|
+
var KEY_IGNORE = TMP_KEY_IGNORE;
|
|
3786
|
+
var define2 = (object, key, value) => {
|
|
3787
|
+
Object.defineProperty(object, key, { value });
|
|
3788
|
+
return value;
|
|
3789
|
+
};
|
|
3790
|
+
var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
|
3791
|
+
var RETURN_FALSE = () => false;
|
|
3792
|
+
var sanitizeRange = (range) => range.replace(REGEX_REGEXP_RANGE, (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY);
|
|
3793
|
+
var cleanRangeBackSlash = (slashes) => {
|
|
3794
|
+
const { length } = slashes;
|
|
3795
|
+
return slashes.slice(0, length - length % 2);
|
|
3796
|
+
};
|
|
3797
|
+
var REPLACERS = [
|
|
3798
|
+
[
|
|
3799
|
+
/^\uFEFF/,
|
|
3800
|
+
() => EMPTY
|
|
3801
|
+
],
|
|
3802
|
+
[
|
|
3803
|
+
/((?:\\\\)*?)(\\?\s+)$/,
|
|
3804
|
+
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
|
|
3805
|
+
],
|
|
3806
|
+
[
|
|
3807
|
+
/(\\+?)\s/g,
|
|
3808
|
+
(_, m1) => {
|
|
3809
|
+
const { length } = m1;
|
|
3810
|
+
return m1.slice(0, length - length % 2) + SPACE;
|
|
3811
|
+
}
|
|
3812
|
+
],
|
|
3813
|
+
[
|
|
3814
|
+
/[\\$.|*+(){^]/g,
|
|
3815
|
+
(match) => `\\${match}`
|
|
3816
|
+
],
|
|
3817
|
+
[
|
|
3818
|
+
/(?!\\)\?/g,
|
|
3819
|
+
() => "[^/]"
|
|
3820
|
+
],
|
|
3821
|
+
[
|
|
3822
|
+
/^\//,
|
|
3823
|
+
() => "^"
|
|
3824
|
+
],
|
|
3825
|
+
[
|
|
3826
|
+
/\//g,
|
|
3827
|
+
() => "\\/"
|
|
3828
|
+
],
|
|
3829
|
+
[
|
|
3830
|
+
/^\^*\\\*\\\*\\\//,
|
|
3831
|
+
() => "^(?:.*\\/)?"
|
|
3832
|
+
],
|
|
3833
|
+
[
|
|
3834
|
+
/^(?=[^^])/,
|
|
3835
|
+
function startingReplacer() {
|
|
3836
|
+
return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
|
|
3837
|
+
}
|
|
3838
|
+
],
|
|
3839
|
+
[
|
|
3840
|
+
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
|
3841
|
+
(_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
3842
|
+
],
|
|
3843
|
+
[
|
|
3844
|
+
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
3845
|
+
(_, p1, p2) => {
|
|
3846
|
+
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
3847
|
+
return p1 + unescaped;
|
|
3848
|
+
}
|
|
3849
|
+
],
|
|
3850
|
+
[
|
|
3851
|
+
/\\\\\\(?=[$.|*+(){^])/g,
|
|
3852
|
+
() => ESCAPE
|
|
3853
|
+
],
|
|
3854
|
+
[
|
|
3855
|
+
/\\\\/g,
|
|
3856
|
+
() => ESCAPE
|
|
3857
|
+
],
|
|
3858
|
+
[
|
|
3859
|
+
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
|
|
3860
|
+
(match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]"
|
|
3861
|
+
],
|
|
3862
|
+
[
|
|
3863
|
+
/(?:[^*])$/,
|
|
3864
|
+
(match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
|
|
3865
|
+
]
|
|
3866
|
+
];
|
|
3867
|
+
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
|
|
3868
|
+
var MODE_IGNORE = "regex";
|
|
3869
|
+
var MODE_CHECK_IGNORE = "checkRegex";
|
|
3870
|
+
var UNDERSCORE = "_";
|
|
3871
|
+
var TRAILING_WILD_CARD_REPLACERS = {
|
|
3872
|
+
[MODE_IGNORE](_, p1) {
|
|
3873
|
+
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
3874
|
+
return `${prefix}(?=$|\\/$)`;
|
|
3875
|
+
},
|
|
3876
|
+
[MODE_CHECK_IGNORE](_, p1) {
|
|
3877
|
+
const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
|
|
3878
|
+
return `${prefix}(?=$|\\/$)`;
|
|
3879
|
+
}
|
|
3880
|
+
};
|
|
3881
|
+
var makeRegexPrefix = (pattern) => REPLACERS.reduce((prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)), pattern);
|
|
3882
|
+
var isString = (subject) => typeof subject === "string";
|
|
3883
|
+
var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
3884
|
+
var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
|
|
3885
|
+
|
|
3886
|
+
class IgnoreRule {
|
|
3887
|
+
constructor(pattern, mark, body, ignoreCase, negative, prefix) {
|
|
3888
|
+
this.pattern = pattern;
|
|
3889
|
+
this.mark = mark;
|
|
3890
|
+
this.negative = negative;
|
|
3891
|
+
define2(this, "body", body);
|
|
3892
|
+
define2(this, "ignoreCase", ignoreCase);
|
|
3893
|
+
define2(this, "regexPrefix", prefix);
|
|
3894
|
+
}
|
|
3895
|
+
get regex() {
|
|
3896
|
+
const key = UNDERSCORE + MODE_IGNORE;
|
|
3897
|
+
if (this[key]) {
|
|
3898
|
+
return this[key];
|
|
3899
|
+
}
|
|
3900
|
+
return this._make(MODE_IGNORE, key);
|
|
3901
|
+
}
|
|
3902
|
+
get checkRegex() {
|
|
3903
|
+
const key = UNDERSCORE + MODE_CHECK_IGNORE;
|
|
3904
|
+
if (this[key]) {
|
|
3905
|
+
return this[key];
|
|
3906
|
+
}
|
|
3907
|
+
return this._make(MODE_CHECK_IGNORE, key);
|
|
3908
|
+
}
|
|
3909
|
+
_make(mode, key) {
|
|
3910
|
+
const str = this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD, TRAILING_WILD_CARD_REPLACERS[mode]);
|
|
3911
|
+
const regex2 = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
|
|
3912
|
+
return define2(this, key, regex2);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
var createRule = ({
|
|
3916
|
+
pattern,
|
|
3917
|
+
mark
|
|
3918
|
+
}, ignoreCase) => {
|
|
3919
|
+
let negative = false;
|
|
3920
|
+
let body = pattern;
|
|
3921
|
+
if (body.indexOf("!") === 0) {
|
|
3922
|
+
negative = true;
|
|
3923
|
+
body = body.substr(1);
|
|
3924
|
+
}
|
|
3925
|
+
body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
|
|
3926
|
+
const regexPrefix = makeRegexPrefix(body);
|
|
3927
|
+
return new IgnoreRule(pattern, mark, body, ignoreCase, negative, regexPrefix);
|
|
3928
|
+
};
|
|
3929
|
+
|
|
3930
|
+
class RuleManager {
|
|
3931
|
+
constructor(ignoreCase) {
|
|
3932
|
+
this._ignoreCase = ignoreCase;
|
|
3933
|
+
this._rules = [];
|
|
3934
|
+
}
|
|
3935
|
+
_add(pattern) {
|
|
3936
|
+
if (pattern && pattern[KEY_IGNORE]) {
|
|
3937
|
+
this._rules = this._rules.concat(pattern._rules._rules);
|
|
3938
|
+
this._added = true;
|
|
3939
|
+
return;
|
|
3940
|
+
}
|
|
3941
|
+
if (isString(pattern)) {
|
|
3942
|
+
pattern = {
|
|
3943
|
+
pattern
|
|
3944
|
+
};
|
|
3945
|
+
}
|
|
3946
|
+
if (checkPattern(pattern.pattern)) {
|
|
3947
|
+
const rule = createRule(pattern, this._ignoreCase);
|
|
3948
|
+
this._added = true;
|
|
3949
|
+
this._rules.push(rule);
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
add(pattern) {
|
|
3953
|
+
this._added = false;
|
|
3954
|
+
makeArray(isString(pattern) ? splitPattern(pattern) : pattern).forEach(this._add, this);
|
|
3955
|
+
return this._added;
|
|
3956
|
+
}
|
|
3957
|
+
test(path5, checkUnignored, mode) {
|
|
3958
|
+
let ignored = false;
|
|
3959
|
+
let unignored = false;
|
|
3960
|
+
let matchedRule;
|
|
3961
|
+
this._rules.forEach((rule) => {
|
|
3962
|
+
const { negative } = rule;
|
|
3963
|
+
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
3964
|
+
return;
|
|
3965
|
+
}
|
|
3966
|
+
const matched = rule[mode].test(path5);
|
|
3967
|
+
if (!matched) {
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
ignored = !negative;
|
|
3971
|
+
unignored = negative;
|
|
3972
|
+
matchedRule = negative ? UNDEFINED : rule;
|
|
3973
|
+
});
|
|
3974
|
+
const ret = {
|
|
3975
|
+
ignored,
|
|
3976
|
+
unignored
|
|
3977
|
+
};
|
|
3978
|
+
if (matchedRule) {
|
|
3979
|
+
ret.rule = matchedRule;
|
|
3980
|
+
}
|
|
3981
|
+
return ret;
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
var throwError = (message, Ctor) => {
|
|
3985
|
+
throw new Ctor(message);
|
|
3986
|
+
};
|
|
3987
|
+
var checkPath = (path5, originalPath, doThrow) => {
|
|
3988
|
+
if (!isString(path5)) {
|
|
3989
|
+
return doThrow(`path must be a string, but got \`${originalPath}\``, TypeError);
|
|
3990
|
+
}
|
|
3991
|
+
if (!path5) {
|
|
3992
|
+
return doThrow(`path must not be empty`, TypeError);
|
|
3993
|
+
}
|
|
3994
|
+
if (checkPath.isNotRelative(path5)) {
|
|
3995
|
+
const r2 = "`path.relative()`d";
|
|
3996
|
+
return doThrow(`path should be a ${r2} string, but got "${originalPath}"`, RangeError);
|
|
3997
|
+
}
|
|
3998
|
+
return true;
|
|
3999
|
+
};
|
|
4000
|
+
var isNotRelative = (path5) => REGEX_TEST_INVALID_PATH.test(path5);
|
|
4001
|
+
checkPath.isNotRelative = isNotRelative;
|
|
4002
|
+
checkPath.convert = (p) => p;
|
|
4003
|
+
|
|
4004
|
+
class Ignore {
|
|
4005
|
+
constructor({
|
|
4006
|
+
ignorecase = true,
|
|
4007
|
+
ignoreCase = ignorecase,
|
|
4008
|
+
allowRelativePaths = false
|
|
4009
|
+
} = {}) {
|
|
4010
|
+
define2(this, KEY_IGNORE, true);
|
|
4011
|
+
this._rules = new RuleManager(ignoreCase);
|
|
4012
|
+
this._strictPathCheck = !allowRelativePaths;
|
|
4013
|
+
this._initCache();
|
|
4014
|
+
}
|
|
4015
|
+
_initCache() {
|
|
4016
|
+
this._ignoreCache = Object.create(null);
|
|
4017
|
+
this._testCache = Object.create(null);
|
|
4018
|
+
}
|
|
4019
|
+
add(pattern) {
|
|
4020
|
+
if (this._rules.add(pattern)) {
|
|
4021
|
+
this._initCache();
|
|
4022
|
+
}
|
|
4023
|
+
return this;
|
|
4024
|
+
}
|
|
4025
|
+
addPattern(pattern) {
|
|
4026
|
+
return this.add(pattern);
|
|
4027
|
+
}
|
|
4028
|
+
_test(originalPath, cache2, checkUnignored, slices) {
|
|
4029
|
+
const path5 = originalPath && checkPath.convert(originalPath);
|
|
4030
|
+
checkPath(path5, originalPath, this._strictPathCheck ? throwError : RETURN_FALSE);
|
|
4031
|
+
return this._t(path5, cache2, checkUnignored, slices);
|
|
4032
|
+
}
|
|
4033
|
+
checkIgnore(path5) {
|
|
4034
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path5)) {
|
|
4035
|
+
return this.test(path5);
|
|
4036
|
+
}
|
|
4037
|
+
const slices = path5.split(SLASH2).filter(Boolean);
|
|
4038
|
+
slices.pop();
|
|
4039
|
+
if (slices.length) {
|
|
4040
|
+
const parent = this._t(slices.join(SLASH2) + SLASH2, this._testCache, true, slices);
|
|
4041
|
+
if (parent.ignored) {
|
|
4042
|
+
return parent;
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
return this._rules.test(path5, false, MODE_CHECK_IGNORE);
|
|
4046
|
+
}
|
|
4047
|
+
_t(path5, cache2, checkUnignored, slices) {
|
|
4048
|
+
if (path5 in cache2) {
|
|
4049
|
+
return cache2[path5];
|
|
4050
|
+
}
|
|
4051
|
+
if (!slices) {
|
|
4052
|
+
slices = path5.split(SLASH2).filter(Boolean);
|
|
4053
|
+
}
|
|
4054
|
+
slices.pop();
|
|
4055
|
+
if (!slices.length) {
|
|
4056
|
+
return cache2[path5] = this._rules.test(path5, checkUnignored, MODE_IGNORE);
|
|
4057
|
+
}
|
|
4058
|
+
const parent = this._t(slices.join(SLASH2) + SLASH2, cache2, checkUnignored, slices);
|
|
4059
|
+
return cache2[path5] = parent.ignored ? parent : this._rules.test(path5, checkUnignored, MODE_IGNORE);
|
|
4060
|
+
}
|
|
4061
|
+
ignores(path5) {
|
|
4062
|
+
return this._test(path5, this._ignoreCache, false).ignored;
|
|
4063
|
+
}
|
|
4064
|
+
createFilter() {
|
|
4065
|
+
return (path5) => !this.ignores(path5);
|
|
4066
|
+
}
|
|
4067
|
+
filter(paths) {
|
|
4068
|
+
return makeArray(paths).filter(this.createFilter());
|
|
4069
|
+
}
|
|
4070
|
+
test(path5) {
|
|
4071
|
+
return this._test(path5, this._testCache, true);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
var factory = (options2) => new Ignore(options2);
|
|
4075
|
+
var isPathValid = (path5) => checkPath(path5 && checkPath.convert(path5), path5, RETURN_FALSE);
|
|
4076
|
+
var setupWindows = () => {
|
|
4077
|
+
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
4078
|
+
checkPath.convert = makePosix;
|
|
4079
|
+
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
4080
|
+
checkPath.isNotRelative = (path5) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path5) || isNotRelative(path5);
|
|
4081
|
+
};
|
|
4082
|
+
if (typeof process !== "undefined" && process.platform === "win32") {
|
|
4083
|
+
setupWindows();
|
|
4084
|
+
}
|
|
4085
|
+
module.exports = factory;
|
|
4086
|
+
factory.default = factory;
|
|
4087
|
+
module.exports.isPathValid = isPathValid;
|
|
4088
|
+
define2(module.exports, Symbol.for("setupWindows"), setupWindows);
|
|
4089
|
+
});
|
|
4090
|
+
|
|
3764
4091
|
// ../../node_modules/.bun/fuzzysort@3.1.0/node_modules/fuzzysort/fuzzysort.js
|
|
3765
4092
|
var require_fuzzysort = __commonJS((exports, module) => {
|
|
3766
4093
|
((root, UMD) => {
|
|
@@ -3949,8 +4276,8 @@ var require_fuzzysort = __commonJS((exports, module) => {
|
|
|
3949
4276
|
results.total = resultsLen + limitedCount;
|
|
3950
4277
|
return results;
|
|
3951
4278
|
};
|
|
3952
|
-
var highlight = (result,
|
|
3953
|
-
var callback = typeof
|
|
4279
|
+
var highlight = (result, open2 = "<b>", close = "</b>") => {
|
|
4280
|
+
var callback = typeof open2 === "function" ? open2 : undefined;
|
|
3954
4281
|
var target = result.target;
|
|
3955
4282
|
var targetLen = target.length;
|
|
3956
4283
|
var indexes = result.indexes;
|
|
@@ -3969,7 +4296,7 @@ var require_fuzzysort = __commonJS((exports, module) => {
|
|
|
3969
4296
|
parts.push(highlighted);
|
|
3970
4297
|
highlighted = "";
|
|
3971
4298
|
} else {
|
|
3972
|
-
highlighted +=
|
|
4299
|
+
highlighted += open2;
|
|
3973
4300
|
}
|
|
3974
4301
|
}
|
|
3975
4302
|
if (indexesI === indexes.length) {
|
|
@@ -4018,8 +4345,8 @@ var require_fuzzysort = __commonJS((exports, module) => {
|
|
|
4018
4345
|
set ["indexes"](indexes) {
|
|
4019
4346
|
return this._indexes = indexes;
|
|
4020
4347
|
}
|
|
4021
|
-
["highlight"](
|
|
4022
|
-
return highlight(this,
|
|
4348
|
+
["highlight"](open2, close) {
|
|
4349
|
+
return highlight(this, open2, close);
|
|
4023
4350
|
}
|
|
4024
4351
|
get ["score"]() {
|
|
4025
4352
|
return normalizeScore(this._score);
|
|
@@ -10165,8 +10492,16 @@ var ghostConfigSchema = exports_external.object({
|
|
|
10165
10492
|
$schema: exports_external.string().optional(),
|
|
10166
10493
|
registries: exports_external.record(registryConfigSchema).default({}),
|
|
10167
10494
|
componentPath: safeRelativePathSchema.optional(),
|
|
10168
|
-
|
|
10169
|
-
|
|
10495
|
+
exclude: exports_external.array(globPatternSchema).default([
|
|
10496
|
+
"**/AGENTS.md",
|
|
10497
|
+
"**/CLAUDE.md",
|
|
10498
|
+
"**/CONTEXT.md",
|
|
10499
|
+
".opencode",
|
|
10500
|
+
"opencode.jsonc",
|
|
10501
|
+
"opencode.json"
|
|
10502
|
+
]).describe("Glob patterns to exclude from the symlink farm"),
|
|
10503
|
+
include: exports_external.array(globPatternSchema).default([]).describe("Glob patterns to re-include from excluded set (for power users)"),
|
|
10504
|
+
renameWindow: exports_external.boolean().default(true).describe("Set terminal/tmux window name when launching OpenCode")
|
|
10170
10505
|
});
|
|
10171
10506
|
|
|
10172
10507
|
// src/utils/errors.ts
|
|
@@ -10237,6 +10572,13 @@ class IntegrityError extends OCXError {
|
|
|
10237
10572
|
this.name = "IntegrityError";
|
|
10238
10573
|
}
|
|
10239
10574
|
}
|
|
10575
|
+
|
|
10576
|
+
class SelfUpdateError extends OCXError {
|
|
10577
|
+
constructor(message) {
|
|
10578
|
+
super(message, "UPDATE_ERROR", EXIT_CODES.GENERAL);
|
|
10579
|
+
this.name = "SelfUpdateError";
|
|
10580
|
+
}
|
|
10581
|
+
}
|
|
10240
10582
|
class GhostConfigError extends OCXError {
|
|
10241
10583
|
constructor(message) {
|
|
10242
10584
|
super(message, "CONFIG_ERROR", EXIT_CODES.CONFIG);
|
|
@@ -10334,7 +10676,17 @@ var profileSchema = exports_external.object({
|
|
|
10334
10676
|
// src/profile/manager.ts
|
|
10335
10677
|
var DEFAULT_GHOST_CONFIG = {
|
|
10336
10678
|
$schema: "https://ocx.kdco.dev/schemas/ghost.json",
|
|
10337
|
-
registries: {}
|
|
10679
|
+
registries: {},
|
|
10680
|
+
exclude: [
|
|
10681
|
+
"**/AGENTS.md",
|
|
10682
|
+
"**/CLAUDE.md",
|
|
10683
|
+
"**/CONTEXT.md",
|
|
10684
|
+
".opencode",
|
|
10685
|
+
"opencode.jsonc",
|
|
10686
|
+
"opencode.json"
|
|
10687
|
+
],
|
|
10688
|
+
include: [],
|
|
10689
|
+
renameWindow: true
|
|
10338
10690
|
};
|
|
10339
10691
|
|
|
10340
10692
|
class ProfileManager {
|
|
@@ -10528,7 +10880,7 @@ class GhostConfigProvider {
|
|
|
10528
10880
|
// package.json
|
|
10529
10881
|
var package_default = {
|
|
10530
10882
|
name: "ocx",
|
|
10531
|
-
version: "1.
|
|
10883
|
+
version: "1.3.0",
|
|
10532
10884
|
description: "OCX CLI - ShadCN-style registry for OpenCode extensions. Install agents, plugins, skills, and MCP servers.",
|
|
10533
10885
|
author: "kdcokenny",
|
|
10534
10886
|
license: "MIT",
|
|
@@ -10576,9 +10928,11 @@ var package_default = {
|
|
|
10576
10928
|
test: "bun test"
|
|
10577
10929
|
},
|
|
10578
10930
|
dependencies: {
|
|
10931
|
+
chokidar: "^5.0.0",
|
|
10579
10932
|
commander: "^14.0.0",
|
|
10580
10933
|
diff: "^8.0.0",
|
|
10581
10934
|
fuzzysort: "^3.1.0",
|
|
10935
|
+
ignore: "^7.0.5",
|
|
10582
10936
|
"jsonc-parser": "3.3.1",
|
|
10583
10937
|
kleur: "^4.1.5",
|
|
10584
10938
|
ora: "^8.2.0",
|
|
@@ -10920,6 +11274,19 @@ function isContentIdentical(existing, incoming) {
|
|
|
10920
11274
|
var isCI = Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.JENKINS_URL || process.env.BUILDKITE);
|
|
10921
11275
|
var isTTY = Boolean(process.stdout.isTTY && !isCI);
|
|
10922
11276
|
var supportsColor = Boolean(isTTY && process.env.FORCE_COLOR !== "0" && process.env.NO_COLOR === undefined);
|
|
11277
|
+
function parseEnvBool(value, defaultValue) {
|
|
11278
|
+
if (value == null || value === "") {
|
|
11279
|
+
return defaultValue;
|
|
11280
|
+
}
|
|
11281
|
+
const normalized = value.trim().toLowerCase();
|
|
11282
|
+
if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on") {
|
|
11283
|
+
return true;
|
|
11284
|
+
}
|
|
11285
|
+
if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "off") {
|
|
11286
|
+
return false;
|
|
11287
|
+
}
|
|
11288
|
+
return defaultValue;
|
|
11289
|
+
}
|
|
10923
11290
|
// src/utils/git-context.ts
|
|
10924
11291
|
import { basename, resolve } from "path";
|
|
10925
11292
|
function getGitEnv() {
|
|
@@ -11194,6 +11561,15 @@ function handleError(error, options2 = {}) {
|
|
|
11194
11561
|
}
|
|
11195
11562
|
process.exit(EXIT_CODES.GENERAL);
|
|
11196
11563
|
}
|
|
11564
|
+
function wrapAction(action) {
|
|
11565
|
+
return async (...args) => {
|
|
11566
|
+
try {
|
|
11567
|
+
await action(...args);
|
|
11568
|
+
} catch (error) {
|
|
11569
|
+
handleError(error);
|
|
11570
|
+
}
|
|
11571
|
+
};
|
|
11572
|
+
}
|
|
11197
11573
|
function formatErrorAsJson(error) {
|
|
11198
11574
|
if (error instanceof OCXError) {
|
|
11199
11575
|
return {
|
|
@@ -12714,20 +13090,18 @@ function parseNpmSpecifier(specifier) {
|
|
|
12714
13090
|
function isNpmSpecifier(input) {
|
|
12715
13091
|
return input.trim().startsWith("npm:");
|
|
12716
13092
|
}
|
|
12717
|
-
async function validateNpmPackage(packageName) {
|
|
13093
|
+
async function validateNpmPackage(packageName, signal) {
|
|
12718
13094
|
validateNpmPackageName(packageName);
|
|
12719
13095
|
const encodedName = packageName.startsWith("@") ? `@${encodeURIComponent(packageName.slice(1))}` : encodeURIComponent(packageName);
|
|
12720
13096
|
const url = `${NPM_REGISTRY_BASE}/${encodedName}`;
|
|
12721
13097
|
try {
|
|
12722
|
-
const
|
|
12723
|
-
const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT_MS);
|
|
13098
|
+
const fetchSignal = signal ?? AbortSignal.timeout(NPM_FETCH_TIMEOUT_MS);
|
|
12724
13099
|
const response = await fetch(url, {
|
|
12725
|
-
signal:
|
|
13100
|
+
signal: fetchSignal,
|
|
12726
13101
|
headers: {
|
|
12727
13102
|
Accept: "application/json"
|
|
12728
13103
|
}
|
|
12729
13104
|
});
|
|
12730
|
-
clearTimeout(timeoutId);
|
|
12731
13105
|
if (response.status === 404) {
|
|
12732
13106
|
throw new NotFoundError(`npm package \`${packageName}\` not found on registry`);
|
|
12733
13107
|
}
|
|
@@ -12740,8 +13114,8 @@ async function validateNpmPackage(packageName) {
|
|
|
12740
13114
|
if (error instanceof NotFoundError || error instanceof NetworkError) {
|
|
12741
13115
|
throw error;
|
|
12742
13116
|
}
|
|
12743
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
12744
|
-
throw new NetworkError(`Request to npm registry timed out
|
|
13117
|
+
if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) {
|
|
13118
|
+
throw new NetworkError(`Request to npm registry timed out for package \`${packageName}\``);
|
|
12745
13119
|
}
|
|
12746
13120
|
const message = error instanceof Error ? error.message : String(error);
|
|
12747
13121
|
throw new NetworkError(`Failed to fetch npm package \`${packageName}\`: ${message}`);
|
|
@@ -12765,8 +13139,8 @@ function validateOpenCodePlugin(packageJson) {
|
|
|
12765
13139
|
}
|
|
12766
13140
|
return { valid: true, warnings };
|
|
12767
13141
|
}
|
|
12768
|
-
async function fetchPackageVersion(packageName, version) {
|
|
12769
|
-
const metadata = await validateNpmPackage(packageName);
|
|
13142
|
+
async function fetchPackageVersion(packageName, version, signal) {
|
|
13143
|
+
const metadata = await validateNpmPackage(packageName, signal);
|
|
12770
13144
|
const resolvedVersion = version ?? metadata["dist-tags"].latest;
|
|
12771
13145
|
const versionData = metadata.versions[resolvedVersion];
|
|
12772
13146
|
if (!versionData) {
|
|
@@ -14387,317 +14761,1905 @@ async function runGhostInit(options2) {
|
|
|
14387
14761
|
}
|
|
14388
14762
|
}
|
|
14389
14763
|
|
|
14390
|
-
// src/
|
|
14391
|
-
import {
|
|
14392
|
-
import {
|
|
14393
|
-
import
|
|
14394
|
-
|
|
14395
|
-
|
|
14396
|
-
|
|
14397
|
-
}
|
|
14398
|
-
|
|
14399
|
-
|
|
14400
|
-
|
|
14401
|
-
|
|
14402
|
-
|
|
14764
|
+
// src/commands/ghost/opencode.ts
|
|
14765
|
+
import { renameSync, rmSync } from "fs";
|
|
14766
|
+
import { copyFile, mkdir as mkdir5, readdir as readdir5 } from "fs/promises";
|
|
14767
|
+
import path7 from "path";
|
|
14768
|
+
var {Glob: Glob3 } = globalThis.Bun;
|
|
14769
|
+
|
|
14770
|
+
// src/utils/file-sync.ts
|
|
14771
|
+
import { existsSync as existsSync2, lstatSync, mkdirSync, readFileSync, rmdirSync, unlinkSync } from "fs";
|
|
14772
|
+
import { dirname as dirname5, join as join6, relative as relative4 } from "path";
|
|
14773
|
+
|
|
14774
|
+
// ../../node_modules/.bun/chokidar@5.0.0/node_modules/chokidar/index.js
|
|
14775
|
+
import { EventEmitter } from "events";
|
|
14776
|
+
import { stat as statcb, Stats } from "fs";
|
|
14777
|
+
import { readdir as readdir3, stat as stat4 } from "fs/promises";
|
|
14778
|
+
import * as sp2 from "path";
|
|
14779
|
+
|
|
14780
|
+
// ../../node_modules/.bun/readdirp@5.0.0/node_modules/readdirp/index.js
|
|
14781
|
+
import { lstat, readdir as readdir2, realpath, stat as stat2 } from "fs/promises";
|
|
14782
|
+
import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "path";
|
|
14783
|
+
import { Readable } from "stream";
|
|
14784
|
+
var EntryTypes = {
|
|
14785
|
+
FILE_TYPE: "files",
|
|
14786
|
+
DIR_TYPE: "directories",
|
|
14787
|
+
FILE_DIR_TYPE: "files_directories",
|
|
14788
|
+
EVERYTHING_TYPE: "all"
|
|
14789
|
+
};
|
|
14790
|
+
var defaultOptions = {
|
|
14791
|
+
root: ".",
|
|
14792
|
+
fileFilter: (_entryInfo) => true,
|
|
14793
|
+
directoryFilter: (_entryInfo) => true,
|
|
14794
|
+
type: EntryTypes.FILE_TYPE,
|
|
14795
|
+
lstat: false,
|
|
14796
|
+
depth: 2147483648,
|
|
14797
|
+
alwaysStat: false,
|
|
14798
|
+
highWaterMark: 4096
|
|
14799
|
+
};
|
|
14800
|
+
Object.freeze(defaultOptions);
|
|
14801
|
+
var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR";
|
|
14802
|
+
var NORMAL_FLOW_ERRORS = new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]);
|
|
14803
|
+
var ALL_TYPES = [
|
|
14804
|
+
EntryTypes.DIR_TYPE,
|
|
14805
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
14806
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
14807
|
+
EntryTypes.FILE_TYPE
|
|
14808
|
+
];
|
|
14809
|
+
var DIR_TYPES = new Set([
|
|
14810
|
+
EntryTypes.DIR_TYPE,
|
|
14811
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
14812
|
+
EntryTypes.FILE_DIR_TYPE
|
|
14813
|
+
]);
|
|
14814
|
+
var FILE_TYPES = new Set([
|
|
14815
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
14816
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
14817
|
+
EntryTypes.FILE_TYPE
|
|
14818
|
+
]);
|
|
14819
|
+
var isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
|
|
14820
|
+
var wantBigintFsStats = process.platform === "win32";
|
|
14821
|
+
var emptyFn = (_entryInfo) => true;
|
|
14822
|
+
var normalizeFilter = (filter) => {
|
|
14823
|
+
if (filter === undefined)
|
|
14824
|
+
return emptyFn;
|
|
14825
|
+
if (typeof filter === "function")
|
|
14826
|
+
return filter;
|
|
14827
|
+
if (typeof filter === "string") {
|
|
14828
|
+
const fl = filter.trim();
|
|
14829
|
+
return (entry) => entry.basename === fl;
|
|
14830
|
+
}
|
|
14831
|
+
if (Array.isArray(filter)) {
|
|
14832
|
+
const trItems = filter.map((item) => item.trim());
|
|
14833
|
+
return (entry) => trItems.some((f) => entry.basename === f);
|
|
14834
|
+
}
|
|
14835
|
+
return emptyFn;
|
|
14836
|
+
};
|
|
14837
|
+
|
|
14838
|
+
class ReaddirpStream extends Readable {
|
|
14839
|
+
parents;
|
|
14840
|
+
reading;
|
|
14841
|
+
parent;
|
|
14842
|
+
_stat;
|
|
14843
|
+
_maxDepth;
|
|
14844
|
+
_wantsDir;
|
|
14845
|
+
_wantsFile;
|
|
14846
|
+
_wantsEverything;
|
|
14847
|
+
_root;
|
|
14848
|
+
_isDirent;
|
|
14849
|
+
_statsProp;
|
|
14850
|
+
_rdOptions;
|
|
14851
|
+
_fileFilter;
|
|
14852
|
+
_directoryFilter;
|
|
14853
|
+
constructor(options2 = {}) {
|
|
14854
|
+
super({
|
|
14855
|
+
objectMode: true,
|
|
14856
|
+
autoDestroy: true,
|
|
14857
|
+
highWaterMark: options2.highWaterMark
|
|
14858
|
+
});
|
|
14859
|
+
const opts = { ...defaultOptions, ...options2 };
|
|
14860
|
+
const { root, type } = opts;
|
|
14861
|
+
this._fileFilter = normalizeFilter(opts.fileFilter);
|
|
14862
|
+
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
14863
|
+
const statMethod = opts.lstat ? lstat : stat2;
|
|
14864
|
+
if (wantBigintFsStats) {
|
|
14865
|
+
this._stat = (path5) => statMethod(path5, { bigint: true });
|
|
14866
|
+
} else {
|
|
14867
|
+
this._stat = statMethod;
|
|
14868
|
+
}
|
|
14869
|
+
this._maxDepth = opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
|
|
14870
|
+
this._wantsDir = type ? DIR_TYPES.has(type) : false;
|
|
14871
|
+
this._wantsFile = type ? FILE_TYPES.has(type) : false;
|
|
14872
|
+
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
14873
|
+
this._root = presolve(root);
|
|
14874
|
+
this._isDirent = !opts.alwaysStat;
|
|
14875
|
+
this._statsProp = this._isDirent ? "dirent" : "stats";
|
|
14876
|
+
this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
|
|
14877
|
+
this.parents = [this._exploreDir(root, 1)];
|
|
14878
|
+
this.reading = false;
|
|
14879
|
+
this.parent = undefined;
|
|
14880
|
+
}
|
|
14881
|
+
async _read(batch) {
|
|
14882
|
+
if (this.reading)
|
|
14883
|
+
return;
|
|
14884
|
+
this.reading = true;
|
|
14403
14885
|
try {
|
|
14404
|
-
|
|
14405
|
-
|
|
14406
|
-
|
|
14407
|
-
|
|
14886
|
+
while (!this.destroyed && batch > 0) {
|
|
14887
|
+
const par = this.parent;
|
|
14888
|
+
const fil = par && par.files;
|
|
14889
|
+
if (fil && fil.length > 0) {
|
|
14890
|
+
const { path: path5, depth } = par;
|
|
14891
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path5));
|
|
14892
|
+
const awaited = await Promise.all(slice);
|
|
14893
|
+
for (const entry of awaited) {
|
|
14894
|
+
if (!entry)
|
|
14895
|
+
continue;
|
|
14896
|
+
if (this.destroyed)
|
|
14897
|
+
return;
|
|
14898
|
+
const entryType = await this._getEntryType(entry);
|
|
14899
|
+
if (entryType === "directory" && this._directoryFilter(entry)) {
|
|
14900
|
+
if (depth <= this._maxDepth) {
|
|
14901
|
+
this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
|
|
14902
|
+
}
|
|
14903
|
+
if (this._wantsDir) {
|
|
14904
|
+
this.push(entry);
|
|
14905
|
+
batch--;
|
|
14906
|
+
}
|
|
14907
|
+
} else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) {
|
|
14908
|
+
if (this._wantsFile) {
|
|
14909
|
+
this.push(entry);
|
|
14910
|
+
batch--;
|
|
14911
|
+
}
|
|
14912
|
+
}
|
|
14913
|
+
}
|
|
14914
|
+
} else {
|
|
14915
|
+
const parent = this.parents.pop();
|
|
14916
|
+
if (!parent) {
|
|
14917
|
+
this.push(null);
|
|
14918
|
+
break;
|
|
14919
|
+
}
|
|
14920
|
+
this.parent = await parent;
|
|
14921
|
+
if (this.destroyed)
|
|
14922
|
+
return;
|
|
14923
|
+
}
|
|
14924
|
+
}
|
|
14925
|
+
} catch (error) {
|
|
14926
|
+
this.destroy(error);
|
|
14927
|
+
} finally {
|
|
14928
|
+
this.reading = false;
|
|
14408
14929
|
}
|
|
14409
|
-
} catch {
|
|
14410
|
-
return false;
|
|
14411
|
-
}
|
|
14412
|
-
}
|
|
14413
|
-
async function migrate(dryRun = false) {
|
|
14414
|
-
const result = {
|
|
14415
|
-
success: false,
|
|
14416
|
-
migratedFiles: [],
|
|
14417
|
-
backupPath: null,
|
|
14418
|
-
errors: []
|
|
14419
|
-
};
|
|
14420
|
-
const legacyDir = getLegacyConfigDir();
|
|
14421
|
-
const profilesDir = getProfilesDir();
|
|
14422
|
-
try {
|
|
14423
|
-
await stat2(legacyDir);
|
|
14424
|
-
} catch {
|
|
14425
|
-
result.errors.push(`No legacy config found at ${legacyDir}`);
|
|
14426
|
-
return result;
|
|
14427
|
-
}
|
|
14428
|
-
try {
|
|
14429
|
-
await stat2(profilesDir);
|
|
14430
|
-
result.errors.push(`Profiles directory already exists at ${profilesDir}`);
|
|
14431
|
-
return result;
|
|
14432
|
-
} catch {}
|
|
14433
|
-
const legacyFiles = await readdir2(legacyDir);
|
|
14434
|
-
const filesToMigrate = legacyFiles.filter((f) => f === "ghost.jsonc" || f === "opencode.jsonc" || f === "AGENTS.md");
|
|
14435
|
-
if (filesToMigrate.length === 0) {
|
|
14436
|
-
result.errors.push("No migratable files found in legacy config");
|
|
14437
|
-
return result;
|
|
14438
|
-
}
|
|
14439
|
-
if (dryRun) {
|
|
14440
|
-
result.migratedFiles = filesToMigrate.map((f) => path5.join(legacyDir, f));
|
|
14441
|
-
result.backupPath = `${legacyDir}.bak`;
|
|
14442
|
-
result.success = true;
|
|
14443
|
-
return result;
|
|
14444
14930
|
}
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
const defaultProfileDir = getProfileDir("default");
|
|
14448
|
-
for (const file of filesToMigrate) {
|
|
14449
|
-
const srcPath = path5.join(legacyDir, file);
|
|
14450
|
-
const destPath = path5.join(defaultProfileDir, file);
|
|
14451
|
-
await Bun.write(destPath, Bun.file(srcPath));
|
|
14452
|
-
await chmod(destPath, 384);
|
|
14453
|
-
result.migratedFiles.push(file);
|
|
14454
|
-
}
|
|
14455
|
-
const backupPath = `${legacyDir}.bak`;
|
|
14456
|
-
await rename2(legacyDir, backupPath);
|
|
14457
|
-
result.backupPath = backupPath;
|
|
14458
|
-
result.success = true;
|
|
14459
|
-
return result;
|
|
14460
|
-
}
|
|
14461
|
-
|
|
14462
|
-
// src/commands/ghost/migrate.ts
|
|
14463
|
-
function registerGhostMigrateCommand(parent) {
|
|
14464
|
-
parent.command("migrate").description("Migrate from legacy ~/.config/ocx/ to new profiles system").option("--dry-run", "Preview changes without making them").action(async (options2) => {
|
|
14931
|
+
async _exploreDir(path5, depth) {
|
|
14932
|
+
let files;
|
|
14465
14933
|
try {
|
|
14466
|
-
await
|
|
14934
|
+
files = await readdir2(path5, this._rdOptions);
|
|
14467
14935
|
} catch (error) {
|
|
14468
|
-
|
|
14936
|
+
this._onError(error);
|
|
14469
14937
|
}
|
|
14470
|
-
|
|
14471
|
-
}
|
|
14472
|
-
async function runMigrate(options2) {
|
|
14473
|
-
const needsMigrationResult = await needsMigration();
|
|
14474
|
-
if (!needsMigrationResult) {
|
|
14475
|
-
const legacyDir = getLegacyConfigDir();
|
|
14476
|
-
const profilesDir = getProfilesDir();
|
|
14477
|
-
console.log("No migration needed.");
|
|
14478
|
-
console.log(` Legacy config: ${legacyDir} (not found)`);
|
|
14479
|
-
console.log(` Profiles: ${profilesDir}`);
|
|
14480
|
-
return;
|
|
14938
|
+
return { files, depth, path: path5 };
|
|
14481
14939
|
}
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14485
|
-
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
process.exit(1);
|
|
14493
|
-
}
|
|
14494
|
-
if (options2.dryRun) {
|
|
14495
|
-
console.log("Migration preview:");
|
|
14496
|
-
console.log(`
|
|
14497
|
-
Files to migrate to default profile:`);
|
|
14498
|
-
for (const file of result.migratedFiles) {
|
|
14499
|
-
console.log(` ${file}`);
|
|
14500
|
-
}
|
|
14501
|
-
console.log(`
|
|
14502
|
-
Legacy config will be renamed to:`);
|
|
14503
|
-
console.log(` ${result.backupPath}`);
|
|
14504
|
-
console.log(`
|
|
14505
|
-
Run without --dry-run to perform migration.`);
|
|
14506
|
-
} else {
|
|
14507
|
-
console.log("Migration complete!");
|
|
14508
|
-
console.log(`
|
|
14509
|
-
Migrated to default profile:`);
|
|
14510
|
-
for (const file of result.migratedFiles) {
|
|
14511
|
-
console.log(` ${file}`);
|
|
14940
|
+
async _formatEntry(dirent, path5) {
|
|
14941
|
+
let entry;
|
|
14942
|
+
const basename2 = this._isDirent ? dirent.name : dirent;
|
|
14943
|
+
try {
|
|
14944
|
+
const fullPath = presolve(pjoin(path5, basename2));
|
|
14945
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename2 };
|
|
14946
|
+
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
14947
|
+
} catch (err) {
|
|
14948
|
+
this._onError(err);
|
|
14949
|
+
return;
|
|
14512
14950
|
}
|
|
14513
|
-
|
|
14514
|
-
Legacy config backed up to:`);
|
|
14515
|
-
console.log(` ${result.backupPath}`);
|
|
14516
|
-
console.log(`
|
|
14517
|
-
Profile location:`);
|
|
14518
|
-
console.log(` ${getProfileDir("default")}`);
|
|
14951
|
+
return entry;
|
|
14519
14952
|
}
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
import path8 from "path";
|
|
14526
|
-
|
|
14527
|
-
// src/utils/opencode-discovery.ts
|
|
14528
|
-
import { exists } from "fs/promises";
|
|
14529
|
-
import { dirname as dirname3, join as join4 } from "path";
|
|
14530
|
-
var CONFIG_FILES = ["opencode.jsonc", "opencode.json"];
|
|
14531
|
-
var RULE_FILES = ["AGENTS.md", "CLAUDE.md", "CONTEXT.md"];
|
|
14532
|
-
var CONFIG_DIRS = [".opencode"];
|
|
14533
|
-
async function findUp(target, start, stop) {
|
|
14534
|
-
let current = start;
|
|
14535
|
-
const result = [];
|
|
14536
|
-
while (true) {
|
|
14537
|
-
const search = join4(current, target);
|
|
14538
|
-
if (await exists(search).catch(() => false)) {
|
|
14539
|
-
result.push(search);
|
|
14953
|
+
_onError(err) {
|
|
14954
|
+
if (isNormalFlowError(err) && !this.destroyed) {
|
|
14955
|
+
this.emit("warn", err);
|
|
14956
|
+
} else {
|
|
14957
|
+
this.destroy(err);
|
|
14540
14958
|
}
|
|
14541
|
-
if (stop === current)
|
|
14542
|
-
break;
|
|
14543
|
-
const parent = dirname3(current);
|
|
14544
|
-
if (parent === current)
|
|
14545
|
-
break;
|
|
14546
|
-
current = parent;
|
|
14547
14959
|
}
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14551
|
-
const { targets, start, stop } = options2;
|
|
14552
|
-
let current = start;
|
|
14553
|
-
while (true) {
|
|
14554
|
-
for (const target of targets) {
|
|
14555
|
-
const search = join4(current, target);
|
|
14556
|
-
if (await exists(search).catch(() => false)) {
|
|
14557
|
-
yield search;
|
|
14558
|
-
}
|
|
14960
|
+
async _getEntryType(entry) {
|
|
14961
|
+
if (!entry && this._statsProp in entry) {
|
|
14962
|
+
return "";
|
|
14559
14963
|
}
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
14563
|
-
if (
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
14573
|
-
|
|
14964
|
+
const stats = entry[this._statsProp];
|
|
14965
|
+
if (stats.isFile())
|
|
14966
|
+
return "file";
|
|
14967
|
+
if (stats.isDirectory())
|
|
14968
|
+
return "directory";
|
|
14969
|
+
if (stats && stats.isSymbolicLink()) {
|
|
14970
|
+
const full = entry.fullPath;
|
|
14971
|
+
try {
|
|
14972
|
+
const entryRealPath = await realpath(full);
|
|
14973
|
+
const entryRealPathStats = await lstat(entryRealPath);
|
|
14974
|
+
if (entryRealPathStats.isFile()) {
|
|
14975
|
+
return "file";
|
|
14976
|
+
}
|
|
14977
|
+
if (entryRealPathStats.isDirectory()) {
|
|
14978
|
+
const len = entryRealPath.length;
|
|
14979
|
+
if (full.startsWith(entryRealPath) && full.substr(len, 1) === psep) {
|
|
14980
|
+
const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
|
|
14981
|
+
recursiveError.code = RECURSIVE_ERROR_CODE;
|
|
14982
|
+
return this._onError(recursiveError);
|
|
14983
|
+
}
|
|
14984
|
+
return "directory";
|
|
14985
|
+
}
|
|
14986
|
+
} catch (error) {
|
|
14987
|
+
this._onError(error);
|
|
14988
|
+
return "";
|
|
14989
|
+
}
|
|
14574
14990
|
}
|
|
14575
14991
|
}
|
|
14576
|
-
|
|
14577
|
-
const
|
|
14578
|
-
|
|
14579
|
-
excluded.add(path6);
|
|
14580
|
-
}
|
|
14992
|
+
_includeAsFile(entry) {
|
|
14993
|
+
const stats = entry && entry[this._statsProp];
|
|
14994
|
+
return stats && this._wantsEverything && !stats.isDirectory();
|
|
14581
14995
|
}
|
|
14582
|
-
|
|
14583
|
-
|
|
14996
|
+
}
|
|
14997
|
+
function readdirp(root, options2 = {}) {
|
|
14998
|
+
let type = options2.entryType || options2.type;
|
|
14999
|
+
if (type === "both")
|
|
15000
|
+
type = EntryTypes.FILE_DIR_TYPE;
|
|
15001
|
+
if (type)
|
|
15002
|
+
options2.type = type;
|
|
15003
|
+
if (!root) {
|
|
15004
|
+
throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)");
|
|
15005
|
+
} else if (typeof root !== "string") {
|
|
15006
|
+
throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)");
|
|
15007
|
+
} else if (type && !ALL_TYPES.includes(type)) {
|
|
15008
|
+
throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`);
|
|
14584
15009
|
}
|
|
14585
|
-
|
|
15010
|
+
options2.root = root;
|
|
15011
|
+
return new ReaddirpStream(options2);
|
|
14586
15012
|
}
|
|
14587
15013
|
|
|
14588
|
-
//
|
|
14589
|
-
import {
|
|
14590
|
-
import {
|
|
14591
|
-
import {
|
|
14592
|
-
import
|
|
14593
|
-
|
|
14594
|
-
|
|
14595
|
-
|
|
14596
|
-
var
|
|
14597
|
-
|
|
14598
|
-
|
|
14599
|
-
|
|
14600
|
-
|
|
14601
|
-
|
|
14602
|
-
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
|
|
14606
|
-
|
|
14607
|
-
|
|
14608
|
-
|
|
14609
|
-
|
|
14610
|
-
|
|
14611
|
-
|
|
14612
|
-
|
|
14613
|
-
|
|
14614
|
-
|
|
14615
|
-
|
|
14616
|
-
|
|
14617
|
-
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
|
|
14621
|
-
|
|
14622
|
-
|
|
14623
|
-
|
|
14624
|
-
|
|
14625
|
-
|
|
14626
|
-
|
|
14627
|
-
|
|
14628
|
-
|
|
14629
|
-
|
|
14630
|
-
|
|
14631
|
-
|
|
14632
|
-
|
|
14633
|
-
|
|
14634
|
-
|
|
15014
|
+
// ../../node_modules/.bun/chokidar@5.0.0/node_modules/chokidar/handler.js
|
|
15015
|
+
import { watch as fs_watch, unwatchFile, watchFile } from "fs";
|
|
15016
|
+
import { realpath as fsrealpath, lstat as lstat2, open, stat as stat3 } from "fs/promises";
|
|
15017
|
+
import { type as osType } from "os";
|
|
15018
|
+
import * as sp from "path";
|
|
15019
|
+
var STR_DATA = "data";
|
|
15020
|
+
var STR_END = "end";
|
|
15021
|
+
var STR_CLOSE = "close";
|
|
15022
|
+
var EMPTY_FN = () => {};
|
|
15023
|
+
var pl = process.platform;
|
|
15024
|
+
var isWindows = pl === "win32";
|
|
15025
|
+
var isMacos = pl === "darwin";
|
|
15026
|
+
var isLinux = pl === "linux";
|
|
15027
|
+
var isFreeBSD = pl === "freebsd";
|
|
15028
|
+
var isIBMi = osType() === "OS400";
|
|
15029
|
+
var EVENTS = {
|
|
15030
|
+
ALL: "all",
|
|
15031
|
+
READY: "ready",
|
|
15032
|
+
ADD: "add",
|
|
15033
|
+
CHANGE: "change",
|
|
15034
|
+
ADD_DIR: "addDir",
|
|
15035
|
+
UNLINK: "unlink",
|
|
15036
|
+
UNLINK_DIR: "unlinkDir",
|
|
15037
|
+
RAW: "raw",
|
|
15038
|
+
ERROR: "error"
|
|
15039
|
+
};
|
|
15040
|
+
var EV = EVENTS;
|
|
15041
|
+
var THROTTLE_MODE_WATCH = "watch";
|
|
15042
|
+
var statMethods = { lstat: lstat2, stat: stat3 };
|
|
15043
|
+
var KEY_LISTENERS = "listeners";
|
|
15044
|
+
var KEY_ERR = "errHandlers";
|
|
15045
|
+
var KEY_RAW = "rawEmitters";
|
|
15046
|
+
var HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
|
|
15047
|
+
var binaryExtensions = new Set([
|
|
15048
|
+
"3dm",
|
|
15049
|
+
"3ds",
|
|
15050
|
+
"3g2",
|
|
15051
|
+
"3gp",
|
|
15052
|
+
"7z",
|
|
15053
|
+
"a",
|
|
15054
|
+
"aac",
|
|
15055
|
+
"adp",
|
|
15056
|
+
"afdesign",
|
|
15057
|
+
"afphoto",
|
|
15058
|
+
"afpub",
|
|
15059
|
+
"ai",
|
|
15060
|
+
"aif",
|
|
15061
|
+
"aiff",
|
|
15062
|
+
"alz",
|
|
15063
|
+
"ape",
|
|
15064
|
+
"apk",
|
|
15065
|
+
"appimage",
|
|
15066
|
+
"ar",
|
|
15067
|
+
"arj",
|
|
15068
|
+
"asf",
|
|
15069
|
+
"au",
|
|
15070
|
+
"avi",
|
|
15071
|
+
"bak",
|
|
15072
|
+
"baml",
|
|
15073
|
+
"bh",
|
|
15074
|
+
"bin",
|
|
15075
|
+
"bk",
|
|
15076
|
+
"bmp",
|
|
15077
|
+
"btif",
|
|
15078
|
+
"bz2",
|
|
15079
|
+
"bzip2",
|
|
15080
|
+
"cab",
|
|
15081
|
+
"caf",
|
|
15082
|
+
"cgm",
|
|
15083
|
+
"class",
|
|
15084
|
+
"cmx",
|
|
15085
|
+
"cpio",
|
|
15086
|
+
"cr2",
|
|
15087
|
+
"cur",
|
|
15088
|
+
"dat",
|
|
15089
|
+
"dcm",
|
|
15090
|
+
"deb",
|
|
15091
|
+
"dex",
|
|
15092
|
+
"djvu",
|
|
15093
|
+
"dll",
|
|
15094
|
+
"dmg",
|
|
15095
|
+
"dng",
|
|
15096
|
+
"doc",
|
|
15097
|
+
"docm",
|
|
15098
|
+
"docx",
|
|
15099
|
+
"dot",
|
|
15100
|
+
"dotm",
|
|
15101
|
+
"dra",
|
|
15102
|
+
"DS_Store",
|
|
15103
|
+
"dsk",
|
|
15104
|
+
"dts",
|
|
15105
|
+
"dtshd",
|
|
15106
|
+
"dvb",
|
|
15107
|
+
"dwg",
|
|
15108
|
+
"dxf",
|
|
15109
|
+
"ecelp4800",
|
|
15110
|
+
"ecelp7470",
|
|
15111
|
+
"ecelp9600",
|
|
15112
|
+
"egg",
|
|
15113
|
+
"eol",
|
|
15114
|
+
"eot",
|
|
15115
|
+
"epub",
|
|
15116
|
+
"exe",
|
|
15117
|
+
"f4v",
|
|
15118
|
+
"fbs",
|
|
15119
|
+
"fh",
|
|
15120
|
+
"fla",
|
|
15121
|
+
"flac",
|
|
15122
|
+
"flatpak",
|
|
15123
|
+
"fli",
|
|
15124
|
+
"flv",
|
|
15125
|
+
"fpx",
|
|
15126
|
+
"fst",
|
|
15127
|
+
"fvt",
|
|
15128
|
+
"g3",
|
|
15129
|
+
"gh",
|
|
15130
|
+
"gif",
|
|
15131
|
+
"graffle",
|
|
15132
|
+
"gz",
|
|
15133
|
+
"gzip",
|
|
15134
|
+
"h261",
|
|
15135
|
+
"h263",
|
|
15136
|
+
"h264",
|
|
15137
|
+
"icns",
|
|
15138
|
+
"ico",
|
|
15139
|
+
"ief",
|
|
15140
|
+
"img",
|
|
15141
|
+
"ipa",
|
|
15142
|
+
"iso",
|
|
15143
|
+
"jar",
|
|
15144
|
+
"jpeg",
|
|
15145
|
+
"jpg",
|
|
15146
|
+
"jpgv",
|
|
15147
|
+
"jpm",
|
|
15148
|
+
"jxr",
|
|
15149
|
+
"key",
|
|
15150
|
+
"ktx",
|
|
15151
|
+
"lha",
|
|
15152
|
+
"lib",
|
|
15153
|
+
"lvp",
|
|
15154
|
+
"lz",
|
|
15155
|
+
"lzh",
|
|
15156
|
+
"lzma",
|
|
15157
|
+
"lzo",
|
|
15158
|
+
"m3u",
|
|
15159
|
+
"m4a",
|
|
15160
|
+
"m4v",
|
|
15161
|
+
"mar",
|
|
15162
|
+
"mdi",
|
|
15163
|
+
"mht",
|
|
15164
|
+
"mid",
|
|
15165
|
+
"midi",
|
|
15166
|
+
"mj2",
|
|
15167
|
+
"mka",
|
|
15168
|
+
"mkv",
|
|
15169
|
+
"mmr",
|
|
15170
|
+
"mng",
|
|
15171
|
+
"mobi",
|
|
15172
|
+
"mov",
|
|
15173
|
+
"movie",
|
|
15174
|
+
"mp3",
|
|
15175
|
+
"mp4",
|
|
15176
|
+
"mp4a",
|
|
15177
|
+
"mpeg",
|
|
15178
|
+
"mpg",
|
|
15179
|
+
"mpga",
|
|
15180
|
+
"mxu",
|
|
15181
|
+
"nef",
|
|
15182
|
+
"npx",
|
|
15183
|
+
"numbers",
|
|
15184
|
+
"nupkg",
|
|
15185
|
+
"o",
|
|
15186
|
+
"odp",
|
|
15187
|
+
"ods",
|
|
15188
|
+
"odt",
|
|
15189
|
+
"oga",
|
|
15190
|
+
"ogg",
|
|
15191
|
+
"ogv",
|
|
15192
|
+
"otf",
|
|
15193
|
+
"ott",
|
|
15194
|
+
"pages",
|
|
15195
|
+
"pbm",
|
|
15196
|
+
"pcx",
|
|
15197
|
+
"pdb",
|
|
15198
|
+
"pdf",
|
|
15199
|
+
"pea",
|
|
15200
|
+
"pgm",
|
|
15201
|
+
"pic",
|
|
15202
|
+
"png",
|
|
15203
|
+
"pnm",
|
|
15204
|
+
"pot",
|
|
15205
|
+
"potm",
|
|
15206
|
+
"potx",
|
|
15207
|
+
"ppa",
|
|
15208
|
+
"ppam",
|
|
15209
|
+
"ppm",
|
|
15210
|
+
"pps",
|
|
15211
|
+
"ppsm",
|
|
15212
|
+
"ppsx",
|
|
15213
|
+
"ppt",
|
|
15214
|
+
"pptm",
|
|
15215
|
+
"pptx",
|
|
15216
|
+
"psd",
|
|
15217
|
+
"pya",
|
|
15218
|
+
"pyc",
|
|
15219
|
+
"pyo",
|
|
15220
|
+
"pyv",
|
|
15221
|
+
"qt",
|
|
15222
|
+
"rar",
|
|
15223
|
+
"ras",
|
|
15224
|
+
"raw",
|
|
15225
|
+
"resources",
|
|
15226
|
+
"rgb",
|
|
15227
|
+
"rip",
|
|
15228
|
+
"rlc",
|
|
15229
|
+
"rmf",
|
|
15230
|
+
"rmvb",
|
|
15231
|
+
"rpm",
|
|
15232
|
+
"rtf",
|
|
15233
|
+
"rz",
|
|
15234
|
+
"s3m",
|
|
15235
|
+
"s7z",
|
|
15236
|
+
"scpt",
|
|
15237
|
+
"sgi",
|
|
15238
|
+
"shar",
|
|
15239
|
+
"snap",
|
|
15240
|
+
"sil",
|
|
15241
|
+
"sketch",
|
|
15242
|
+
"slk",
|
|
15243
|
+
"smv",
|
|
15244
|
+
"snk",
|
|
15245
|
+
"so",
|
|
15246
|
+
"stl",
|
|
15247
|
+
"suo",
|
|
15248
|
+
"sub",
|
|
15249
|
+
"swf",
|
|
15250
|
+
"tar",
|
|
15251
|
+
"tbz",
|
|
15252
|
+
"tbz2",
|
|
15253
|
+
"tga",
|
|
15254
|
+
"tgz",
|
|
15255
|
+
"thmx",
|
|
15256
|
+
"tif",
|
|
15257
|
+
"tiff",
|
|
15258
|
+
"tlz",
|
|
15259
|
+
"ttc",
|
|
15260
|
+
"ttf",
|
|
15261
|
+
"txz",
|
|
15262
|
+
"udf",
|
|
15263
|
+
"uvh",
|
|
15264
|
+
"uvi",
|
|
15265
|
+
"uvm",
|
|
15266
|
+
"uvp",
|
|
15267
|
+
"uvs",
|
|
15268
|
+
"uvu",
|
|
15269
|
+
"viv",
|
|
15270
|
+
"vob",
|
|
15271
|
+
"war",
|
|
15272
|
+
"wav",
|
|
15273
|
+
"wax",
|
|
15274
|
+
"wbmp",
|
|
15275
|
+
"wdp",
|
|
15276
|
+
"weba",
|
|
15277
|
+
"webm",
|
|
15278
|
+
"webp",
|
|
15279
|
+
"whl",
|
|
15280
|
+
"wim",
|
|
15281
|
+
"wm",
|
|
15282
|
+
"wma",
|
|
15283
|
+
"wmv",
|
|
15284
|
+
"wmx",
|
|
15285
|
+
"woff",
|
|
15286
|
+
"woff2",
|
|
15287
|
+
"wrm",
|
|
15288
|
+
"wvx",
|
|
15289
|
+
"xbm",
|
|
15290
|
+
"xif",
|
|
15291
|
+
"xla",
|
|
15292
|
+
"xlam",
|
|
15293
|
+
"xls",
|
|
15294
|
+
"xlsb",
|
|
15295
|
+
"xlsm",
|
|
15296
|
+
"xlsx",
|
|
15297
|
+
"xlt",
|
|
15298
|
+
"xltm",
|
|
15299
|
+
"xltx",
|
|
15300
|
+
"xm",
|
|
15301
|
+
"xmind",
|
|
15302
|
+
"xpi",
|
|
15303
|
+
"xpm",
|
|
15304
|
+
"xwd",
|
|
15305
|
+
"xz",
|
|
15306
|
+
"z",
|
|
15307
|
+
"zip",
|
|
15308
|
+
"zipx"
|
|
15309
|
+
]);
|
|
15310
|
+
var isBinaryPath = (filePath) => binaryExtensions.has(sp.extname(filePath).slice(1).toLowerCase());
|
|
15311
|
+
var foreach = (val, fn) => {
|
|
15312
|
+
if (val instanceof Set) {
|
|
15313
|
+
val.forEach(fn);
|
|
15314
|
+
} else {
|
|
15315
|
+
fn(val);
|
|
14635
15316
|
}
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
|
|
15317
|
+
};
|
|
15318
|
+
var addAndConvert = (main2, prop, item) => {
|
|
15319
|
+
let container = main2[prop];
|
|
15320
|
+
if (!(container instanceof Set)) {
|
|
15321
|
+
main2[prop] = container = new Set([container]);
|
|
14639
15322
|
}
|
|
14640
|
-
|
|
14641
|
-
|
|
15323
|
+
container.add(item);
|
|
15324
|
+
};
|
|
15325
|
+
var clearItem = (cont) => (key) => {
|
|
15326
|
+
const set = cont[key];
|
|
15327
|
+
if (set instanceof Set) {
|
|
15328
|
+
set.clear();
|
|
15329
|
+
} else {
|
|
15330
|
+
delete cont[key];
|
|
14642
15331
|
}
|
|
14643
|
-
}
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
|
|
14648
|
-
|
|
14649
|
-
|
|
14650
|
-
var REMOVING_THRESHOLD_MS = 60 * 60 * 1000;
|
|
14651
|
-
var GHOST_DIR_PREFIX = "ocx-ghost-";
|
|
14652
|
-
var REMOVING_SUFFIX = "-removing";
|
|
14653
|
-
var GHOST_MARKER_FILE = ".ocx-ghost-marker";
|
|
14654
|
-
async function createSymlinkFarm(sourceDir, excludePaths, options2) {
|
|
14655
|
-
if (!isAbsolute(sourceDir)) {
|
|
14656
|
-
throw new Error(`sourceDir must be an absolute path, got: ${sourceDir}`);
|
|
15332
|
+
};
|
|
15333
|
+
var delFromSet = (main2, prop, item) => {
|
|
15334
|
+
const container = main2[prop];
|
|
15335
|
+
if (container instanceof Set) {
|
|
15336
|
+
container.delete(item);
|
|
15337
|
+
} else if (container === item) {
|
|
15338
|
+
delete main2[prop];
|
|
14657
15339
|
}
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
15340
|
+
};
|
|
15341
|
+
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
15342
|
+
var FsWatchInstances = new Map;
|
|
15343
|
+
function createFsWatchInstance(path5, options2, listener, errHandler, emitRaw) {
|
|
15344
|
+
const handleEvent = (rawEvent, evPath) => {
|
|
15345
|
+
listener(path5);
|
|
15346
|
+
emitRaw(rawEvent, evPath, { watchedPath: path5 });
|
|
15347
|
+
if (evPath && path5 !== evPath) {
|
|
15348
|
+
fsWatchBroadcast(sp.resolve(path5, evPath), KEY_LISTENERS, sp.join(path5, evPath));
|
|
15349
|
+
}
|
|
15350
|
+
};
|
|
14661
15351
|
try {
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
return tempDir;
|
|
15352
|
+
return fs_watch(path5, {
|
|
15353
|
+
persistent: options2.persistent
|
|
15354
|
+
}, handleEvent);
|
|
14666
15355
|
} catch (error) {
|
|
14667
|
-
|
|
14668
|
-
|
|
15356
|
+
errHandler(error);
|
|
15357
|
+
return;
|
|
14669
15358
|
}
|
|
14670
15359
|
}
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
|
|
15360
|
+
var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
15361
|
+
const cont = FsWatchInstances.get(fullPath);
|
|
15362
|
+
if (!cont)
|
|
15363
|
+
return;
|
|
15364
|
+
foreach(cont[listenerType], (listener) => {
|
|
15365
|
+
listener(val1, val2, val3);
|
|
15366
|
+
});
|
|
15367
|
+
};
|
|
15368
|
+
var setFsWatchListener = (path5, fullPath, options2, handlers) => {
|
|
15369
|
+
const { listener, errHandler, rawEmitter } = handlers;
|
|
15370
|
+
let cont = FsWatchInstances.get(fullPath);
|
|
15371
|
+
let watcher;
|
|
15372
|
+
if (!options2.persistent) {
|
|
15373
|
+
watcher = createFsWatchInstance(path5, options2, listener, errHandler, rawEmitter);
|
|
15374
|
+
if (!watcher)
|
|
15375
|
+
return;
|
|
15376
|
+
return watcher.close.bind(watcher);
|
|
14677
15377
|
}
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
|
|
14688
|
-
|
|
14689
|
-
|
|
15378
|
+
if (cont) {
|
|
15379
|
+
addAndConvert(cont, KEY_LISTENERS, listener);
|
|
15380
|
+
addAndConvert(cont, KEY_ERR, errHandler);
|
|
15381
|
+
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
15382
|
+
} else {
|
|
15383
|
+
watcher = createFsWatchInstance(path5, options2, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
|
|
15384
|
+
if (!watcher)
|
|
15385
|
+
return;
|
|
15386
|
+
watcher.on(EV.ERROR, async (error) => {
|
|
15387
|
+
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
|
|
15388
|
+
if (cont)
|
|
15389
|
+
cont.watcherUnusable = true;
|
|
15390
|
+
if (isWindows && error.code === "EPERM") {
|
|
15391
|
+
try {
|
|
15392
|
+
const fd = await open(path5, "r");
|
|
15393
|
+
await fd.close();
|
|
15394
|
+
broadcastErr(error);
|
|
15395
|
+
} catch (err) {}
|
|
15396
|
+
} else {
|
|
15397
|
+
broadcastErr(error);
|
|
15398
|
+
}
|
|
15399
|
+
});
|
|
15400
|
+
cont = {
|
|
15401
|
+
listeners: listener,
|
|
15402
|
+
errHandlers: errHandler,
|
|
15403
|
+
rawEmitters: rawEmitter,
|
|
15404
|
+
watcher
|
|
15405
|
+
};
|
|
15406
|
+
FsWatchInstances.set(fullPath, cont);
|
|
15407
|
+
}
|
|
15408
|
+
return () => {
|
|
15409
|
+
delFromSet(cont, KEY_LISTENERS, listener);
|
|
15410
|
+
delFromSet(cont, KEY_ERR, errHandler);
|
|
15411
|
+
delFromSet(cont, KEY_RAW, rawEmitter);
|
|
15412
|
+
if (isEmptySet(cont.listeners)) {
|
|
15413
|
+
cont.watcher.close();
|
|
15414
|
+
FsWatchInstances.delete(fullPath);
|
|
15415
|
+
HANDLER_KEYS.forEach(clearItem(cont));
|
|
15416
|
+
cont.watcher = undefined;
|
|
15417
|
+
Object.freeze(cont);
|
|
15418
|
+
}
|
|
15419
|
+
};
|
|
15420
|
+
};
|
|
15421
|
+
var FsWatchFileInstances = new Map;
|
|
15422
|
+
var setFsWatchFileListener = (path5, fullPath, options2, handlers) => {
|
|
15423
|
+
const { listener, rawEmitter } = handlers;
|
|
15424
|
+
let cont = FsWatchFileInstances.get(fullPath);
|
|
15425
|
+
const copts = cont && cont.options;
|
|
15426
|
+
if (copts && (copts.persistent < options2.persistent || copts.interval > options2.interval)) {
|
|
15427
|
+
unwatchFile(fullPath);
|
|
15428
|
+
cont = undefined;
|
|
15429
|
+
}
|
|
15430
|
+
if (cont) {
|
|
15431
|
+
addAndConvert(cont, KEY_LISTENERS, listener);
|
|
15432
|
+
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
15433
|
+
} else {
|
|
15434
|
+
cont = {
|
|
15435
|
+
listeners: listener,
|
|
15436
|
+
rawEmitters: rawEmitter,
|
|
15437
|
+
options: options2,
|
|
15438
|
+
watcher: watchFile(fullPath, options2, (curr, prev) => {
|
|
15439
|
+
foreach(cont.rawEmitters, (rawEmitter2) => {
|
|
15440
|
+
rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
|
|
15441
|
+
});
|
|
15442
|
+
const currmtime = curr.mtimeMs;
|
|
15443
|
+
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
15444
|
+
foreach(cont.listeners, (listener2) => listener2(path5, curr));
|
|
15445
|
+
}
|
|
15446
|
+
})
|
|
15447
|
+
};
|
|
15448
|
+
FsWatchFileInstances.set(fullPath, cont);
|
|
15449
|
+
}
|
|
15450
|
+
return () => {
|
|
15451
|
+
delFromSet(cont, KEY_LISTENERS, listener);
|
|
15452
|
+
delFromSet(cont, KEY_RAW, rawEmitter);
|
|
15453
|
+
if (isEmptySet(cont.listeners)) {
|
|
15454
|
+
FsWatchFileInstances.delete(fullPath);
|
|
15455
|
+
unwatchFile(fullPath);
|
|
15456
|
+
cont.options = cont.watcher = undefined;
|
|
15457
|
+
Object.freeze(cont);
|
|
15458
|
+
}
|
|
15459
|
+
};
|
|
15460
|
+
};
|
|
15461
|
+
|
|
15462
|
+
class NodeFsHandler {
|
|
15463
|
+
fsw;
|
|
15464
|
+
_boundHandleError;
|
|
15465
|
+
constructor(fsW) {
|
|
15466
|
+
this.fsw = fsW;
|
|
15467
|
+
this._boundHandleError = (error) => fsW._handleError(error);
|
|
15468
|
+
}
|
|
15469
|
+
_watchWithNodeFs(path5, listener) {
|
|
15470
|
+
const opts = this.fsw.options;
|
|
15471
|
+
const directory = sp.dirname(path5);
|
|
15472
|
+
const basename3 = sp.basename(path5);
|
|
15473
|
+
const parent = this.fsw._getWatchedDir(directory);
|
|
15474
|
+
parent.add(basename3);
|
|
15475
|
+
const absolutePath = sp.resolve(path5);
|
|
15476
|
+
const options2 = {
|
|
15477
|
+
persistent: opts.persistent
|
|
15478
|
+
};
|
|
15479
|
+
if (!listener)
|
|
15480
|
+
listener = EMPTY_FN;
|
|
15481
|
+
let closer;
|
|
15482
|
+
if (opts.usePolling) {
|
|
15483
|
+
const enableBin = opts.interval !== opts.binaryInterval;
|
|
15484
|
+
options2.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
15485
|
+
closer = setFsWatchFileListener(path5, absolutePath, options2, {
|
|
15486
|
+
listener,
|
|
15487
|
+
rawEmitter: this.fsw._emitRaw
|
|
15488
|
+
});
|
|
15489
|
+
} else {
|
|
15490
|
+
closer = setFsWatchListener(path5, absolutePath, options2, {
|
|
15491
|
+
listener,
|
|
15492
|
+
errHandler: this._boundHandleError,
|
|
15493
|
+
rawEmitter: this.fsw._emitRaw
|
|
15494
|
+
});
|
|
15495
|
+
}
|
|
15496
|
+
return closer;
|
|
15497
|
+
}
|
|
15498
|
+
_handleFile(file, stats, initialAdd) {
|
|
15499
|
+
if (this.fsw.closed) {
|
|
15500
|
+
return;
|
|
15501
|
+
}
|
|
15502
|
+
const dirname4 = sp.dirname(file);
|
|
15503
|
+
const basename3 = sp.basename(file);
|
|
15504
|
+
const parent = this.fsw._getWatchedDir(dirname4);
|
|
15505
|
+
let prevStats = stats;
|
|
15506
|
+
if (parent.has(basename3))
|
|
15507
|
+
return;
|
|
15508
|
+
const listener = async (path5, newStats) => {
|
|
15509
|
+
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
15510
|
+
return;
|
|
15511
|
+
if (!newStats || newStats.mtimeMs === 0) {
|
|
15512
|
+
try {
|
|
15513
|
+
const newStats2 = await stat3(file);
|
|
15514
|
+
if (this.fsw.closed)
|
|
15515
|
+
return;
|
|
15516
|
+
const at = newStats2.atimeMs;
|
|
15517
|
+
const mt = newStats2.mtimeMs;
|
|
15518
|
+
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
15519
|
+
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
15520
|
+
}
|
|
15521
|
+
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
15522
|
+
this.fsw._closeFile(path5);
|
|
15523
|
+
prevStats = newStats2;
|
|
15524
|
+
const closer2 = this._watchWithNodeFs(file, listener);
|
|
15525
|
+
if (closer2)
|
|
15526
|
+
this.fsw._addPathCloser(path5, closer2);
|
|
15527
|
+
} else {
|
|
15528
|
+
prevStats = newStats2;
|
|
15529
|
+
}
|
|
15530
|
+
} catch (error) {
|
|
15531
|
+
this.fsw._remove(dirname4, basename3);
|
|
15532
|
+
}
|
|
15533
|
+
} else if (parent.has(basename3)) {
|
|
15534
|
+
const at = newStats.atimeMs;
|
|
15535
|
+
const mt = newStats.mtimeMs;
|
|
15536
|
+
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
15537
|
+
this.fsw._emit(EV.CHANGE, file, newStats);
|
|
15538
|
+
}
|
|
15539
|
+
prevStats = newStats;
|
|
15540
|
+
}
|
|
15541
|
+
};
|
|
15542
|
+
const closer = this._watchWithNodeFs(file, listener);
|
|
15543
|
+
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
|
|
15544
|
+
if (!this.fsw._throttle(EV.ADD, file, 0))
|
|
15545
|
+
return;
|
|
15546
|
+
this.fsw._emit(EV.ADD, file, stats);
|
|
15547
|
+
}
|
|
15548
|
+
return closer;
|
|
15549
|
+
}
|
|
15550
|
+
async _handleSymlink(entry, directory, path5, item) {
|
|
15551
|
+
if (this.fsw.closed) {
|
|
15552
|
+
return;
|
|
15553
|
+
}
|
|
15554
|
+
const full = entry.fullPath;
|
|
15555
|
+
const dir = this.fsw._getWatchedDir(directory);
|
|
15556
|
+
if (!this.fsw.options.followSymlinks) {
|
|
15557
|
+
this.fsw._incrReadyCount();
|
|
15558
|
+
let linkPath;
|
|
15559
|
+
try {
|
|
15560
|
+
linkPath = await fsrealpath(path5);
|
|
15561
|
+
} catch (e3) {
|
|
15562
|
+
this.fsw._emitReady();
|
|
15563
|
+
return true;
|
|
15564
|
+
}
|
|
15565
|
+
if (this.fsw.closed)
|
|
15566
|
+
return;
|
|
15567
|
+
if (dir.has(item)) {
|
|
15568
|
+
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
15569
|
+
this.fsw._symlinkPaths.set(full, linkPath);
|
|
15570
|
+
this.fsw._emit(EV.CHANGE, path5, entry.stats);
|
|
15571
|
+
}
|
|
15572
|
+
} else {
|
|
15573
|
+
dir.add(item);
|
|
15574
|
+
this.fsw._symlinkPaths.set(full, linkPath);
|
|
15575
|
+
this.fsw._emit(EV.ADD, path5, entry.stats);
|
|
15576
|
+
}
|
|
15577
|
+
this.fsw._emitReady();
|
|
15578
|
+
return true;
|
|
15579
|
+
}
|
|
15580
|
+
if (this.fsw._symlinkPaths.has(full)) {
|
|
15581
|
+
return true;
|
|
15582
|
+
}
|
|
15583
|
+
this.fsw._symlinkPaths.set(full, true);
|
|
15584
|
+
}
|
|
15585
|
+
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
|
|
15586
|
+
directory = sp.join(directory, "");
|
|
15587
|
+
const throttleKey = target ? `${directory}:${target}` : directory;
|
|
15588
|
+
throttler = this.fsw._throttle("readdir", throttleKey, 1000);
|
|
15589
|
+
if (!throttler)
|
|
15590
|
+
return;
|
|
15591
|
+
const previous = this.fsw._getWatchedDir(wh.path);
|
|
15592
|
+
const current = new Set;
|
|
15593
|
+
let stream = this.fsw._readdirp(directory, {
|
|
15594
|
+
fileFilter: (entry) => wh.filterPath(entry),
|
|
15595
|
+
directoryFilter: (entry) => wh.filterDir(entry)
|
|
15596
|
+
});
|
|
15597
|
+
if (!stream)
|
|
15598
|
+
return;
|
|
15599
|
+
stream.on(STR_DATA, async (entry) => {
|
|
15600
|
+
if (this.fsw.closed) {
|
|
15601
|
+
stream = undefined;
|
|
15602
|
+
return;
|
|
15603
|
+
}
|
|
15604
|
+
const item = entry.path;
|
|
15605
|
+
let path5 = sp.join(directory, item);
|
|
15606
|
+
current.add(item);
|
|
15607
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path5, item)) {
|
|
15608
|
+
return;
|
|
15609
|
+
}
|
|
15610
|
+
if (this.fsw.closed) {
|
|
15611
|
+
stream = undefined;
|
|
15612
|
+
return;
|
|
15613
|
+
}
|
|
15614
|
+
if (item === target || !target && !previous.has(item)) {
|
|
15615
|
+
this.fsw._incrReadyCount();
|
|
15616
|
+
path5 = sp.join(dir, sp.relative(dir, path5));
|
|
15617
|
+
this._addToNodeFs(path5, initialAdd, wh, depth + 1);
|
|
15618
|
+
}
|
|
15619
|
+
}).on(EV.ERROR, this._boundHandleError);
|
|
15620
|
+
return new Promise((resolve3, reject) => {
|
|
15621
|
+
if (!stream)
|
|
15622
|
+
return reject();
|
|
15623
|
+
stream.once(STR_END, () => {
|
|
15624
|
+
if (this.fsw.closed) {
|
|
15625
|
+
stream = undefined;
|
|
15626
|
+
return;
|
|
15627
|
+
}
|
|
15628
|
+
const wasThrottled = throttler ? throttler.clear() : false;
|
|
15629
|
+
resolve3(undefined);
|
|
15630
|
+
previous.getChildren().filter((item) => {
|
|
15631
|
+
return item !== directory && !current.has(item);
|
|
15632
|
+
}).forEach((item) => {
|
|
15633
|
+
this.fsw._remove(directory, item);
|
|
15634
|
+
});
|
|
15635
|
+
stream = undefined;
|
|
15636
|
+
if (wasThrottled)
|
|
15637
|
+
this._handleRead(directory, false, wh, target, dir, depth, throttler);
|
|
15638
|
+
});
|
|
15639
|
+
});
|
|
15640
|
+
}
|
|
15641
|
+
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath2) {
|
|
15642
|
+
const parentDir = this.fsw._getWatchedDir(sp.dirname(dir));
|
|
15643
|
+
const tracked = parentDir.has(sp.basename(dir));
|
|
15644
|
+
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
|
|
15645
|
+
this.fsw._emit(EV.ADD_DIR, dir, stats);
|
|
15646
|
+
}
|
|
15647
|
+
parentDir.add(sp.basename(dir));
|
|
15648
|
+
this.fsw._getWatchedDir(dir);
|
|
15649
|
+
let throttler;
|
|
15650
|
+
let closer;
|
|
15651
|
+
const oDepth = this.fsw.options.depth;
|
|
15652
|
+
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath2)) {
|
|
15653
|
+
if (!target) {
|
|
15654
|
+
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
|
|
15655
|
+
if (this.fsw.closed)
|
|
15656
|
+
return;
|
|
15657
|
+
}
|
|
15658
|
+
closer = this._watchWithNodeFs(dir, (dirPath, stats2) => {
|
|
15659
|
+
if (stats2 && stats2.mtimeMs === 0)
|
|
15660
|
+
return;
|
|
15661
|
+
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
|
|
15662
|
+
});
|
|
15663
|
+
}
|
|
15664
|
+
return closer;
|
|
15665
|
+
}
|
|
15666
|
+
async _addToNodeFs(path5, initialAdd, priorWh, depth, target) {
|
|
15667
|
+
const ready = this.fsw._emitReady;
|
|
15668
|
+
if (this.fsw._isIgnored(path5) || this.fsw.closed) {
|
|
15669
|
+
ready();
|
|
15670
|
+
return false;
|
|
15671
|
+
}
|
|
15672
|
+
const wh = this.fsw._getWatchHelpers(path5);
|
|
15673
|
+
if (priorWh) {
|
|
15674
|
+
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
15675
|
+
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
15676
|
+
}
|
|
15677
|
+
try {
|
|
15678
|
+
const stats = await statMethods[wh.statMethod](wh.watchPath);
|
|
15679
|
+
if (this.fsw.closed)
|
|
15680
|
+
return;
|
|
15681
|
+
if (this.fsw._isIgnored(wh.watchPath, stats)) {
|
|
15682
|
+
ready();
|
|
15683
|
+
return false;
|
|
15684
|
+
}
|
|
15685
|
+
const follow = this.fsw.options.followSymlinks;
|
|
15686
|
+
let closer;
|
|
15687
|
+
if (stats.isDirectory()) {
|
|
15688
|
+
const absPath = sp.resolve(path5);
|
|
15689
|
+
const targetPath = follow ? await fsrealpath(path5) : path5;
|
|
15690
|
+
if (this.fsw.closed)
|
|
15691
|
+
return;
|
|
15692
|
+
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
15693
|
+
if (this.fsw.closed)
|
|
15694
|
+
return;
|
|
15695
|
+
if (absPath !== targetPath && targetPath !== undefined) {
|
|
15696
|
+
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
15697
|
+
}
|
|
15698
|
+
} else if (stats.isSymbolicLink()) {
|
|
15699
|
+
const targetPath = follow ? await fsrealpath(path5) : path5;
|
|
15700
|
+
if (this.fsw.closed)
|
|
15701
|
+
return;
|
|
15702
|
+
const parent = sp.dirname(wh.watchPath);
|
|
15703
|
+
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
15704
|
+
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
15705
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path5, wh, targetPath);
|
|
15706
|
+
if (this.fsw.closed)
|
|
15707
|
+
return;
|
|
15708
|
+
if (targetPath !== undefined) {
|
|
15709
|
+
this.fsw._symlinkPaths.set(sp.resolve(path5), targetPath);
|
|
15710
|
+
}
|
|
15711
|
+
} else {
|
|
15712
|
+
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
15713
|
+
}
|
|
15714
|
+
ready();
|
|
15715
|
+
if (closer)
|
|
15716
|
+
this.fsw._addPathCloser(path5, closer);
|
|
15717
|
+
return false;
|
|
15718
|
+
} catch (error) {
|
|
15719
|
+
if (this.fsw._handleError(error)) {
|
|
15720
|
+
ready();
|
|
15721
|
+
return path5;
|
|
15722
|
+
}
|
|
15723
|
+
}
|
|
15724
|
+
}
|
|
15725
|
+
}
|
|
15726
|
+
|
|
15727
|
+
// ../../node_modules/.bun/chokidar@5.0.0/node_modules/chokidar/index.js
|
|
15728
|
+
/*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
|
|
15729
|
+
var SLASH = "/";
|
|
15730
|
+
var SLASH_SLASH = "//";
|
|
15731
|
+
var ONE_DOT = ".";
|
|
15732
|
+
var TWO_DOTS = "..";
|
|
15733
|
+
var STRING_TYPE = "string";
|
|
15734
|
+
var BACK_SLASH_RE = /\\/g;
|
|
15735
|
+
var DOUBLE_SLASH_RE = /\/\//g;
|
|
15736
|
+
var DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
|
|
15737
|
+
var REPLACER_RE = /^\.[/\\]/;
|
|
15738
|
+
function arrify(item) {
|
|
15739
|
+
return Array.isArray(item) ? item : [item];
|
|
15740
|
+
}
|
|
15741
|
+
var isMatcherObject = (matcher) => typeof matcher === "object" && matcher !== null && !(matcher instanceof RegExp);
|
|
15742
|
+
function createPattern(matcher) {
|
|
15743
|
+
if (typeof matcher === "function")
|
|
15744
|
+
return matcher;
|
|
15745
|
+
if (typeof matcher === "string")
|
|
15746
|
+
return (string) => matcher === string;
|
|
15747
|
+
if (matcher instanceof RegExp)
|
|
15748
|
+
return (string) => matcher.test(string);
|
|
15749
|
+
if (typeof matcher === "object" && matcher !== null) {
|
|
15750
|
+
return (string) => {
|
|
15751
|
+
if (matcher.path === string)
|
|
15752
|
+
return true;
|
|
15753
|
+
if (matcher.recursive) {
|
|
15754
|
+
const relative4 = sp2.relative(matcher.path, string);
|
|
15755
|
+
if (!relative4) {
|
|
15756
|
+
return false;
|
|
15757
|
+
}
|
|
15758
|
+
return !relative4.startsWith("..") && !sp2.isAbsolute(relative4);
|
|
15759
|
+
}
|
|
15760
|
+
return false;
|
|
15761
|
+
};
|
|
15762
|
+
}
|
|
15763
|
+
return () => false;
|
|
15764
|
+
}
|
|
15765
|
+
function normalizePath(path5) {
|
|
15766
|
+
if (typeof path5 !== "string")
|
|
15767
|
+
throw new Error("string expected");
|
|
15768
|
+
path5 = sp2.normalize(path5);
|
|
15769
|
+
path5 = path5.replace(/\\/g, "/");
|
|
15770
|
+
let prepend = false;
|
|
15771
|
+
if (path5.startsWith("//"))
|
|
15772
|
+
prepend = true;
|
|
15773
|
+
path5 = path5.replace(DOUBLE_SLASH_RE, "/");
|
|
15774
|
+
if (prepend)
|
|
15775
|
+
path5 = "/" + path5;
|
|
15776
|
+
return path5;
|
|
15777
|
+
}
|
|
15778
|
+
function matchPatterns(patterns, testString, stats) {
|
|
15779
|
+
const path5 = normalizePath(testString);
|
|
15780
|
+
for (let index = 0;index < patterns.length; index++) {
|
|
15781
|
+
const pattern = patterns[index];
|
|
15782
|
+
if (pattern(path5, stats)) {
|
|
15783
|
+
return true;
|
|
15784
|
+
}
|
|
15785
|
+
}
|
|
15786
|
+
return false;
|
|
15787
|
+
}
|
|
15788
|
+
function anymatch(matchers, testString) {
|
|
15789
|
+
if (matchers == null) {
|
|
15790
|
+
throw new TypeError("anymatch: specify first argument");
|
|
15791
|
+
}
|
|
15792
|
+
const matchersArray = arrify(matchers);
|
|
15793
|
+
const patterns = matchersArray.map((matcher) => createPattern(matcher));
|
|
15794
|
+
if (testString == null) {
|
|
15795
|
+
return (testString2, stats) => {
|
|
15796
|
+
return matchPatterns(patterns, testString2, stats);
|
|
15797
|
+
};
|
|
15798
|
+
}
|
|
15799
|
+
return matchPatterns(patterns, testString);
|
|
15800
|
+
}
|
|
15801
|
+
var unifyPaths = (paths_) => {
|
|
15802
|
+
const paths = arrify(paths_).flat();
|
|
15803
|
+
if (!paths.every((p) => typeof p === STRING_TYPE)) {
|
|
15804
|
+
throw new TypeError(`Non-string provided as watch path: ${paths}`);
|
|
15805
|
+
}
|
|
15806
|
+
return paths.map(normalizePathToUnix);
|
|
15807
|
+
};
|
|
15808
|
+
var toUnix = (string) => {
|
|
15809
|
+
let str = string.replace(BACK_SLASH_RE, SLASH);
|
|
15810
|
+
let prepend = false;
|
|
15811
|
+
if (str.startsWith(SLASH_SLASH)) {
|
|
15812
|
+
prepend = true;
|
|
15813
|
+
}
|
|
15814
|
+
str = str.replace(DOUBLE_SLASH_RE, SLASH);
|
|
15815
|
+
if (prepend) {
|
|
15816
|
+
str = SLASH + str;
|
|
15817
|
+
}
|
|
15818
|
+
return str;
|
|
15819
|
+
};
|
|
15820
|
+
var normalizePathToUnix = (path5) => toUnix(sp2.normalize(toUnix(path5)));
|
|
15821
|
+
var normalizeIgnored = (cwd = "") => (path5) => {
|
|
15822
|
+
if (typeof path5 === "string") {
|
|
15823
|
+
return normalizePathToUnix(sp2.isAbsolute(path5) ? path5 : sp2.join(cwd, path5));
|
|
15824
|
+
} else {
|
|
15825
|
+
return path5;
|
|
15826
|
+
}
|
|
15827
|
+
};
|
|
15828
|
+
var getAbsolutePath = (path5, cwd) => {
|
|
15829
|
+
if (sp2.isAbsolute(path5)) {
|
|
15830
|
+
return path5;
|
|
15831
|
+
}
|
|
15832
|
+
return sp2.join(cwd, path5);
|
|
15833
|
+
};
|
|
15834
|
+
var EMPTY_SET = Object.freeze(new Set);
|
|
15835
|
+
|
|
15836
|
+
class DirEntry {
|
|
15837
|
+
path;
|
|
15838
|
+
_removeWatcher;
|
|
15839
|
+
items;
|
|
15840
|
+
constructor(dir, removeWatcher) {
|
|
15841
|
+
this.path = dir;
|
|
15842
|
+
this._removeWatcher = removeWatcher;
|
|
15843
|
+
this.items = new Set;
|
|
15844
|
+
}
|
|
15845
|
+
add(item) {
|
|
15846
|
+
const { items } = this;
|
|
15847
|
+
if (!items)
|
|
15848
|
+
return;
|
|
15849
|
+
if (item !== ONE_DOT && item !== TWO_DOTS)
|
|
15850
|
+
items.add(item);
|
|
15851
|
+
}
|
|
15852
|
+
async remove(item) {
|
|
15853
|
+
const { items } = this;
|
|
15854
|
+
if (!items)
|
|
15855
|
+
return;
|
|
15856
|
+
items.delete(item);
|
|
15857
|
+
if (items.size > 0)
|
|
15858
|
+
return;
|
|
15859
|
+
const dir = this.path;
|
|
15860
|
+
try {
|
|
15861
|
+
await readdir3(dir);
|
|
15862
|
+
} catch (err) {
|
|
15863
|
+
if (this._removeWatcher) {
|
|
15864
|
+
this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
|
|
15865
|
+
}
|
|
15866
|
+
}
|
|
15867
|
+
}
|
|
15868
|
+
has(item) {
|
|
15869
|
+
const { items } = this;
|
|
15870
|
+
if (!items)
|
|
15871
|
+
return;
|
|
15872
|
+
return items.has(item);
|
|
15873
|
+
}
|
|
15874
|
+
getChildren() {
|
|
15875
|
+
const { items } = this;
|
|
15876
|
+
if (!items)
|
|
15877
|
+
return [];
|
|
15878
|
+
return [...items.values()];
|
|
15879
|
+
}
|
|
15880
|
+
dispose() {
|
|
15881
|
+
this.items.clear();
|
|
15882
|
+
this.path = "";
|
|
15883
|
+
this._removeWatcher = EMPTY_FN;
|
|
15884
|
+
this.items = EMPTY_SET;
|
|
15885
|
+
Object.freeze(this);
|
|
15886
|
+
}
|
|
15887
|
+
}
|
|
15888
|
+
var STAT_METHOD_F = "stat";
|
|
15889
|
+
var STAT_METHOD_L = "lstat";
|
|
15890
|
+
|
|
15891
|
+
class WatchHelper {
|
|
15892
|
+
fsw;
|
|
15893
|
+
path;
|
|
15894
|
+
watchPath;
|
|
15895
|
+
fullWatchPath;
|
|
15896
|
+
dirParts;
|
|
15897
|
+
followSymlinks;
|
|
15898
|
+
statMethod;
|
|
15899
|
+
constructor(path5, follow, fsw) {
|
|
15900
|
+
this.fsw = fsw;
|
|
15901
|
+
const watchPath = path5;
|
|
15902
|
+
this.path = path5 = path5.replace(REPLACER_RE, "");
|
|
15903
|
+
this.watchPath = watchPath;
|
|
15904
|
+
this.fullWatchPath = sp2.resolve(watchPath);
|
|
15905
|
+
this.dirParts = [];
|
|
15906
|
+
this.dirParts.forEach((parts) => {
|
|
15907
|
+
if (parts.length > 1)
|
|
15908
|
+
parts.pop();
|
|
15909
|
+
});
|
|
15910
|
+
this.followSymlinks = follow;
|
|
15911
|
+
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
|
|
15912
|
+
}
|
|
15913
|
+
entryPath(entry) {
|
|
15914
|
+
return sp2.join(this.watchPath, sp2.relative(this.watchPath, entry.fullPath));
|
|
15915
|
+
}
|
|
15916
|
+
filterPath(entry) {
|
|
15917
|
+
const { stats } = entry;
|
|
15918
|
+
if (stats && stats.isSymbolicLink())
|
|
15919
|
+
return this.filterDir(entry);
|
|
15920
|
+
const resolvedPath = this.entryPath(entry);
|
|
15921
|
+
return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
|
|
15922
|
+
}
|
|
15923
|
+
filterDir(entry) {
|
|
15924
|
+
return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
|
|
15925
|
+
}
|
|
15926
|
+
}
|
|
15927
|
+
|
|
15928
|
+
class FSWatcher extends EventEmitter {
|
|
15929
|
+
closed;
|
|
15930
|
+
options;
|
|
15931
|
+
_closers;
|
|
15932
|
+
_ignoredPaths;
|
|
15933
|
+
_throttled;
|
|
15934
|
+
_streams;
|
|
15935
|
+
_symlinkPaths;
|
|
15936
|
+
_watched;
|
|
15937
|
+
_pendingWrites;
|
|
15938
|
+
_pendingUnlinks;
|
|
15939
|
+
_readyCount;
|
|
15940
|
+
_emitReady;
|
|
15941
|
+
_closePromise;
|
|
15942
|
+
_userIgnored;
|
|
15943
|
+
_readyEmitted;
|
|
15944
|
+
_emitRaw;
|
|
15945
|
+
_boundRemove;
|
|
15946
|
+
_nodeFsHandler;
|
|
15947
|
+
constructor(_opts = {}) {
|
|
15948
|
+
super();
|
|
15949
|
+
this.closed = false;
|
|
15950
|
+
this._closers = new Map;
|
|
15951
|
+
this._ignoredPaths = new Set;
|
|
15952
|
+
this._throttled = new Map;
|
|
15953
|
+
this._streams = new Set;
|
|
15954
|
+
this._symlinkPaths = new Map;
|
|
15955
|
+
this._watched = new Map;
|
|
15956
|
+
this._pendingWrites = new Map;
|
|
15957
|
+
this._pendingUnlinks = new Map;
|
|
15958
|
+
this._readyCount = 0;
|
|
15959
|
+
this._readyEmitted = false;
|
|
15960
|
+
const awf = _opts.awaitWriteFinish;
|
|
15961
|
+
const DEF_AWF = { stabilityThreshold: 2000, pollInterval: 100 };
|
|
15962
|
+
const opts = {
|
|
15963
|
+
persistent: true,
|
|
15964
|
+
ignoreInitial: false,
|
|
15965
|
+
ignorePermissionErrors: false,
|
|
15966
|
+
interval: 100,
|
|
15967
|
+
binaryInterval: 300,
|
|
15968
|
+
followSymlinks: true,
|
|
15969
|
+
usePolling: false,
|
|
15970
|
+
atomic: true,
|
|
15971
|
+
..._opts,
|
|
15972
|
+
ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),
|
|
15973
|
+
awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === "object" ? { ...DEF_AWF, ...awf } : false
|
|
15974
|
+
};
|
|
15975
|
+
if (isIBMi)
|
|
15976
|
+
opts.usePolling = true;
|
|
15977
|
+
if (opts.atomic === undefined)
|
|
15978
|
+
opts.atomic = !opts.usePolling;
|
|
15979
|
+
const envPoll = process.env.CHOKIDAR_USEPOLLING;
|
|
15980
|
+
if (envPoll !== undefined) {
|
|
15981
|
+
const envLower = envPoll.toLowerCase();
|
|
15982
|
+
if (envLower === "false" || envLower === "0")
|
|
15983
|
+
opts.usePolling = false;
|
|
15984
|
+
else if (envLower === "true" || envLower === "1")
|
|
15985
|
+
opts.usePolling = true;
|
|
15986
|
+
else
|
|
15987
|
+
opts.usePolling = !!envLower;
|
|
15988
|
+
}
|
|
15989
|
+
const envInterval = process.env.CHOKIDAR_INTERVAL;
|
|
15990
|
+
if (envInterval)
|
|
15991
|
+
opts.interval = Number.parseInt(envInterval, 10);
|
|
15992
|
+
let readyCalls = 0;
|
|
15993
|
+
this._emitReady = () => {
|
|
15994
|
+
readyCalls++;
|
|
15995
|
+
if (readyCalls >= this._readyCount) {
|
|
15996
|
+
this._emitReady = EMPTY_FN;
|
|
15997
|
+
this._readyEmitted = true;
|
|
15998
|
+
process.nextTick(() => this.emit(EVENTS.READY));
|
|
15999
|
+
}
|
|
16000
|
+
};
|
|
16001
|
+
this._emitRaw = (...args) => this.emit(EVENTS.RAW, ...args);
|
|
16002
|
+
this._boundRemove = this._remove.bind(this);
|
|
16003
|
+
this.options = opts;
|
|
16004
|
+
this._nodeFsHandler = new NodeFsHandler(this);
|
|
16005
|
+
Object.freeze(opts);
|
|
16006
|
+
}
|
|
16007
|
+
_addIgnoredPath(matcher) {
|
|
16008
|
+
if (isMatcherObject(matcher)) {
|
|
16009
|
+
for (const ignored of this._ignoredPaths) {
|
|
16010
|
+
if (isMatcherObject(ignored) && ignored.path === matcher.path && ignored.recursive === matcher.recursive) {
|
|
16011
|
+
return;
|
|
16012
|
+
}
|
|
16013
|
+
}
|
|
16014
|
+
}
|
|
16015
|
+
this._ignoredPaths.add(matcher);
|
|
16016
|
+
}
|
|
16017
|
+
_removeIgnoredPath(matcher) {
|
|
16018
|
+
this._ignoredPaths.delete(matcher);
|
|
16019
|
+
if (typeof matcher === "string") {
|
|
16020
|
+
for (const ignored of this._ignoredPaths) {
|
|
16021
|
+
if (isMatcherObject(ignored) && ignored.path === matcher) {
|
|
16022
|
+
this._ignoredPaths.delete(ignored);
|
|
16023
|
+
}
|
|
16024
|
+
}
|
|
16025
|
+
}
|
|
16026
|
+
}
|
|
16027
|
+
add(paths_, _origAdd, _internal) {
|
|
16028
|
+
const { cwd } = this.options;
|
|
16029
|
+
this.closed = false;
|
|
16030
|
+
this._closePromise = undefined;
|
|
16031
|
+
let paths = unifyPaths(paths_);
|
|
16032
|
+
if (cwd) {
|
|
16033
|
+
paths = paths.map((path5) => {
|
|
16034
|
+
const absPath = getAbsolutePath(path5, cwd);
|
|
16035
|
+
return absPath;
|
|
16036
|
+
});
|
|
16037
|
+
}
|
|
16038
|
+
paths.forEach((path5) => {
|
|
16039
|
+
this._removeIgnoredPath(path5);
|
|
16040
|
+
});
|
|
16041
|
+
this._userIgnored = undefined;
|
|
16042
|
+
if (!this._readyCount)
|
|
16043
|
+
this._readyCount = 0;
|
|
16044
|
+
this._readyCount += paths.length;
|
|
16045
|
+
Promise.all(paths.map(async (path5) => {
|
|
16046
|
+
const res = await this._nodeFsHandler._addToNodeFs(path5, !_internal, undefined, 0, _origAdd);
|
|
16047
|
+
if (res)
|
|
16048
|
+
this._emitReady();
|
|
16049
|
+
return res;
|
|
16050
|
+
})).then((results) => {
|
|
16051
|
+
if (this.closed)
|
|
16052
|
+
return;
|
|
16053
|
+
results.forEach((item) => {
|
|
16054
|
+
if (item)
|
|
16055
|
+
this.add(sp2.dirname(item), sp2.basename(_origAdd || item));
|
|
16056
|
+
});
|
|
16057
|
+
});
|
|
16058
|
+
return this;
|
|
16059
|
+
}
|
|
16060
|
+
unwatch(paths_) {
|
|
16061
|
+
if (this.closed)
|
|
16062
|
+
return this;
|
|
16063
|
+
const paths = unifyPaths(paths_);
|
|
16064
|
+
const { cwd } = this.options;
|
|
16065
|
+
paths.forEach((path5) => {
|
|
16066
|
+
if (!sp2.isAbsolute(path5) && !this._closers.has(path5)) {
|
|
16067
|
+
if (cwd)
|
|
16068
|
+
path5 = sp2.join(cwd, path5);
|
|
16069
|
+
path5 = sp2.resolve(path5);
|
|
16070
|
+
}
|
|
16071
|
+
this._closePath(path5);
|
|
16072
|
+
this._addIgnoredPath(path5);
|
|
16073
|
+
if (this._watched.has(path5)) {
|
|
16074
|
+
this._addIgnoredPath({
|
|
16075
|
+
path: path5,
|
|
16076
|
+
recursive: true
|
|
16077
|
+
});
|
|
16078
|
+
}
|
|
16079
|
+
this._userIgnored = undefined;
|
|
16080
|
+
});
|
|
16081
|
+
return this;
|
|
16082
|
+
}
|
|
16083
|
+
close() {
|
|
16084
|
+
if (this._closePromise) {
|
|
16085
|
+
return this._closePromise;
|
|
16086
|
+
}
|
|
16087
|
+
this.closed = true;
|
|
16088
|
+
this.removeAllListeners();
|
|
16089
|
+
const closers = [];
|
|
16090
|
+
this._closers.forEach((closerList) => closerList.forEach((closer) => {
|
|
16091
|
+
const promise = closer();
|
|
16092
|
+
if (promise instanceof Promise)
|
|
16093
|
+
closers.push(promise);
|
|
16094
|
+
}));
|
|
16095
|
+
this._streams.forEach((stream) => stream.destroy());
|
|
16096
|
+
this._userIgnored = undefined;
|
|
16097
|
+
this._readyCount = 0;
|
|
16098
|
+
this._readyEmitted = false;
|
|
16099
|
+
this._watched.forEach((dirent) => dirent.dispose());
|
|
16100
|
+
this._closers.clear();
|
|
16101
|
+
this._watched.clear();
|
|
16102
|
+
this._streams.clear();
|
|
16103
|
+
this._symlinkPaths.clear();
|
|
16104
|
+
this._throttled.clear();
|
|
16105
|
+
this._closePromise = closers.length ? Promise.all(closers).then(() => {
|
|
16106
|
+
return;
|
|
16107
|
+
}) : Promise.resolve();
|
|
16108
|
+
return this._closePromise;
|
|
16109
|
+
}
|
|
16110
|
+
getWatched() {
|
|
16111
|
+
const watchList = {};
|
|
16112
|
+
this._watched.forEach((entry, dir) => {
|
|
16113
|
+
const key = this.options.cwd ? sp2.relative(this.options.cwd, dir) : dir;
|
|
16114
|
+
const index = key || ONE_DOT;
|
|
16115
|
+
watchList[index] = entry.getChildren().sort();
|
|
16116
|
+
});
|
|
16117
|
+
return watchList;
|
|
16118
|
+
}
|
|
16119
|
+
emitWithAll(event, args) {
|
|
16120
|
+
this.emit(event, ...args);
|
|
16121
|
+
if (event !== EVENTS.ERROR)
|
|
16122
|
+
this.emit(EVENTS.ALL, event, ...args);
|
|
16123
|
+
}
|
|
16124
|
+
async _emit(event, path5, stats) {
|
|
16125
|
+
if (this.closed)
|
|
16126
|
+
return;
|
|
16127
|
+
const opts = this.options;
|
|
16128
|
+
if (isWindows)
|
|
16129
|
+
path5 = sp2.normalize(path5);
|
|
16130
|
+
if (opts.cwd)
|
|
16131
|
+
path5 = sp2.relative(opts.cwd, path5);
|
|
16132
|
+
const args = [path5];
|
|
16133
|
+
if (stats != null)
|
|
16134
|
+
args.push(stats);
|
|
16135
|
+
const awf = opts.awaitWriteFinish;
|
|
16136
|
+
let pw;
|
|
16137
|
+
if (awf && (pw = this._pendingWrites.get(path5))) {
|
|
16138
|
+
pw.lastChange = new Date;
|
|
16139
|
+
return this;
|
|
16140
|
+
}
|
|
16141
|
+
if (opts.atomic) {
|
|
16142
|
+
if (event === EVENTS.UNLINK) {
|
|
16143
|
+
this._pendingUnlinks.set(path5, [event, ...args]);
|
|
16144
|
+
setTimeout(() => {
|
|
16145
|
+
this._pendingUnlinks.forEach((entry, path6) => {
|
|
16146
|
+
this.emit(...entry);
|
|
16147
|
+
this.emit(EVENTS.ALL, ...entry);
|
|
16148
|
+
this._pendingUnlinks.delete(path6);
|
|
16149
|
+
});
|
|
16150
|
+
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
16151
|
+
return this;
|
|
16152
|
+
}
|
|
16153
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path5)) {
|
|
16154
|
+
event = EVENTS.CHANGE;
|
|
16155
|
+
this._pendingUnlinks.delete(path5);
|
|
16156
|
+
}
|
|
16157
|
+
}
|
|
16158
|
+
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
16159
|
+
const awfEmit = (err, stats2) => {
|
|
16160
|
+
if (err) {
|
|
16161
|
+
event = EVENTS.ERROR;
|
|
16162
|
+
args[0] = err;
|
|
16163
|
+
this.emitWithAll(event, args);
|
|
16164
|
+
} else if (stats2) {
|
|
16165
|
+
if (args.length > 1) {
|
|
16166
|
+
args[1] = stats2;
|
|
16167
|
+
} else {
|
|
16168
|
+
args.push(stats2);
|
|
16169
|
+
}
|
|
16170
|
+
this.emitWithAll(event, args);
|
|
16171
|
+
}
|
|
16172
|
+
};
|
|
16173
|
+
this._awaitWriteFinish(path5, awf.stabilityThreshold, event, awfEmit);
|
|
16174
|
+
return this;
|
|
16175
|
+
}
|
|
16176
|
+
if (event === EVENTS.CHANGE) {
|
|
16177
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path5, 50);
|
|
16178
|
+
if (isThrottled)
|
|
16179
|
+
return this;
|
|
16180
|
+
}
|
|
16181
|
+
if (opts.alwaysStat && stats === undefined && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
16182
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path5) : path5;
|
|
16183
|
+
let stats2;
|
|
16184
|
+
try {
|
|
16185
|
+
stats2 = await stat4(fullPath);
|
|
16186
|
+
} catch (err) {}
|
|
16187
|
+
if (!stats2 || this.closed)
|
|
16188
|
+
return;
|
|
16189
|
+
args.push(stats2);
|
|
16190
|
+
}
|
|
16191
|
+
this.emitWithAll(event, args);
|
|
16192
|
+
return this;
|
|
16193
|
+
}
|
|
16194
|
+
_handleError(error) {
|
|
16195
|
+
const code = error && error.code;
|
|
16196
|
+
if (error && code !== "ENOENT" && code !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code !== "EPERM" && code !== "EACCES")) {
|
|
16197
|
+
this.emit(EVENTS.ERROR, error);
|
|
16198
|
+
}
|
|
16199
|
+
return error || this.closed;
|
|
16200
|
+
}
|
|
16201
|
+
_throttle(actionType, path5, timeout) {
|
|
16202
|
+
if (!this._throttled.has(actionType)) {
|
|
16203
|
+
this._throttled.set(actionType, new Map);
|
|
16204
|
+
}
|
|
16205
|
+
const action = this._throttled.get(actionType);
|
|
16206
|
+
if (!action)
|
|
16207
|
+
throw new Error("invalid throttle");
|
|
16208
|
+
const actionPath = action.get(path5);
|
|
16209
|
+
if (actionPath) {
|
|
16210
|
+
actionPath.count++;
|
|
16211
|
+
return false;
|
|
16212
|
+
}
|
|
16213
|
+
let timeoutObject;
|
|
16214
|
+
const clear = () => {
|
|
16215
|
+
const item = action.get(path5);
|
|
16216
|
+
const count = item ? item.count : 0;
|
|
16217
|
+
action.delete(path5);
|
|
16218
|
+
clearTimeout(timeoutObject);
|
|
16219
|
+
if (item)
|
|
16220
|
+
clearTimeout(item.timeoutObject);
|
|
16221
|
+
return count;
|
|
16222
|
+
};
|
|
16223
|
+
timeoutObject = setTimeout(clear, timeout);
|
|
16224
|
+
const thr = { timeoutObject, clear, count: 0 };
|
|
16225
|
+
action.set(path5, thr);
|
|
16226
|
+
return thr;
|
|
16227
|
+
}
|
|
16228
|
+
_incrReadyCount() {
|
|
16229
|
+
return this._readyCount++;
|
|
16230
|
+
}
|
|
16231
|
+
_awaitWriteFinish(path5, threshold, event, awfEmit) {
|
|
16232
|
+
const awf = this.options.awaitWriteFinish;
|
|
16233
|
+
if (typeof awf !== "object")
|
|
16234
|
+
return;
|
|
16235
|
+
const pollInterval = awf.pollInterval;
|
|
16236
|
+
let timeoutHandler;
|
|
16237
|
+
let fullPath = path5;
|
|
16238
|
+
if (this.options.cwd && !sp2.isAbsolute(path5)) {
|
|
16239
|
+
fullPath = sp2.join(this.options.cwd, path5);
|
|
16240
|
+
}
|
|
16241
|
+
const now = new Date;
|
|
16242
|
+
const writes = this._pendingWrites;
|
|
16243
|
+
function awaitWriteFinishFn(prevStat) {
|
|
16244
|
+
statcb(fullPath, (err, curStat) => {
|
|
16245
|
+
if (err || !writes.has(path5)) {
|
|
16246
|
+
if (err && err.code !== "ENOENT")
|
|
16247
|
+
awfEmit(err);
|
|
16248
|
+
return;
|
|
16249
|
+
}
|
|
16250
|
+
const now2 = Number(new Date);
|
|
16251
|
+
if (prevStat && curStat.size !== prevStat.size) {
|
|
16252
|
+
writes.get(path5).lastChange = now2;
|
|
16253
|
+
}
|
|
16254
|
+
const pw = writes.get(path5);
|
|
16255
|
+
const df = now2 - pw.lastChange;
|
|
16256
|
+
if (df >= threshold) {
|
|
16257
|
+
writes.delete(path5);
|
|
16258
|
+
awfEmit(undefined, curStat);
|
|
16259
|
+
} else {
|
|
16260
|
+
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
16261
|
+
}
|
|
16262
|
+
});
|
|
16263
|
+
}
|
|
16264
|
+
if (!writes.has(path5)) {
|
|
16265
|
+
writes.set(path5, {
|
|
16266
|
+
lastChange: now,
|
|
16267
|
+
cancelWait: () => {
|
|
16268
|
+
writes.delete(path5);
|
|
16269
|
+
clearTimeout(timeoutHandler);
|
|
16270
|
+
return event;
|
|
16271
|
+
}
|
|
16272
|
+
});
|
|
16273
|
+
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
|
|
16274
|
+
}
|
|
16275
|
+
}
|
|
16276
|
+
_isIgnored(path5, stats) {
|
|
16277
|
+
if (this.options.atomic && DOT_RE.test(path5))
|
|
16278
|
+
return true;
|
|
16279
|
+
if (!this._userIgnored) {
|
|
16280
|
+
const { cwd } = this.options;
|
|
16281
|
+
const ign = this.options.ignored;
|
|
16282
|
+
const ignored = (ign || []).map(normalizeIgnored(cwd));
|
|
16283
|
+
const ignoredPaths = [...this._ignoredPaths];
|
|
16284
|
+
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
16285
|
+
this._userIgnored = anymatch(list, undefined);
|
|
16286
|
+
}
|
|
16287
|
+
return this._userIgnored(path5, stats);
|
|
16288
|
+
}
|
|
16289
|
+
_isntIgnored(path5, stat5) {
|
|
16290
|
+
return !this._isIgnored(path5, stat5);
|
|
16291
|
+
}
|
|
16292
|
+
_getWatchHelpers(path5) {
|
|
16293
|
+
return new WatchHelper(path5, this.options.followSymlinks, this);
|
|
16294
|
+
}
|
|
16295
|
+
_getWatchedDir(directory) {
|
|
16296
|
+
const dir = sp2.resolve(directory);
|
|
16297
|
+
if (!this._watched.has(dir))
|
|
16298
|
+
this._watched.set(dir, new DirEntry(dir, this._boundRemove));
|
|
16299
|
+
return this._watched.get(dir);
|
|
16300
|
+
}
|
|
16301
|
+
_hasReadPermissions(stats) {
|
|
16302
|
+
if (this.options.ignorePermissionErrors)
|
|
16303
|
+
return true;
|
|
16304
|
+
return Boolean(Number(stats.mode) & 256);
|
|
16305
|
+
}
|
|
16306
|
+
_remove(directory, item, isDirectory) {
|
|
16307
|
+
const path5 = sp2.join(directory, item);
|
|
16308
|
+
const fullPath = sp2.resolve(path5);
|
|
16309
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path5) || this._watched.has(fullPath);
|
|
16310
|
+
if (!this._throttle("remove", path5, 100))
|
|
16311
|
+
return;
|
|
16312
|
+
if (!isDirectory && this._watched.size === 1) {
|
|
16313
|
+
this.add(directory, item, true);
|
|
16314
|
+
}
|
|
16315
|
+
const wp = this._getWatchedDir(path5);
|
|
16316
|
+
const nestedDirectoryChildren = wp.getChildren();
|
|
16317
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path5, nested));
|
|
16318
|
+
const parent = this._getWatchedDir(directory);
|
|
16319
|
+
const wasTracked = parent.has(item);
|
|
16320
|
+
parent.remove(item);
|
|
16321
|
+
if (this._symlinkPaths.has(fullPath)) {
|
|
16322
|
+
this._symlinkPaths.delete(fullPath);
|
|
16323
|
+
}
|
|
16324
|
+
let relPath = path5;
|
|
16325
|
+
if (this.options.cwd)
|
|
16326
|
+
relPath = sp2.relative(this.options.cwd, path5);
|
|
16327
|
+
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
16328
|
+
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
16329
|
+
if (event === EVENTS.ADD)
|
|
16330
|
+
return;
|
|
16331
|
+
}
|
|
16332
|
+
this._watched.delete(path5);
|
|
16333
|
+
this._watched.delete(fullPath);
|
|
16334
|
+
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
16335
|
+
if (wasTracked && !this._isIgnored(path5))
|
|
16336
|
+
this._emit(eventName, path5);
|
|
16337
|
+
this._closePath(path5);
|
|
16338
|
+
}
|
|
16339
|
+
_closePath(path5) {
|
|
16340
|
+
this._closeFile(path5);
|
|
16341
|
+
const dir = sp2.dirname(path5);
|
|
16342
|
+
this._getWatchedDir(dir).remove(sp2.basename(path5));
|
|
16343
|
+
}
|
|
16344
|
+
_closeFile(path5) {
|
|
16345
|
+
const closers = this._closers.get(path5);
|
|
16346
|
+
if (!closers)
|
|
16347
|
+
return;
|
|
16348
|
+
closers.forEach((closer) => closer());
|
|
16349
|
+
this._closers.delete(path5);
|
|
16350
|
+
}
|
|
16351
|
+
_addPathCloser(path5, closer) {
|
|
16352
|
+
if (!closer)
|
|
16353
|
+
return;
|
|
16354
|
+
let list = this._closers.get(path5);
|
|
16355
|
+
if (!list) {
|
|
16356
|
+
list = [];
|
|
16357
|
+
this._closers.set(path5, list);
|
|
16358
|
+
}
|
|
16359
|
+
list.push(closer);
|
|
16360
|
+
}
|
|
16361
|
+
_readdirp(root, opts) {
|
|
16362
|
+
if (this.closed)
|
|
16363
|
+
return;
|
|
16364
|
+
const options2 = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
|
|
16365
|
+
let stream = readdirp(root, options2);
|
|
16366
|
+
this._streams.add(stream);
|
|
16367
|
+
stream.once(STR_CLOSE, () => {
|
|
16368
|
+
stream = undefined;
|
|
16369
|
+
});
|
|
16370
|
+
stream.once(STR_END, () => {
|
|
16371
|
+
if (stream) {
|
|
16372
|
+
this._streams.delete(stream);
|
|
16373
|
+
stream = undefined;
|
|
16374
|
+
}
|
|
16375
|
+
});
|
|
16376
|
+
return stream;
|
|
16377
|
+
}
|
|
16378
|
+
}
|
|
16379
|
+
function watch(paths, options2 = {}) {
|
|
16380
|
+
const watcher = new FSWatcher(options2);
|
|
16381
|
+
watcher.add(paths);
|
|
16382
|
+
return watcher;
|
|
16383
|
+
}
|
|
16384
|
+
var chokidar_default = { watch, FSWatcher };
|
|
16385
|
+
|
|
16386
|
+
// src/utils/file-sync.ts
|
|
16387
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
16388
|
+
var OS_JUNK = [/\.DS_Store$/, /Thumbs\.db$/];
|
|
16389
|
+
function loadGitignore(projectDir) {
|
|
16390
|
+
const ig = import_ignore.default();
|
|
16391
|
+
const gitignorePath = join6(projectDir, ".gitignore");
|
|
16392
|
+
try {
|
|
16393
|
+
if (existsSync2(gitignorePath)) {
|
|
16394
|
+
ig.add(readFileSync(gitignorePath, "utf8"));
|
|
16395
|
+
}
|
|
16396
|
+
} catch (err) {
|
|
16397
|
+
const code = err.code;
|
|
16398
|
+
if (code !== "ENOENT") {
|
|
16399
|
+
console.warn(`Warning: Could not read .gitignore: ${err.message}`);
|
|
16400
|
+
}
|
|
16401
|
+
}
|
|
16402
|
+
return ig;
|
|
16403
|
+
}
|
|
16404
|
+
function normalizePath2(p) {
|
|
16405
|
+
return p.replace(/\\/g, "/").replace(/\/$/, "");
|
|
16406
|
+
}
|
|
16407
|
+
function isSymlink(filePath) {
|
|
16408
|
+
try {
|
|
16409
|
+
return lstatSync(filePath).isSymbolicLink();
|
|
16410
|
+
} catch (err) {
|
|
16411
|
+
if (err.code === "ENOENT")
|
|
16412
|
+
return false;
|
|
16413
|
+
throw err;
|
|
16414
|
+
}
|
|
16415
|
+
}
|
|
16416
|
+
function isExcluded(relativePath, gitignore) {
|
|
16417
|
+
if (!relativePath)
|
|
16418
|
+
return false;
|
|
16419
|
+
if (OS_JUNK.some((p) => p.test(relativePath)))
|
|
16420
|
+
return true;
|
|
16421
|
+
if (gitignore.ignores(relativePath))
|
|
16422
|
+
return true;
|
|
16423
|
+
return false;
|
|
16424
|
+
}
|
|
16425
|
+
async function syncFile(src, dest) {
|
|
16426
|
+
mkdirSync(dirname5(dest), { recursive: true });
|
|
16427
|
+
await Bun.write(dest, Bun.file(src));
|
|
16428
|
+
}
|
|
16429
|
+
function syncDelete(dest) {
|
|
16430
|
+
try {
|
|
16431
|
+
unlinkSync(dest);
|
|
16432
|
+
} catch (err) {
|
|
16433
|
+
if (err.code !== "ENOENT")
|
|
16434
|
+
throw err;
|
|
16435
|
+
}
|
|
16436
|
+
}
|
|
16437
|
+
function createFileSync(tempDir, projectDir, options2) {
|
|
16438
|
+
const failures = [];
|
|
16439
|
+
const syncedFiles = new Set;
|
|
16440
|
+
const gitignore = loadGitignore(projectDir);
|
|
16441
|
+
const isCI2 = process.env.CI === "true";
|
|
16442
|
+
const watcher = chokidar_default.watch(tempDir, {
|
|
16443
|
+
followSymlinks: false,
|
|
16444
|
+
ignoreInitial: true,
|
|
16445
|
+
usePolling: isCI2,
|
|
16446
|
+
interval: 100,
|
|
16447
|
+
awaitWriteFinish: {
|
|
16448
|
+
stabilityThreshold: 200,
|
|
16449
|
+
pollInterval: 50
|
|
16450
|
+
},
|
|
16451
|
+
ignored: (filePath) => isExcluded(relative4(tempDir, filePath), gitignore)
|
|
16452
|
+
});
|
|
16453
|
+
watcher.on("error", (err) => {
|
|
16454
|
+
failures.push({ path: "<watcher>", error: err });
|
|
16455
|
+
});
|
|
16456
|
+
const handleAdd = async (filePath) => {
|
|
16457
|
+
if (isSymlink(filePath))
|
|
16458
|
+
return;
|
|
16459
|
+
const relativePath = relative4(tempDir, filePath);
|
|
16460
|
+
const normalizedPath = normalizePath2(relativePath);
|
|
16461
|
+
if (options2?.overlayFiles?.has(normalizedPath))
|
|
16462
|
+
return;
|
|
16463
|
+
const destPath = join6(projectDir, relativePath);
|
|
16464
|
+
try {
|
|
16465
|
+
await syncFile(filePath, destPath);
|
|
16466
|
+
syncedFiles.add(normalizedPath);
|
|
16467
|
+
} catch (err) {
|
|
16468
|
+
failures.push({ path: relativePath, error: err });
|
|
16469
|
+
}
|
|
16470
|
+
};
|
|
16471
|
+
const handleChange = async (filePath) => {
|
|
16472
|
+
if (isSymlink(filePath))
|
|
16473
|
+
return;
|
|
16474
|
+
const relativePath = relative4(tempDir, filePath);
|
|
16475
|
+
const normalizedPath = normalizePath2(relativePath);
|
|
16476
|
+
if (options2?.overlayFiles?.has(normalizedPath))
|
|
16477
|
+
return;
|
|
16478
|
+
const destPath = join6(projectDir, relativePath);
|
|
16479
|
+
try {
|
|
16480
|
+
await syncFile(filePath, destPath);
|
|
16481
|
+
} catch (err) {
|
|
16482
|
+
failures.push({ path: relativePath, error: err });
|
|
16483
|
+
}
|
|
16484
|
+
};
|
|
16485
|
+
const handleUnlink = async (filePath) => {
|
|
16486
|
+
const relativePath = relative4(tempDir, filePath);
|
|
16487
|
+
const normalizedPath = normalizePath2(relativePath);
|
|
16488
|
+
if (!syncedFiles.has(normalizedPath))
|
|
16489
|
+
return;
|
|
16490
|
+
const destPath = join6(projectDir, relativePath);
|
|
16491
|
+
try {
|
|
16492
|
+
syncDelete(destPath);
|
|
16493
|
+
syncedFiles.delete(normalizedPath);
|
|
16494
|
+
} catch (err) {
|
|
16495
|
+
failures.push({ path: relativePath, error: err });
|
|
16496
|
+
}
|
|
16497
|
+
};
|
|
16498
|
+
const handleAddDir = (dirPath) => {
|
|
16499
|
+
if (isSymlink(dirPath))
|
|
16500
|
+
return;
|
|
16501
|
+
const relativePath = relative4(tempDir, dirPath);
|
|
16502
|
+
try {
|
|
16503
|
+
mkdirSync(join6(projectDir, relativePath), { recursive: true });
|
|
14690
16504
|
} catch (err) {
|
|
14691
16505
|
if (err.code !== "EEXIST") {
|
|
14692
|
-
|
|
16506
|
+
failures.push({ path: relativePath, error: err });
|
|
16507
|
+
}
|
|
16508
|
+
}
|
|
16509
|
+
};
|
|
16510
|
+
const handleUnlinkDir = (dirPath) => {
|
|
16511
|
+
const relativePath = relative4(tempDir, dirPath);
|
|
16512
|
+
const destPath = join6(projectDir, relativePath);
|
|
16513
|
+
try {
|
|
16514
|
+
rmdirSync(destPath);
|
|
16515
|
+
} catch (err) {
|
|
16516
|
+
const code = err.code;
|
|
16517
|
+
if (code !== "ENOENT" && code !== "ENOTEMPTY") {
|
|
16518
|
+
failures.push({ path: relativePath, error: err });
|
|
16519
|
+
}
|
|
16520
|
+
}
|
|
16521
|
+
};
|
|
16522
|
+
watcher.on("add", handleAdd);
|
|
16523
|
+
watcher.on("change", handleChange);
|
|
16524
|
+
watcher.on("unlink", handleUnlink);
|
|
16525
|
+
watcher.on("addDir", handleAddDir);
|
|
16526
|
+
watcher.on("unlinkDir", handleUnlinkDir);
|
|
16527
|
+
return {
|
|
16528
|
+
close: async () => {
|
|
16529
|
+
await watcher.close();
|
|
16530
|
+
},
|
|
16531
|
+
getFailures: () => [...failures],
|
|
16532
|
+
getSyncCount: () => syncedFiles.size
|
|
16533
|
+
};
|
|
16534
|
+
}
|
|
16535
|
+
|
|
16536
|
+
// src/utils/symlink-farm.ts
|
|
16537
|
+
import { randomBytes } from "crypto";
|
|
16538
|
+
import { mkdir as mkdir4, readdir as readdir4, rename as rename2, rm as rm2, stat as stat5, symlink as symlink2 } from "fs/promises";
|
|
16539
|
+
import { tmpdir } from "os";
|
|
16540
|
+
import { dirname as dirname6, isAbsolute as isAbsolute2, join as join7, relative as relative5 } from "path";
|
|
16541
|
+
|
|
16542
|
+
// src/utils/pattern-filter.ts
|
|
16543
|
+
import path5 from "path";
|
|
16544
|
+
var {Glob: Glob2 } = globalThis.Bun;
|
|
16545
|
+
function normalizeForMatching(absolutePath, projectRoot) {
|
|
16546
|
+
const relativePath = path5.relative(projectRoot, absolutePath);
|
|
16547
|
+
return relativePath.split(path5.sep).join("/").replace(/^\.\//, "");
|
|
16548
|
+
}
|
|
16549
|
+
class PathMatcher {
|
|
16550
|
+
includeGlobs;
|
|
16551
|
+
excludeGlobs;
|
|
16552
|
+
includePatterns;
|
|
16553
|
+
excludePatterns;
|
|
16554
|
+
constructor(includePatterns = [], excludePatterns = []) {
|
|
16555
|
+
this.includePatterns = includePatterns;
|
|
16556
|
+
this.excludePatterns = excludePatterns;
|
|
16557
|
+
this.includeGlobs = includePatterns.map((p) => ({ pattern: p, glob: new Glob2(p) }));
|
|
16558
|
+
this.excludeGlobs = excludePatterns.map((p) => ({ pattern: p, glob: new Glob2(p) }));
|
|
16559
|
+
}
|
|
16560
|
+
getDisposition(relativePath) {
|
|
16561
|
+
const matchesExclude = this.excludeGlobs.some((g) => g.glob.match(relativePath));
|
|
16562
|
+
const matchesInclude = this.includeGlobs.some((g) => g.glob.match(relativePath));
|
|
16563
|
+
if (this.includePatterns.length > 0 && this.excludePatterns.length > 0) {
|
|
16564
|
+
if (matchesInclude) {
|
|
16565
|
+
return { type: "included" };
|
|
16566
|
+
}
|
|
16567
|
+
if (matchesExclude) {
|
|
16568
|
+
return { type: "excluded" };
|
|
16569
|
+
}
|
|
16570
|
+
const hasIncludesInside = this.includePatterns.some((pattern) => {
|
|
16571
|
+
if (pattern.startsWith(`${relativePath}/`))
|
|
16572
|
+
return true;
|
|
16573
|
+
if (pattern.startsWith("**/"))
|
|
16574
|
+
return true;
|
|
16575
|
+
return false;
|
|
16576
|
+
});
|
|
16577
|
+
const hasExcludesInside2 = this.excludePatterns.some((pattern) => {
|
|
16578
|
+
if (pattern.startsWith(`${relativePath}/`))
|
|
16579
|
+
return true;
|
|
16580
|
+
if (pattern.startsWith("**/"))
|
|
16581
|
+
return true;
|
|
16582
|
+
return false;
|
|
16583
|
+
});
|
|
16584
|
+
if (hasIncludesInside || hasExcludesInside2) {
|
|
16585
|
+
return { type: "partial", patterns: this.includePatterns };
|
|
14693
16586
|
}
|
|
16587
|
+
return { type: "included" };
|
|
16588
|
+
}
|
|
16589
|
+
if (this.includePatterns.length > 0) {
|
|
16590
|
+
if (matchesInclude) {
|
|
16591
|
+
return { type: "included" };
|
|
16592
|
+
}
|
|
16593
|
+
const hasIncludesInside = this.includePatterns.some((pattern) => {
|
|
16594
|
+
if (pattern.startsWith(`${relativePath}/`))
|
|
16595
|
+
return true;
|
|
16596
|
+
if (pattern.startsWith("**/"))
|
|
16597
|
+
return true;
|
|
16598
|
+
return false;
|
|
16599
|
+
});
|
|
16600
|
+
if (hasIncludesInside) {
|
|
16601
|
+
return { type: "partial", patterns: this.includePatterns };
|
|
16602
|
+
}
|
|
16603
|
+
return { type: "excluded" };
|
|
16604
|
+
}
|
|
16605
|
+
if (matchesExclude) {
|
|
16606
|
+
return { type: "excluded" };
|
|
16607
|
+
}
|
|
16608
|
+
const hasExcludesInside = this.excludePatterns.some((pattern) => {
|
|
16609
|
+
if (pattern.startsWith(`${relativePath}/`))
|
|
16610
|
+
return true;
|
|
16611
|
+
if (pattern.startsWith("**/"))
|
|
16612
|
+
return true;
|
|
16613
|
+
return false;
|
|
16614
|
+
});
|
|
16615
|
+
if (hasExcludesInside) {
|
|
16616
|
+
return { type: "partial", patterns: [] };
|
|
14694
16617
|
}
|
|
16618
|
+
return { type: "included" };
|
|
16619
|
+
}
|
|
16620
|
+
targetsInside(dirPath) {
|
|
16621
|
+
const normalizedDir = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
|
|
16622
|
+
return this.includePatterns.some((p) => p.startsWith(normalizedDir));
|
|
16623
|
+
}
|
|
16624
|
+
getInnerPatterns(dirPath) {
|
|
16625
|
+
const normalizedDir = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
|
|
16626
|
+
return this.includePatterns.filter((p) => p.startsWith(normalizedDir));
|
|
16627
|
+
}
|
|
16628
|
+
hasIncludePatterns() {
|
|
16629
|
+
return this.includePatterns.length > 0;
|
|
16630
|
+
}
|
|
16631
|
+
}
|
|
16632
|
+
function createPathMatcher(includePatterns = [], excludePatterns = []) {
|
|
16633
|
+
return new PathMatcher(includePatterns, excludePatterns);
|
|
16634
|
+
}
|
|
16635
|
+
|
|
16636
|
+
// src/utils/symlink-farm.ts
|
|
16637
|
+
var STALE_SESSION_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
16638
|
+
var REMOVING_THRESHOLD_MS = 60 * 60 * 1000;
|
|
16639
|
+
var GHOST_DIR_PREFIX = "ocx-ghost-";
|
|
16640
|
+
var REMOVING_SUFFIX = "-removing";
|
|
16641
|
+
var GHOST_MARKER_FILE = ".ocx-ghost-marker";
|
|
16642
|
+
async function createSymlinkFarm(sourceDir, options2) {
|
|
16643
|
+
if (!isAbsolute2(sourceDir)) {
|
|
16644
|
+
throw new Error(`sourceDir must be an absolute path, got: ${sourceDir}`);
|
|
16645
|
+
}
|
|
16646
|
+
const suffix = randomBytes(4).toString("hex");
|
|
16647
|
+
const tempDir = join7(tmpdir(), `${GHOST_DIR_PREFIX}${suffix}`);
|
|
16648
|
+
await Bun.write(join7(tempDir, GHOST_MARKER_FILE), "");
|
|
16649
|
+
try {
|
|
16650
|
+
const matcher = createPathMatcher(options2?.includePatterns ?? [], options2?.excludePatterns ?? []);
|
|
16651
|
+
const plan = await computeSymlinkPlan(sourceDir, sourceDir, matcher);
|
|
16652
|
+
await executeSymlinkPlan(plan, sourceDir, tempDir);
|
|
16653
|
+
return tempDir;
|
|
16654
|
+
} catch (error) {
|
|
16655
|
+
await rm2(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
16656
|
+
throw error;
|
|
14695
16657
|
}
|
|
14696
16658
|
}
|
|
14697
16659
|
async function cleanupSymlinkFarm(tempDir) {
|
|
14698
16660
|
const removingPath = `${tempDir}${REMOVING_SUFFIX}`;
|
|
14699
16661
|
try {
|
|
14700
|
-
await
|
|
16662
|
+
await rename2(tempDir, removingPath);
|
|
14701
16663
|
} catch {
|
|
14702
16664
|
return;
|
|
14703
16665
|
}
|
|
@@ -14705,24 +16667,24 @@ async function cleanupSymlinkFarm(tempDir) {
|
|
|
14705
16667
|
}
|
|
14706
16668
|
async function cleanupOrphanedGhostDirs(tempBase = tmpdir()) {
|
|
14707
16669
|
let cleanedCount = 0;
|
|
14708
|
-
if (!
|
|
16670
|
+
if (!isAbsolute2(tempBase)) {
|
|
14709
16671
|
throw new Error(`tempBase must be an absolute path, got: ${tempBase}`);
|
|
14710
16672
|
}
|
|
14711
16673
|
let dirNames;
|
|
14712
16674
|
try {
|
|
14713
|
-
dirNames = await
|
|
16675
|
+
dirNames = await readdir4(tempBase);
|
|
14714
16676
|
} catch {
|
|
14715
16677
|
return 0;
|
|
14716
16678
|
}
|
|
14717
16679
|
for (const dirName of dirNames) {
|
|
14718
|
-
const dirPath =
|
|
16680
|
+
const dirPath = join7(tempBase, dirName);
|
|
14719
16681
|
const isRemovingDir = dirName.endsWith(REMOVING_SUFFIX);
|
|
14720
16682
|
const isGhostDir = dirName.startsWith(GHOST_DIR_PREFIX) && !isRemovingDir;
|
|
14721
16683
|
if (!isRemovingDir && !isGhostDir)
|
|
14722
16684
|
continue;
|
|
14723
16685
|
let stats;
|
|
14724
16686
|
try {
|
|
14725
|
-
stats = await
|
|
16687
|
+
stats = await stat5(dirPath);
|
|
14726
16688
|
} catch {
|
|
14727
16689
|
continue;
|
|
14728
16690
|
}
|
|
@@ -14735,7 +16697,7 @@ async function cleanupOrphanedGhostDirs(tempBase = tmpdir()) {
|
|
|
14735
16697
|
try {
|
|
14736
16698
|
if (isGhostDir) {
|
|
14737
16699
|
const removingPath = `${dirPath}${REMOVING_SUFFIX}`;
|
|
14738
|
-
await
|
|
16700
|
+
await rename2(dirPath, removingPath);
|
|
14739
16701
|
await rm2(removingPath, { recursive: true, force: true });
|
|
14740
16702
|
} else {
|
|
14741
16703
|
await rm2(dirPath, { recursive: true, force: true });
|
|
@@ -14745,21 +16707,8 @@ async function cleanupOrphanedGhostDirs(tempBase = tmpdir()) {
|
|
|
14745
16707
|
}
|
|
14746
16708
|
return cleanedCount;
|
|
14747
16709
|
}
|
|
14748
|
-
async function
|
|
14749
|
-
if (
|
|
14750
|
-
return { action: "skip" };
|
|
14751
|
-
}
|
|
14752
|
-
if (disposition.type === "included") {
|
|
14753
|
-
return { action: "includeWhole" };
|
|
14754
|
-
}
|
|
14755
|
-
if (isDirectory) {
|
|
14756
|
-
const nestedPlan = await computeNestedPlan();
|
|
14757
|
-
return { action: "partial", nestedPlan };
|
|
14758
|
-
}
|
|
14759
|
-
return { action: "skip" };
|
|
14760
|
-
}
|
|
14761
|
-
async function computeSymlinkPlan(sourceDir, projectRoot, excludedPaths, matcher, insideExcludedTree = false) {
|
|
14762
|
-
if (!isAbsolute(sourceDir)) {
|
|
16710
|
+
async function computeSymlinkPlan(sourceDir, projectRoot, matcher) {
|
|
16711
|
+
if (!isAbsolute2(sourceDir)) {
|
|
14763
16712
|
throw new Error(`sourceDir must be an absolute path, got: ${sourceDir}`);
|
|
14764
16713
|
}
|
|
14765
16714
|
const plan = {
|
|
@@ -14767,49 +16716,25 @@ async function computeSymlinkPlan(sourceDir, projectRoot, excludedPaths, matcher
|
|
|
14767
16716
|
files: [],
|
|
14768
16717
|
partialDirs: new Map
|
|
14769
16718
|
};
|
|
14770
|
-
const entries = await
|
|
16719
|
+
const entries = await readdir4(sourceDir, { withFileTypes: true });
|
|
14771
16720
|
for (const entry of entries) {
|
|
14772
|
-
const sourcePath =
|
|
16721
|
+
const sourcePath = join7(sourceDir, entry.name);
|
|
14773
16722
|
const relativePath = normalizeForMatching(sourcePath, projectRoot);
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
const result = await handleExcludedEntry(entry.isDirectory(), disposition, () => computeSymlinkPlan(sourcePath, projectRoot, excludedPaths, matcher, true));
|
|
14777
|
-
if (result.action === "skip") {
|
|
14778
|
-
continue;
|
|
14779
|
-
}
|
|
14780
|
-
if (result.action === "includeWhole") {
|
|
14781
|
-
if (entry.isDirectory()) {
|
|
14782
|
-
plan.wholeDirs.push(entry.name);
|
|
14783
|
-
} else {
|
|
14784
|
-
plan.files.push(entry.name);
|
|
14785
|
-
}
|
|
14786
|
-
continue;
|
|
14787
|
-
}
|
|
14788
|
-
plan.partialDirs.set(entry.name, result.nestedPlan);
|
|
16723
|
+
const disposition = matcher.getDisposition(relativePath);
|
|
16724
|
+
if (disposition.type === "excluded") {
|
|
14789
16725
|
continue;
|
|
14790
16726
|
}
|
|
14791
|
-
if (
|
|
14792
|
-
if (
|
|
14793
|
-
|
|
14794
|
-
}
|
|
14795
|
-
|
|
14796
|
-
const result = await handleExcludedEntry(entry.isDirectory(), disposition, () => computeSymlinkPlan(sourcePath, projectRoot, excludedPaths, matcher, true));
|
|
14797
|
-
if (result.action === "skip") {
|
|
14798
|
-
continue;
|
|
14799
|
-
}
|
|
14800
|
-
if (result.action === "includeWhole") {
|
|
14801
|
-
if (entry.isDirectory()) {
|
|
14802
|
-
plan.wholeDirs.push(entry.name);
|
|
14803
|
-
} else {
|
|
14804
|
-
plan.files.push(entry.name);
|
|
14805
|
-
}
|
|
14806
|
-
continue;
|
|
16727
|
+
if (disposition.type === "included") {
|
|
16728
|
+
if (entry.isDirectory()) {
|
|
16729
|
+
plan.wholeDirs.push(entry.name);
|
|
16730
|
+
} else {
|
|
16731
|
+
plan.files.push(entry.name);
|
|
14807
16732
|
}
|
|
14808
|
-
plan.partialDirs.set(entry.name, result.nestedPlan);
|
|
14809
16733
|
continue;
|
|
14810
16734
|
}
|
|
14811
16735
|
if (entry.isDirectory()) {
|
|
14812
|
-
|
|
16736
|
+
const nestedPlan = await computeSymlinkPlan(sourcePath, projectRoot, matcher);
|
|
16737
|
+
plan.partialDirs.set(entry.name, nestedPlan);
|
|
14813
16738
|
} else {
|
|
14814
16739
|
plan.files.push(entry.name);
|
|
14815
16740
|
}
|
|
@@ -14817,33 +16742,34 @@ async function computeSymlinkPlan(sourceDir, projectRoot, excludedPaths, matcher
|
|
|
14817
16742
|
return plan;
|
|
14818
16743
|
}
|
|
14819
16744
|
async function executeSymlinkPlan(plan, sourceRoot, targetRoot) {
|
|
14820
|
-
if (!
|
|
16745
|
+
if (!isAbsolute2(sourceRoot)) {
|
|
14821
16746
|
throw new Error(`sourceRoot must be an absolute path, got: ${sourceRoot}`);
|
|
14822
16747
|
}
|
|
14823
|
-
if (!
|
|
16748
|
+
if (!isAbsolute2(targetRoot)) {
|
|
14824
16749
|
throw new Error(`targetRoot must be an absolute path, got: ${targetRoot}`);
|
|
14825
16750
|
}
|
|
14826
16751
|
for (const dirName of plan.wholeDirs) {
|
|
14827
|
-
const sourcePath =
|
|
14828
|
-
const targetPath =
|
|
16752
|
+
const sourcePath = join7(sourceRoot, dirName);
|
|
16753
|
+
const targetPath = join7(targetRoot, dirName);
|
|
14829
16754
|
await symlink2(sourcePath, targetPath);
|
|
14830
16755
|
}
|
|
14831
16756
|
for (const fileName of plan.files) {
|
|
14832
|
-
const sourcePath =
|
|
14833
|
-
const targetPath =
|
|
16757
|
+
const sourcePath = join7(sourceRoot, fileName);
|
|
16758
|
+
const targetPath = join7(targetRoot, fileName);
|
|
14834
16759
|
await symlink2(sourcePath, targetPath);
|
|
14835
16760
|
}
|
|
14836
16761
|
for (const [dirName, nestedPlan] of plan.partialDirs) {
|
|
14837
|
-
const sourcePath =
|
|
14838
|
-
const targetPath =
|
|
16762
|
+
const sourcePath = join7(sourceRoot, dirName);
|
|
16763
|
+
const targetPath = join7(targetRoot, dirName);
|
|
14839
16764
|
await mkdir4(targetPath, { recursive: true });
|
|
14840
16765
|
await executeSymlinkPlan(nestedPlan, sourcePath, targetPath);
|
|
14841
16766
|
}
|
|
14842
16767
|
}
|
|
14843
16768
|
|
|
14844
16769
|
// src/utils/terminal-title.ts
|
|
14845
|
-
import
|
|
16770
|
+
import path6 from "path";
|
|
14846
16771
|
var MAX_BRANCH_LENGTH = 20;
|
|
16772
|
+
var titleSaved = false;
|
|
14847
16773
|
function isInsideTmux() {
|
|
14848
16774
|
return Boolean(process.env.TMUX);
|
|
14849
16775
|
}
|
|
@@ -14864,8 +16790,27 @@ function setTerminalName(name) {
|
|
|
14864
16790
|
setTmuxWindowName(name);
|
|
14865
16791
|
setTerminalTitle(name);
|
|
14866
16792
|
}
|
|
16793
|
+
function saveTerminalTitle() {
|
|
16794
|
+
if (titleSaved || !isTTY) {
|
|
16795
|
+
return;
|
|
16796
|
+
}
|
|
16797
|
+
process.stdout.write("\x1B[22;2t");
|
|
16798
|
+
titleSaved = true;
|
|
16799
|
+
}
|
|
16800
|
+
function restoreTerminalTitle() {
|
|
16801
|
+
if (!titleSaved) {
|
|
16802
|
+
return;
|
|
16803
|
+
}
|
|
16804
|
+
if (isInsideTmux()) {
|
|
16805
|
+
Bun.spawnSync(["tmux", "set-window-option", "automatic-rename", "on"]);
|
|
16806
|
+
}
|
|
16807
|
+
if (isTTY) {
|
|
16808
|
+
process.stdout.write("\x1B[23;2t");
|
|
16809
|
+
}
|
|
16810
|
+
titleSaved = false;
|
|
16811
|
+
}
|
|
14867
16812
|
function formatTerminalName(cwd, profileName, gitInfo) {
|
|
14868
|
-
const repoName = gitInfo.repoName ??
|
|
16813
|
+
const repoName = gitInfo.repoName ?? path6.basename(cwd);
|
|
14869
16814
|
if (!gitInfo.branch) {
|
|
14870
16815
|
return `ghost[${profileName}]:${repoName}`;
|
|
14871
16816
|
}
|
|
@@ -14875,7 +16820,7 @@ function formatTerminalName(cwd, profileName, gitInfo) {
|
|
|
14875
16820
|
|
|
14876
16821
|
// src/commands/ghost/opencode.ts
|
|
14877
16822
|
function registerGhostOpenCodeCommand(parent) {
|
|
14878
|
-
parent.command("opencode").description("Launch OpenCode with ghost mode configuration").option("-p, --profile <name>", "Use specific profile").addOption(sharedOptions.json()).addOption(sharedOptions.quiet()).allowUnknownOption().allowExcessArguments(true).action(async (options2, command) => {
|
|
16823
|
+
parent.command("opencode").description("Launch OpenCode with ghost mode configuration").option("-p, --profile <name>", "Use specific profile").option("--no-rename", "Disable terminal/tmux window renaming").addOption(sharedOptions.json()).addOption(sharedOptions.quiet()).allowUnknownOption().allowExcessArguments(true).action(async (options2, command) => {
|
|
14879
16824
|
try {
|
|
14880
16825
|
const args = command.args;
|
|
14881
16826
|
await runGhostOpenCode(args, options2);
|
|
@@ -14889,11 +16834,6 @@ async function runGhostOpenCode(args, options2) {
|
|
|
14889
16834
|
if (!await manager.isInitialized()) {
|
|
14890
16835
|
throw new ProfilesNotInitializedError;
|
|
14891
16836
|
}
|
|
14892
|
-
if (!options2.quiet && await needsMigration()) {
|
|
14893
|
-
console.log("Notice: Found legacy config at ~/.config/ocx/");
|
|
14894
|
-
console.log(`Run 'ocx ghost migrate' to upgrade to the new profiles system.
|
|
14895
|
-
`);
|
|
14896
|
-
}
|
|
14897
16837
|
await cleanupOrphanedGhostDirs();
|
|
14898
16838
|
const profileName = await manager.getCurrent(options2.profile);
|
|
14899
16839
|
const profile = await manager.get(profileName);
|
|
@@ -14906,28 +16846,29 @@ async function runGhostOpenCode(args, options2) {
|
|
|
14906
16846
|
}
|
|
14907
16847
|
const cwd = process.cwd();
|
|
14908
16848
|
const gitContext = await detectGitRepo(cwd);
|
|
14909
|
-
const gitRoot = gitContext?.workTree ?? cwd;
|
|
14910
|
-
const discoveredPaths = await discoverProjectFiles(cwd, gitRoot);
|
|
14911
16849
|
const ghostConfig = profile.ghost;
|
|
14912
|
-
const tempDir = await createSymlinkFarm(cwd,
|
|
16850
|
+
const tempDir = await createSymlinkFarm(cwd, {
|
|
14913
16851
|
includePatterns: ghostConfig.include,
|
|
14914
16852
|
excludePatterns: ghostConfig.exclude
|
|
14915
16853
|
});
|
|
14916
|
-
const
|
|
14917
|
-
await injectGhostFiles(tempDir, profileDir, ghostFiles);
|
|
14918
|
-
if (profile.hasAgents) {
|
|
14919
|
-
const agentsPath = getProfileAgents(profileName);
|
|
14920
|
-
const destAgentsPath = path8.join(tempDir, "AGENTS.md");
|
|
14921
|
-
await copyFilePromise(agentsPath, destAgentsPath);
|
|
14922
|
-
}
|
|
16854
|
+
const overlayFiles = await injectProfileOverlay(tempDir, profileDir, ghostConfig.include);
|
|
14923
16855
|
let cleanupDone = false;
|
|
16856
|
+
let fileSync;
|
|
16857
|
+
fileSync = createFileSync(tempDir, cwd, { overlayFiles });
|
|
14924
16858
|
const performCleanup = async () => {
|
|
14925
16859
|
if (cleanupDone)
|
|
14926
16860
|
return;
|
|
14927
16861
|
cleanupDone = true;
|
|
14928
16862
|
await cleanupSymlinkFarm(tempDir);
|
|
14929
16863
|
};
|
|
16864
|
+
const shouldRename = options2.rename !== false && ghostConfig.renameWindow !== false;
|
|
14930
16865
|
const exitHandler = () => {
|
|
16866
|
+
if (shouldRename) {
|
|
16867
|
+
restoreTerminalTitle();
|
|
16868
|
+
}
|
|
16869
|
+
if (fileSync) {
|
|
16870
|
+
fileSync.close().catch(() => {});
|
|
16871
|
+
}
|
|
14931
16872
|
if (!cleanupDone && tempDir) {
|
|
14932
16873
|
try {
|
|
14933
16874
|
const removingPath = `${tempDir}${REMOVING_SUFFIX}`;
|
|
@@ -14942,8 +16883,11 @@ async function runGhostOpenCode(args, options2) {
|
|
|
14942
16883
|
const sigtermHandler = () => proc?.kill("SIGTERM");
|
|
14943
16884
|
process.on("SIGINT", sigintHandler);
|
|
14944
16885
|
process.on("SIGTERM", sigtermHandler);
|
|
14945
|
-
|
|
14946
|
-
|
|
16886
|
+
if (shouldRename) {
|
|
16887
|
+
saveTerminalTitle();
|
|
16888
|
+
const gitInfo = await getGitInfo(cwd);
|
|
16889
|
+
setTerminalName(formatTerminalName(cwd, profileName, gitInfo));
|
|
16890
|
+
}
|
|
14947
16891
|
proc = Bun.spawn({
|
|
14948
16892
|
cmd: ["opencode", ...args],
|
|
14949
16893
|
cwd: tempDir,
|
|
@@ -14963,14 +16907,62 @@ async function runGhostOpenCode(args, options2) {
|
|
|
14963
16907
|
});
|
|
14964
16908
|
try {
|
|
14965
16909
|
const exitCode = await proc.exited;
|
|
16910
|
+
process.off("SIGINT", sigintHandler);
|
|
16911
|
+
process.off("SIGTERM", sigtermHandler);
|
|
16912
|
+
process.off("exit", exitHandler);
|
|
16913
|
+
if (fileSync) {
|
|
16914
|
+
await fileSync.close();
|
|
16915
|
+
const syncCount = fileSync.getSyncCount();
|
|
16916
|
+
const failures = fileSync.getFailures();
|
|
16917
|
+
if (syncCount > 0 && !options2.quiet) {
|
|
16918
|
+
logger.info(`Synced ${syncCount} new files to project`);
|
|
16919
|
+
}
|
|
16920
|
+
if (failures.length > 0) {
|
|
16921
|
+
logger.warn(`${failures.length} files failed to sync`);
|
|
16922
|
+
for (const f of failures) {
|
|
16923
|
+
logger.debug(` ${f.path}: ${f.error.message}`);
|
|
16924
|
+
}
|
|
16925
|
+
}
|
|
16926
|
+
}
|
|
16927
|
+
await performCleanup();
|
|
14966
16928
|
process.exit(exitCode);
|
|
14967
|
-
}
|
|
16929
|
+
} catch (error) {
|
|
14968
16930
|
process.off("SIGINT", sigintHandler);
|
|
14969
16931
|
process.off("SIGTERM", sigtermHandler);
|
|
14970
16932
|
process.off("exit", exitHandler);
|
|
16933
|
+
if (fileSync) {
|
|
16934
|
+
await fileSync.close();
|
|
16935
|
+
}
|
|
14971
16936
|
await performCleanup();
|
|
16937
|
+
throw error;
|
|
14972
16938
|
}
|
|
14973
16939
|
}
|
|
16940
|
+
function userExplicitlyIncluded(relativePath, compiledPatterns) {
|
|
16941
|
+
if (compiledPatterns.length === 0)
|
|
16942
|
+
return false;
|
|
16943
|
+
return compiledPatterns.some((glob) => glob.match(relativePath));
|
|
16944
|
+
}
|
|
16945
|
+
async function injectProfileOverlay(tempDir, profileDir, includePatterns) {
|
|
16946
|
+
const entries = await readdir5(profileDir, { withFileTypes: true, recursive: true });
|
|
16947
|
+
const injectedFiles = new Set;
|
|
16948
|
+
const compiledIncludePatterns = includePatterns.map((p) => new Glob3(p));
|
|
16949
|
+
for (const entry of entries) {
|
|
16950
|
+
const relativePath = path7.relative(profileDir, path7.join(entry.parentPath, entry.name));
|
|
16951
|
+
if (relativePath === "ghost.jsonc")
|
|
16952
|
+
continue;
|
|
16953
|
+
if (relativePath === ".gitignore")
|
|
16954
|
+
continue;
|
|
16955
|
+
if (userExplicitlyIncluded(relativePath, compiledIncludePatterns))
|
|
16956
|
+
continue;
|
|
16957
|
+
if (entry.isDirectory())
|
|
16958
|
+
continue;
|
|
16959
|
+
const destPath = path7.join(tempDir, relativePath);
|
|
16960
|
+
await mkdir5(path7.dirname(destPath), { recursive: true });
|
|
16961
|
+
await copyFile(path7.join(entry.parentPath, entry.name), destPath);
|
|
16962
|
+
injectedFiles.add(normalizePath2(relativePath));
|
|
16963
|
+
}
|
|
16964
|
+
return new Set(injectedFiles);
|
|
16965
|
+
}
|
|
14974
16966
|
|
|
14975
16967
|
// src/commands/ghost/profile/add.ts
|
|
14976
16968
|
function registerProfileAddCommand(parent) {
|
|
@@ -15506,13 +17498,12 @@ function registerGhostCommand(program2) {
|
|
|
15506
17498
|
registerGhostSearchCommand(ghost);
|
|
15507
17499
|
registerGhostOpenCodeCommand(ghost);
|
|
15508
17500
|
registerGhostProfileCommand(ghost);
|
|
15509
|
-
registerGhostMigrateCommand(ghost);
|
|
15510
17501
|
}
|
|
15511
17502
|
|
|
15512
17503
|
// src/commands/init.ts
|
|
15513
|
-
import { existsSync as
|
|
15514
|
-
import { cp, mkdir as
|
|
15515
|
-
import { join as
|
|
17504
|
+
import { existsSync as existsSync3 } from "fs";
|
|
17505
|
+
import { cp, mkdir as mkdir6, readdir as readdir6, readFile, rm as rm3, writeFile as writeFile2 } from "fs/promises";
|
|
17506
|
+
import { join as join8 } from "path";
|
|
15516
17507
|
var TEMPLATE_REPO = "kdcokenny/ocx";
|
|
15517
17508
|
var TEMPLATE_PATH = "examples/registry-starter";
|
|
15518
17509
|
function registerInitCommand(program2) {
|
|
@@ -15530,8 +17521,8 @@ function registerInitCommand(program2) {
|
|
|
15530
17521
|
}
|
|
15531
17522
|
async function runInit(options2) {
|
|
15532
17523
|
const cwd = options2.cwd ?? process.cwd();
|
|
15533
|
-
const configPath =
|
|
15534
|
-
if (
|
|
17524
|
+
const configPath = join8(cwd, "ocx.jsonc");
|
|
17525
|
+
if (existsSync3(configPath)) {
|
|
15535
17526
|
throw new ConflictError(`ocx.jsonc already exists at ${configPath}
|
|
15536
17527
|
|
|
15537
17528
|
` + `To reset, delete the config and run init again:
|
|
@@ -15578,7 +17569,7 @@ async function runInitRegistry(directory, options2) {
|
|
|
15578
17569
|
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(namespace)) {
|
|
15579
17570
|
throw new ValidationError("Invalid namespace format: must start with letter/number, use hyphens only between segments (e.g., 'my-registry')");
|
|
15580
17571
|
}
|
|
15581
|
-
const existingFiles = await
|
|
17572
|
+
const existingFiles = await readdir6(cwd).catch(() => []);
|
|
15582
17573
|
const hasVisibleFiles = existingFiles.some((f) => !f.startsWith("."));
|
|
15583
17574
|
if (hasVisibleFiles && !options2.force) {
|
|
15584
17575
|
throw new ConflictError("Directory is not empty. Use --force to overwrite existing files.");
|
|
@@ -15589,7 +17580,7 @@ async function runInitRegistry(directory, options2) {
|
|
|
15589
17580
|
if (spin)
|
|
15590
17581
|
spin.text = options2.local ? "Copying template..." : "Fetching template...";
|
|
15591
17582
|
if (options2.local) {
|
|
15592
|
-
await
|
|
17583
|
+
await mkdir6(cwd, { recursive: true });
|
|
15593
17584
|
await copyDir(options2.local, cwd);
|
|
15594
17585
|
} else {
|
|
15595
17586
|
const version = options2.canary ? "main" : await getLatestVersion();
|
|
@@ -15605,7 +17596,7 @@ async function runInitRegistry(directory, options2) {
|
|
|
15605
17596
|
logger.info("");
|
|
15606
17597
|
logger.info("Next steps:");
|
|
15607
17598
|
logger.info(" 1. bun install");
|
|
15608
|
-
logger.info(" 2. Edit registry.
|
|
17599
|
+
logger.info(" 2. Edit registry.jsonc with your components");
|
|
15609
17600
|
logger.info(" 3. bun run build");
|
|
15610
17601
|
logger.info("");
|
|
15611
17602
|
logger.info("Deploy to:");
|
|
@@ -15637,10 +17628,10 @@ async function fetchAndExtractTemplate(destDir, version, verbose) {
|
|
|
15637
17628
|
if (!response.ok || !response.body) {
|
|
15638
17629
|
throw new NetworkError(`Failed to fetch template from ${tarballUrl}: ${response.statusText}`);
|
|
15639
17630
|
}
|
|
15640
|
-
const tempDir =
|
|
15641
|
-
await
|
|
17631
|
+
const tempDir = join8(destDir, ".ocx-temp");
|
|
17632
|
+
await mkdir6(tempDir, { recursive: true });
|
|
15642
17633
|
try {
|
|
15643
|
-
const tarPath =
|
|
17634
|
+
const tarPath = join8(tempDir, "template.tar.gz");
|
|
15644
17635
|
const arrayBuffer = await response.arrayBuffer();
|
|
15645
17636
|
await writeFile2(tarPath, Buffer.from(arrayBuffer));
|
|
15646
17637
|
const proc = Bun.spawn(["tar", "-xzf", tarPath, "-C", tempDir], {
|
|
@@ -15652,12 +17643,12 @@ async function fetchAndExtractTemplate(destDir, version, verbose) {
|
|
|
15652
17643
|
const stderr = await new Response(proc.stderr).text();
|
|
15653
17644
|
throw new Error(`Failed to extract template: ${stderr}`);
|
|
15654
17645
|
}
|
|
15655
|
-
const extractedDirs = await
|
|
17646
|
+
const extractedDirs = await readdir6(tempDir);
|
|
15656
17647
|
const extractedDir = extractedDirs.find((d) => d.startsWith("ocx-"));
|
|
15657
17648
|
if (!extractedDir) {
|
|
15658
17649
|
throw new Error("Failed to find extracted template directory");
|
|
15659
17650
|
}
|
|
15660
|
-
const templateDir =
|
|
17651
|
+
const templateDir = join8(tempDir, extractedDir, TEMPLATE_PATH);
|
|
15661
17652
|
await copyDir(templateDir, destDir);
|
|
15662
17653
|
} finally {
|
|
15663
17654
|
await rm3(tempDir, { recursive: true, force: true });
|
|
@@ -15665,15 +17656,15 @@ async function fetchAndExtractTemplate(destDir, version, verbose) {
|
|
|
15665
17656
|
}
|
|
15666
17657
|
async function replacePlaceholders(dir, values) {
|
|
15667
17658
|
const filesToProcess = [
|
|
15668
|
-
"registry.
|
|
17659
|
+
"registry.jsonc",
|
|
15669
17660
|
"package.json",
|
|
15670
17661
|
"wrangler.jsonc",
|
|
15671
17662
|
"README.md",
|
|
15672
17663
|
"AGENTS.md"
|
|
15673
17664
|
];
|
|
15674
17665
|
for (const file of filesToProcess) {
|
|
15675
|
-
const filePath =
|
|
15676
|
-
if (!
|
|
17666
|
+
const filePath = join8(dir, file);
|
|
17667
|
+
if (!existsSync3(filePath))
|
|
15677
17668
|
continue;
|
|
15678
17669
|
let content2 = await readFile(filePath).then((b) => b.toString());
|
|
15679
17670
|
content2 = content2.replace(/my-registry/g, values.namespace);
|
|
@@ -15682,11 +17673,393 @@ async function replacePlaceholders(dir, values) {
|
|
|
15682
17673
|
}
|
|
15683
17674
|
}
|
|
15684
17675
|
|
|
15685
|
-
// src/
|
|
17676
|
+
// src/self-update/version-provider.ts
|
|
17677
|
+
class BuildTimeVersionProvider {
|
|
17678
|
+
version = "1.3.0";
|
|
17679
|
+
}
|
|
17680
|
+
var defaultVersionProvider = new BuildTimeVersionProvider;
|
|
17681
|
+
|
|
17682
|
+
// src/self-update/check.ts
|
|
17683
|
+
var VERSION_CHECK_TIMEOUT_MS = 200;
|
|
17684
|
+
var PACKAGE_NAME = "ocx";
|
|
17685
|
+
function parseVersion2(v) {
|
|
17686
|
+
const [main2 = ""] = v.split("-");
|
|
17687
|
+
const parts = main2.split(".");
|
|
17688
|
+
const major = parseInt(parts[0] ?? "", 10);
|
|
17689
|
+
const minor = parseInt(parts[1] ?? "", 10);
|
|
17690
|
+
const patch = parseInt(parts[2] ?? "", 10);
|
|
17691
|
+
if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) {
|
|
17692
|
+
return null;
|
|
17693
|
+
}
|
|
17694
|
+
return { major, minor, patch };
|
|
17695
|
+
}
|
|
17696
|
+
function compareSemver2(a, b) {
|
|
17697
|
+
const vA = parseVersion2(a);
|
|
17698
|
+
const vB = parseVersion2(b);
|
|
17699
|
+
if (!vA || !vB) {
|
|
17700
|
+
return null;
|
|
17701
|
+
}
|
|
17702
|
+
if (vA.major !== vB.major)
|
|
17703
|
+
return vA.major - vB.major;
|
|
17704
|
+
if (vA.minor !== vB.minor)
|
|
17705
|
+
return vA.minor - vB.minor;
|
|
17706
|
+
return vA.patch - vB.patch;
|
|
17707
|
+
}
|
|
17708
|
+
async function checkForUpdate(versionProvider) {
|
|
17709
|
+
const provider = versionProvider ?? defaultVersionProvider;
|
|
17710
|
+
const current = provider.version || "0.0.0-dev";
|
|
17711
|
+
if (current === "0.0.0-dev") {
|
|
17712
|
+
return null;
|
|
17713
|
+
}
|
|
17714
|
+
try {
|
|
17715
|
+
const result = await fetchPackageVersion(PACKAGE_NAME, undefined, AbortSignal.timeout(VERSION_CHECK_TIMEOUT_MS));
|
|
17716
|
+
const latest = result.version;
|
|
17717
|
+
const comparison = compareSemver2(latest, current);
|
|
17718
|
+
if (comparison === null) {
|
|
17719
|
+
return null;
|
|
17720
|
+
}
|
|
17721
|
+
return {
|
|
17722
|
+
current,
|
|
17723
|
+
latest,
|
|
17724
|
+
updateAvailable: comparison > 0
|
|
17725
|
+
};
|
|
17726
|
+
} catch {
|
|
17727
|
+
return null;
|
|
17728
|
+
}
|
|
17729
|
+
}
|
|
17730
|
+
|
|
17731
|
+
// src/self-update/detect-method.ts
|
|
17732
|
+
function parseInstallMethod(input) {
|
|
17733
|
+
const VALID_METHODS = ["curl", "npm", "yarn", "pnpm", "bun"];
|
|
17734
|
+
const method = VALID_METHODS.find((m) => m === input);
|
|
17735
|
+
if (!method) {
|
|
17736
|
+
throw new SelfUpdateError(`Invalid install method: "${input}"
|
|
17737
|
+
Valid methods: ${VALID_METHODS.join(", ")}`);
|
|
17738
|
+
}
|
|
17739
|
+
return method;
|
|
17740
|
+
}
|
|
17741
|
+
var isCompiledBinary = () => Bun.main.startsWith("/$bunfs/");
|
|
17742
|
+
var isTempExecution = (path8) => path8.includes("/_npx/") || path8.includes("/.cache/bunx/") || path8.includes("/.pnpm/_temp/");
|
|
17743
|
+
var isYarnGlobalInstall = (path8) => path8.includes("/.yarn/global") || path8.includes("/.config/yarn/global");
|
|
17744
|
+
var isPnpmGlobalInstall = (path8) => path8.includes("/.pnpm/") || path8.includes("/pnpm/global");
|
|
17745
|
+
var isBunGlobalInstall = (path8) => path8.includes("/.bun/bin") || path8.includes("/.bun/install/global");
|
|
17746
|
+
var isNpmGlobalInstall = (path8) => path8.includes("/.npm/") || path8.includes("/node_modules/");
|
|
17747
|
+
function detectInstallMethod() {
|
|
17748
|
+
if (isCompiledBinary()) {
|
|
17749
|
+
return "curl";
|
|
17750
|
+
}
|
|
17751
|
+
const scriptPath = process.argv[1] ?? "";
|
|
17752
|
+
if (isTempExecution(scriptPath))
|
|
17753
|
+
return "unknown";
|
|
17754
|
+
if (isYarnGlobalInstall(scriptPath))
|
|
17755
|
+
return "yarn";
|
|
17756
|
+
if (isPnpmGlobalInstall(scriptPath))
|
|
17757
|
+
return "pnpm";
|
|
17758
|
+
if (isBunGlobalInstall(scriptPath))
|
|
17759
|
+
return "bun";
|
|
17760
|
+
if (isNpmGlobalInstall(scriptPath))
|
|
17761
|
+
return "npm";
|
|
17762
|
+
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
17763
|
+
if (userAgent.includes("yarn"))
|
|
17764
|
+
return "yarn";
|
|
17765
|
+
if (userAgent.includes("pnpm"))
|
|
17766
|
+
return "pnpm";
|
|
17767
|
+
if (userAgent.includes("bun"))
|
|
17768
|
+
return "bun";
|
|
17769
|
+
if (userAgent.includes("npm"))
|
|
17770
|
+
return "npm";
|
|
17771
|
+
return "unknown";
|
|
17772
|
+
}
|
|
17773
|
+
function getExecutablePath() {
|
|
17774
|
+
if (typeof Bun !== "undefined" && Bun.main.startsWith("/$bunfs/")) {
|
|
17775
|
+
return process.execPath;
|
|
17776
|
+
}
|
|
17777
|
+
return process.argv[1] ?? process.execPath;
|
|
17778
|
+
}
|
|
17779
|
+
|
|
17780
|
+
// src/self-update/download.ts
|
|
17781
|
+
import { chmodSync, existsSync as existsSync4, renameSync as renameSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
17782
|
+
var GITHUB_REPO2 = "kdcokenny/ocx";
|
|
17783
|
+
var DEFAULT_DOWNLOAD_BASE_URL = `https://github.com/${GITHUB_REPO2}/releases/download`;
|
|
17784
|
+
var PLATFORM_MAP = {
|
|
17785
|
+
"arm64-darwin": "ocx-darwin-arm64",
|
|
17786
|
+
"x64-darwin": "ocx-darwin-x64",
|
|
17787
|
+
"arm64-linux": "ocx-linux-arm64",
|
|
17788
|
+
"x64-linux": "ocx-linux-x64",
|
|
17789
|
+
"x64-win32": "ocx-windows-x64.exe"
|
|
17790
|
+
};
|
|
17791
|
+
function getDownloadBaseUrl() {
|
|
17792
|
+
const envUrl = process.env.OCX_DOWNLOAD_URL;
|
|
17793
|
+
if (envUrl) {
|
|
17794
|
+
return envUrl.replace(/\/+$/, "");
|
|
17795
|
+
}
|
|
17796
|
+
return DEFAULT_DOWNLOAD_BASE_URL;
|
|
17797
|
+
}
|
|
17798
|
+
function getDownloadUrl(version) {
|
|
17799
|
+
const platform = `${process.arch}-${process.platform}`;
|
|
17800
|
+
const target = PLATFORM_MAP[platform];
|
|
17801
|
+
if (!target) {
|
|
17802
|
+
const supported = Object.keys(PLATFORM_MAP).join(", ");
|
|
17803
|
+
throw new SelfUpdateError(`Unsupported platform: ${platform}
|
|
17804
|
+
` + `Supported platforms: ${supported}`);
|
|
17805
|
+
}
|
|
17806
|
+
const baseUrl = getDownloadBaseUrl();
|
|
17807
|
+
return `${baseUrl}/v${version}/${target}`;
|
|
17808
|
+
}
|
|
17809
|
+
async function downloadWithProgress(url, dest) {
|
|
17810
|
+
const spin = createSpinner({ text: "Downloading update..." });
|
|
17811
|
+
spin.start();
|
|
17812
|
+
let response;
|
|
17813
|
+
try {
|
|
17814
|
+
response = await fetch(url, { redirect: "follow" });
|
|
17815
|
+
} catch (error) {
|
|
17816
|
+
spin.fail("Download failed");
|
|
17817
|
+
throw new SelfUpdateError(`Network error: ${error instanceof Error ? error.message : String(error)}`);
|
|
17818
|
+
}
|
|
17819
|
+
if (!response.ok) {
|
|
17820
|
+
spin.fail("Download failed");
|
|
17821
|
+
throw new SelfUpdateError(`Failed to download: HTTP ${response.status} ${response.statusText}`);
|
|
17822
|
+
}
|
|
17823
|
+
if (!response.body) {
|
|
17824
|
+
spin.fail("Download failed");
|
|
17825
|
+
throw new SelfUpdateError("Download failed: Empty response body");
|
|
17826
|
+
}
|
|
17827
|
+
const reader = response.body.getReader();
|
|
17828
|
+
const writer = Bun.file(dest).writer();
|
|
17829
|
+
const total = Number(response.headers.get("content-length") || 0);
|
|
17830
|
+
let received = 0;
|
|
17831
|
+
try {
|
|
17832
|
+
while (true) {
|
|
17833
|
+
const { done, value } = await reader.read();
|
|
17834
|
+
if (done)
|
|
17835
|
+
break;
|
|
17836
|
+
await writer.write(value);
|
|
17837
|
+
received += value.length;
|
|
17838
|
+
if (total > 0) {
|
|
17839
|
+
const pct = Math.round(received / total * 100);
|
|
17840
|
+
spin.text = `Downloading... ${pct}%`;
|
|
17841
|
+
}
|
|
17842
|
+
}
|
|
17843
|
+
await writer.end();
|
|
17844
|
+
spin.succeed("Download complete");
|
|
17845
|
+
} catch (error) {
|
|
17846
|
+
spin.fail("Download failed");
|
|
17847
|
+
await writer.end();
|
|
17848
|
+
throw new SelfUpdateError(`Download interrupted: ${error instanceof Error ? error.message : String(error)}`);
|
|
17849
|
+
}
|
|
17850
|
+
}
|
|
17851
|
+
async function downloadToTemp(version) {
|
|
17852
|
+
const execPath = getExecutablePath();
|
|
17853
|
+
const tempPath = `${execPath}.new.${Date.now()}`;
|
|
17854
|
+
const url = getDownloadUrl(version);
|
|
17855
|
+
await downloadWithProgress(url, tempPath);
|
|
17856
|
+
try {
|
|
17857
|
+
chmodSync(tempPath, 493);
|
|
17858
|
+
} catch (error) {
|
|
17859
|
+
if (existsSync4(tempPath)) {
|
|
17860
|
+
unlinkSync2(tempPath);
|
|
17861
|
+
}
|
|
17862
|
+
throw new SelfUpdateError(`Failed to set permissions: ${error instanceof Error ? error.message : String(error)}`);
|
|
17863
|
+
}
|
|
17864
|
+
return { tempPath, execPath };
|
|
17865
|
+
}
|
|
17866
|
+
function atomicReplace(tempPath, execPath) {
|
|
17867
|
+
const backupPath = `${execPath}.backup`;
|
|
17868
|
+
try {
|
|
17869
|
+
if (existsSync4(execPath)) {
|
|
17870
|
+
renameSync2(execPath, backupPath);
|
|
17871
|
+
}
|
|
17872
|
+
renameSync2(tempPath, execPath);
|
|
17873
|
+
if (existsSync4(backupPath)) {
|
|
17874
|
+
unlinkSync2(backupPath);
|
|
17875
|
+
}
|
|
17876
|
+
} catch (error) {
|
|
17877
|
+
if (existsSync4(backupPath) && !existsSync4(execPath)) {
|
|
17878
|
+
try {
|
|
17879
|
+
renameSync2(backupPath, execPath);
|
|
17880
|
+
} catch {}
|
|
17881
|
+
}
|
|
17882
|
+
if (existsSync4(tempPath)) {
|
|
17883
|
+
try {
|
|
17884
|
+
unlinkSync2(tempPath);
|
|
17885
|
+
} catch {}
|
|
17886
|
+
}
|
|
17887
|
+
throw new SelfUpdateError(`Update failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
17888
|
+
}
|
|
17889
|
+
}
|
|
17890
|
+
function cleanupTempFile(tempPath) {
|
|
17891
|
+
if (existsSync4(tempPath)) {
|
|
17892
|
+
try {
|
|
17893
|
+
unlinkSync2(tempPath);
|
|
17894
|
+
} catch {}
|
|
17895
|
+
}
|
|
17896
|
+
}
|
|
17897
|
+
|
|
17898
|
+
// src/self-update/notify.ts
|
|
17899
|
+
function notifyUpdate(current, latest) {
|
|
17900
|
+
if (!process.stdout.isTTY)
|
|
17901
|
+
return;
|
|
17902
|
+
console.error(`${kleur_default.cyan("info")}: update available - ${kleur_default.green(latest)} (current: ${kleur_default.dim(current)})`);
|
|
17903
|
+
console.error(` run ${kleur_default.cyan("`ocx self update`")} to upgrade`);
|
|
17904
|
+
}
|
|
17905
|
+
function notifyUpToDate(version) {
|
|
17906
|
+
console.error(`${kleur_default.cyan("info")}: ocx unchanged - ${kleur_default.dim(version)}`);
|
|
17907
|
+
}
|
|
17908
|
+
function notifyUpdated(from, to) {
|
|
17909
|
+
console.error(` ${kleur_default.green("ocx updated")} - ${to} (from ${from})`);
|
|
17910
|
+
}
|
|
17911
|
+
|
|
17912
|
+
// src/self-update/verify.ts
|
|
15686
17913
|
import { createHash as createHash2 } from "crypto";
|
|
15687
|
-
|
|
15688
|
-
|
|
15689
|
-
|
|
17914
|
+
var GITHUB_REPO3 = "kdcokenny/ocx";
|
|
17915
|
+
function parseSha256Sums(content2) {
|
|
17916
|
+
const checksums = new Map;
|
|
17917
|
+
for (const line of content2.split(`
|
|
17918
|
+
`)) {
|
|
17919
|
+
const match = line.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
|
|
17920
|
+
if (match?.[1] && match[2]) {
|
|
17921
|
+
checksums.set(match[2].trim(), match[1].toLowerCase());
|
|
17922
|
+
}
|
|
17923
|
+
}
|
|
17924
|
+
return checksums;
|
|
17925
|
+
}
|
|
17926
|
+
function hashContent2(content2) {
|
|
17927
|
+
return createHash2("sha256").update(content2).digest("hex");
|
|
17928
|
+
}
|
|
17929
|
+
async function fetchChecksums(version) {
|
|
17930
|
+
const url = `https://github.com/${GITHUB_REPO3}/releases/download/v${version}/SHA256SUMS.txt`;
|
|
17931
|
+
const response = await fetch(url);
|
|
17932
|
+
if (!response.ok) {
|
|
17933
|
+
throw new SelfUpdateError(`Failed to fetch checksums: ${response.status}`);
|
|
17934
|
+
}
|
|
17935
|
+
const content2 = await response.text();
|
|
17936
|
+
return parseSha256Sums(content2);
|
|
17937
|
+
}
|
|
17938
|
+
async function verifyChecksum(filePath, expectedHash, filename) {
|
|
17939
|
+
const file = Bun.file(filePath);
|
|
17940
|
+
const content2 = await file.arrayBuffer();
|
|
17941
|
+
const actualHash = hashContent2(Buffer.from(content2));
|
|
17942
|
+
if (actualHash !== expectedHash) {
|
|
17943
|
+
throw new IntegrityError(filename, expectedHash, actualHash);
|
|
17944
|
+
}
|
|
17945
|
+
}
|
|
17946
|
+
|
|
17947
|
+
// src/commands/self/update.ts
|
|
17948
|
+
var SEMVER_PATTERN = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
|
|
17949
|
+
async function updateCommand(options2) {
|
|
17950
|
+
const method = options2.method ? parseInstallMethod(options2.method) : detectInstallMethod();
|
|
17951
|
+
const result = await checkForUpdate();
|
|
17952
|
+
if (!result) {
|
|
17953
|
+
throw new SelfUpdateError("Failed to check for updates");
|
|
17954
|
+
}
|
|
17955
|
+
const { current, latest, updateAvailable } = result;
|
|
17956
|
+
if (!updateAvailable && !options2.force) {
|
|
17957
|
+
notifyUpToDate(current);
|
|
17958
|
+
return;
|
|
17959
|
+
}
|
|
17960
|
+
const targetVersion = latest;
|
|
17961
|
+
switch (method) {
|
|
17962
|
+
case "curl": {
|
|
17963
|
+
await updateViaCurl(current, targetVersion);
|
|
17964
|
+
break;
|
|
17965
|
+
}
|
|
17966
|
+
case "npm":
|
|
17967
|
+
case "pnpm":
|
|
17968
|
+
case "bun":
|
|
17969
|
+
case "unknown": {
|
|
17970
|
+
await updateViaPackageManager(method, current, targetVersion);
|
|
17971
|
+
break;
|
|
17972
|
+
}
|
|
17973
|
+
}
|
|
17974
|
+
}
|
|
17975
|
+
async function updateViaCurl(current, targetVersion) {
|
|
17976
|
+
const url = getDownloadUrl(targetVersion);
|
|
17977
|
+
const filename = url.split("/").pop();
|
|
17978
|
+
if (!filename) {
|
|
17979
|
+
throw new SelfUpdateError("Failed to determine binary filename from download URL");
|
|
17980
|
+
}
|
|
17981
|
+
const checksums = await fetchChecksums(targetVersion);
|
|
17982
|
+
const expectedHash = checksums.get(filename);
|
|
17983
|
+
if (!expectedHash) {
|
|
17984
|
+
throw new SelfUpdateError(`Security error: No checksum found for ${filename}. Update aborted.`);
|
|
17985
|
+
}
|
|
17986
|
+
const { tempPath, execPath } = await downloadToTemp(targetVersion);
|
|
17987
|
+
try {
|
|
17988
|
+
await verifyChecksum(tempPath, expectedHash, filename);
|
|
17989
|
+
} catch (error) {
|
|
17990
|
+
cleanupTempFile(tempPath);
|
|
17991
|
+
throw error;
|
|
17992
|
+
}
|
|
17993
|
+
atomicReplace(tempPath, execPath);
|
|
17994
|
+
notifyUpdated(current, targetVersion);
|
|
17995
|
+
}
|
|
17996
|
+
async function runPackageManager(cmd) {
|
|
17997
|
+
const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
17998
|
+
const exitCode = await proc.exited;
|
|
17999
|
+
if (exitCode !== 0) {
|
|
18000
|
+
const stderr = await new Response(proc.stderr).text();
|
|
18001
|
+
throw new SelfUpdateError(`Package manager command failed: ${stderr.trim()}`);
|
|
18002
|
+
}
|
|
18003
|
+
}
|
|
18004
|
+
async function updateViaPackageManager(method, current, targetVersion) {
|
|
18005
|
+
if (!SEMVER_PATTERN.test(targetVersion)) {
|
|
18006
|
+
throw new SelfUpdateError(`Invalid version format: ${targetVersion}`);
|
|
18007
|
+
}
|
|
18008
|
+
const spin = createSpinner({ text: `Updating via ${method}...` });
|
|
18009
|
+
spin.start();
|
|
18010
|
+
try {
|
|
18011
|
+
switch (method) {
|
|
18012
|
+
case "npm": {
|
|
18013
|
+
await runPackageManager(["npm", "install", "-g", `ocx@${targetVersion}`]);
|
|
18014
|
+
break;
|
|
18015
|
+
}
|
|
18016
|
+
case "yarn": {
|
|
18017
|
+
await runPackageManager(["yarn", "global", "add", `ocx@${targetVersion}`]);
|
|
18018
|
+
break;
|
|
18019
|
+
}
|
|
18020
|
+
case "pnpm": {
|
|
18021
|
+
await runPackageManager(["pnpm", "install", "-g", `ocx@${targetVersion}`]);
|
|
18022
|
+
break;
|
|
18023
|
+
}
|
|
18024
|
+
case "bun": {
|
|
18025
|
+
await runPackageManager(["bun", "install", "-g", `ocx@${targetVersion}`]);
|
|
18026
|
+
break;
|
|
18027
|
+
}
|
|
18028
|
+
case "unknown": {
|
|
18029
|
+
throw new SelfUpdateError(`Could not detect install method. Update manually with one of:
|
|
18030
|
+
` + ` npm install -g ocx@latest
|
|
18031
|
+
` + ` pnpm install -g ocx@latest
|
|
18032
|
+
` + " bun install -g ocx@latest");
|
|
18033
|
+
}
|
|
18034
|
+
}
|
|
18035
|
+
spin.succeed(`Updated via ${method}`);
|
|
18036
|
+
notifyUpdated(current, targetVersion);
|
|
18037
|
+
} catch (error) {
|
|
18038
|
+
if (error instanceof SelfUpdateError) {
|
|
18039
|
+
spin.fail(`Update failed`);
|
|
18040
|
+
throw error;
|
|
18041
|
+
}
|
|
18042
|
+
spin.fail(`Update failed`);
|
|
18043
|
+
throw new SelfUpdateError(`Failed to run ${method}: ${error instanceof Error ? error.message : String(error)}`);
|
|
18044
|
+
}
|
|
18045
|
+
}
|
|
18046
|
+
function registerSelfUpdateCommand(parent) {
|
|
18047
|
+
parent.command("update").description("Update OCX to the latest version").option("-f, --force", "Reinstall even if already up to date").option("-m, --method <method>", "Override install method detection (curl|npm|pnpm|bun)").action(wrapAction(async (options2) => {
|
|
18048
|
+
await updateCommand(options2);
|
|
18049
|
+
}));
|
|
18050
|
+
}
|
|
18051
|
+
|
|
18052
|
+
// src/commands/self/index.ts
|
|
18053
|
+
function registerSelfCommand(program2) {
|
|
18054
|
+
const self = program2.command("self").description("Manage the OCX CLI");
|
|
18055
|
+
registerSelfUpdateCommand(self);
|
|
18056
|
+
}
|
|
18057
|
+
|
|
18058
|
+
// src/commands/update.ts
|
|
18059
|
+
import { createHash as createHash3 } from "crypto";
|
|
18060
|
+
import { existsSync as existsSync5 } from "fs";
|
|
18061
|
+
import { mkdir as mkdir7, writeFile as writeFile3 } from "fs/promises";
|
|
18062
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
15690
18063
|
function registerUpdateCommand(program2) {
|
|
15691
18064
|
program2.command("update [components...]").description("Update installed components (use @version suffix to pin, e.g., kdco/agents@1.2.0)").option("--all", "Update all installed components").option("--registry <name>", "Update all components from a specific registry").option("--dry-run", "Preview changes without applying").option("--cwd <path>", "Working directory", process.cwd()).option("-q, --quiet", "Suppress output").option("-v, --verbose", "Verbose output").option("--json", "Output as JSON").action(async (components, options2) => {
|
|
15692
18065
|
try {
|
|
@@ -15698,7 +18071,7 @@ function registerUpdateCommand(program2) {
|
|
|
15698
18071
|
}
|
|
15699
18072
|
async function runUpdate(componentNames, options2) {
|
|
15700
18073
|
const cwd = options2.cwd ?? process.cwd();
|
|
15701
|
-
const lockPath =
|
|
18074
|
+
const lockPath = join9(cwd, "ocx.lock");
|
|
15702
18075
|
const config = await readOcxConfig(cwd);
|
|
15703
18076
|
if (!config) {
|
|
15704
18077
|
throw new ConfigError("No ocx.jsonc found. Run 'ocx init' first.");
|
|
@@ -15823,10 +18196,10 @@ Version cannot be empty. Use 'kdco/agents@1.2.0' or omit the version for latest.
|
|
|
15823
18196
|
const fileObj = update.component.files.find((f) => f.path === file.path);
|
|
15824
18197
|
if (!fileObj)
|
|
15825
18198
|
continue;
|
|
15826
|
-
const targetPath =
|
|
15827
|
-
const targetDir =
|
|
15828
|
-
if (!
|
|
15829
|
-
await
|
|
18199
|
+
const targetPath = join9(cwd, fileObj.target);
|
|
18200
|
+
const targetDir = dirname7(targetPath);
|
|
18201
|
+
if (!existsSync5(targetDir)) {
|
|
18202
|
+
await mkdir7(targetDir, { recursive: true });
|
|
15830
18203
|
}
|
|
15831
18204
|
await writeFile3(targetPath, file.content);
|
|
15832
18205
|
if (options2.verbose) {
|
|
@@ -15947,21 +18320,49 @@ function outputDryRun(results, options2) {
|
|
|
15947
18320
|
}
|
|
15948
18321
|
}
|
|
15949
18322
|
}
|
|
15950
|
-
async function
|
|
15951
|
-
return
|
|
18323
|
+
async function hashContent3(content2) {
|
|
18324
|
+
return createHash3("sha256").update(content2).digest("hex");
|
|
15952
18325
|
}
|
|
15953
18326
|
async function hashBundle2(files) {
|
|
15954
18327
|
const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
|
|
15955
18328
|
const manifestParts = [];
|
|
15956
18329
|
for (const file of sorted) {
|
|
15957
|
-
const hash = await
|
|
18330
|
+
const hash = await hashContent3(file.content);
|
|
15958
18331
|
manifestParts.push(`${file.path}:${hash}`);
|
|
15959
18332
|
}
|
|
15960
|
-
return
|
|
18333
|
+
return hashContent3(manifestParts.join(`
|
|
15961
18334
|
`));
|
|
15962
18335
|
}
|
|
18336
|
+
|
|
18337
|
+
// src/self-update/index.ts
|
|
18338
|
+
function shouldCheckForUpdate() {
|
|
18339
|
+
if (process.env.OCX_SELF_UPDATE === "off")
|
|
18340
|
+
return false;
|
|
18341
|
+
if (parseEnvBool(process.env.OCX_NO_UPDATE_CHECK, false))
|
|
18342
|
+
return false;
|
|
18343
|
+
if (process.env.CI)
|
|
18344
|
+
return false;
|
|
18345
|
+
if (!process.stdout.isTTY)
|
|
18346
|
+
return false;
|
|
18347
|
+
return true;
|
|
18348
|
+
}
|
|
18349
|
+
function registerUpdateCheckHook(program2) {
|
|
18350
|
+
program2.hook("postAction", async (thisCommand) => {
|
|
18351
|
+
if (thisCommand.name() === "update" && thisCommand.parent?.name() === "self") {
|
|
18352
|
+
return;
|
|
18353
|
+
}
|
|
18354
|
+
if (!shouldCheckForUpdate())
|
|
18355
|
+
return;
|
|
18356
|
+
try {
|
|
18357
|
+
const result = await checkForUpdate();
|
|
18358
|
+
if (result?.updateAvailable) {
|
|
18359
|
+
notifyUpdate(result.current, result.latest);
|
|
18360
|
+
}
|
|
18361
|
+
} catch {}
|
|
18362
|
+
});
|
|
18363
|
+
}
|
|
15963
18364
|
// src/index.ts
|
|
15964
|
-
var version = "1.
|
|
18365
|
+
var version = "1.3.0";
|
|
15965
18366
|
async function main2() {
|
|
15966
18367
|
const program2 = new Command().name("ocx").description("OpenCode Extensions - Install agents, skills, plugins, and commands").version(version);
|
|
15967
18368
|
registerInitCommand(program2);
|
|
@@ -15972,6 +18373,8 @@ async function main2() {
|
|
|
15972
18373
|
registerRegistryCommand(program2);
|
|
15973
18374
|
registerBuildCommand(program2);
|
|
15974
18375
|
registerGhostCommand(program2);
|
|
18376
|
+
registerSelfCommand(program2);
|
|
18377
|
+
registerUpdateCheckHook(program2);
|
|
15975
18378
|
await program2.parseAsync(process.argv);
|
|
15976
18379
|
}
|
|
15977
18380
|
if (import.meta.main) {
|
|
@@ -15988,4 +18391,4 @@ export {
|
|
|
15988
18391
|
buildRegistry
|
|
15989
18392
|
};
|
|
15990
18393
|
|
|
15991
|
-
//# debugId=
|
|
18394
|
+
//# debugId=FE5CBBF0E272CBE164756E2164756E21
|