happy-imou-cloud 2.0.22 → 2.1.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.
Files changed (35) hide show
  1. package/bin/happy-cloud.mjs +1 -1
  2. package/dist/{BaseReasoningProcessor-CJVv1aNR.cjs → BaseReasoningProcessor-C9mH8EVn.cjs} +3 -3
  3. package/dist/{BaseReasoningProcessor-mIqqngd3.mjs → BaseReasoningProcessor-DQkzwRuf.mjs} +3 -3
  4. package/dist/ProviderSelectionHandler-5Dedbm8j.cjs +265 -0
  5. package/dist/ProviderSelectionHandler-BlrrLPlo.mjs +261 -0
  6. package/dist/{api-DP-RQUao.cjs → api-Bd-MnOS4.cjs} +24 -2
  7. package/dist/{api-DrijKeDb.mjs → api-w_CUxb9Q.mjs} +25 -3
  8. package/dist/{command-BZphfJrt.cjs → command-DoDmHNxR.cjs} +3 -3
  9. package/dist/{command--vV6BSsL.mjs → command-mTWwCqTY.mjs} +3 -3
  10. package/dist/{index-CqCEZDFi.cjs → index-BQmJ4NAa.cjs} +199 -79
  11. package/dist/{index-BIki80pQ.mjs → index-GuXV-pxB.mjs} +196 -76
  12. package/dist/index.cjs +3 -3
  13. package/dist/index.mjs +3 -3
  14. package/dist/lib.cjs +1 -1
  15. package/dist/lib.d.cts +95 -92
  16. package/dist/lib.d.mts +95 -92
  17. package/dist/lib.mjs +1 -1
  18. package/dist/{persistence-yVTbf_Ng.cjs → persistence-BL06LLVz.cjs} +1 -1
  19. package/dist/{persistence-C3NBdZdz.mjs → persistence-MSy70is3.mjs} +1 -1
  20. package/dist/{registerKillSessionHandler-CHEj7UjN.mjs → registerKillSessionHandler-CjWfUfc3.mjs} +428 -13
  21. package/dist/{registerKillSessionHandler-QmBN446A.cjs → registerKillSessionHandler-D9kwxy6B.cjs} +430 -12
  22. package/dist/{runClaude-BuI6OOEv.cjs → runClaude-D2ZEXue8.cjs} +11 -9
  23. package/dist/{runClaude-D0DD_Ya5.mjs → runClaude-DpZ95Twb.mjs} +8 -6
  24. package/dist/{runCodex-BzZ0jODI.mjs → runCodex-CJwaep2R.mjs} +9 -7
  25. package/dist/{runCodex-1jTTmCvq.cjs → runCodex-Dz_1ho8d.cjs} +12 -10
  26. package/dist/{runGemini-Bx2SYAyG.mjs → runGemini-BehqjM73.mjs} +192 -71
  27. package/dist/{runGemini-1gJRE8oT.cjs → runGemini-Dfu6LltX.cjs} +192 -71
  28. package/package.json +1 -1
  29. package/scripts/build.mjs +66 -66
  30. package/scripts/devtools/README.md +9 -9
  31. package/scripts/e2e/fake-codex-acp-agent.mjs +139 -139
  32. package/scripts/e2e/local-server-session-roundtrip.mjs +1063 -1063
  33. package/scripts/release-smoke.mjs +3 -0
  34. package/dist/ProviderSelectionHandler-BjLyIfSR.mjs +0 -673
  35. package/dist/ProviderSelectionHandler-e4zL4Y5_.cjs +0 -680
@@ -3,10 +3,10 @@
3
3
  var ink = require('ink');
4
4
  var React = require('react');
5
5
  var node_crypto = require('node:crypto');
6
- var api = require('./api-DP-RQUao.cjs');
7
- var registerKillSessionHandler = require('./registerKillSessionHandler-QmBN446A.cjs');
8
- var index = require('./index-CqCEZDFi.cjs');
9
- var BaseReasoningProcessor = require('./BaseReasoningProcessor-CJVv1aNR.cjs');
6
+ var api = require('./api-Bd-MnOS4.cjs');
7
+ var registerKillSessionHandler = require('./registerKillSessionHandler-D9kwxy6B.cjs');
8
+ var index = require('./index-BQmJ4NAa.cjs');
9
+ var BaseReasoningProcessor = require('./BaseReasoningProcessor-C9mH8EVn.cjs');
10
10
  require('cross-spawn');
