@scorehub/creator-outreach 1.2.0 → 1.2.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiktok-creator-outreach",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "ScoreHub AI's TikTok creator marketing expert for building clear creator profiles and validating focused collaboration shortlists across Southeast Asia.",
5
5
  "author": {
6
6
  "name": "Ben",
package/README.en.md CHANGED
@@ -23,7 +23,7 @@ For the public release, end users only need to:
23
23
 
24
24
  Tiky uses `npx -y @scorehub/creator-outreach@latest` to run a bootstrap check before every new conversation. When the machine is uninitialized, Tiky explains that the ScoreHub local component is required and waits for explicit confirmation. It then prefetches and self-checks `@scorehub/mcp-server@latest` before updating Tiky's files and the WorkBuddy `scorehub` MCP configuration. WorkBuddy must be fully restarted after the initial installation.
25
25
 
26
- That first confirmation also authorizes future silent creator-outreach updates, so Tiky does not ask again for every release. The MCP command always remains `npx -y @scorehub/mcp-server@latest`; it is never pinned to a specific version.
26
+ That first confirmation also authorizes future silent creator-outreach updates, so Tiky does not ask again for every release. The MCP launcher always resolves `@scorehub/mcp-server@latest`; it is never pinned to a specific version.
27
27
 
28
28
  The share link only installs Tiky into WorkBuddy. In the normal public path, users do not need to install `@scorehub/mcp-server` manually or edit `mcp.json` themselves.
29
29
 
@@ -41,7 +41,7 @@ The installer configures WorkBuddy only. If it does not find WorkBuddy, it exits
41
41
  |---|---|---|---|
42
42
  | WorkBuddy | `~/.workbuddy/` exists | Plugin files in `~/.workbuddy/plugins/.../`; MCP configuration merged into `~/.workbuddy/mcp.json` | Restart, then select “ScoreHub AI TikTok Creator Marketing Expert” from the Agent list |
43
43
 
44
- Before writing the configuration, Tiky verifies the MCP server with `--self-check --json`. WorkBuddy then starts it through `npx -y @scorehub/mcp-server@latest`. If WorkBuddy provides a working bundled Node.js / npx, Tiky reuses it. Tiky only guides users to the official Node.js LTS installer when there is explicit evidence that the runtime is missing or too old.
44
+ Before writing the configuration, Tiky verifies the MCP server with `--self-check --json`, then WorkBuddy starts it automatically. macOS and Linux use `npx -y @scorehub/mcp-server@latest`. Windows reuses WorkBuddy's managed `node.exe` to load the matching npm `npx-cli.js` directly, avoiding `.cmd` / `.bat` process-launch compatibility problems. Tiky only guides users to the official Node.js LTS installer when the managed runtime is explicitly missing or too old.
45
45
 
46
46
  ## First Use and Authorization
47
47
 
package/README.md CHANGED
@@ -23,7 +23,7 @@ English version: [README.en.md](./README.en.md)
23
23
 
24
24
  Tiky 每个新会话都会通过 `npx -y @scorehub/creator-outreach@latest` 执行 bootstrap 检查。未初始化时,她会说明需要安装 ScoreHub 本地组件,并等待你明确确认;确认后预拉取并自检 `@scorehub/mcp-server@latest`,再更新 Tiky 本地文件和 WorkBuddy 的 `scorehub` MCP 配置。安装完成后需要完全重启 WorkBuddy。
25
25
 
26
- 首次确认同时授权后续静默更新 creator-outreach,不会对每个新版本重复询问。MCP 配置始终使用 `npx -y @scorehub/mcp-server@latest`,不固定具体版本。
26
+ 首次确认同时授权后续静默更新 creator-outreach,不会对每个新版本重复询问。MCP 始终运行 `@scorehub/mcp-server@latest`,不固定具体版本。
27
27
 
28
28
  分享链接只负责把 Tiky 放进 WorkBuddy。默认情况下,你不需要手工安装 `@scorehub/mcp-server`,也不需要自己编辑 `mcp.json`。
29
29
 
@@ -41,7 +41,7 @@ npx -y @scorehub/creator-outreach
41
41
  |---|---|---|---|
42
42
  | WorkBuddy | `~/.workbuddy/` 存在 | 插件文件写入 `~/.workbuddy/plugins/.../`;MCP 配置合并到 `~/.workbuddy/mcp.json` | 重启后在 Agent 列表选择“ScoreHub AI TikTok达人营销专家” |
43
43
 
44
- MCP Server(`@scorehub/mcp-server`)在写入配置前会先通过 `--self-check --json` 验证可执行性,之后由 `npx -y @scorehub/mcp-server@latest` 在 WorkBuddy 启动时自动拉起。WorkBuddy 若已自带可用 Node.js / npx,会优先复用;只有在存在明确缺失证据时,Tiky 才会引导你安装官方 Node.js LTS。
44
+ MCP Server(`@scorehub/mcp-server`)在写入配置前会先通过 `--self-check --json` 验证可执行性,之后由 WorkBuddy 自动拉起。macOS / Linux 使用 `npx -y @scorehub/mcp-server@latest`;Windows 复用 WorkBuddy 托管的 `node.exe` 直接加载同版本 npm 的 `npx-cli.js`,避免 `.cmd` / `.bat` 的进程启动兼容问题。只有在托管运行时明确缺失或版本过低时,Tiky 才会引导你安装官方 Node.js LTS。
45
45
 
46
46
  ## 首次使用与授权
47
47
 
package/install.js CHANGED
@@ -48,23 +48,33 @@ function getPaths(homeDir) {
48
48
  };
49
49
  }
