Files
persistent-chat-plugin/lib/patch-activate.mjs
卡若AI ba786b3700 feat: Persistent Chat v1.1 CO-Chat 整合上传 Gitea
同步 enhance/bridge/smoke/retire 模块、vendor 3.3.34、增强面板与 PRD 文档,
Hub 接入 hub-routes-prd,含安装/经验/模块说明与 co-integration 运维脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 17:42:16 +08:00

187 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* W7 补丁headless 激活 vendor co-chat extension无 --quit-cursor不杀 Cursor
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const CURSOR_APP = '/Applications/Cursor.app';
const WORKBENCH = path.join(CURSOR_APP, 'Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js');
const EXTHOST = path.join(CURSOR_APP, 'Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js');
const EXT_CANDIDATES = [
path.join(__dirname, '../vendor/cochat-engine/3.3.34/dist/extension.js'),
path.join(process.env.PCHAT_WORKSPACE || '/Users/karuo/Documents/个人', '.persistent-chat-v1.1/vendor/cochat-engine/3.3.34/dist/extension.js'),
path.join(process.env.HOME || '', '.cursor/extensions/co-chat.co-chat-panel-3.3.34/dist/extension.js'),
].map((p) => path.normalize(p));
function emit(obj) {
console.log(JSON.stringify(obj));
}
function resolveExtensionPath() {
for (const p of EXT_CANDIDATES) {
if (fs.existsSync(p)) return p;
}
return null;
}
function evt() {
const subs = [];
return {
event: (cb) => {
subs.push(cb);
return { dispose: () => {} };
},
fire: (v) => subs.forEach((f) => f(v)),
};
}
function installVscodeMock(extPath) {
const mockPath = path.join('/tmp', 'pchat-vscode-mock-v2.cjs');
const extRoot = path.dirname(path.dirname(extPath));
const cfgStore = {
'kc.noQuotaMcp': true,
'kc.corePatchBundle': true,
'kc.endlessRetries': true,
'kc.agentSortPerf': true,
'kc.mcpTimeoutGuard': true,
'kc.mcpSelfHeal': true,
'kc.agentStreamRetry': true,
'kc.disableCursorUpdate': true,
};
const content = `
const path = require('path');
const fs = require('fs');
const extPath = ${JSON.stringify(extPath)};
const extRoot = ${JSON.stringify(extRoot)};
const cfgStore = ${JSON.stringify(cfgStore)};
const subs = [];
function evt(){const s=[];return{event:(cb)=>{s.push(cb);return{dispose:()=>{}};},fire:(v)=>s.forEach(f=>f(v))};}
module.exports = {
ExtensionContext: function() {
this.subscriptions = [];
this.globalState = {
get: (k,d)=>cfgStore[k]!==undefined?cfgStore[k]:d,
update: async (k,v)=>{cfgStore[k]=v;},
keys: ()=>Object.keys(cfgStore),
};
this.secrets = { get: async () => undefined, store: async () => {}, delete: async () => {} };
this.extensionPath = extRoot;
this.extensionUri = { fsPath: extRoot };
this.storagePath = path.join(require('os').tmpdir(), 'pchat-ext-storage');
this.globalStoragePath = path.join(require('os').tmpdir(), 'pchat-global-storage');
this.logPath = path.join(require('os').tmpdir(), 'pchat-log');
for (const d of [this.storagePath, this.globalStoragePath, this.logPath]) {
try { fs.mkdirSync(d, { recursive: true }); } catch {}
}
},
workspace: {
getConfiguration: (section) => ({
get: (k, d) => cfgStore[k] !== undefined ? cfgStore[k] : d,
update: async (k, v) => { cfgStore[k] = v; },
has: (k) => cfgStore[k] !== undefined,
}),
workspaceFolders: [{ uri: { fsPath: process.cwd() }, name: 'ws', index: 0 }],
fs: {},
onDidChangeConfiguration: evt().event,
onDidChangeWorkspaceFolders: evt().event,
onDidOpenTextDocument: evt().event,
onDidCloseTextDocument: evt().event,
onDidSaveTextDocument: evt().event,
onDidCreateFiles: evt().event,
onDidDeleteFiles: evt().event,
onDidRenameFiles: evt().event,
textDocuments: [],
getWorkspaceFolder: () => ({ uri: { fsPath: process.cwd() } }),
},
window: {
showInformationMessage: async () => undefined,
showErrorMessage: async () => undefined,
showWarningMessage: async () => undefined,
createOutputChannel: () => ({ appendLine: () => {}, show: () => {}, dispose: () => {} }),
createStatusBarItem: () => ({ show: () => {}, hide: () => {}, dispose: () => {}, text: '' }),
},
extensions: {
all: [{ id: 'co-chat.co-chat-panel', extensionPath: extRoot, packageJSON: { version: '3.3.34' } }],
getExtension: (id) => id && String(id).includes('co-chat') ? { exports: {}, extensionPath: extRoot, packageJSON: { version: '3.3.34', name: 'co-chat-panel' } } : undefined,
},
env: { appRoot: ${JSON.stringify(path.join(CURSOR_APP, 'Contents/Resources/app'))}, uriScheme: 'vscode', language: 'zh-cn' },
commands: { registerCommand: () => ({ dispose: () => {} }), executeCommand: async () => {} },
Uri: { file: (f) => ({ fsPath: f, scheme: 'file' }), parse: (u) => ({ fsPath: u, scheme: 'file' }) },
EventEmitter: class { constructor(){this._e=evt();this.event=this._e.event;} fire(v){this._e.fire(v);} },
StatusBarAlignment: { Left: 1, Right: 2 },
ViewColumn: { One: 1 },
ConfigurationTarget: { Global: 1, Workspace: 2 },
};
`;
fs.writeFileSync(mockPath, content);
const Module = require('module');
const orig = Module._resolveFilename;
Module._resolveFilename = function (request, parent, isMain, options) {
if (request === 'vscode') return mockPath;
return orig.call(this, request, parent, isMain, options);
};
}
function verifyMarkers() {
const wb = fs.existsSync(WORKBENCH) ? fs.readFileSync(WORKBENCH, 'utf8') : '';
const eh = fs.existsSync(EXTHOST) ? fs.readFileSync(EXTHOST, 'utf8') : '';
const checks = [
{ id: 'noQuotaMcp', start: 'CO_NO_QUOTA_MCP_V1_START', src: wb },
{ id: 'composerBridge', start: 'CO_COMPOSER_BRIDGE_START', src: wb },
{ id: 'exthost', start: 'CO_NO_QUOTA_EXTHOST_V1_START', src: eh },
];
const found = checks.filter((m) => m.src.includes(m.start)).map((m) => m.id);
return { patchOk: found.length >= 3, found, total: checks.length, details: checks.map((m) => ({ id: m.id, start: m.start, found: m.src.includes(m.start) })) };
}
async function main() {
const mode = process.argv[2] || 'apply';
if (mode === 'verify') {
emit({ ok: true, ...verifyMarkers(), method: 'verify' });
return;
}
const extPath = resolveExtensionPath();
if (!extPath) {
emit({ ok: false, error: '未找到 extension.js', candidates: EXT_CANDIDATES });
process.exitCode = 1;
return;
}
process.env.CO_SKIP_LICENSE = '1';
process.env.PCHAT_NO_LICENSE = '1';
try {
installVscodeMock(extPath);
const mod = await import(pathToFileURL(extPath).href);
const activate = mod.activate || mod.default?.activate;
if (typeof activate !== 'function') throw new Error('无 activate');
const vscode = require('vscode');
const ctx = new vscode.ExtensionContext();
await activate(ctx);
await new Promise((r) => setTimeout(r, 2500));
} catch (e) {
emit({ ok: false, error: String(e.message || e), extPath, stack: String(e.stack || '').slice(0, 400) });
process.exitCode = 1;
return;
}
const v = verifyMarkers();
emit({
ok: v.patchOk,
...v,
extPath,
method: 'patch-activate',
hint: v.patchOk ? 'W8 OK — 请 Cmd+Q 重启 Cursor 使补丁生效' : '补丁未写入:请保存工作后 Cmd+Q 重启 Cursor再点 W7',
});
}
main().catch((e) => {
emit({ ok: false, error: String(e.stack || e.message || e) });
process.exitCode = 1;
});