@yeaft/webchat-agent 1.0.340 → 1.0.342

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/cli.js CHANGED
@@ -41,8 +41,10 @@ import {
41
41
  buildUpgradeInstallCommand,
42
42
  buildUpgradeMetadataUrl,
43
43
  buildUpgradeVersionCommand,
44
+ createWindowsUpgradeRun,
44
45
  launchWindowsUpgradeScript,
45
46
  prepareWindowsUpgradeRunner,
47
+ releaseWindowsUpgradeLock,
46
48
  resolveWindowsNpmCliPath,
47
49
  resolveWindowsPm2CliPath,
48
50
  } from './upgrade-command.js';
@@ -511,9 +513,9 @@ async function upgrade(args = []) {
511
513
  console.log(`Upgrading to ${latest}...`);
512
514
 
513
515
  if (platform() === 'win32') {
514
- // On Windows, the current process locks its own files. npm cannot overwrite
515
- // them while this process is running. Spawn a detached Node runner that waits
516
- // for us to exit, then runs npm install and optionally restarts the service.
516
+ // On Windows, the current process locks its own files. A short-lived
517
+ // bootstrap detaches the updater before this process exits; the updater then
518
+ // installs the exact version and restores the selected service instance.
517
519
  await upgradeWindows(latest, instanceId);
518
520
  } else {
519
521
  execSync(buildUpgradeInstallCommand(`${pkg.name}@${latest}`), { stdio: 'inherit' });
@@ -544,10 +546,6 @@ async function upgradeWindows(latestVersion, instanceId = DEFAULT_INSTANCE_ID) {
544
546
  const logDir = join(configDir, 'logs');
545
547
  mkdirSync(logDir, { recursive: true });
546
548
 
547
- const runnerPath = join(upgradeDir, 'windows-upgrade-runner.js');
548
- const commandPath = join(upgradeDir, 'upgrade-command.js');
549
- const payloadPath = join(upgradeDir, 'payload.json');
550
- const handoffPath = join(upgradeDir, 'started');
551
549
  const logPath = join(logDir, 'upgrade.log');
552
550
  const ecosystemPath = join(configDir, 'ecosystem.config.cjs');
553
551
  const pm2AppName = getPm2AppName(instanceId);
@@ -563,46 +561,70 @@ async function upgradeWindows(latestVersion, instanceId = DEFAULT_INSTANCE_ID) {
563
561
  } catch {}
564
562
  }
565
563
 
564
+ const run = createWindowsUpgradeRun(upgradeDir);
565
+ const {
566
+ runId,
567
+ lockPath,
568
+ bootstrapPath,
569
+ runnerPath,
570
+ commandPath,
571
+ payloadPath,
572
+ handoffPath,
573
+ authorizePath,
574
+ cancelPath,
575
+ } = run;
566
576
  const payload = {
577
+ runId,
578
+ lockPath,
567
579
  parentPid: process.pid,
568
580
  packageSpec: `${pkg.name}@${latestVersion}`,
569
581
  globalInstall: true,
570
582
  installDir: dirname(dirname(__dirname)),
571
583
  logPath,
572
584
  handoffPath,
585
+ authorizePath,
586
+ cancelPath,
587
+ bootstrapPath,
573
588
  runnerPath,
574
589
  commandPath,
575
590
  payloadPath,
576
591
  nodePath: process.execPath,
577
592
  npmCliPath,
578
593
  pm2CliPath: isPm2 ? pm2CliPath : null,
594
+ pm2AppName: isPm2 ? pm2AppName : null,
579
595
  ecosystemPath: isPm2 ? ecosystemPath : null,
580
596
  };
581
- prepareWindowsUpgradeRunner({
582
- sourceRunnerPath: join(__dirname, 'windows-upgrade-runner.js'),
583
- sourceCommandPath: join(__dirname, 'upgrade-command.js'),
584
- runnerPath,
585
- commandPath,
586
- payloadPath,
587
- payload,
588
- });
597
+ try {
598
+ prepareWindowsUpgradeRunner({
599
+ sourceBootstrapPath: join(__dirname, 'windows-upgrade-bootstrap.js'),
600
+ sourceRunnerPath: join(__dirname, 'windows-upgrade-runner.js'),
601
+ sourceCommandPath: join(__dirname, 'upgrade-command.js'),
602
+ bootstrapPath,
603
+ runnerPath,
604
+ commandPath,
605
+ payloadPath,
606
+ payload,
607
+ });
608
+ } catch (err) {
609
+ releaseWindowsUpgradeLock(lockPath, runId);
610
+ throw err;
611
+ }
589
612
 
590
613
  const launcher = await launchWindowsUpgradeScript({
614
+ runId,
591
615
  nodePath: process.execPath,
616
+ bootstrapPath,
592
617
  runnerPath,
593
618
  payloadPath,
594
619
  logPath,
595
620
  handoffPath,
621
+ authorizePath,
622
+ cancelPath,
623
+ lockPath,
596
624
  spawnProcess: spawn,
597
- onHandoff: isPm2
598
- ? () => {
599
- execFileSync(process.execPath, [pm2CliPath, 'delete', pm2AppName], { stdio: 'pipe' });
600
- console.log(`PM2 app ${pm2AppName} deleted after upgrade handoff.`);
601
- }
602
- : undefined,
603
625
  });
604
-
605
626
  console.log(`Upgrade runner spawned via ${launcher}.`);
627
+
606
628
  console.log('This process will exit now. The upgrade will proceed after exit.');
607
629
  console.log(`Check upgrade log: ${logPath}`);
608
630
  process.exit(0);
@@ -8,8 +8,10 @@ import { getConfigDir, getServiceName, getPm2AppName, getLaunchdPlistPath, DEFAU
8
8
  import {
9
9
  buildUpgradeInstallCommand,
10
10
  buildUpgradeMetadataArgs,
11
+ createWindowsUpgradeRun,
11
12
  launchWindowsUpgradeScript,
12
13
  prepareWindowsUpgradeRunner,
14
+ releaseWindowsUpgradeLock,
13
15
  resolveWindowsNpmCliPath,
14
16
  resolveWindowsPm2CliPath,
15
17
  } from '../upgrade-command.js';
@@ -245,8 +247,8 @@ export async function handleUpgradeAgent() {
245
247
  await spawnUnixUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId);
246
248
  }
247
249
 
248
- // Windows deletes PM2 inside the verified handoff callback. Unix keeps the
249
- // existing pre-exit behavior because its detached launcher has no marker.
250
+ // On Windows the detached runner removes the PM2 app only after this
251
+ // process exits. Unix keeps the existing pre-exit service-manager behavior.
250
252
  const isPm2 = !!process.env.pm_id;
251
253
  if (!isWindows && isPm2) {
252
254
  try {
@@ -271,10 +273,6 @@ async function spawnWindowsUpgradeScript(pkgName, installDir, isGlobalInstall, l
271
273
  const logDir = join(configDir, 'logs');
272
274
  mkdirSync(logDir, { recursive: true });
273
275
 
274
- const runnerPath = join(upgradeDir, 'windows-upgrade-runner.js');
275
- const commandPath = join(upgradeDir, 'upgrade-command.js');
276
- const payloadPath = join(upgradeDir, 'payload.json');
277
- const handoffPath = join(upgradeDir, 'started');
278
276
  const logPath = join(logDir, 'upgrade.log');
279
277
  const ecosystemPath = join(configDir, 'ecosystem.config.cjs');
280
278
  const npmCliPath = resolveWindowsNpmCliPath(process.execPath);
@@ -285,45 +283,68 @@ async function spawnWindowsUpgradeScript(pkgName, installDir, isGlobalInstall, l
285
283
  throw new Error('PM2 manages this Agent, but its JavaScript CLI entry point could not be resolved');
286
284
  }
287
285
 
286
+ const run = createWindowsUpgradeRun(upgradeDir);
287
+ const {
288
+ runId,
289
+ lockPath,
290
+ bootstrapPath,
291
+ runnerPath,
292
+ commandPath,
293
+ payloadPath,
294
+ handoffPath,
295
+ authorizePath,
296
+ cancelPath,
297
+ } = run;
288
298
  const payload = {
299
+ runId,
300
+ lockPath,
289
301
  parentPid: process.pid,
290
302
  packageSpec: `${pkgName}@${latestVersion}`,
291
303
  globalInstall: isGlobalInstall,
292
304
  installDir,
293
305
  logPath,
294
306
  handoffPath,
307
+ authorizePath,
308
+ cancelPath,
309
+ bootstrapPath,
295
310
  runnerPath,
296
311
  commandPath,
297
312
  payloadPath,
298
313
  nodePath: process.execPath,
299
314
  npmCliPath,
300
315
  pm2CliPath,
316
+ pm2AppName: isPm2 ? getPm2AppName(instanceId) : null,
301
317
  ecosystemPath: isPm2 ? ecosystemPath : null,
302
318
  };
303
- prepareWindowsUpgradeRunner({
304
- sourceRunnerPath: fileURLToPath(new URL('../windows-upgrade-runner.js', import.meta.url)),
305
- sourceCommandPath: fileURLToPath(new URL('../upgrade-command.js', import.meta.url)),
306
- runnerPath,
307
- commandPath,
308
- payloadPath,
309
- payload,
310
- });
319
+ try {
320
+ prepareWindowsUpgradeRunner({
321
+ sourceBootstrapPath: fileURLToPath(new URL('../windows-upgrade-bootstrap.js', import.meta.url)),
322
+ sourceRunnerPath: fileURLToPath(new URL('../windows-upgrade-runner.js', import.meta.url)),
323
+ sourceCommandPath: fileURLToPath(new URL('../upgrade-command.js', import.meta.url)),
324
+ bootstrapPath,
325
+ runnerPath,
326
+ commandPath,
327
+ payloadPath,
328
+ payload,
329
+ });
330
+ } catch (err) {
331
+ releaseWindowsUpgradeLock(lockPath, runId);
332
+ throw err;
333
+ }
311
334
 
312
335
  const launcher = await launchWindowsUpgradeScript({
336
+ runId,
313
337
  nodePath: process.execPath,
338
+ bootstrapPath,
314
339
  runnerPath,
315
340
  payloadPath,
316
341
  logPath,
317
342
  handoffPath,
343
+ authorizePath,
344
+ cancelPath,
345
+ lockPath,
318
346
  spawnProcess: spawn,
319
- onHandoff: isPm2
320
- ? () => {
321
- execFileSync(process.execPath, [pm2CliPath, 'delete', getPm2AppName(instanceId)], { stdio: 'pipe', env: safeEnv });
322
- console.log('[Agent] PM2 app deleted after upgrade handoff');
323
- }
324
- : undefined,
325
347
  });
326
-
327
348
  console.log(`[Agent] Spawned Windows upgrade runner via ${launcher} (pm2=${isPm2}, dir=${installDir})`);
328
349
  await sendToServer({ type: 'upgrade_agent_ack', success: true, version: latestVersion, pendingRestart: true });
329
350
  }
@@ -702,6 +702,9 @@ export const stmts = {
702
702
  updateYeaftProjectInstruction: db.prepare(`
703
703
  UPDATE yeaft_projects SET instruction = ?, updated_at = ? WHERE user_id = ? AND id = ?
704
704
  `),
705
+ updateYeaftProjectSortOrder: db.prepare(`
706
+ UPDATE yeaft_projects SET sort_order = ?, updated_at = ? WHERE user_id = ? AND id = ?
707
+ `),
705
708
  deleteYeaftProject: db.prepare(`
706
709
  DELETE FROM yeaft_projects WHERE user_id = ? AND id = ?
707
710
  `),
@@ -138,6 +138,30 @@ export const yeaftProjectDb = {
138
138
  return { projectId: id };
139
139
  },
140
140
 
141
+ reorder(userId, projectIds) {
142
+ const ownerId = requireUserId(userId);
143
+ const projects = this.list(ownerId);
144
+ const ids = Array.isArray(projectIds)
145
+ ? projectIds.map(value => typeof value === 'string' ? value.trim() : '')
146
+ : [];
147
+ const currentIds = new Set(projects.map(project => project.id));
148
+ if (ids.length !== projects.length
149
+ || ids.some(id => !id || !currentIds.has(id))
150
+ || new Set(ids).size !== ids.length) {
151
+ throw new YeaftProjectDbError('invalid_project_order', 'Complete Project order is required');
152
+ }
153
+ const now = Date.now();
154
+ transaction(() => {
155
+ ids.forEach((id, index) => {
156
+ const result = stmts.updateYeaftProjectSortOrder.run(index, now, ownerId, id);
157
+ if (result.changes !== 1) {
158
+ throw new YeaftProjectDbError('project_order_changed', 'Project identity changed during reorder');
159
+ }
160
+ });
161
+ })();
162
+ return this.list(ownerId);
163
+ },
164
+
141
165
  moveSession(userId, {
142
166
  agentId,
143
167
  sessionId,
@@ -1336,6 +1336,7 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
1336
1336
  else if (op === 'update_instruction') {
1337
1337
  result = yeaftProjectDb.updateInstruction(client.userId, msg.projectId, msg.instruction);
1338
1338
  } else if (op === 'delete') result = yeaftProjectDb.delete(client.userId, msg.projectId);
1339
+ else if (op === 'reorder') result = yeaftProjectDb.reorder(client.userId, msg.projectIds);
1339
1340
  else if (op === 'move_session') {
1340
1341
  const agentId = msg.targetAgentId || msg.agentId;
1341
1342
  const sessionId = typeof msg.sessionId === 'string' ? msg.sessionId.trim() : '';
@@ -1 +1 @@
1
- {"version":"1.0.340"}
1
+ {"version":"1.0.342"}