50
50
 
51
- function resolveWorkBuddyNode(workBuddyDir) {
51
+ function resolveWorkBuddyLauncher(workBuddyDir, platform = process.platform) {
52
52
  const versionsDir = path.join(workBuddyDir, 'binaries', 'node', 'versions');
53
- const npxExecutable = process.platform === 'win32' ? 'npx.cmd' : 'npx';
54
53
  try {
55
- const versions = fs.readdirSync(versionsDir).sort().reverse();
54
+ const versions = fs.readdirSync(versionsDir)
55
+ .sort((left, right) => right.localeCompare(left, undefined, { numeric: true }));
56
56
  for (const version of versions) {
57
57
  const versionDir = path.join(versionsDir, version);
58
+ if (platform === 'win32') {
59
+ const command = path.join(versionDir, 'node.exe');
60
+ const npxCli = path.join(versionDir, 'node_modules', 'npm', 'bin', 'npx-cli.js');
61
+ if (fs.existsSync(command) && fs.existsSync(npxCli)) {
62
+ return { command, argsPrefix: [npxCli], binDir: versionDir };
63
+ }
64
+ continue;
65
+ }
66
+
58
67
  const binDirs = [path.join(versionDir, 'bin'), versionDir];
59
68
  for (const binDir of binDirs) {
60
- const npxPath = path.join(binDir, npxExecutable);
61
- if (fs.existsSync(npxPath)) return { npx: npxPath, binDir };
69
+ const command = path.join(binDir, 'npx');
70
+ if (fs.existsSync(command)) return { command, argsPrefix: [], binDir };
62
71
  }
63
72
  }
64
73
  } catch {
65
74
  // WorkBuddy or its bundled Node.js is not available at this path.
66
75
  }
67
- return { npx: 'npx', binDir: null };
76
+ if (platform === 'win32') return null;
77
+ return { command: 'npx', argsPrefix: [], binDir: null };
68
78
  }
69
79
 
70
80
  function readJson(filePath) {
@@ -144,11 +154,11 @@ function buildMcpEnvironment(version, binDir) {
144
154
  });
145
155
  }
146
156
 
