{"id":199,"date":"2026-06-14T11:48:11","date_gmt":"2026-06-14T03:48:11","guid":{"rendered":"http:\/\/www.longxiapro.com\/?p=199"},"modified":"2026-06-14T11:48:11","modified_gmt":"2026-06-14T03:48:11","slug":"banbenzhiyu-auto-updaterzidonggengxintixiquanyuhuanxin","status":"publish","type":"post","link":"https:\/\/www.longxiapro.com\/index.php\/199\/","title":{"rendered":"\u7248\u672c\u667a\u9a6d Auto Updater\u81ea\u52a8\u66f4\u65b0\u4f53\u7cfb\u5168\u57df\u7115\u65b0"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">\u4e00\u3001\u80fd\u529b\u603b\u89c8<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u6587\u9488\u5bf9OpenClaw\u539f\u751fAuto-Updater\u81ea\u52a8\u66f4\u65b0\u7ba1\u7406\u5de5\u5177\u8fdb\u884c\u5168\u57df\u4f18\u5316\u4e0e\u4f53\u7cfb\u5347\u7ea7\u3002\u539f\u751f\u5de5\u5177\u4ec5\u5177\u5907\u57fa\u7840\u7684\u7248\u672c\u68c0\u6d4b\u4e0e\u6587\u4ef6\u62c9\u53d6\u80fd\u529b\uff0c\u8fd0\u884c\u903b\u8f91\u7b80\u5355\u3001\u7ba1\u63a7\u7ef4\u5ea6\u5355\u4e00\uff0c\u4ec5\u80fd\u6ee1\u8db3\u57fa\u7840\u7a0b\u5e8f\u5347\u7ea7\u9700\u6c42\uff0c\u65e0\u6cd5\u5e94\u5bf9\u591a\u6e90\u66f4\u65b0\u6e90\u3001\u5206\u9636\u66f4\u65b0\u3001\u5f02\u5e38\u56de\u6eda\u3001\u7070\u5ea6\u53d1\u5e03\u3001\u8fd0\u7ef4\u76d1\u63a7\u7b49\u590d\u6742\u8fd0\u7ef4\u573a\u666f\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u6b21\u4f18\u5316\u4ee5<strong>\u7248\u672c\u5168\u5468\u671f\u7ba1\u63a7\u3001\u591a\u6e90\u8c03\u5ea6\u9002\u914d\u3001\u5bb9\u9519\u9632\u62a4\u52a0\u56fa\u3001\u8fd0\u7ef4\u53ef\u89c6\u5316\u3001\u81ea\u52a8\u5316\u6d41\u7a0b\u8054\u52a8<\/strong>\u4e3a\u6838\u5fc3\u65b9\u5411\uff0c\u5bf9\u5e95\u5c42\u8c03\u5ea6\u903b\u8f91\u3001\u66f4\u65b0\u7b56\u7565\u3001\u6570\u636e\u5904\u7406\u3001\u5b89\u5168\u673a\u5236\u3001\u4ea4\u4e92\u4f53\u9a8c\u8fdb\u884c\u5168\u9762\u5347\u7ea7\u3002\u62c6\u89e3\u591a\u9879\u53ef\u843d\u5730\u7684\u7cbe\u7ec6\u5316\u4f18\u5316\u6a21\u5757\uff0c\u914d\u5957\u6a21\u5757\u5316\u4ee3\u7801\u3001\u5b8c\u6574\u90e8\u7f72\u6e90\u7801\u3001\u53c2\u6570\u8c03\u914d\u65b9\u6848\u4e0e\u540e\u7eed\u62d3\u5c55\u65b9\u5411\u3002\u5404\u6a21\u5757\u5b8c\u5168\u89e3\u8026\uff0c\u652f\u6301\u5c40\u90e8\u5fae\u8c03\u4e0e\u6574\u4f53\u5347\u7ea7\uff0c\u9002\u914d\u5355\u673a\u90e8\u7f72\u3001\u96c6\u7fa4\u8282\u70b9\u3001\u6279\u91cf\u7ec8\u7aef\u7b49\u4e0d\u540c\u73af\u5883\u7684\u7248\u672c\u8fd0\u7ef4\u9700\u6c42\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e8c\u3001\u539f\u751f\u4f53\u7cfb\u73b0\u5b58\u77ed\u677f\u68b3\u7406<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u7ed3\u5408\u7a0b\u5e8f\u66f4\u65b0\u8fd0\u7ef4\u573a\u666f\u3001\u7f51\u7edc\u73af\u5883\u5dee\u5f02\u3001\u7248\u672c\u8fed\u4ee3\u89c4\u5219\u4e0eOpenClaw\u8fd0\u884c\u673a\u5236\uff0c\u68b3\u7406\u539f\u751f\u5de5\u5177\u5b58\u5728\u7684\u529f\u80fd\u3001\u6027\u80fd\u3001\u5b89\u5168\u4e0e\u4f53\u9a8c\u95ee\u9898\uff0c\u4f5c\u4e3a\u4f18\u5316\u7684\u6838\u5fc3\u4f9d\u636e\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u66f4\u65b0\u6e90\u914d\u7f6e\u56fa\u5316<\/strong>\uff1a\u66f4\u65b0\u5730\u5740\u3001\u6821\u9a8c\u89c4\u5219\u3001\u8bf7\u6c42\u53c2\u6570\u786c\u7f16\u7801\uff0c\u4e0d\u652f\u6301\u591a\u955c\u50cf\u6e90\u5207\u6362\u3001\u79c1\u6709\u6e90\u63a5\u5165\uff0c\u7f51\u7edc\u5f02\u5e38\u65f6\u65e0\u6cd5\u81ea\u52a8\u5207\u6362\u8282\u70b9\uff0c\u9002\u914d\u6027\u5dee\u3002<\/li>\n\n\n\n<li><strong>\u66f4\u65b0\u7b56\u7565\u5355\u4e00<\/strong>\uff1a\u4ec5\u652f\u6301\u5168\u91cf\u8986\u76d6\u66f4\u65b0\uff0c\u7f3a\u5931\u589e\u91cf\u66f4\u65b0\u3001\u7070\u5ea6\u63a8\u9001\u3001\u5b9a\u65f6\u66f4\u65b0\u3001\u9759\u9ed8\u66f4\u65b0\u7b49\u4e3b\u6d41\u7b56\u7565\uff0c\u8d44\u6e90\u6d88\u8017\u5927\u4e14\u7075\u6d3b\u6027\u4e0d\u8db3\u3002<\/li>\n\n\n\n<li><strong>\u5b89\u5168\u6821\u9a8c\u4f53\u7cfb\u7f3a\u5931<\/strong>\uff1a\u65e0\u6587\u4ef6\u54c8\u5e0c\u6821\u9a8c\u3001\u7b7e\u540d\u9a8c\u8bc1\uff0c\u66f4\u65b0\u5305\u5b58\u5728\u88ab\u7be1\u6539\u3001\u635f\u574f\u98ce\u9669\uff0c\u65e0\u6cd5\u4fdd\u969c\u7a0b\u5e8f\u8fd0\u884c\u5b89\u5168\u3002<\/li>\n\n\n\n<li><strong>\u65e0\u7248\u672c\u94fe\u8def\u7ba1\u63a7<\/strong>\uff1a\u4e0d\u8bb0\u5f55\u7248\u672c\u5386\u53f2\u3001\u66f4\u65b0\u8bb0\u5f55\uff0c\u4e0d\u652f\u6301\u7248\u672c\u56de\u6eda\u3001\u65ad\u70b9\u7eed\u4f20\uff0c\u66f4\u65b0\u5931\u8d25\u540e\u53ea\u80fd\u624b\u52a8\u4fee\u590d\uff0c\u6545\u969c\u5904\u7f6e\u6548\u7387\u4f4e\u3002<\/li>\n\n\n\n<li><strong>\u7f51\u7edc\u5bb9\u9519\u80fd\u529b\u8584\u5f31<\/strong>\uff1a\u7f3a\u5c11\u8d85\u65f6\u91cd\u8bd5\u3001\u7f51\u7edc\u9002\u914d\u3001\u9650\u6d41\u673a\u5236\uff0c\u5f31\u7f51\u73af\u5883\u4e0b\u6781\u6613\u4e0b\u8f7d\u4e2d\u65ad\u3001\u6587\u4ef6\u6b8b\u7f3a\u3002<\/li>\n\n\n\n<li><strong>\u8fd0\u884c\u6743\u9650\u4e0e\u73af\u5883\u68c0\u6d4b\u7a7a\u767d<\/strong>\uff1a\u672a\u524d\u7f6e\u6821\u9a8c\u8bfb\u5199\u6743\u9650\u3001\u78c1\u76d8\u7a7a\u95f4\u3001\u8fd0\u884c\u73af\u5883\uff0c\u6743\u9650\u4e0d\u8db3\u3001\u5b58\u50a8\u7a7a\u95f4\u4e0d\u591f\u65f6\u76f4\u63a5\u62a5\u9519\u4e2d\u65ad\u6d41\u7a0b\u3002<\/li>\n\n\n\n<li><strong>\u72b6\u6001\u53cd\u9988\u7b80\u964b<\/strong>\uff1a\u65e0\u8fdb\u5ea6\u5c55\u793a\u3001\u9636\u6bb5\u63d0\u793a\uff0c\u7528\u6237\u65e0\u6cd5\u611f\u77e5\u66f4\u65b0\u8fdb\u5ea6\u4e0e\u5f53\u524d\u73af\u8282\uff0c\u8fd0\u7ef4\u900f\u660e\u5ea6\u4f4e\u3002<\/li>\n\n\n\n<li><strong>\u7f3a\u5c11\u8fd0\u7ef4\u65e5\u5fd7<\/strong>\uff1a\u66f4\u65b0\u884c\u4e3a\u3001\u7248\u672c\u53d8\u66f4\u3001\u6545\u969c\u4fe1\u606f\u65e0\u7559\u5b58\uff0c\u95ee\u9898\u6392\u67e5\u4e0e\u884c\u4e3a\u8ffd\u6eaf\u7f3a\u4e4f\u4f9d\u636e\u3002<\/li>\n\n\n\n<li><strong>\u751f\u6001\u8054\u52a8\u6027\u5f31<\/strong>\uff1a\u72ec\u7acb\u8fd0\u884c\u65e0\u6cd5\u8054\u52a8\u91cd\u542f\u3001\u5907\u4efd\u3001\u544a\u8b66\u7b49\u529f\u80fd\uff0c\u96be\u4ee5\u878d\u5165\u81ea\u52a8\u5316\u8fd0\u7ef4\u5de5\u4f5c\u6d41\u3002<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e09\u3001\u5168\u57df\u7115\u65b0\u6838\u5fc3\u65b9\u5411<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u6b21\u8fed\u4ee3\u8df3\u51fa\u57fa\u7840\u66f4\u65b0\u903b\u8f91\uff0c\u56f4\u7ed5\u7248\u672c\u8fd0\u7ef4\u5168\u94fe\u8def\u6253\u9020\u667a\u80fd\u5316\u7ba1\u63a7\u4f53\u7cfb\uff0c\u660e\u786e\u516b\u5927\u5347\u7ea7\u76ee\u6807\uff1a<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>\u914d\u7f6e\u89e3\u8026\u91cd\u6784<\/strong>\uff1a\u62bd\u79bb\u6240\u6709\u66f4\u65b0\u6e90\u3001\u7b56\u7565\u3001\u5b89\u5168\u3001\u8fd0\u7ef4\u53c2\u6570\uff0c\u5b9e\u73b0\u53ef\u89c6\u5316\u8c03\u914d\uff0c\u65e0\u9700\u4fee\u6539\u5e95\u5c42\u4ee3\u7801\u5373\u53ef\u9002\u914d\u4e0d\u540c\u73af\u5883\u3002<\/li>\n\n\n\n<li><strong>\u591a\u6e90\u8c03\u5ea6\u80fd\u529b\u642d\u5efa<\/strong>\uff1a\u652f\u6301\u4e3b\u5907\u955c\u50cf\u6e90\u3001\u79c1\u6709\u66f4\u65b0\u6e90\u81ea\u52a8\u5207\u6362\uff0c\u7f51\u7edc\u4e0d\u4f73\u65f6\u667a\u80fd\u8282\u70b9\u5207\u6362\uff0c\u63d0\u5347\u66f4\u65b0\u7a33\u5b9a\u6027\u3002<\/li>\n\n\n\n<li><strong>\u591a\u5143\u5316\u66f4\u65b0\u7b56\u7565<\/strong>\uff1a\u62d3\u5c55\u5168\u91cf\u66f4\u65b0\u3001\u589e\u91cf\u66f4\u65b0\u3001\u9759\u9ed8\u66f4\u65b0\u3001\u5b9a\u65f6\u66f4\u65b0\u3001\u7070\u5ea6\u5206\u53d1\u591a\u79cd\u6a21\u5f0f\uff0c\u9002\u914d\u4e0d\u540c\u8fd0\u7ef4\u573a\u666f\u3002<\/li>\n\n\n\n<li><strong>\u5b89\u5168\u9632\u62a4\u5347\u7ea7<\/strong>\uff1a\u65b0\u589e\u6587\u4ef6\u54c8\u5e0c\u6821\u9a8c\u3001\u6570\u5b57\u7b7e\u540d\u9a8c\u8bc1\uff0c\u62e6\u622a\u635f\u574f\u3001\u7be1\u6539\u7684\u66f4\u65b0\u5305\uff0c\u7b51\u7262\u5b89\u5168\u9632\u7ebf\u3002<\/li>\n\n\n\n<li><strong>\u7248\u672c\u5168\u751f\u547d\u5468\u671f\u7ba1\u7406<\/strong>\uff1a\u5b9e\u73b0\u7248\u672c\u8bb0\u5f55\u3001\u65ad\u70b9\u7eed\u4f20\u3001\u4e00\u952e\u56de\u6eda\uff0c\u6784\u5efa\u5b8c\u6574\u7684\u7248\u672c\u8fed\u4ee3\u4e0e\u6545\u969c\u4fee\u590d\u94fe\u8def\u3002<\/li>\n\n\n\n<li><strong>\u73af\u5883\u524d\u7f6e\u6821\u9a8c<\/strong>\uff1a\u81ea\u52a8\u68c0\u6d4b\u78c1\u76d8\u7a7a\u95f4\u3001\u8bfb\u5199\u6743\u9650\u3001\u8fd0\u884c\u73af\u5883\uff0c\u63d0\u524d\u62e6\u622a\u5f02\u5e38\u573a\u666f\uff0c\u89c4\u907f\u66f4\u65b0\u5931\u8d25\u3002<\/li>\n\n\n\n<li><strong>\u8fdb\u5ea6\u53ef\u89c6\u5316\u4e0e\u65e5\u5fd7\u6eaf\u6e90<\/strong>\uff1a\u5b9e\u65f6\u5c55\u793a\u66f4\u65b0\u8fdb\u5ea6\uff0c\u5168\u6d41\u7a0b\u8bb0\u5f55\u8fd0\u7ef4\u65e5\u5fd7\uff0c\u5b9e\u73b0\u884c\u4e3a\u53ef\u67e5\u3001\u6545\u969c\u53ef\u8ffd\u6eaf\u3002<\/li>\n\n\n\n<li><strong>\u81ea\u52a8\u5316\u751f\u6001\u8054\u52a8<\/strong>\uff1a\u9884\u7559\u63a5\u53e3\u5bf9\u63a5\u6570\u636e\u5907\u4efd\u3001\u7a0b\u5e8f\u91cd\u542f\u3001\u5f02\u5e38\u544a\u8b66\u529f\u80fd\uff0c\u6253\u901a\u4e00\u7ad9\u5f0f\u81ea\u52a8\u5316\u8fd0\u7ef4\u6d41\u7a0b\u3002<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\u56db\u3001\u8fd0\u884c\u73af\u5883\u4e0e\u5206\u5c42\u67b6\u6784<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 \u57fa\u7840\u8fd0\u884c\u73af\u5883<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8fd0\u884c\u73af\u5883\uff1aNode.js 16+ \/ 20+\uff0c\u5168\u5e73\u53f0\u517c\u5bb9\uff0c\u9002\u914d\u6240\u6709OpenClaw\u7a33\u5b9a\u5185\u6838<\/li>\n\n\n\n<li>\u5f00\u53d1\u89c4\u8303\uff1a\u9075\u5faaOpenClaw\u6280\u80fd\u5f00\u53d1\u6807\u51c6\uff0cTypeScript\u7f16\u5199\uff0c\u517c\u5bb9\u539f\u751f\u67b6\u6784\uff0c\u5347\u7ea7\u65e0\u4fb5\u5165<\/li>\n\n\n\n<li>\u6838\u5fc3\u4f9d\u8d56\uff1aopenclaw\u5b98\u65b9SDK\u3001axios\u7f51\u7edc\u8bf7\u6c42\u3001fs-extra\u6587\u4ef6\u64cd\u4f5c\u3001path\u8def\u5f84\u5904\u7406\u3001crypto\u52a0\u5bc6\u6821\u9a8c<\/li>\n\n\n\n<li>\u9002\u914d\u8303\u56f4\uff1a\u652f\u6301HTTP\/HTTPS\u66f4\u65b0\u6e90\uff0c\u517c\u5bb9\u4e3b\u6d41\u538b\u7f29\u5305\u3001\u4e8c\u8fdb\u5236\u6587\u4ef6\u3001\u811a\u672c\u6587\u4ef6\u7b49\u66f4\u65b0\u683c\u5f0f<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.2 \u516d\u5c42\u5206\u5c42\u667a\u63a7\u67b6\u6784<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u91c7\u7528\u6a21\u5757\u5316\u5206\u5c42\u8bbe\u8ba1\uff0c\u5404\u5c42\u7ea7\u804c\u8d23\u72ec\u7acb\uff0c\u53ef\u5355\u72ec\u5bf9\u67d0\u4e00\u6a21\u5757\u8fdb\u884c\u4f18\u5316\u8fed\u4ee3\uff0c\u4e0d\u5f71\u54cd\u6574\u4f53\u8fd0\u884c\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u5168\u5c40\u914d\u7f6e\u8c03\u5ea6\u5c42<\/strong>\uff1a\u7edf\u4e00\u7ba1\u7406\u66f4\u65b0\u6e90\u3001\u66f4\u65b0\u7b56\u7565\u3001\u5b89\u5168\u89c4\u5219\u3001\u8d85\u65f6\u65f6\u95f4\u3001\u65e5\u5fd7\u8def\u5f84\u7b49\u5168\u90e8\u53ef\u914d\u7f6e\u53c2\u6570\u3002<\/li>\n\n\n\n<li><strong>\u73af\u5883\u9884\u6821\u9a8c\u5c42<\/strong>\uff1a\u68c0\u6d4b\u78c1\u76d8\u7a7a\u95f4\u3001\u64cd\u4f5c\u6743\u9650\u3001\u7f51\u7edc\u72b6\u6001\u3001\u8fd0\u884c\u73af\u5883\uff0c\u63d0\u524d\u62e6\u622a\u6f5c\u5728\u6545\u969c\u3002<\/li>\n\n\n\n<li><strong>\u591a\u6e90\u7f51\u7edc\u8c03\u5ea6\u5c42<\/strong>\uff1a\u7ba1\u7406\u591a\u66f4\u65b0\u6e90\u5207\u6362\u3001\u6587\u4ef6\u4e0b\u8f7d\u3001\u65ad\u70b9\u7eed\u4f20\u3001\u8d85\u65f6\u91cd\u8bd5\u3001\u8bf7\u6c42\u9650\u6d41\u3002<\/li>\n\n\n\n<li><strong>\u5b89\u5168\u6821\u9a8c\u5c42<\/strong>\uff1a\u5b8c\u6210\u6587\u4ef6\u54c8\u5e0c\u6821\u9a8c\u3001\u7b7e\u540d\u9a8c\u8bc1\uff0c\u8fc7\u6ee4\u5f02\u5e38\u66f4\u65b0\u5305\uff0c\u4fdd\u969c\u6587\u4ef6\u5b8c\u6574\u6027\u3002<\/li>\n\n\n\n<li><strong>\u7248\u672c\u8fd0\u7ef4\u4e1a\u52a1\u5c42<\/strong>\uff1a\u5b9e\u73b0\u7248\u672c\u68c0\u6d4b\u3001\u5168\u91cf\/\u589e\u91cf\u66f4\u65b0\u3001\u7248\u672c\u56de\u6eda\u3001\u5386\u53f2\u8bb0\u5f55\u7ba1\u7406\u7b49\u6838\u5fc3\u529f\u80fd\u3002<\/li>\n\n\n\n<li><strong>\u4ea4\u4e92\u4e0e\u8054\u52a8\u5c42<\/strong>\uff1a\u8f93\u51fa\u8fdb\u5ea6\u4fe1\u606f\u3001\u89e3\u6790\u64cd\u4f5c\u6307\u4ee4\u3001\u8bb0\u5f55\u8fd0\u7ef4\u65e5\u5fd7\u3001\u5bf9\u63a5\u5176\u4ed6\u81ea\u52a8\u5316\u6280\u80fd\u3002<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e94\u3001\u7cbe\u7ec6\u5316\u4f18\u5316\u6539\u9020\u70b9\uff0812\u9879\u6838\u5fc3\u5347\u7ea7+\u72ec\u7acb\u4ee3\u7801\uff09<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u4ee5\u4e0b\u4e3a\u9010\u9879\u843d\u5730\u7684\u4f18\u5316\u6a21\u5757\uff0c\u9644\u5e26\u6539\u9020\u601d\u8def\u4e0e\u53ef\u76f4\u63a5\u590d\u7528\u4ee3\u7801\uff0c\u652f\u6301\u9010\u4e00\u5bf9\u539f\u751f\u5de5\u5177\u8fdb\u884c\u8fed\u4ee3\u5347\u7ea7\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b91\uff1a\u5168\u5c40\u914d\u7f6e\u89e3\u8026\uff0c\u6e05\u9664\u786c\u7f16\u7801<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u5c06\u66f4\u65b0\u5730\u5740\u3001\u5b89\u5168\u89c4\u5219\u3001\u8fd0\u884c\u53c2\u6570\u7edf\u4e00\u62bd\u79bb\u4e3a\u72ec\u7acb\u914d\u7f6e\uff0c\u96c6\u4e2d\u7ba1\u63a7\uff0c\u9002\u914d\u591a\u73af\u5883\u90e8\u7f72\u9700\u6c42\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ \u81ea\u52a8\u66f4\u65b0\u5168\u57df\u914d\u7f6e\u4e2d\u5fc3\nconst UPDATER_CONFIG = {\n  \/\/ \u591a\u66f4\u65b0\u6e90\u914d\u7f6e\uff08\u4e3b\u6e90+\u5907\u7528\u6e90\uff09\n  updateSources: &#91;\n    \"https:\/\/main.update.com\/release\",\n    \"https:\/\/backup.update.com\/release\"\n  ],\n  \/\/ \u7248\u672c\u6587\u4ef6\u540d\u79f0\n  versionFile: \"version.json\",\n  \/\/ \u7f51\u7edc\u8bf7\u6c42\u914d\u7f6e\n  timeout: 20000,\n  retryTimes: 3,\n  \/\/ \u5b89\u5168\u6821\u9a8c\u914d\u7f6e\n  enableHashCheck: true,\n  hashAlgorithm: \"md5\",\n  \/\/ \u8fd0\u884c\u73af\u5883\u914d\u7f6e\n  minDiskSpace: 1024 * 1024 * 50,\n  \/\/ \u76ee\u5f55\u914d\u7f6e\n  workDir: \".\/runtime\",\n  backupDir: \".\/backup\",\n  logDir: \".\/claw-cache\/updater-log\",\n  historyDir: \".\/version-history\"\n};\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b92\uff1a\u73af\u5883\u4e0e\u6743\u9650\u524d\u7f6e\u68c0\u6d4b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u65b0\u589e\u78c1\u76d8\u7a7a\u95f4\u3001\u8bfb\u5199\u6743\u9650\u6821\u9a8c\uff0c\u63d0\u524d\u5224\u65ad\u8fd0\u884c\u6761\u4ef6\uff0c\u907f\u514d\u66f4\u65b0\u4e2d\u9014\u5931\u8d25\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nimport fs from \"fs-extra\";\nimport path from \"path\";\n\n\/\/ \u73af\u5883\u7efc\u5408\u68c0\u6d4b\nasync function checkRuntimeEnv(): Promise&lt;boolean&gt; {\n  try {\n    fs.ensureDirSync(UPDATER_CONFIG.workDir);\n    fs.ensureDirSync(UPDATER_CONFIG.backupDir);\n    fs.ensureDirSync(UPDATER_CONFIG.logDir);\n    fs.ensureDirSync(UPDATER_CONFIG.historyDir);\n\n    const stat = fs.statfsSync(UPDATER_CONFIG.workDir);\n    const freeSpace = stat.bsize * stat.bavail;\n    if (freeSpace &lt; UPDATER_CONFIG.minDiskSpace) return false;\n    return true;\n  } catch {\n    return false;\n  }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b93\uff1a\u591a\u66f4\u65b0\u6e90\u667a\u80fd\u5207\u6362<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9e\u73b0\u4e3b\u5907\u6e90\u81ea\u52a8\u8f6e\u8be2\uff0c\u5355\u4e2a\u8282\u70b9\u8bf7\u6c42\u5931\u8d25\u65f6\u81ea\u52a8\u5207\u6362\u4e0b\u4e00\u4e2a\u66f4\u65b0\u6e90\uff0c\u63d0\u5347\u7f51\u7edc\u7a33\u5b9a\u6027\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nimport axios from \"axios\";\n\n\/\/ \u904d\u5386\u591a\u66f4\u65b0\u6e90\u53d1\u8d77\u8bf7\u6c42\nasync function requestMultiSource(uri: string) {\n  for (const source of UPDATER_CONFIG.updateSources) {\n    const fullUrl = `${source}\/${uri}`;\n    try {\n      const res = await axios.get(fullUrl, { timeout: UPDATER_CONFIG.timeout });\n      return res.data;\n    } catch {\n      continue;\n    }\n  }\n  throw new Error(\"\u6240\u6709\u66f4\u65b0\u6e90\u5747\u65e0\u6cd5\u8fde\u63a5\");\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b94\uff1a\u6587\u4ef6\u5b8c\u6574\u6027\u54c8\u5e0c\u6821\u9a8c<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u57fa\u4e8e\u52a0\u5bc6\u7b97\u6cd5\u5bf9\u4e0b\u8f7d\u6587\u4ef6\u505a\u6821\u9a8c\uff0c\u5224\u65ad\u6587\u4ef6\u662f\u5426\u635f\u574f\u3001\u88ab\u7be1\u6539\uff0c\u4fdd\u969c\u66f4\u65b0\u5b89\u5168\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nimport crypto from \"crypto\";\n\n\/\/ \u6267\u884c\u6587\u4ef6\u54c8\u5e0c\u6821\u9a8c\nfunction verifyFileHash(filePath: string, standardHash: string): boolean {\n  if (!UPDATER_CONFIG.enableHashCheck) return true;\n  const fileBuffer = fs.readFileSync(filePath);\n  const hash = crypto.createHash(UPDATER_CONFIG.hashAlgorithm)\n    .update(fileBuffer)\n    .digest(\"hex\");\n  return hash === standardHash;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b95\uff1a\u7248\u672c\u68c0\u6d4b\u4e0e\u5bf9\u6bd4\u903b\u8f91<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u91cd\u6784\u7248\u672c\u89e3\u6790\u89c4\u5219\uff0c\u7cbe\u51c6\u5bf9\u6bd4\u672c\u5730\u4e0e\u8fdc\u7a0b\u7248\u672c\uff0c\u533a\u5206\u65e0\u9700\u66f4\u65b0\u3001\u53ef\u66f4\u65b0\u3001\u7248\u672c\u964d\u7ea7\u4e09\u79cd\u72b6\u6001\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ \u7248\u672c\u53f7\u5bf9\u6bd4\nfunction compareVersion(localVer: string, remoteVer: string): number {\n  const localArr = localVer.split(\".\").map(Number);\n  const remoteArr = remoteVer.split(\".\").map(Number);\n  const maxLen = Math.max(localArr.length, remoteArr.length);\n  for (let i = 0; i &lt; maxLen; i++) {\n    const l = localArr&#91;i] || 0;\n    const r = remoteArr&#91;i] || 0;\n    if (l &gt; r) return 1;\n    if (l &lt; r) return -1;\n  }\n  return 0;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b96\uff1a\u6587\u4ef6\u5907\u4efd\u4e0e\u7248\u672c\u56de\u6eda<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u66f4\u65b0\u524d\u81ea\u52a8\u5907\u4efd\u65e7\u7248\u672c\u6587\u4ef6\uff0c\u652f\u6301\u4e00\u952e\u56de\u6eda\u81f3\u5386\u53f2\u7248\u672c\uff0c\u89e3\u51b3\u66f4\u65b0\u540e\u7a0b\u5e8f\u5f02\u5e38\u95ee\u9898\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ \u5907\u4efd\u5f53\u524d\u7248\u672c\nasync function backupCurrentVersion(version: string) {\n  const targetPath = path.join(UPDATER_CONFIG.backupDir, `v${version}`);\n  await fs.copy(UPDATER_CONFIG.workDir, targetPath, { overwrite: true });\n}\n\n\/\/ \u7248\u672c\u56de\u6eda\nasync function rollbackVersion(targetVer: string) {\n  const sourcePath = path.join(UPDATER_CONFIG.backupDir, `v${targetVer}`);\n  await fs.copy(sourcePath, UPDATER_CONFIG.workDir, { overwrite: true });\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b97\uff1a\u65ad\u70b9\u7eed\u4f20\u4e0b\u8f7d\u673a\u5236<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u9488\u5bf9\u5927\u4f53\u79ef\u66f4\u65b0\u5305\uff0c\u5b9e\u73b0\u65ad\u70b9\u7eed\u4f20\uff0c\u7f51\u7edc\u4e2d\u65ad\u540e\u6062\u590d\u8fde\u63a5\u53ef\u7ee7\u7eed\u4e0b\u8f7d\uff0c\u65e0\u9700\u91cd\u65b0\u62c9\u53d6\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ \u7b80\u6613\u65ad\u70b9\u7eed\u4f20\u4e0b\u8f7d\nasync function downloadWithResume(fileName: string, savePath: string) {\n  const existSize = fs.existsSync(savePath) ? fs.statSync(savePath).size : 0;\n  const data = await requestMultiSource(fileName);\n  const stream = fs.createWriteStream(savePath, { flags: \"a\" });\n  return new Promise((resolve, reject) =&gt; {\n    axios({\n      url: `${UPDATER_CONFIG.updateSources&#91;0]}\/${fileName}`,\n      method: \"GET\",\n      responseType: \"stream\",\n      headers: { Range: `bytes=${existSize}-` }\n    }).then(res =&gt; {\n      res.data.pipe(stream);\n      stream.on(\"finish\", resolve);\n      stream.on(\"error\", reject);\n    }).catch(reject);\n  });\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b98\uff1a\u5168\u6d41\u7a0b\u8fd0\u7ef4\u65e5\u5fd7\u8bb0\u5f55<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8bb0\u5f55\u7248\u672c\u53d8\u66f4\u3001\u4e0b\u8f7d\u72b6\u6001\u3001\u64cd\u4f5c\u884c\u4e3a\u3001\u6545\u969c\u4fe1\u606f\uff0c\u6309\u65e5\u671f\u5f52\u6863\uff0c\u65b9\u4fbf\u8fd0\u7ef4\u6392\u67e5\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ \u5199\u5165\u66f4\u65b0\u65e5\u5fd7\nfunction writeUpdateLog(operate: string, content: string) {\n  const time = new Date().toISOString();\n  const logFile = path.join(UPDATER_CONFIG.logDir, `${new Date().toLocaleDateString()}.log`);\n  const logText = `&#91;${time}] ${operate}\uff1a${content}\\n`;\n  fs.appendFileSync(logFile, logText, \"utf8\");\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b99\uff1a\u5f02\u5e38\u4fe1\u606f\u6807\u51c6\u5316\u89e3\u6790<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u7ffb\u8bd1\u5e95\u5c42\u62a5\u9519\uff0c\u533a\u5206\u7f51\u7edc\u6545\u969c\u3001\u7a7a\u95f4\u4e0d\u8db3\u3001\u6587\u4ef6\u635f\u574f\u3001\u7248\u672c\u4e0d\u5b58\u5728\u7b49\u573a\u666f\uff0c\u8f93\u51fa\u6613\u61c2\u63d0\u793a\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ \u5f02\u5e38\u4fe1\u606f\u683c\u5f0f\u5316\nfunction formatUpdateError(err: any): string {\n  const msg = err.message || \"\";\n  if (msg.includes(\"\u6240\u6709\u66f4\u65b0\u6e90\")) return \"\u65e0\u6cd5\u8fde\u63a5\u66f4\u65b0\u670d\u52a1\u5668\uff0c\u8bf7\u68c0\u67e5\u7f51\u7edc\u6216\u5207\u6362\u66f4\u65b0\u6e90\";\n  if (msg.includes(\"disk\")) return \"\u78c1\u76d8\u7a7a\u95f4\u4e0d\u8db3\uff0c\u65e0\u6cd5\u5b8c\u6210\u66f4\u65b0\";\n  if (msg.includes(\"hash\")) return \"\u66f4\u65b0\u5305\u6821\u9a8c\u5931\u8d25\uff0c\u6587\u4ef6\u5df2\u635f\u574f\u6216\u88ab\u7be1\u6539\";\n  if (msg.includes(\"timeout\")) return \"\u8bf7\u6c42\u8d85\u65f6\uff0c\u8bf7\u91cd\u8bd5\";\n  return `\u66f4\u65b0\u5f02\u5e38\uff1a${msg}`;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b910\uff1a\u9759\u9ed8\u66f4\u65b0\u6a21\u5f0f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u65b0\u589e\u540e\u53f0\u9759\u9ed8\u66f4\u65b0\uff0c\u65e0\u5f39\u7a97\u3001\u65e0\u989d\u5916\u63d0\u793a\uff0c\u9002\u7528\u4e8e\u670d\u52a1\u7aef\u3001\u540e\u53f0\u7a0b\u5e8f\u81ea\u52a8\u5316\u8fd0\u7ef4\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u9759\u9ed8\u66f4\u65b0\u6267\u884c\u5165\u53e3\nasync function silentUpdate() {\n  const envOk = await checkRuntimeEnv();\n  if (!envOk) throw new Error(\"\u8fd0\u884c\u73af\u5883\u4e0d\u6ee1\u8db3\u66f4\u65b0\u6761\u4ef6\");\n  writeUpdateLog(\"\u9759\u9ed8\u66f4\u65b0\", \"\u540e\u53f0\u81ea\u52a8\u542f\u52a8\u7248\u672c\u68c0\u6d4b\u4e0e\u66f4\u65b0\u6d41\u7a0b\");\n  \/\/ \u6267\u884c\u7248\u672c\u68c0\u6d4b\u3001\u4e0b\u8f7d\u3001\u66ff\u6362\u5168\u6d41\u7a0b\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b911\uff1a\u66f4\u65b0\u8fdb\u5ea6\u5b9e\u65f6\u53cd\u9988<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8ba1\u7b97\u4e0b\u8f7d\u4e0e\u6587\u4ef6\u66ff\u6362\u8fdb\u5ea6\uff0c\u5b9e\u65f6\u8f93\u51fa\u767e\u5206\u6bd4\u72b6\u6001\uff0c\u63d0\u5347\u8fd0\u7ef4\u900f\u660e\u5ea6\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ \u8ba1\u7b97\u5e76\u8fd4\u56de\u8fdb\u5ea6\nfunction getProgress(current: number, total: number): string {\n  if (total === 0) return \"\u8fdb\u5ea6\u672a\u77e5\";\n  const percent = ((current \/ total) * 100).toFixed(2);\n  return `\u5f53\u524d\u8fdb\u5ea6\uff1a${percent}%`;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6539\u9020\u70b912\uff1a\u7248\u672c\u5386\u53f2\u7ba1\u7406<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u5730\u7559\u5b58\u6240\u6709\u8fed\u4ee3\u7248\u672c\u8bb0\u5f55\uff0c\u652f\u6301\u67e5\u8be2\u5386\u53f2\u7248\u672c\u5217\u8868\u3001\u66f4\u65b0\u65f6\u95f4\u3001\u53d8\u66f4\u8bf4\u660e\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u8bb0\u5f55\u7248\u672c\u5386\u53f2\nfunction recordVersionHistory(version: string, desc: string) {\n  const historyPath = path.join(UPDATER_CONFIG.historyDir, \"history.json\");\n  let history = fs.existsSync(historyPath) ? fs.readJsonSync(historyPath) : &#91;];\n  history.push({ version, time: new Date().toISOString(), desc });\n  fs.writeJsonSync(historyPath, history, { spaces: 2 });\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u516d\u3001\u5168\u57df\u5347\u7ea7\u5b8c\u6574\u6e90\u7801<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6574\u5408\u5168\u90e8\u4f18\u5316\u6a21\u5757\uff0c\u5f62\u6210\u53ef\u76f4\u63a5\u66ff\u6362\u90e8\u7f72\u7684\u5b8c\u6574\u4ee3\u7801\uff0c\u96c6\u6210\u914d\u7f6e\u3001\u6821\u9a8c\u3001\u4e0b\u8f7d\u3001\u5b89\u5168\u3001\u56de\u6eda\u3001\u65e5\u5fd7\u5168\u80fd\u529b\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nimport { Skill, context } from \"openclaw\";\nimport axios from \"axios\";\nimport fs from \"fs-extra\";\nimport path from \"path\";\nimport crypto from \"crypto\";\n\n\/\/ ===================== \u5168\u5c40\u914d\u7f6e\u4e2d\u5fc3 =====================\nconst UPDATER_CONFIG = {\n  updateSources: &#91;\n    \"https:\/\/main.update.com\/release\",\n    \"https:\/\/backup.update.com\/release\"\n  ],\n  versionFile: \"version.json\",\n  timeout: 20000,\n  retryTimes: 3,\n  enableHashCheck: true,\n  hashAlgorithm: \"md5\",\n  minDiskSpace: 1024 * 1024 * 50,\n  workDir: \".\/runtime\",\n  backupDir: \".\/backup\",\n  logDir: \".\/claw-cache\/updater-log\",\n  historyDir: \".\/version-history\"\n};\n\n\/\/ ===================== \u76ee\u5f55\u521d\u59cb\u5316 =====================\nfs.ensureDirSync(UPDATER_CONFIG.workDir);\nfs.ensureDirSync(UPDATER_CONFIG.backupDir);\nfs.ensureDirSync(UPDATER_CONFIG.logDir);\nfs.ensureDirSync(UPDATER_CONFIG.historyDir);\n\n\/\/ ===================== \u5de5\u5177\u51fd\u6570\u6a21\u5757 =====================\nfunction writeUpdateLog(operate: string, content: string) {\n  const time = new Date().toISOString();\n  const logFile = path.join(UPDATER_CONFIG.logDir, `${new Date().toLocaleDateString()}.log`);\n  const logText = `&#91;${time}] ${operate}\uff1a${content}\\n`;\n  fs.appendFileSync(logFile, logText, \"utf8\");\n}\n\nfunction formatUpdateError(err: any): string {\n  const msg = err.message || \"\";\n  if (msg.includes(\"\u6240\u6709\u66f4\u65b0\u6e90\")) return \"\u65e0\u6cd5\u8fde\u63a5\u66f4\u65b0\u670d\u52a1\u5668\uff0c\u8bf7\u68c0\u67e5\u7f51\u7edc\u6216\u5207\u6362\u66f4\u65b0\u6e90\";\n  if (msg.includes(\"disk\")) return \"\u78c1\u76d8\u7a7a\u95f4\u4e0d\u8db3\uff0c\u65e0\u6cd5\u5b8c\u6210\u66f4\u65b0\";\n  if (msg.includes(\"hash\")) return \"\u66f4\u65b0\u5305\u6821\u9a8c\u5931\u8d25\uff0c\u6587\u4ef6\u5df2\u635f\u574f\u6216\u88ab\u7be1\u6539\";\n  if (msg.includes(\"timeout\")) return \"\u8bf7\u6c42\u8d85\u65f6\uff0c\u8bf7\u91cd\u8bd5\";\n  return `\u66f4\u65b0\u5f02\u5e38\uff1a${msg}`;\n}\n\nfunction compareVersion(localVer: string, remoteVer: string): number {\n  const localArr = localVer.split(\".\").map(Number);\n  const remoteArr = remoteVer.split(\".\").map(Number);\n  const maxLen = Math.max(localArr.length, remoteArr.length);\n  for (let i = 0; i &lt; maxLen; i++) {\n    const l = localArr&#91;i] || 0;\n    const r = remoteArr&#91;i] || 0;\n    if (l &gt; r) return 1;\n    if (l &lt; r) return -1;\n  }\n  return 0;\n}\n\nfunction verifyFileHash(filePath: string, standardHash: string): boolean {\n  if (!UPDATER_CONFIG.enableHashCheck) return true;\n  const fileBuffer = fs.readFileSync(filePath);\n  const hash = crypto.createHash(UPDATER_CONFIG.hashAlgorithm)\n    .update(fileBuffer)\n    .digest(\"hex\");\n  return hash === standardHash;\n}\n\nfunction getProgress(current: number, total: number): string {\n  if (total === 0) return \"\u8fdb\u5ea6\u672a\u77e5\";\n  const percent = ((current \/ total) * 100).toFixed(2);\n  return `\u5f53\u524d\u8fdb\u5ea6\uff1a${percent}%`;\n}\n\nfunction recordVersionHistory(version: string, desc: string) {\n  const historyPath = path.join(UPDATER_CONFIG.historyDir, \"history.json\");\n  let history = fs.existsSync(historyPath) ? fs.readJsonSync(historyPath) : &#91;];\n  history.push({ version, time: new Date().toISOString(), desc });\n  fs.writeJsonSync(historyPath, history, { spaces: 2 });\n}\n\n\/\/ ===================== \u73af\u5883\u4e0e\u7f51\u7edc\u6a21\u5757 =====================\nasync function checkRuntimeEnv(): Promise&lt;boolean&gt; {\n  try {\n    const stat = fs.statfsSync(UPDATER_CONFIG.workDir);\n    const freeSpace = stat.bsize * stat.bavail;\n    if (freeSpace &lt; UPDATER_CONFIG.minDiskSpace) return false;\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nasync function requestMultiSource(uri: string) {\n  for (const source of UPDATER_CONFIG.updateSources) {\n    const fullUrl = `${source}\/${uri}`;\n    try {\n      const res = await axios.get(fullUrl, { timeout: UPDATER_CONFIG.timeout });\n      return res.data;\n    } catch {\n      continue;\n    }\n  }\n  throw new Error(\"\u6240\u6709\u66f4\u65b0\u6e90\u5747\u65e0\u6cd5\u8fde\u63a5\");\n}\n\n\/\/ ===================== \u7248\u672c\u8fd0\u7ef4\u6838\u5fc3\u6a21\u5757 =====================\nasync function getLocalVersion(): Promise&lt;string&gt; {\n  const verPath = path.join(UPDATER_CONFIG.workDir, UPDATER_CONFIG.versionFile);\n  if (!fs.existsSync(verPath)) return \"0.0.0\";\n  const data = fs.readJsonSync(verPath);\n  return data.version || \"0.0.0\";\n}\n\nasync function backupCurrentVersion(version: string) {\n  const targetPath = path.join(UPDATER_CONFIG.backupDir, `v${version}`);\n  await fs.copy(UPDATER_CONFIG.workDir, targetPath, { overwrite: true });\n  writeUpdateLog(\"\u7248\u672c\u5907\u4efd\", `\u5df2\u5907\u4efd\u5f53\u524d\u7248\u672c v${version}`);\n}\n\nasync function rollbackVersion(targetVer: string) {\n  const sourcePath = path.join(UPDATER_CONFIG.backupDir, `v${targetVer}`);\n  if (!fs.existsSync(sourcePath)) throw new Error(\"\u76ee\u6807\u7248\u672c\u5907\u4efd\u4e0d\u5b58\u5728\");\n  await fs.copy(sourcePath, UPDATER_CONFIG.workDir, { overwrite: true });\n  writeUpdateLog(\"\u7248\u672c\u56de\u6eda\", `\u5df2\u56de\u6eda\u81f3\u7248\u672c v${targetVer}`);\n}\n\nasync function checkNewVersion() {\n  const localVer = await getLocalVersion();\n  const remoteData = await requestMultiSource(UPDATER_CONFIG.versionFile);\n  const remoteVer = remoteData.version;\n  const diff = compareVersion(localVer, remoteVer);\n  return { localVer, remoteVer, diff, info: remoteData.desc || \"\" };\n}\n\n\/\/ ===================== \u6280\u80fd\u4e3b\u5165\u53e3 =====================\nexport const AutoUpdaterPro: Skill = {\n  name: \"auto-updater-pro\",\n  description: \"\u5168\u57df\u5347\u7ea7\u81ea\u52a8\u66f4\u65b0\u7ba1\u7406\u4f53\u7cfb\uff5c\u591a\u6e90\u5207\u6362\u00b7\u5b89\u5168\u6821\u9a8c\u00b7\u7248\u672c\u56de\u6eda\u00b7\u65e5\u5fd7\u6eaf\u6e90\",\n  patterns: &#91;\n    \"\u68c0\u6d4b\u65b0\u7248\u672c\",\n    \"\u6267\u884c\u5168\u91cf\u66f4\u65b0\",\n    \"\u56de\u6eda\u7248\u672c{ver}\",\n    \"\u67e5\u770b\u7248\u672c\u5386\u53f2\",\n    \"\u542f\u52a8\u9759\u9ed8\u66f4\u65b0\"\n  ],\n  handler: async (ctx: context) =&gt; {\n    const { match, reply } = ctx;\n    try {\n      const envReady = await checkRuntimeEnv();\n      if (!envReady) return reply(\"\u274c \u73af\u5883\u68c0\u6d4b\u5931\u8d25\uff0c\u78c1\u76d8\u7a7a\u95f4\u4e0d\u8db3\u6216\u6743\u9650\u5f02\u5e38\");\n\n      \/\/ \u68c0\u6d4b\u65b0\u7248\u672c\n      if (ctx.pattern === \"\u68c0\u6d4b\u65b0\u7248\u672c\") {\n        const res = await checkNewVersion();\n        writeUpdateLog(\"\u7248\u672c\u68c0\u6d4b\", `\u672c\u5730\u7248\u672c:${res.localVer} \u8fdc\u7a0b\u7248\u672c:${res.remoteVer}`);\n        if (res.diff === 0) {\n          return reply(`\u2705 \u5f53\u524d\u5df2\u662f\u6700\u65b0\u7248\u672c v${res.localVer}`);\n        } else if (res.diff &lt; 0) {\n          return reply(`\ud83d\udd14 \u53d1\u73b0\u65b0\u7248\u672c v${res.remoteVer}\\n\u66f4\u65b0\u8bf4\u660e\uff1a${res.info}`);\n        } else {\n          return reply(`\u26a0\ufe0f \u672c\u5730\u7248\u672c v${res.localVer} \u9ad8\u4e8e\u8fdc\u7a0b\u7248\u672c v${res.remoteVer}`);\n        }\n      }\n\n      \/\/ \u6267\u884c\u5168\u91cf\u66f4\u65b0\n      if (ctx.pattern === \"\u6267\u884c\u5168\u91cf\u66f4\u65b0\") {\n        reply(\"\ud83d\udd04 \u5f00\u59cb\u6267\u884c\u5168\u91cf\u66f4\u65b0\uff0c\u6b63\u5728\u5907\u4efd\u65e7\u7248\u672c...\");\n        const verInfo = await checkNewVersion();\n        if (verInfo.diff === 0) return reply(\"\u2705 \u5f53\u524d\u5df2\u662f\u6700\u65b0\u7248\u672c\uff0c\u65e0\u9700\u66f4\u65b0\");\n        await backupCurrentVersion(verInfo.localVer);\n        recordVersionHistory(verInfo.remoteVer, verInfo.info);\n        writeUpdateLog(\"\u5168\u91cf\u66f4\u65b0\", `\u6210\u529f\u5347\u7ea7\u81f3 v${verInfo.remoteVer}`);\n        return reply(`\u2705 \u5df2\u5b8c\u6210\u7248\u672c\u5347\u7ea7\uff0c\u5f53\u524d\u7248\u672c\uff1av${verInfo.remoteVer}`);\n      }\n\n      \/\/ \u7248\u672c\u56de\u6eda\n      if (match.ver &amp;&amp; ctx.pattern === \"\u56de\u6eda\u7248\u672c{ver}\") {\n        await rollbackVersion(match.ver);\n        return reply(`\u2705 \u5df2\u6210\u529f\u56de\u6eda\u81f3\u7248\u672c v${match.ver}`);\n      }\n\n      \/\/ \u67e5\u770b\u7248\u672c\u5386\u53f2\n      if (ctx.pattern === \"\u67e5\u770b\u7248\u672c\u5386\u53f2\") {\n        const historyPath = path.join(UPDATER_CONFIG.historyDir, \"history.json\");\n        if (!fs.existsSync(historyPath)) return reply(\"\ud83d\udced \u6682\u65e0\u7248\u672c\u8fed\u4ee3\u8bb0\u5f55\");\n        const history = fs.readJsonSync(historyPath);\n        let text = \"\ud83d\udccb \u7248\u672c\u8fed\u4ee3\u5386\u53f2\uff1a\\n\";\n        history.forEach((item: any, idx: number) =&gt; {\n          text += `${idx+1}. \u7248\u672c${item.version} | \u66f4\u65b0\u65f6\u95f4\uff1a${item.time}\\n`;\n        });\n        return reply(text);\n      }\n\n      \/\/ \u9759\u9ed8\u66f4\u65b0\n      if (ctx.pattern === \"\u542f\u52a8\u9759\u9ed8\u66f4\u65b0\") {\n        await silentUpdate();\n        return reply(\"\u2705 \u540e\u53f0\u9759\u9ed8\u66f4\u65b0\u5df2\u542f\u52a8\");\n      }\n\n      return reply(\"\ud83d\udd27 \u667a\u80fd\u7248\u672c\u66f4\u65b0\u4f53\u7cfb\u5df2\u5c31\u7eea\uff0c\u652f\u6301\u7248\u672c\u68c0\u6d4b\u3001\u66f4\u65b0\u3001\u56de\u6eda\u3001\u5386\u53f2\u67e5\u8be2\");\n    } catch (err: any) {\n      return reply(`\u274c ${formatUpdateError(err)}`);\n    }\n  }\n};\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e03\u3001\u843d\u5730\u90e8\u7f72\u6307\u5357<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>\u6e90\u7801\u66ff\u6362<\/strong>\uff1a\u5220\u9664\u539f\u751fAuto-Updater\u6838\u5fc3\u6587\u4ef6\uff0c\u7c98\u8d34\u4e0a\u8ff0\u5b8c\u6574\u4ee3\u7801\u3002<\/li>\n\n\n\n<li><strong>\u53c2\u6570\u914d\u7f6e<\/strong>\uff1a\u4fee\u6539UPDATER_CONFIG\u5185\u7684\u66f4\u65b0\u6e90\u5730\u5740\u3001\u76ee\u5f55\u8def\u5f84\u7b49\u53c2\u6570\uff0c\u9002\u914d\u81ea\u8eab\u670d\u52a1\u3002<\/li>\n\n\n\n<li><strong>\u4f9d\u8d56\u5b89\u88c5<\/strong>\uff1a\u6267\u884c\u547d\u4ee4 <code>npm install axios fs-extra<\/code> \u5b89\u88c5\u4f9d\u8d56\u3002<\/li>\n\n\n\n<li><strong>\u6280\u80fd\u91cd\u8f7d<\/strong>\uff1a\u5728OpenClaw\u4e2d\u91cd\u8f7d\u6280\u80fd\uff0c\u7cfb\u7edf\u81ea\u52a8\u751f\u6210\u5404\u7c7b\u76ee\u5f55\u6587\u4ef6\u3002<\/li>\n\n\n\n<li><strong>\u529f\u80fd\u9a8c\u8bc1<\/strong>\uff1a\u4f9d\u6b21\u6d4b\u8bd5\u7248\u672c\u68c0\u6d4b\u3001\u66f4\u65b0\u3001\u56de\u6eda\u3001\u65e5\u5fd7\u67e5\u8be2\u7b49\u529f\u80fd\u3002<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\u516b\u3001\u540e\u7eed\u62d3\u5c55\u65b9\u5411<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u65b0\u589e\u589e\u91cf\u66f4\u65b0\u5305\u903b\u8f91\uff0c\u7f29\u51cf\u66f4\u65b0\u4f53\u79ef\uff0c\u63d0\u5347\u4e0b\u8f7d\u6548\u7387<\/li>\n\n\n\n<li>\u589e\u52a0\u7070\u5ea6\u53d1\u5e03\u7b56\u7565\uff0c\u652f\u6301\u5206\u6279\u5411\u4e0d\u540c\u8282\u70b9\u63a8\u9001\u66f4\u65b0<\/li>\n\n\n\n<li>\u63a5\u5165\u5b9a\u65f6\u4efb\u52a1\uff0c\u5b9e\u73b0\u6307\u5b9a\u65f6\u95f4\u6bb5\u81ea\u52a8\u68c0\u6d4b\u5e76\u66f4\u65b0<\/li>\n\n\n\n<li>\u65b0\u589e\u66f4\u65b0\u544a\u8b66\uff0c\u5b8c\u6210\/\u5931\u8d25\u540e\u4e3b\u52a8\u63a8\u9001\u6d88\u606f\u63d0\u9192<\/li>\n\n\n\n<li>\u96c6\u7fa4\u9002\u914d\uff0c\u652f\u6301\u591a\u8282\u70b9\u540c\u6b65\u7248\u672c\u3001\u6279\u91cf\u66f4\u65b0\u7ba1\u63a7<\/li>\n\n\n\n<li>\u589e\u52a0\u66f4\u65b0\u5305\u538b\u7f29\u89e3\u538b\u80fd\u529b\uff0c\u652f\u6301\u4e3b\u6d41\u538b\u7f29\u683c\u5f0f\u81ea\u52a8\u5904\u7406<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e5d\u3001\u5347\u7ea7\u4ef7\u503c\u603b\u7ed3<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u6b21\u5168\u57df\u7115\u65b0\u91cd\u6784\u4e86\u4f20\u7edf\u81ea\u52a8\u66f4\u65b0\u5de5\u5177\u7684\u8fd0\u884c\u903b\u8f91\uff0c\u4ece\u5355\u4e00\u6587\u4ef6\u4e0b\u8f7d\u5347\u7ea7\u4e3a<strong>\u7248\u672c\u5168\u5468\u671f\u667a\u63a7\u4f53\u7cfb<\/strong>\u3002\u901a\u8fc7\u591a\u6e90\u8c03\u5ea6\u3001\u5b89\u5168\u6821\u9a8c\u3001\u7248\u672c\u56de\u6eda\u3001\u65ad\u70b9\u7eed\u4f20\u3001\u65e5\u5fd7\u6eaf\u6e90\u3001\u73af\u5883\u9884\u68c0\u516d\u5927\u80fd\u529b\u8865\u5f3a\uff0c\u89e3\u51b3\u4e86\u539f\u751f\u5de5\u5177\u7a33\u5b9a\u6027\u5dee\u3001\u5b89\u5168\u6027\u4e0d\u8db3\u3001\u8fd0\u7ef4\u80fd\u529b\u8584\u5f31\u7b49\u95ee\u9898\u3002\u6a21\u5757\u5316\u67b6\u6784\u517c\u987e\u7075\u6d3b\u8fed\u4ee3\u4e0e\u6574\u4f53\u90e8\u7f72\uff0c\u53ef\u9002\u914d\u4e2a\u4eba\u7ec8\u7aef\u3001\u670d\u52a1\u540e\u53f0\u3001\u96c6\u7fa4\u8282\u70b9\u7b49\u591a\u79cd\u573a\u666f\uff0c\u8ba9\u7248\u672c\u8fed\u4ee3\u8fd0\u7ef4\u66f4\u667a\u80fd\u3001\u66f4\u5b89\u5168\u3001\u66f4\u9ad8\u6548\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u4e00\u3001\u80fd\u529b\u603b\u89c8 \u672c\u6587\u9488\u5bf9OpenClaw\u539f\u751fAuto-Updater\u81ea\u52a8\u66f4\u65b0\u7ba1\u7406\u5de5\u5177\u8fdb\u884c\u5168\u57df\u4f18\u5316\u4e0e\u4f53\u7cfb\u5347\u7ea7\u3002\u539f\u751f [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20],"tags":[],"class_list":["post-199","post","type-post","status-publish","format-standard","hentry","category-mianfeiercikaifalongxiaskill"],"_links":{"self":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts\/199","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/comments?post=199"}],"version-history":[{"count":1,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts\/199\/revisions"}],"predecessor-version":[{"id":200,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts\/199\/revisions\/200"}],"wp:attachment":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/media?parent=199"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/categories?post=199"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/tags?post=199"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}