@cloudbase/cloudbase-mcp 2.4.0-alpha.0 → 2.4.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/cli.cjs +423 -18
- package/dist/index.cjs +423 -18
- package/dist/index.js +7716 -7630
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -135023,7 +135023,7 @@ class TelemetryReporter {
|
|
|
135023
135023
|
const nodeVersion = process.version; // Node.js版本
|
|
135024
135024
|
const arch = os_1.default.arch(); // 系统架构
|
|
135025
135025
|
// 从构建时注入的版本号获取MCP版本信息
|
|
135026
|
-
const mcpVersion = process.env.npm_package_version || "2.4.0
|
|
135026
|
+
const mcpVersion = process.env.npm_package_version || "2.4.0" || 0;
|
|
135027
135027
|
return {
|
|
135028
135028
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
135029
135029
|
deviceId: this.deviceId,
|
|
@@ -185505,6 +185505,7 @@ exports.getClaudePrompt = getClaudePrompt;
|
|
|
185505
185505
|
exports.registerRagTools = registerRagTools;
|
|
185506
185506
|
const adm_zip_1 = __importDefault(__webpack_require__(30283));
|
|
185507
185507
|
const fs = __importStar(__webpack_require__(79748));
|
|
185508
|
+
const lockfile_1 = __importDefault(__webpack_require__(80127));
|
|
185508
185509
|
const os = __importStar(__webpack_require__(21820));
|
|
185509
185510
|
const path = __importStar(__webpack_require__(39902));
|
|
185510
185511
|
const zod_1 = __webpack_require__(21614);
|
|
@@ -185521,7 +185522,39 @@ const KnowledgeBaseIdMap = {
|
|
|
185521
185522
|
// ============ 缓存配置 ============
|
|
185522
185523
|
const CACHE_BASE_DIR = path.join(os.homedir(), ".cloudbase-mcp");
|
|
185523
185524
|
const CACHE_META_FILE = path.join(CACHE_BASE_DIR, "cache-meta.json");
|
|
185525
|
+
const LOCK_FILE = path.join(CACHE_BASE_DIR, ".download.lock");
|
|
185524
185526
|
const DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 默认 24 小时
|
|
185527
|
+
// Promise wrapper for lockfile methods
|
|
185528
|
+
function acquireLock(lockPath, options) {
|
|
185529
|
+
return new Promise((resolve, reject) => {
|
|
185530
|
+
if (options) {
|
|
185531
|
+
lockfile_1.default.lock(lockPath, options, (err) => {
|
|
185532
|
+
if (err)
|
|
185533
|
+
reject(err);
|
|
185534
|
+
else
|
|
185535
|
+
resolve();
|
|
185536
|
+
});
|
|
185537
|
+
}
|
|
185538
|
+
else {
|
|
185539
|
+
lockfile_1.default.lock(lockPath, (err) => {
|
|
185540
|
+
if (err)
|
|
185541
|
+
reject(err);
|
|
185542
|
+
else
|
|
185543
|
+
resolve();
|
|
185544
|
+
});
|
|
185545
|
+
}
|
|
185546
|
+
});
|
|
185547
|
+
}
|
|
185548
|
+
function releaseLock(lockPath) {
|
|
185549
|
+
return new Promise((resolve, reject) => {
|
|
185550
|
+
lockfile_1.default.unlock(lockPath, (err) => {
|
|
185551
|
+
if (err)
|
|
185552
|
+
reject(err);
|
|
185553
|
+
else
|
|
185554
|
+
resolve();
|
|
185555
|
+
});
|
|
185556
|
+
});
|
|
185557
|
+
}
|
|
185525
185558
|
// 支持环境变量 CLOUDBASE_MCP_CACHE_TTL_MS 控制缓存过期时间(毫秒)
|
|
185526
185559
|
const parsedCacheTTL = process.env.CLOUDBASE_MCP_CACHE_TTL_MS
|
|
185527
185560
|
? parseInt(process.env.CLOUDBASE_MCP_CACHE_TTL_MS, 10)
|
|
@@ -185691,14 +185724,19 @@ async function _doDownloadResources() {
|
|
|
185691
185724
|
async function downloadResources() {
|
|
185692
185725
|
const webTemplateDir = path.join(CACHE_BASE_DIR, "web-template");
|
|
185693
185726
|
const openAPIDir = path.join(CACHE_BASE_DIR, "openapi");
|
|
185694
|
-
//
|
|
185727
|
+
// 如果已有下载任务在进行中,共享该 Promise
|
|
185728
|
+
if (resourceDownloadPromise) {
|
|
185729
|
+
(0, logger_js_1.debug)("[downloadResources] 共享已有下载任务");
|
|
185730
|
+
return resourceDownloadPromise;
|
|
185731
|
+
}
|
|
185732
|
+
// 先快速检查缓存(不需要锁,因为只是读取)
|
|
185695
185733
|
if (await canUseCache()) {
|
|
185696
185734
|
try {
|
|
185697
185735
|
// 检查两个目录都存在
|
|
185698
185736
|
await Promise.all([fs.access(webTemplateDir), fs.access(openAPIDir)]);
|
|
185699
185737
|
const files = await fs.readdir(openAPIDir);
|
|
185700
185738
|
if (files.length > 0) {
|
|
185701
|
-
(0, logger_js_1.debug)("[downloadResources]
|
|
185739
|
+
(0, logger_js_1.debug)("[downloadResources] 使用缓存(快速路径)");
|
|
185702
185740
|
return {
|
|
185703
185741
|
webTemplateDir,
|
|
185704
185742
|
openAPIDocs: OPENAPI_SOURCES.map((source) => ({
|
|
@@ -185713,21 +185751,61 @@ async function downloadResources() {
|
|
|
185713
185751
|
// 缓存无效,需要重新下载
|
|
185714
185752
|
}
|
|
185715
185753
|
}
|
|
185716
|
-
//
|
|
185717
|
-
if (resourceDownloadPromise) {
|
|
185718
|
-
(0, logger_js_1.debug)("[downloadResources] 共享已有下载任务");
|
|
185719
|
-
return resourceDownloadPromise;
|
|
185720
|
-
}
|
|
185721
|
-
// 创建新的下载任务
|
|
185754
|
+
// 创建新的下载任务,使用文件锁保护
|
|
185722
185755
|
(0, logger_js_1.debug)("[downloadResources] 开始新下载任务");
|
|
185723
185756
|
await fs.mkdir(CACHE_BASE_DIR, { recursive: true });
|
|
185724
|
-
resourceDownloadPromise =
|
|
185725
|
-
|
|
185726
|
-
|
|
185727
|
-
|
|
185728
|
-
|
|
185729
|
-
|
|
185730
|
-
|
|
185757
|
+
resourceDownloadPromise = (async () => {
|
|
185758
|
+
// 尝试获取文件锁,最多等待 6 秒(30 次 × 200ms),每 200ms 轮询一次
|
|
185759
|
+
let lockAcquired = false;
|
|
185760
|
+
try {
|
|
185761
|
+
await acquireLock(LOCK_FILE, {
|
|
185762
|
+
wait: 30 * 200, // 总等待时间:6000ms (6 秒)
|
|
185763
|
+
pollPeriod: 200, // 轮询间隔:200ms
|
|
185764
|
+
stale: 5 * 60 * 1000, // 5 分钟,如果锁文件超过这个时间认为是过期的
|
|
185765
|
+
});
|
|
185766
|
+
lockAcquired = true;
|
|
185767
|
+
(0, logger_js_1.debug)("[downloadResources] 文件锁已获取");
|
|
185768
|
+
// 在持有锁的情况下再次检查缓存(可能其他进程已经下载完成)
|
|
185769
|
+
if (await canUseCache()) {
|
|
185770
|
+
try {
|
|
185771
|
+
// 检查两个目录都存在
|
|
185772
|
+
await Promise.all([fs.access(webTemplateDir), fs.access(openAPIDir)]);
|
|
185773
|
+
const files = await fs.readdir(openAPIDir);
|
|
185774
|
+
if (files.length > 0) {
|
|
185775
|
+
(0, logger_js_1.debug)("[downloadResources] 使用缓存(在锁保护下检查)");
|
|
185776
|
+
return {
|
|
185777
|
+
webTemplateDir,
|
|
185778
|
+
openAPIDocs: OPENAPI_SOURCES.map((source) => ({
|
|
185779
|
+
name: source.name,
|
|
185780
|
+
description: source.description,
|
|
185781
|
+
absolutePath: path.join(openAPIDir, `${source.name}.openapi.yaml`),
|
|
185782
|
+
})).filter((item) => files.includes(`${item.name}.openapi.yaml`)),
|
|
185783
|
+
};
|
|
185784
|
+
}
|
|
185785
|
+
}
|
|
185786
|
+
catch {
|
|
185787
|
+
// 缓存无效,需要重新下载
|
|
185788
|
+
}
|
|
185789
|
+
}
|
|
185790
|
+
// 执行下载
|
|
185791
|
+
const result = await _doDownloadResources();
|
|
185792
|
+
await updateCache();
|
|
185793
|
+
(0, logger_js_1.debug)("[downloadResources] 缓存已更新");
|
|
185794
|
+
return result;
|
|
185795
|
+
}
|
|
185796
|
+
finally {
|
|
185797
|
+
// 释放文件锁
|
|
185798
|
+
if (lockAcquired) {
|
|
185799
|
+
try {
|
|
185800
|
+
await releaseLock(LOCK_FILE);
|
|
185801
|
+
(0, logger_js_1.debug)("[downloadResources] 文件锁已释放");
|
|
185802
|
+
}
|
|
185803
|
+
catch (error) {
|
|
185804
|
+
(0, logger_js_1.warn)("[downloadResources] 释放文件锁失败", { error });
|
|
185805
|
+
}
|
|
185806
|
+
}
|
|
185807
|
+
}
|
|
185808
|
+
})().finally(() => {
|
|
185731
185809
|
resourceDownloadPromise = null;
|
|
185732
185810
|
});
|
|
185733
185811
|
return resourceDownloadPromise;
|
|
@@ -200871,7 +200949,7 @@ ${envIdSection}
|
|
|
200871
200949
|
## 环境信息
|
|
200872
200950
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
200873
200951
|
- Node.js版本: ${process.version}
|
|
200874
|
-
- MCP 版本:${process.env.npm_package_version || "2.4.0
|
|
200952
|
+
- MCP 版本:${process.env.npm_package_version || "2.4.0" || 0}
|
|
200875
200953
|
- 系统架构: ${os_1.default.arch()}
|
|
200876
200954
|
- 时间: ${new Date().toISOString()}
|
|
200877
200955
|
- 请求ID: ${requestId}
|
|
@@ -215540,7 +215618,7 @@ function registerSetupTools(server) {
|
|
|
215540
215618
|
title: "下载项目模板",
|
|
215541
215619
|
description: `自动下载并部署CloudBase项目模板。⚠️ **MANDATORY FOR NEW PROJECTS** ⚠️
|
|
215542
215620
|
|
|
215543
|
-
**CRITICAL**: This tool MUST be called FIRST when starting a new project.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置(默认)\n- cursor: Cursor AI编辑器\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.4.0
|
|
215621
|
+
**CRITICAL**: This tool MUST be called FIRST when starting a new project.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置(默认)\n- cursor: Cursor AI编辑器\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.4.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
215544
215622
|
inputSchema: {
|
|
215545
215623
|
template: zod_1.z
|
|
215546
215624
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -224631,6 +224709,333 @@ function resolveIds(schema) {
|
|
|
224631
224709
|
}
|
|
224632
224710
|
|
|
224633
224711
|
|
|
224712
|
+
/***/ }),
|
|
224713
|
+
|
|
224714
|
+
/***/ 80127:
|
|
224715
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
224716
|
+
|
|
224717
|
+
var fs = __webpack_require__(29021)
|
|
224718
|
+
|
|
224719
|
+
var wx = 'wx'
|
|
224720
|
+
if (process.version.match(/^v0\.[0-6]/)) {
|
|
224721
|
+
var c = __webpack_require__(81115)
|
|
224722
|
+
wx = c.O_TRUNC | c.O_CREAT | c.O_WRONLY | c.O_EXCL
|
|
224723
|
+
}
|
|
224724
|
+
|
|
224725
|
+
var os = __webpack_require__(21820)
|
|
224726
|
+
exports.filetime = 'ctime'
|
|
224727
|
+
if (os.platform() == "win32") {
|
|
224728
|
+
exports.filetime = 'mtime'
|
|
224729
|
+
}
|
|
224730
|
+
|
|
224731
|
+
var debug
|
|
224732
|
+
var util = __webpack_require__(28354)
|
|
224733
|
+
if (util.debuglog)
|
|
224734
|
+
debug = util.debuglog('LOCKFILE')
|
|
224735
|
+
else if (/\blockfile\b/i.test(process.env.NODE_DEBUG))
|
|
224736
|
+
debug = function() {
|
|
224737
|
+
var msg = util.format.apply(util, arguments)
|
|
224738
|
+
console.error('LOCKFILE %d %s', process.pid, msg)
|
|
224739
|
+
}
|
|
224740
|
+
else
|
|
224741
|
+
debug = function() {}
|
|
224742
|
+
|
|
224743
|
+
var locks = {}
|
|
224744
|
+
|
|
224745
|
+
function hasOwnProperty (obj, prop) {
|
|
224746
|
+
return Object.prototype.hasOwnProperty.call(obj, prop)
|
|
224747
|
+
}
|
|
224748
|
+
|
|
224749
|
+
var onExit = __webpack_require__(29468)
|
|
224750
|
+
onExit(function () {
|
|
224751
|
+
debug('exit listener')
|
|
224752
|
+
// cleanup
|
|
224753
|
+
Object.keys(locks).forEach(exports.unlockSync)
|
|
224754
|
+
})
|
|
224755
|
+
|
|
224756
|
+
// XXX https://github.com/joyent/node/issues/3555
|
|
224757
|
+
// Remove when node 0.8 is deprecated.
|
|
224758
|
+
if (/^v0\.[0-8]\./.test(process.version)) {
|
|
224759
|
+
debug('uncaughtException, version = %s', process.version)
|
|
224760
|
+
process.on('uncaughtException', function H (er) {
|
|
224761
|
+
debug('uncaughtException')
|
|
224762
|
+
var l = process.listeners('uncaughtException').filter(function (h) {
|
|
224763
|
+
return h !== H
|
|
224764
|
+
})
|
|
224765
|
+
if (!l.length) {
|
|
224766
|
+
// cleanup
|
|
224767
|
+
try { Object.keys(locks).forEach(exports.unlockSync) } catch (e) {}
|
|
224768
|
+
process.removeListener('uncaughtException', H)
|
|
224769
|
+
throw er
|
|
224770
|
+
}
|
|
224771
|
+
})
|
|
224772
|
+
}
|
|
224773
|
+
|
|
224774
|
+
exports.unlock = function (path, cb) {
|
|
224775
|
+
debug('unlock', path)
|
|
224776
|
+
// best-effort. unlocking an already-unlocked lock is a noop
|
|
224777
|
+
delete locks[path]
|
|
224778
|
+
fs.unlink(path, function (unlinkEr) { cb && cb() })
|
|
224779
|
+
}
|
|
224780
|
+
|
|
224781
|
+
exports.unlockSync = function (path) {
|
|
224782
|
+
debug('unlockSync', path)
|
|
224783
|
+
// best-effort. unlocking an already-unlocked lock is a noop
|
|
224784
|
+
try { fs.unlinkSync(path) } catch (er) {}
|
|
224785
|
+
delete locks[path]
|
|
224786
|
+
}
|
|
224787
|
+
|
|
224788
|
+
|
|
224789
|
+
// if the file can be opened in readonly mode, then it's there.
|
|
224790
|
+
// if the error is something other than ENOENT, then it's not.
|
|
224791
|
+
exports.check = function (path, opts, cb) {
|
|
224792
|
+
if (typeof opts === 'function') cb = opts, opts = {}
|
|
224793
|
+
debug('check', path, opts)
|
|
224794
|
+
fs.open(path, 'r', function (er, fd) {
|
|
224795
|
+
if (er) {
|
|
224796
|
+
if (er.code !== 'ENOENT') return cb(er)
|
|
224797
|
+
return cb(null, false)
|
|
224798
|
+
}
|
|
224799
|
+
|
|
224800
|
+
if (!opts.stale) {
|
|
224801
|
+
return fs.close(fd, function (er) {
|
|
224802
|
+
return cb(er, true)
|
|
224803
|
+
})
|
|
224804
|
+
}
|
|
224805
|
+
|
|
224806
|
+
fs.fstat(fd, function (er, st) {
|
|
224807
|
+
if (er) return fs.close(fd, function (er2) {
|
|
224808
|
+
return cb(er)
|
|
224809
|
+
})
|
|
224810
|
+
|
|
224811
|
+
fs.close(fd, function (er) {
|
|
224812
|
+
var age = Date.now() - st[exports.filetime].getTime()
|
|
224813
|
+
return cb(er, age <= opts.stale)
|
|
224814
|
+
})
|
|
224815
|
+
})
|
|
224816
|
+
})
|
|
224817
|
+
}
|
|
224818
|
+
|
|
224819
|
+
exports.checkSync = function (path, opts) {
|
|
224820
|
+
opts = opts || {}
|
|
224821
|
+
debug('checkSync', path, opts)
|
|
224822
|
+
if (opts.wait) {
|
|
224823
|
+
throw new Error('opts.wait not supported sync for obvious reasons')
|
|
224824
|
+
}
|
|
224825
|
+
|
|
224826
|
+
try {
|
|
224827
|
+
var fd = fs.openSync(path, 'r')
|
|
224828
|
+
} catch (er) {
|
|
224829
|
+
if (er.code !== 'ENOENT') throw er
|
|
224830
|
+
return false
|
|
224831
|
+
}
|
|
224832
|
+
|
|
224833
|
+
if (!opts.stale) {
|
|
224834
|
+
try { fs.closeSync(fd) } catch (er) {}
|
|
224835
|
+
return true
|
|
224836
|
+
}
|
|
224837
|
+
|
|
224838
|
+
// file exists. however, might be stale
|
|
224839
|
+
if (opts.stale) {
|
|
224840
|
+
try {
|
|
224841
|
+
var st = fs.fstatSync(fd)
|
|
224842
|
+
} finally {
|
|
224843
|
+
fs.closeSync(fd)
|
|
224844
|
+
}
|
|
224845
|
+
var age = Date.now() - st[exports.filetime].getTime()
|
|
224846
|
+
return (age <= opts.stale)
|
|
224847
|
+
}
|
|
224848
|
+
}
|
|
224849
|
+
|
|
224850
|
+
|
|
224851
|
+
|
|
224852
|
+
var req = 1
|
|
224853
|
+
exports.lock = function (path, opts, cb) {
|
|
224854
|
+
if (typeof opts === 'function') cb = opts, opts = {}
|
|
224855
|
+
opts.req = opts.req || req++
|
|
224856
|
+
debug('lock', path, opts)
|
|
224857
|
+
opts.start = opts.start || Date.now()
|
|
224858
|
+
|
|
224859
|
+
if (typeof opts.retries === 'number' && opts.retries > 0) {
|
|
224860
|
+
debug('has retries', opts.retries)
|
|
224861
|
+
var retries = opts.retries
|
|
224862
|
+
opts.retries = 0
|
|
224863
|
+
cb = (function (orig) { return function cb (er, fd) {
|
|
224864
|
+
debug('retry-mutated callback')
|
|
224865
|
+
retries -= 1
|
|
224866
|
+
if (!er || retries < 0) return orig(er, fd)
|
|
224867
|
+
|
|
224868
|
+
debug('lock retry', path, opts)
|
|
224869
|
+
|
|
224870
|
+
if (opts.retryWait) setTimeout(retry, opts.retryWait)
|
|
224871
|
+
else retry()
|
|
224872
|
+
|
|
224873
|
+
function retry () {
|
|
224874
|
+
opts.start = Date.now()
|
|
224875
|
+
debug('retrying', opts.start)
|
|
224876
|
+
exports.lock(path, opts, cb)
|
|
224877
|
+
}
|
|
224878
|
+
}})(cb)
|
|
224879
|
+
}
|
|
224880
|
+
|
|
224881
|
+
// try to engage the lock.
|
|
224882
|
+
// if this succeeds, then we're in business.
|
|
224883
|
+
fs.open(path, wx, function (er, fd) {
|
|
224884
|
+
if (!er) {
|
|
224885
|
+
debug('locked', path, fd)
|
|
224886
|
+
locks[path] = fd
|
|
224887
|
+
return fs.close(fd, function () {
|
|
224888
|
+
return cb()
|
|
224889
|
+
})
|
|
224890
|
+
}
|
|
224891
|
+
|
|
224892
|
+
debug('failed to acquire lock', er)
|
|
224893
|
+
|
|
224894
|
+
// something other than "currently locked"
|
|
224895
|
+
// maybe eperm or something.
|
|
224896
|
+
if (er.code !== 'EEXIST') {
|
|
224897
|
+
debug('not EEXIST error', er)
|
|
224898
|
+
return cb(er)
|
|
224899
|
+
}
|
|
224900
|
+
|
|
224901
|
+
// someone's got this one. see if it's valid.
|
|
224902
|
+
if (!opts.stale) return notStale(er, path, opts, cb)
|
|
224903
|
+
|
|
224904
|
+
return maybeStale(er, path, opts, false, cb)
|
|
224905
|
+
})
|
|
224906
|
+
debug('lock return')
|
|
224907
|
+
}
|
|
224908
|
+
|
|
224909
|
+
|
|
224910
|
+
// Staleness checking algorithm
|
|
224911
|
+
// 1. acquire $lock, fail
|
|
224912
|
+
// 2. stat $lock, find that it is stale
|
|
224913
|
+
// 3. acquire $lock.STALE
|
|
224914
|
+
// 4. stat $lock, assert that it is still stale
|
|
224915
|
+
// 5. unlink $lock
|
|
224916
|
+
// 6. link $lock.STALE $lock
|
|
224917
|
+
// 7. unlink $lock.STALE
|
|
224918
|
+
// On any failure, clean up whatever we've done, and raise the error.
|
|
224919
|
+
function maybeStale (originalEr, path, opts, hasStaleLock, cb) {
|
|
224920
|
+
fs.stat(path, function (statEr, st) {
|
|
224921
|
+
if (statEr) {
|
|
224922
|
+
if (statEr.code === 'ENOENT') {
|
|
224923
|
+
// expired already!
|
|
224924
|
+
opts.stale = false
|
|
224925
|
+
debug('lock stale enoent retry', path, opts)
|
|
224926
|
+
exports.lock(path, opts, cb)
|
|
224927
|
+
return
|
|
224928
|
+
}
|
|
224929
|
+
return cb(statEr)
|
|
224930
|
+
}
|
|
224931
|
+
|
|
224932
|
+
var age = Date.now() - st[exports.filetime].getTime()
|
|
224933
|
+
if (age <= opts.stale) return notStale(originalEr, path, opts, cb)
|
|
224934
|
+
|
|
224935
|
+
debug('lock stale', path, opts)
|
|
224936
|
+
if (hasStaleLock) {
|
|
224937
|
+
exports.unlock(path, function (er) {
|
|
224938
|
+
if (er) return cb(er)
|
|
224939
|
+
debug('lock stale retry', path, opts)
|
|
224940
|
+
fs.link(path + '.STALE', path, function (er) {
|
|
224941
|
+
fs.unlink(path + '.STALE', function () {
|
|
224942
|
+
// best effort. if the unlink fails, oh well.
|
|
224943
|
+
cb(er)
|
|
224944
|
+
})
|
|
224945
|
+
})
|
|
224946
|
+
})
|
|
224947
|
+
} else {
|
|
224948
|
+
debug('acquire .STALE file lock', opts)
|
|
224949
|
+
exports.lock(path + '.STALE', opts, function (er) {
|
|
224950
|
+
if (er) return cb(er)
|
|
224951
|
+
maybeStale(originalEr, path, opts, true, cb)
|
|
224952
|
+
})
|
|
224953
|
+
}
|
|
224954
|
+
})
|
|
224955
|
+
}
|
|
224956
|
+
|
|
224957
|
+
function notStale (er, path, opts, cb) {
|
|
224958
|
+
debug('notStale', path, opts)
|
|
224959
|
+
|
|
224960
|
+
// if we can't wait, then just call it a failure
|
|
224961
|
+
if (typeof opts.wait !== 'number' || opts.wait <= 0) {
|
|
224962
|
+
debug('notStale, wait is not a number')
|
|
224963
|
+
return cb(er)
|
|
224964
|
+
}
|
|
224965
|
+
|
|
224966
|
+
// poll for some ms for the lock to clear
|
|
224967
|
+
var now = Date.now()
|
|
224968
|
+
var start = opts.start || now
|
|
224969
|
+
var end = start + opts.wait
|
|
224970
|
+
|
|
224971
|
+
if (end <= now)
|
|
224972
|
+
return cb(er)
|
|
224973
|
+
|
|
224974
|
+
debug('now=%d, wait until %d (delta=%d)', start, end, end-start)
|
|
224975
|
+
var wait = Math.min(end - start, opts.pollPeriod || 100)
|
|
224976
|
+
var timer = setTimeout(poll, wait)
|
|
224977
|
+
|
|
224978
|
+
function poll () {
|
|
224979
|
+
debug('notStale, polling', path, opts)
|
|
224980
|
+
exports.lock(path, opts, cb)
|
|
224981
|
+
}
|
|
224982
|
+
}
|
|
224983
|
+
|
|
224984
|
+
exports.lockSync = function (path, opts) {
|
|
224985
|
+
opts = opts || {}
|
|
224986
|
+
opts.req = opts.req || req++
|
|
224987
|
+
debug('lockSync', path, opts)
|
|
224988
|
+
if (opts.wait || opts.retryWait) {
|
|
224989
|
+
throw new Error('opts.wait not supported sync for obvious reasons')
|
|
224990
|
+
}
|
|
224991
|
+
|
|
224992
|
+
try {
|
|
224993
|
+
var fd = fs.openSync(path, wx)
|
|
224994
|
+
locks[path] = fd
|
|
224995
|
+
try { fs.closeSync(fd) } catch (er) {}
|
|
224996
|
+
debug('locked sync!', path, fd)
|
|
224997
|
+
return
|
|
224998
|
+
} catch (er) {
|
|
224999
|
+
if (er.code !== 'EEXIST') return retryThrow(path, opts, er)
|
|
225000
|
+
|
|
225001
|
+
if (opts.stale) {
|
|
225002
|
+
var st = fs.statSync(path)
|
|
225003
|
+
var ct = st[exports.filetime].getTime()
|
|
225004
|
+
if (!(ct % 1000) && (opts.stale % 1000)) {
|
|
225005
|
+
// probably don't have subsecond resolution.
|
|
225006
|
+
// round up the staleness indicator.
|
|
225007
|
+
// Yes, this will be wrong 1/1000 times on platforms
|
|
225008
|
+
// with subsecond stat precision, but that's acceptable
|
|
225009
|
+
// in exchange for not mistakenly removing locks on
|
|
225010
|
+
// most other systems.
|
|
225011
|
+
opts.stale = 1000 * Math.ceil(opts.stale / 1000)
|
|
225012
|
+
}
|
|
225013
|
+
var age = Date.now() - ct
|
|
225014
|
+
if (age > opts.stale) {
|
|
225015
|
+
debug('lockSync stale', path, opts, age)
|
|
225016
|
+
exports.unlockSync(path)
|
|
225017
|
+
return exports.lockSync(path, opts)
|
|
225018
|
+
}
|
|
225019
|
+
}
|
|
225020
|
+
|
|
225021
|
+
// failed to lock!
|
|
225022
|
+
debug('failed to lock', path, opts, er)
|
|
225023
|
+
return retryThrow(path, opts, er)
|
|
225024
|
+
}
|
|
225025
|
+
}
|
|
225026
|
+
|
|
225027
|
+
function retryThrow (path, opts, er) {
|
|
225028
|
+
if (typeof opts.retries === 'number' && opts.retries > 0) {
|
|
225029
|
+
var newRT = opts.retries - 1
|
|
225030
|
+
debug('retryThrow', path, opts, newRT)
|
|
225031
|
+
opts.retries = newRT
|
|
225032
|
+
return exports.lockSync(path, opts)
|
|
225033
|
+
}
|
|
225034
|
+
throw er
|
|
225035
|
+
}
|
|
225036
|
+
|
|
225037
|
+
|
|
225038
|
+
|
|
224634
225039
|
/***/ }),
|
|
224635
225040
|
|
|
224636
225041
|
/***/ 80280:
|