11
11
  require('@agentclientprotocol/sdk');
12
12
  require('ps-list');
@@ -17,7 +17,7 @@ require('node:child_process');
17
17
  require('node:readline');
18
18
  require('tweetnacl');
19
19
  require('axios');
20
- require('./persistence-yVTbf_Ng.cjs');
20
+ require('./persistence-BL06LLVz.cjs');
21
21
  require('open');
22
22
  require('chalk');
23
23
  require('fs');
@@ -432,6 +432,93 @@ class ConversationHistory extends registerKillSessionHandler.ConversationHistory
432
432
  }
433
433
  }
434
434
 
435
+ const GEMINI_PERMISSION_MODES = [
436
+ "default",
437
+ "read-only",
438
+ "safe-yolo",
439
+ "yolo"
440
+ ];
441
+ function isGeminiPermissionMode(value) {
442
+ return typeof value === "string" && GEMINI_PERMISSION_MODES.includes(value);
443
+ }
444
+ function hasMetaOverride(message, key) {
445
+ return Object.prototype.hasOwnProperty.call(message.meta ?? {}, key);
446
+ }
447
+ function resolveGeminiQueuedMessage(message, currentState) {
448
+ const previousState = { ...currentState };
449
+ const currentPermissionMode = isGeminiPermissionMode(currentState.permissionMode) ? currentState.permissionMode : void 0;
450
+ let permissionMode = currentPermissionMode;
451
+ let invalidPermissionMode = null;
452
+ if (hasMetaOverride(message, "permissionMode")) {
453
+ if (isGeminiPermissionMode(message.meta?.permissionMode)) {
454
+ permissionMode = message.meta.permissionMode;
455
+ } else if (message.meta?.permissionMode) {
456
+ invalidPermissionMode = String(message.meta.permissionMode);
457
+ }
458
+ }
459
+ const hasModelOverride = hasMetaOverride(message, "model");
460
+ const model = hasModelOverride ? message.meta?.model || void 0 : currentState.model;
461
+ const originalUserMessage = message.content.text;
462
+ let queuedText = originalUserMessage;
463
+ const isFirstMessage = currentState.isFirstMessage ?? true;
464
+ let nextIsFirstMessage = isFirstMessage;
465
+ if (isFirstMessage && message.meta?.appendSystemPrompt) {
466
+ queuedText = `${message.meta.appendSystemPrompt}
467
+
468
+ ${originalUserMessage}`;
469
+ nextIsFirstMessage = false;
470
+ }
471
+ const nextState = {
472
+ permissionMode: permissionMode ?? "default",
473
+ model,
474
+ isFirstMessage: nextIsFirstMessage
475
+ };
476
+ return {
477
+ previousState,
478
+ nextState,
479
+ invalidPermissionMode,
480
+ hasModelOverride,
481
+ queuedMessage: {
482
+ text: queuedText,
483
+ mode: {
484
+ permissionMode: nextState.permissionMode ?? "default",
485
+ model,
486
+ originalUserMessage
487
+ }
488
+ }
489
+ };
490
+ }
491
+ function bindGeminiUserMessageQueue(opts) {
492
+ let currentState = { ...opts.initialState };
493
+ opts.session.onUserMessage((message) => {
494
+ const happyOrgResult = opts.happyOrg ? registerKillSessionHandler.resolveHappyOrgQueuedTurn({
495
+ metadata: opts.happyOrg.getMetadata(),
496
+ message
497
+ }) : {
498
+ nextMetadata: null,
499
+ queuedTurn: null,
500
+ blocked: false,
501
+ statusMessage: void 0
502
+ };
503
+ if (opts.happyOrg && happyOrgResult.nextMetadata) {
504
+ opts.happyOrg.updateMetadata(() => happyOrgResult.nextMetadata);
505
+ }
506
+ if (happyOrgResult.blocked) {
507
+ if (happyOrgResult.statusMessage) {
508
+ opts.happyOrg?.emitStatusMessage(happyOrgResult.statusMessage);
509
+ }
510
+ api.logger.debugLargeJson("[gemini] User message blocked by Happy Org runtime:", message);
511
+ return;
512
+ }
513
+ const resolution = resolveGeminiQueuedMessage(message, currentState);
514
+ currentState = resolution.nextState;
515
+ resolution.queuedMessage.mode.happyOrg = happyOrgResult.queuedTurn;
516
+ opts.onResolvedMessage?.(resolution);
517
+ opts.messageQueue.push(resolution.queuedMessage.text, resolution.queuedMessage.mode);
518
+ api.logger.debugLargeJson("[gemini] User message queued:", message);
519
+ });
520
+ }
521
+
435
522
  async function runGemini(opts) {
436
523
  const sessionTag = node_crypto.randomUUID();
437
524
  api.connectionState.setBackend("Gemini");
@@ -513,67 +600,21 @@ async function runGemini(opts) {
513
600
  await registerKillSessionHandler.syncControlledByUserState(session, false);
514
601
  const messageQueue = new registerKillSessionHandler.MessageQueue2((mode) => registerKillSessionHandler.hashObject({
515
602
  permissionMode: mode.permissionMode,
516
- model: mode.model
603
+ model: mode.model,
604
+ happyOrg: mode.happyOrg ? {
605
+ taskId: mode.happyOrg.context.taskId,
606
+ organizationId: mode.happyOrg.context.organizationId,
607
+ memberAgentId: mode.happyOrg.context.memberAgentId,
608
+ supervisorAgentId: mode.happyOrg.context.supervisorAgentId,
609
+ reopenContext: mode.happyOrg.reopenContext ?? null
610
+ } : null
517
611
  }));
518
612
  const conversationHistory = new ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
519
- let currentPermissionMode = void 0;
520
- let currentModel = void 0;
521
- session.onUserMessage((message) => {
522
- let messagePermissionMode = currentPermissionMode;
523
- if (message.meta?.permissionMode) {
524
- const validModes = ["default", "read-only", "safe-yolo", "yolo"];
525
- if (validModes.includes(message.meta.permissionMode)) {
526
- messagePermissionMode = message.meta.permissionMode;
527
- currentPermissionMode = messagePermissionMode;
528
- updatePermissionMode(messagePermissionMode);
529
- api.logger.debug(`[Gemini] Permission mode updated from user message to: ${currentPermissionMode}`);
530
- } else {
531
- api.logger.debug(`[Gemini] Invalid permission mode received: ${message.meta.permissionMode}`);
532
- }
533
- } else {
534
- api.logger.debug(`[Gemini] User message received with no permission mode override, using current: ${currentPermissionMode ?? "default (effective)"}`);
535
- }
536
- if (currentPermissionMode === void 0) {
537
- currentPermissionMode = "default";
538
- updatePermissionMode("default");
539
- }
540
- let messageModel = currentModel;
541
- if (message.meta?.hasOwnProperty("model")) {
542
- if (message.meta.model === null) {
543
- messageModel = void 0;
544
- currentModel = void 0;
545
- } else if (message.meta.model) {
546
- const previousModel = currentModel;
547
- messageModel = message.meta.model;
548
- currentModel = messageModel;
549
- if (previousModel !== messageModel) {
550
- updateDisplayedModel(messageModel, true);
551
- messageBuffer.addMessage(`Model changed to: ${messageModel}`, "system");
552
- api.logger.debug(`[Gemini] Model changed from ${previousModel} to ${messageModel}`);
553
- }
554
- }
555
- }
556
- const originalUserMessage = message.content.text;
557
- let fullPrompt = originalUserMessage;
558
- if (isFirstMessage && message.meta?.appendSystemPrompt) {
559
- fullPrompt = message.meta.appendSystemPrompt + "\n\n" + originalUserMessage;
560
- isFirstMessage = false;
561
- }
562
- const mode = {
563
- permissionMode: messagePermissionMode || "default",
564
- model: messageModel,
565
- originalUserMessage
566
- // Store original message separately
567
- };
568
- messageQueue.push(fullPrompt, mode);
569
- conversationHistory.addUserMessage(originalUserMessage);
570
- });
571
613
  let thinking = false;
572
614
  session.keepAlive(thinking, "remote");
573
615
  const keepAliveInterval = setInterval(() => {
574
616
  session.keepAlive(thinking, "remote");
575
617
  }, 2e3);
576
- let isFirstMessage = true;
577
618
  const sendReady = () => {
578
619
  session.sendSessionEvent({ type: "ready" });
579
620
  try {
@@ -606,12 +647,18 @@ async function runGemini(opts) {
606
647
  let shouldExit = false;
607
648
  let runtimeHandle = null;
608
649
  let unsubscribeRuntimeMessages = null;
650
+ let handleQueuedUserMessage = null;
651
+ let emitGeminiStatusMessage = null;
652
+ let turnAbortedSent = false;
609
653
  async function handleAbort() {
610
654
  api.logger.debug("[Gemini] Abort requested - stopping current task");
611
- session.sendAgentMessage("gemini", {
612
- type: "turn_aborted",
613
- id: node_crypto.randomUUID()
614
- });
655
+ if (!turnAbortedSent) {
656
+ session.sendAgentMessage("gemini", {
657
+ type: "turn_aborted",
658
+ id: node_crypto.randomUUID()
659
+ });
660
+ turnAbortedSent = true;
661
+ }
615
662
  reasoningProcessor.abort();
616
663
  diffProcessor.reset();
617
664
  try {
@@ -672,6 +719,10 @@ async function runGemini(opts) {
672
719
  api.logger.debug(`[gemini] Model unchanged, skipping update message`);
673
720
  }
674
721
  };
722
+ emitGeminiStatusMessage = (message) => {
723
+ messageBuffer.addMessage(message, "status");
724
+ session.sendSessionEvent({ type: "message", message });
725
+ };
675
726
  if (hasTTY) {
676
727
  console.clear();
677
728
  const DisplayComponent = () => {
@@ -712,6 +763,41 @@ async function runGemini(opts) {
712
763
  const updatePermissionMode = (mode) => {
713
764
  permissionHandler.setPermissionMode(mode);
714
765
  };
766
+ handleQueuedUserMessage = ({ previousState, nextState, invalidPermissionMode, hasModelOverride }) => {
767
+ if (invalidPermissionMode) {
768
+ api.logger.debug(`[Gemini] Invalid permission mode received: ${invalidPermissionMode}`);
769
+ } else if (nextState.permissionMode) {
770
+ api.logger.debug(`[Gemini] Permission mode resolved to: ${nextState.permissionMode}`);
771
+ }
772
+ updatePermissionMode(nextState.permissionMode ?? "default");
773
+ if (hasModelOverride && nextState.model && previousState.model !== nextState.model) {
774
+ updateDisplayedModel(nextState.model, true);
775
+ messageBuffer.addMessage(`Model changed to: ${nextState.model}`, "system");
776
+ api.logger.debug(`[Gemini] Model changed from ${previousState.model} to ${nextState.model}`);
777
+ }
778
+ };
779
+ bindGeminiUserMessageQueue({
780
+ session,
781
+ messageQueue,
782
+ initialState: {},
783
+ happyOrg: {
784
+ getMetadata: () => session.getMetadataSnapshot?.() ?? metadata,
785
+ updateMetadata: (handler) => session.updateMetadata(handler),
786
+ emitStatusMessage: (message) => {
787
+ emitGeminiStatusMessage?.(message);
788
+ }
789
+ },
790
+ onResolvedMessage: (resolution) => {
791
+ handleQueuedUserMessage?.(resolution);
792
+ }
793
+ });
794
+ const emitTurnReport = (report) => {
795
+ session.sendAgentMessage("gemini", {
796
+ type: "turn-report",
797
+ id: node_crypto.randomUUID(),
798
+ report
799
+ });
800
+ };
715
801
  let accumulatedResponse = "";
716
802
  let isResponseInProgress = false;
717
803
  let hadToolCallInTurn = false;
@@ -759,10 +845,13 @@ async function runGemini(opts) {
759
845
  api.logger.debug(`[gemini] Status changed: ${msg.status}${statusDetail ? ` - ${statusDetail}` : ""}`);
760
846
  if (msg.status === "error") {
761
847
  api.logger.debug(`[gemini] \u26A0\uFE0F Error status received: ${statusDetail || "Unknown error"}`);
762
- session.sendAgentMessage("gemini", {
763
- type: "turn_aborted",
764
- id: node_crypto.randomUUID()
765
- });
848
+ if (!turnAbortedSent) {
849
+ session.sendAgentMessage("gemini", {
850
+ type: "turn_aborted",
851
+ id: node_crypto.randomUUID()
852
+ });
853
+ turnAbortedSent = true;
854
+ }
766
855
  }
767
856
  if (msg.status === "running") {
768
857
  thinking = true;
@@ -987,6 +1076,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
987
1076
  const userMessageToShow = message.mode?.originalUserMessage || message.message;
988
1077
  messageBuffer.addMessage(userMessageToShow, "user");
989
1078
  isProcessingMessage = true;
1079
+ let turnStatus = "task_complete";
990
1080
  try {
991
1081
  let activeHandle = runtimeHandle;
992
1082
  if (!activeHandle) {
@@ -1007,12 +1097,17 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1007
1097
  isResponseInProgress = false;
1008
1098
  hadToolCallInTurn = false;
1009
1099
  taskStartedSent = false;
1100
+ turnAbortedSent = false;
1010
1101
  let promptToSend = message.message;
1011
1102
  if (injectHistoryContext && conversationHistory.hasHistory()) {
1012
1103
  const historyContext = conversationHistory.getContextForNewSession();
1013
1104
  promptToSend = historyContext + promptToSend;
1014
1105
  api.logger.debug(`[gemini] Injected conversation history context (${historyContext.length} chars)`);
1015
1106
  }
1107
+ if (message.mode.happyOrg) {
1108
+ promptToSend = registerKillSessionHandler.buildHappyOrgTurnPrompt(promptToSend, message.mode.happyOrg);
1109
+ }
1110
+ conversationHistory.addUserMessage(userMessageToShow);
1016
1111
  api.logger.debug(`[gemini] Sending prompt to Gemini (length: ${promptToSend.length}): ${promptToSend.substring(0, 100)}...`);
1017
1112
  api.logger.debug(`[gemini] Full prompt: ${promptToSend}`);
1018
1113
  const MAX_RETRIES = 3;
@@ -1022,7 +1117,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1022
1117
  try {
1023
1118
  await activeHandle.sendPrompt(promptToSend);
1024
1119
  api.logger.debug("[gemini] Prompt sent successfully");
1025
- await registerKillSessionHandler.waitForResponseCompleteWithAbort(activeHandle.backend, abortController.signal, 12e4);
1120
+ await registerKillSessionHandler.waitForResponseCompleteWithAbort(activeHandle.backend, abortController.signal, 10 * 6e4);
1026
1121
  api.logger.debug("[gemini] Response complete");
1027
1122
  break;
1028
1123
  } catch (promptError) {
@@ -1061,6 +1156,14 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1061
1156
  } catch (error) {
1062
1157
  api.logger.debug("[gemini] Error in gemini session:", error);
1063
1158
  const isAbortError = error instanceof Error && error.name === "AbortError";
1159
+ turnStatus = "turn_aborted";
1160
+ if (!turnAbortedSent) {
1161
+ session.sendAgentMessage("gemini", {
1162
+ type: "turn_aborted",
1163
+ id: node_crypto.randomUUID()
1164
+ });
1165
+ turnAbortedSent = true;
1166
+ }
1064
1167
  if (isAbortError) {
1065
1168
  messageBuffer.addMessage("Aborted by user", "status");
1066
1169
  session.sendSessionEvent({ type: "message", message: "Aborted by user" });
@@ -1073,8 +1176,8 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1073
1176
  const errorMessage = errObj.message || errObj.error?.message || "";
1074
1177
  const errorString = String(error);
1075
1178
  if (errorCode === 404 || errorDetails.includes("notFound") || errorDetails.includes("404") || errorMessage.includes("not found") || errorMessage.includes("404")) {
1076
- const currentModel2 = displayedModel || "gemini-2.5-pro";
1077
- errorMsg = `Model "${currentModel2}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
1179
+ const currentModel = displayedModel || "gemini-2.5-pro";
1180
+ errorMsg = `Model "${currentModel}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
1078
1181
  } else if (errorCode === -32603 || errorDetails.includes("empty response") || errorDetails.includes("Model stream ended")) {
1079
1182
  errorMsg = "Gemini API returned empty response after retries. This is a temporary issue - please try again.";
1080
1183
  } else if (errorCode === 429 || errorDetails.includes("429") || errorMessage.includes("429") || errorString.includes("429") || errorDetails.includes("rateLimitExceeded") || errorDetails.includes("RESOURCE_EXHAUSTED") || errorMessage.includes("Rate limit exceeded") || errorMessage.includes("Resource exhausted") || errorString.includes("rateLimitExceeded") || errorString.includes("RESOURCE_EXHAUSTED")) {
@@ -1110,6 +1213,22 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1110
1213
  permissionHandler.reset();
1111
1214
  reasoningProcessor.abort();
1112
1215
  diffProcessor.reset();
1216
+ const finalizedTurn = registerKillSessionHandler.finalizeHappyOrgTurn({
1217
+ metadata: session.getMetadataSnapshot?.() ?? null,
1218
+ queuedTurn: message.mode.happyOrg,
1219
+ responseText: accumulatedResponse,
1220
+ turnStatus
1221
+ });
1222
+ if (finalizedTurn.nextMetadata && typeof session.updateMetadata === "function") {
1223
+ session.updateMetadata(() => finalizedTurn.nextMetadata);
1224
+ }
1225
+ if (finalizedTurn.report) {
1226
+ emitTurnReport(finalizedTurn.report);
1227
+ }
1228
+ if (finalizedTurn.terminateMessage) {
1229
+ emitGeminiStatusMessage?.(finalizedTurn.terminateMessage);
1230
+ }
1231
+ accumulatedResponse = finalizedTurn.cleanedText;
1113
1232
  if (accumulatedResponse.trim()) {
1114
1233
  const { text: messageText, options } = parseOptionsFromText(accumulatedResponse);
1115
1234
  conversationHistory.addAssistantMessage(messageText);
@@ -1153,7 +1272,9 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1153
1272
  reconnectionHandle.cancel();
1154
1273
  }
1155
1274
  try {
1156
- await registerKillSessionHandler.closeProviderSession(session);
1275
+ await registerKillSessionHandler.closeProviderSession(session, {
1276
+ archiveOnClose: true
1277
+ });
1157
1278
  } catch (e) {
1158
1279
  api.logger.debug("[gemini]: Error while closing session", e);
1159
1280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-imou-cloud",
3
- "version": "2.0.22",
3
+ "version": "2.1.0",
4
4
  "description": "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI",
5
5
  "author": "long.zhu",
6
6
  "license": "MIT",
package/scripts/build.mjs CHANGED
@@ -1,66 +1,66 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawnSync } from 'node:child_process';
4
- import { existsSync, readFileSync, rmSync } from 'node:fs';
5
- import { dirname, resolve } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
-
8
- const here = dirname(fileURLToPath(import.meta.url));
9
- const packageRoot = resolve(here, '..');
10
- const workspaceRoot = resolve(packageRoot, '..', '..');
11
- const packageJson = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
12
-
13
- function resolveTool(binName, packageSpecs) {
14
- const suffix = process.platform === 'win32' ? '.cmd' : '';
15
- const packageLocalBin = resolve(packageRoot, 'node_modules', '.bin', `${binName}${suffix}`);
16
- const workspaceLocalBin = resolve(workspaceRoot, 'node_modules', '.bin', `${binName}${suffix}`);
17
-
18
- if (existsSync(packageLocalBin)) {
19
- return {
20
- command: packageLocalBin,
21
- prefixArgs: [],
22
- };
23
- }
24
-
25
- if (existsSync(workspaceLocalBin)) {
26
- return {
27
- command: workspaceLocalBin,
28
- prefixArgs: [],
29
- };
30
- }
31
-
32
- return {
33
- command: 'npx',
34
- prefixArgs: ['-y', ...packageSpecs.flatMap((packageSpec) => ['-p', packageSpec]), binName],
35
- };
36
- }
37
-
38
- function runStep(name, command, args) {
39
- console.log(`\n[build] ${name}`);
40
- console.log(`> ${command} ${args.join(' ')}`);
41
-
42
- const result = spawnSync(command, args, {
43
- cwd: packageRoot,
44
- stdio: 'inherit',
45
- shell: process.platform === 'win32',
46
- env: process.env,
47
- });
48
-
49
- if (result.status !== 0) {
50
- throw new Error(`[build] Step failed: ${name}`);
51
- }
52
- }
53
-
54
- function runTool(name, tool, args) {
55
- runStep(name, tool.command, [...tool.prefixArgs, ...args]);
56
- }
57
-
58
- const tsc = resolveTool('tsc', [`typescript@${packageJson.devDependencies.typescript}`]);
59
- const pkgroll = resolveTool('pkgroll', [
60
- `pkgroll@${packageJson.devDependencies.pkgroll}`,
61
- `typescript@${packageJson.devDependencies.typescript}`,
62
- ]);
63
-
64
- rmSync(resolve(packageRoot, 'dist'), { recursive: true, force: true });
65
- runTool('typecheck', tsc, ['--noEmit']);
66
- runTool('bundle', pkgroll, []);
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const here = dirname(fileURLToPath(import.meta.url));
9
+ const packageRoot = resolve(here, '..');
10
+ const workspaceRoot = resolve(packageRoot, '..', '..');
11
+ const packageJson = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
12
+
13
+ function resolveTool(binName, packageSpecs) {
14
+ const suffix = process.platform === 'win32' ? '.cmd' : '';
15
+ const packageLocalBin = resolve(packageRoot, 'node_modules', '.bin', `${binName}${suffix}`);
16
+ const workspaceLocalBin = resolve(workspaceRoot, 'node_modules', '.bin', `${binName}${suffix}`);
17
+
18
+ if (existsSync(packageLocalBin)) {
19
+ return {
20
+ command: packageLocalBin,
21
+ prefixArgs: [],
22
+ };
23
+ }
24
+
25
+ if (existsSync(workspaceLocalBin)) {
26
+ return {
27
+ command: workspaceLocalBin,
28
+ prefixArgs: [],
29
+ };
30
+ }
31
+
32
+ return {
33
+ command: 'npx',
34
+ prefixArgs: ['-y', ...packageSpecs.flatMap((packageSpec) => ['-p', packageSpec]), binName],
35
+ };
36
+ }
37
+
38
+ function runStep(name, command, args) {
39
+ console.log(`\n[build] ${name}`);
40
+ console.log(`> ${command} ${args.join(' ')}`);
41
+
42
+ const result = spawnSync(command, args, {
43
+ cwd: packageRoot,
44
+ stdio: 'inherit',
45
+ shell: process.platform === 'win32',
46
+ env: process.env,
47
+ });
48
+
49
+ if (result.status !== 0) {
50
+ throw new Error(`[build] Step failed: ${name}`);
51
+ }
52
+ }
53
+
54
+ function runTool(name, tool, args) {
55
+ runStep(name, tool.command, [...tool.prefixArgs, ...args]);
56
+ }
57
+
58
+ const tsc = resolveTool('tsc', [`typescript@${packageJson.devDependencies.typescript}`]);
59
+ const pkgroll = resolveTool('pkgroll', [
60
+ `pkgroll@${packageJson.devDependencies.pkgroll}`,
61
+ `typescript@${packageJson.devDependencies.typescript}`,
62
+ ]);
63
+
64
+ rmSync(resolve(packageRoot, 'dist'), { recursive: true, force: true });
65
+ runTool('typecheck', tsc, ['--noEmit']);
66
+ runTool('bundle', pkgroll, []);
@@ -1,9 +1,9 @@
1
- # CLI Devtools
2
-
3
- 这组脚本用于 CLI 本地开发辅助。
4
-
5
- 当前包含:
6
-
7
- - mock credentials 生成器
8
-
9
- 新增 CLI 专属开发脚本应优先放在这里。
1
+ # CLI Devtools
2
+
3
+ 这组脚本用于 CLI 本地开发辅助。
4
+
5
+ 当前包含:
6
+
7
+ - mock credentials 生成器
8
+
9
+ 新增 CLI 专属开发脚本应优先放在这里。