147
- function buildMcpConfig(version, npx, binDir) {
157
+ function buildMcpConfig(version, launcher) {
148
158
  return {
149
- command: npx,
150
- args: MCP_CONFIG.args.slice(),
151
- env: buildMcpEnvironment(version, binDir),
159
+ command: launcher.command,
160
+ args: launcher.argsPrefix.concat(MCP_CONFIG.args),
161
+ env: buildMcpEnvironment(version, launcher.binDir),
152
162
  };
153
163
  }
154
164
 
@@ -172,6 +182,17 @@ function isManagedMcpConfig(config) {
172
182
  && server.env.SCOREHUB_CLIENT_HOST === 'workbuddy';
173
183
  }
174
184
 
185
+ function needsMcpLauncherMigration(config, platform = process.platform, expectedLauncher) {
186
+ if (platform !== 'win32' || !isManagedMcpConfig(config)) return false;
187
+ const server = config.mcpServers.scorehub;
188
+ const npxCli = server.args.find((arg) => /(?:^|[\\/])npx-cli\.js$/i.test(arg));
189
+ if (!/(?:^|[\\/])node\.exe$/i.test(server.command || '') || !npxCli) return true;
190
+ if (expectedLauncher === undefined) return false;
191
+ if (expectedLauncher === null) return true;
192
+ return server.command.toLowerCase() !== expectedLauncher.command.toLowerCase()
193
+ || npxCli.toLowerCase() !== expectedLauncher.argsPrefix[0].toLowerCase();
194
+ }
195
+
175
196
  function mergeMcpConfig(configPath, serverConfig) {
176
197
  const current = readMcpConfig(configPath);
177
198
  if (current.config === null) throw new Error(current.error);
@@ -228,18 +249,22 @@ function parseSelfCheckOutput(output) {
228
249
  throw new Error('mcp-server self-check did not return a valid JSON payload');
229
250
  }
230
251
 
231
- function runMcpSelfCheck({ npx, env, selfCheck = defaultMcpSelfCheck } = {}) {
232
- return selfCheck({ npx, env });
252
+ function runMcpSelfCheck({ launcher, env, selfCheck = defaultMcpSelfCheck } = {}) {
253
+ return selfCheck({ launcher, env });
233
254
  }
234
255
 
235
- function defaultMcpSelfCheck({ npx, env }) {
256
+ function defaultMcpSelfCheck({ launcher, env, execFile = execFileSync }) {
236
257
  try {
237
- const output = execFileSync(npx, ['-y', '@scorehub/mcp-server@latest', '--self-check', '--json'], {
238
- encoding: 'utf8',
239
- env,
240
- stdio: ['ignore', 'pipe', 'pipe'],
241
- timeout: 30000,
242
- });
258
+ const output = execFile(
259
+ launcher.command,
260
+ launcher.argsPrefix.concat(['-y', '@scorehub/mcp-server@latest', '--self-check', '--json']),
261
+ {
262
+ encoding: 'utf8',
263
+ env,
264
+ stdio: ['ignore', 'pipe', 'pipe'],
265
+ timeout: 30000,
266
+ },
267
+ );
243
268
  const payload = parseSelfCheckOutput(output);
244
269
  if (!payload.ok) throw new Error(`mcp-server self-check failed for Node.js ${payload.node_version}`);
245
270
  return payload;
@@ -268,13 +293,24 @@ function detectStatus({ paths, state, nodeVersion }) {
268
293
  return { status: 'ready', reason: null };
269
294
  }
270
295
 
271
- function check({ homeDir = os.homedir(), nodeVersion = process.versions.node, now = Date.now() } = {}) {
296
+ function check({
297
+ homeDir = os.homedir(),
298
+ nodeVersion = process.versions.node,
299
+ now = Date.now(),
300
+ platform = process.platform,
301
+ } = {}) {
272
302
  const paths = getPaths(homeDir);
273
303
  const state = loadState(paths.bootstrapState);
274
304
  const detected = detectStatus({ paths, state, nodeVersion });
275
305
  const lastCheckAt = state.last_check_at ? Date.parse(state.last_check_at) : Number.NaN;
306
+ const mcp = readMcpConfig(paths.workBuddyMcpJson);
307
+ const expectedLauncher = platform === 'win32'
308
+ ? resolveWorkBuddyLauncher(paths.workBuddyDir, platform)
309
+ : undefined;
310
+ const launcherMigrationDue = mcp.config !== null
311
+ && needsMcpLauncherMigration(mcp.config, platform, expectedLauncher);
276
312
  const updateDue = detected.status === 'ready'
277
- && (!Number.isFinite(lastCheckAt) || now - lastCheckAt >= UPDATE_INTERVAL_MS);
313
+ && (launcherMigrationDue || !Number.isFinite(lastCheckAt) || now - lastCheckAt >= UPDATE_INTERVAL_MS);
278
314
  return {
279
315
  schema_version: STATE_SCHEMA_VERSION,
280
316
  status: detected.status,
@@ -287,11 +323,15 @@ function check({ homeDir = os.homedir(), nodeVersion = process.versions.node, no
287
323
  };
288
324
  }
289
325
 
290
- function assertInstallable({ homeDir, nodeVersion }) {
326
+ function assertInstallable({ homeDir, nodeVersion, platform }) {
291
327
  const paths = getPaths(homeDir);
292
328
  if (!isSupportedNode(nodeVersion)) throw new Error(`Node.js ${MIN_NODE_MAJOR}+ is required. Detected ${nodeVersion}.`);
293
329
  if (!fs.existsSync(paths.workBuddyDir)) throw new Error('No supported client was found. Launch WorkBuddy once, then retry.');
294
- return { paths, node: resolveWorkBuddyNode(paths.workBuddyDir) };
330
+ const launcher = resolveWorkBuddyLauncher(paths.workBuddyDir, platform);
331
+ if (!launcher) {
332
+ throw new Error('WorkBuddy managed Node.js/npm runtime is incomplete: expected node.exe and node_modules/npm/bin/npx-cli.js in the same version directory.');
333
+ }
334
+ return { paths, launcher };
295
335
  }
296
336
 
297
337
  function install({
@@ -300,16 +340,17 @@ function install({
300
340
  selfCheck = defaultMcpSelfCheck,
301
341
  markConsent = true,
302
342
  replacePlugin = replacePluginAtomic,
343
+ platform = process.platform,
303
344
  } = {}) {
304
- const { paths, node } = assertInstallable({ homeDir, nodeVersion });
305
- const env = buildMcpEnvironment(pkg.version, node.binDir);
306
- const mcpCheck = runMcpSelfCheck({ npx: node.npx, env, selfCheck });
345
+ const { paths, launcher } = assertInstallable({ homeDir, nodeVersion, platform });
346
+ const env = buildMcpEnvironment(pkg.version, launcher.binDir);
347
+ const mcpCheck = runMcpSelfCheck({ launcher, env, selfCheck });
307
348
  const mcpSnapshot = snapshotFile(paths.workBuddyMcpJson);
308
349
  const stateSnapshot = snapshotFile(paths.bootstrapState);
309
350
  let pluginBackup = null;
310
351
  let pluginReplaced = false;
311
352
  try {
312
- mergeMcpConfig(paths.workBuddyMcpJson, buildMcpConfig(pkg.version, node.npx, node.binDir));
353
+ mergeMcpConfig(paths.workBuddyMcpJson, buildMcpConfig(pkg.version, launcher));
313
354
  pluginBackup = replacePlugin(paths.workBuddyPluginDir);
314
355
  pluginReplaced = true;
315
356
 
@@ -348,6 +389,7 @@ function update({
348
389
  homeDir = os.homedir(),
349
390
  nodeVersion = process.versions.node,
350
391
  selfCheck = defaultMcpSelfCheck,
392
+ platform = process.platform,
351
393
  } = {}) {
352
394
  const paths = getPaths(homeDir);
353
395
  const state = loadState(paths.bootstrapState);
@@ -357,7 +399,13 @@ function update({
357
399
  return { status: detected.status, reason: detected.reason };
358
400
  }
359
401
  if (state.restart_required) return { status: 'restart_required', reason: 'WorkBuddy must be restarted to load the updated plugin and MCP configuration.' };
360
- if (readInstalledPluginVersion(paths.workBuddyPluginDir) === pkg.version) {
402
+ const mcp = readMcpConfig(paths.workBuddyMcpJson);
403
+ const expectedLauncher = platform === 'win32'
404
+ ? resolveWorkBuddyLauncher(paths.workBuddyDir, platform)
405
+ : undefined;
406
+ const launcherMigrationDue = mcp.config !== null
407
+ && needsMcpLauncherMigration(mcp.config, platform, expectedLauncher);
408
+ if (readInstalledPluginVersion(paths.workBuddyPluginDir) === pkg.version && !launcherMigrationDue) {
361
409
  state.status = 'ready';
362
410
  state.creator_outreach_version = pkg.version;
363
411
  state.last_check_at = new Date().toISOString();
@@ -365,7 +413,7 @@ function update({
365
413
  writeJsonAtomic(paths.bootstrapState, state);
366
414
  return { status: 'ready', creator_outreach_version: pkg.version };
367
415
  }
368
- return install({ homeDir, nodeVersion, selfCheck, markConsent: false });
416
+ return install({ homeDir, nodeVersion, selfCheck, markConsent: false, platform });
369
417
  }
370
418
 
371
419
  function printJson(value) {
@@ -403,6 +451,7 @@ module.exports = {
403
451
  UPDATE_INTERVAL_MS,
404
452
  WORKBUDDY_SHARE_URL,
405
453
  check,
454
+ defaultMcpSelfCheck,
406
455
  detectStatus,
407
456
  getNodeMajorVersion,
408
457
  getPaths,
@@ -410,6 +459,8 @@ module.exports = {
410
459
  isManagedMcpConfig,
411
460
  isSupportedNode,
412
461
  markReady,
462
+ needsMcpLauncherMigration,
463
+ resolveWorkBuddyLauncher,
413
464
  runCli,
414
465
  update,
415
466
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scorehub/creator-outreach",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "ScoreHub AI TikTok达人营销专家 Tiky — Agent 定义 + Skills 配置",
5
5
  "engines": {
6
6
  "node": ">=18"