nolo-cli 0.100.0-alpha.1 → 0.100.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.
- package/README.md +1 -1
- package/{_executeModel-UM3TAOM3.js → _executeModel-WLFACVFR.js} +74 -67
- package/action-PNPQLJHG.js +20 -0
- package/{addContentAction-O7R66HEX.js → addContentAction-RBOUD7SJ.js} +12 -11
- package/{addDialogAgentAction-74OA7KYP.js → addDialogAgentAction-IODCR6TB.js} +18 -19
- package/{agentDeleteCommand-2YAHAZFJ.js → agentDeleteCommand-EFWZSSZA.js} +47 -42
- package/{agentEmailCommands-IYEMKJR3.js → agentEmailCommands-XQMUAB4R.js} +97 -87
- package/agentListCommands-EDV4RJ4D.js +742 -0
- package/{agentMachineCommands-E3IKDCBZ.js → agentMachineCommands-UOEI4TPN.js} +48 -43
- package/{agentProviderCommands-GNPTL5XR.js → agentProviderCommands-IRNZXVGE.js} +1 -1
- package/{agentPullCommand-O5MPVJ5E.js → agentPullCommand-B4T2DJ5H.js} +1 -1
- package/agentRecordCommands-BIV7NMQZ.js +138 -0
- package/agentRunCommand-SY7N2BDL.js +167 -0
- package/{agentRunControl-AVJR4JW4.js → agentRunControl-3HWA2LLQ.js} +2 -2
- package/{agentRuntimeLocal-VXZSDIGT.js → agentRuntimeLocal-SXYAKX3A.js} +48 -39
- package/{agentSlice-B7KLSPJV.js → agentSlice-WBSSBKBB.js} +14 -13
- package/{appDeleteCommands-32MHLR4L.js → appDeleteCommands-GTTBG53T.js} +46 -41
- package/{appDeployCommands-WCQOUSOB.js → appDeployCommands-ZZCHUJAT.js} +46 -41
- package/{appDeployStatusCommands-6QKB7DYN.js → appDeployStatusCommands-GZLLGY7Z.js} +46 -41
- package/{appGetCommands-SFAPP5PB.js → appGetCommands-ZRLWHJ3C.js} +46 -41
- package/{appListCommands-EKUBDRUO.js → appListCommands-OHARWLIL.js} +46 -41
- package/{authCommand-F4EFRRX6.js → authCommand-NHDLY3IB.js} +118 -24
- package/{authCommands-RGOGCPP6.js → authCommands-UDBJVSZU.js} +7 -5
- package/{builtinSkillRegistry-FUAVTRVZ.js → builtinSkillRegistry-MAR5LPAC.js} +10 -8
- package/{chromeCommands-FZYAU75D.js → chromeCommands-K27J4OVZ.js} +124 -28
- package/{chromeOpsCommands-62P2Q2YB.js → chromeOpsCommands-IXRLN57H.js} +4 -3
- package/{chunk-6OZ36FXI.js → chunk-27ONFIEY.js} +22 -4
- package/{chunk-JEDZAO7L.js → chunk-2SYTUTLI.js} +257 -37
- package/{chunk-53HEEOTT.js → chunk-2WJ57RJA.js} +83 -16
- package/{chunk-OIVSQH2O.js → chunk-3BXNTXYI.js} +299 -5
- package/{chunk-ABQYVJBJ.js → chunk-46Y5D2BZ.js} +5 -5
- package/{chunk-EYAQT56M.js → chunk-4DPP773Q.js} +2 -2
- package/{chunk-H62HCIUP.js → chunk-4FFUDT6D.js} +24 -21
- package/chunk-4LCOCOR5.js +50 -0
- package/{chunk-2GRGFCF6.js → chunk-4VJAC32I.js} +2 -1
- package/{chunk-IIVGDG4I.js → chunk-4YSOUKUR.js} +13 -7
- package/{chunk-MC4XLIJ7.js → chunk-523DITKR.js} +1 -1
- package/{chunk-E4MARS3Q.js → chunk-5GVBDNTO.js} +1 -1
- package/chunk-5J3PYNUP.js +255 -0
- package/{chunk-UIZCMBT3.js → chunk-5LTAPGPV.js} +10 -10
- package/chunk-5MMAK75Q.js +15 -0
- package/{chunk-JPGCXHNM.js → chunk-5OA5HMTL.js} +1 -1
- package/{chunk-42V6BK2E.js → chunk-62IL3KQV.js} +4 -34
- package/{chunk-3BMYXEUW.js → chunk-6O4IK4V2.js} +1 -1
- package/{chunk-GQBOR3G6.js → chunk-7KVZ5LDQ.js} +2 -2
- package/{chunk-DNCEALKX.js → chunk-7MMSLEPA.js} +22 -29
- package/{chunk-L5UXH3B2.js → chunk-7TKWCTSY.js} +22 -6
- package/chunk-A4SQRTZ6.js +15 -0
- package/{chunk-7S4YWN7P.js → chunk-ADBZWMFB.js} +4 -4
- package/chunk-AGIM7IMJ.js +147 -0
- package/{chunk-5UTFL4QJ.js → chunk-AOTBKDRK.js} +7 -0
- package/{chunk-Q554LLSI.js → chunk-AUDNYVRC.js} +1 -1
- package/chunk-BMQ2MJUK.js +153 -0
- package/{chunk-6YTOJY35.js → chunk-BZ7NPA6G.js} +35 -2
- package/{chunk-KFFZBS5O.js → chunk-CHNCM7EF.js} +11 -9
- package/chunk-CHOZJMJS.js +78 -0
- package/chunk-CJN5OERD.js +13545 -0
- package/{chunk-ACTMNVHM.js → chunk-CYSYR67N.js} +1 -1
- package/{chunk-GBRG75BQ.js → chunk-D2MDMY2M.js} +1 -0
- package/{chunk-MYUU2OIZ.js → chunk-DNHENQ3T.js} +23 -28
- package/chunk-DZ7R5V3Y.js +164 -0
- package/{chunk-3VNWWVKB.js → chunk-E3YWHWCR.js} +50 -18
- package/{chunk-V4MJDUSR.js → chunk-E73ON47R.js} +2 -2
- package/{chunk-KQ4ONNQD.js → chunk-EKPKW6RM.js} +155 -8
- package/{chunk-TRV2MIDW.js → chunk-EXAJ6DCR.js} +1 -0
- package/{chunk-FUANZCER.js → chunk-F3NUFVSP.js} +24 -5
- package/{chunk-HJUYAD4A.js → chunk-FJ5BGMNM.js} +5 -5
- package/{chunk-2F7RJC6L.js → chunk-FOXQ7BAB.js} +1 -1
- package/{chunk-6IG4XSS6.js → chunk-FY7DCYW7.js} +88 -17
- package/{chunk-6VFARHZW.js → chunk-G53AA7RW.js} +934 -1091
- package/{chunk-J4MOO56S.js → chunk-GARACBQB.js} +10 -2
- package/chunk-GMZLP5DV.js +82 -0
- package/{chunk-NA2YVRDF.js → chunk-GUYEMTRK.js} +551 -516
- package/{chunk-FDTR4QJ6.js → chunk-H6A6X4EK.js} +32 -1
- package/{chunk-DVINR3PL.js → chunk-HDLLCBRF.js} +1 -1
- package/{chunk-QOKAKDPE.js → chunk-HIM3FCLF.js} +0 -1
- package/{chunk-ONXOABJR.js → chunk-HNGRBWYF.js} +3 -0
- package/{chunk-VLTIYV3X.js → chunk-HP57OUJO.js} +4 -4
- package/{chunk-GPCZ7UXG.js → chunk-K6U4NKFJ.js} +1 -1
- package/{chunk-A2G4JCCZ.js → chunk-KLUBZL6X.js} +1 -1
- package/{chunk-4OEFYTWL.js → chunk-LKERWYPX.js} +67 -48
- package/{chunk-OEVAY4J6.js → chunk-LNVJHIGA.js} +18 -20
- package/{chunk-LS2ZANRJ.js → chunk-LOCNXNOU.js} +1 -1
- package/{chunk-PUOC4YRO.js → chunk-M7VLQGQP.js} +11 -0
- package/{chunk-BFU6C242.js → chunk-NMFV26UG.js} +1 -1
- package/{chunk-FZGV4FGK.js → chunk-NUHHBEJC.js} +378 -187
- package/chunk-NWENLDMM.js +42 -0
- package/{chunk-AEOE4Z6Q.js → chunk-NZWAO6LH.js} +13 -15
- package/chunk-OCZKATRC.js +129 -0
- package/{chunk-Q7UVOSGX.js → chunk-ODTDLPBV.js} +20 -11
- package/chunk-OW4APERY.js +870 -0
- package/{chunk-KQHGZWSV.js → chunk-OXME3PEE.js} +1 -1
- package/{chunk-EKXNDUWE.js → chunk-OYPMWCIQ.js} +67 -45
- package/{chunk-TOP2VXOR.js → chunk-PCF3QU5Q.js} +5 -5
- package/chunk-PH275YDS.js +7881 -0
- package/{chunk-WV42OMV7.js → chunk-PTDYOADJ.js} +8 -8
- package/{chunk-5H7KJX7J.js → chunk-SEB5HAUU.js} +5 -5
- package/{chunk-6D6VBVEW.js → chunk-SNMQMOD2.js} +80 -14
- package/{chunk-BYMV6QZG.js → chunk-TNIX77YM.js} +4 -0
- package/{chunk-Z77CRGTW.js → chunk-TVCAR26G.js} +1 -1
- package/chunk-TY3TIUJK.js +89 -0
- package/{chunk-I3TQJXQ7.js → chunk-U3C3IA32.js} +9 -2
- package/{chunk-JGLUFHZG.js → chunk-U6IGAJT4.js} +47 -67
- package/{chunk-RG6JBBX5.js → chunk-U7XXEI5R.js} +166 -13
- package/{chunk-UZGQNTVW.js → chunk-UA324D4O.js} +8 -6
- package/chunk-UJXNKP5V.js +760 -0
- package/{chunk-UKZDAFYT.js → chunk-UM4PH574.js} +6 -10
- package/{chunk-GEPUP7OO.js → chunk-UVKDJUTL.js} +3 -3
- package/{chunk-ZCYXJSTU.js → chunk-V5OWQOJZ.js} +19 -88
- package/{chunk-NA35QHCF.js → chunk-V7Y6KRAH.js} +1 -1
- package/{chunk-3DII57IT.js → chunk-VCAQFMVN.js} +1 -1
- package/{chunk-JRQFUXY7.js → chunk-VEYL5MO5.js} +99 -22
- package/chunk-VIWDGTDA.js +8 -0
- package/chunk-VJVNU77E.js +83 -0
- package/{chunk-UKWGR5SS.js → chunk-VY6VFGKR.js} +194 -1144
- package/{chunk-BP4XNZRM.js → chunk-W72ISAKC.js} +4 -4
- package/{chunk-DHDMC6FO.js → chunk-WCGC6S77.js} +14 -9
- package/{chunk-QDSINZM3.js → chunk-WJ3B6FX6.js} +1 -14
- package/{chunk-GTXHENTD.js → chunk-X34KOYU2.js} +873 -227
- package/{chunk-HVURBGF3.js → chunk-XFUBXRVI.js} +6 -6
- package/{chunk-3QWE3CFY.js → chunk-XIRXOUBV.js} +19 -17
- package/{chunk-O6OSWFZQ.js → chunk-XUWRURDF.js} +6 -0
- package/{chunk-Q67DOFOO.js → chunk-Y3PMXGLH.js} +2 -0
- package/{chunk-ETI2NYVF.js → chunk-YPOPJAKN.js} +4 -4
- package/{chunk-SH43IESU.js → chunk-YUNJN43V.js} +44 -9
- package/chunk-YV25TM3R.js +780 -0
- package/{chunk-X4N6F4VS.js → chunk-Z56IK6UT.js} +29 -78
- package/chunk-ZRAU6T47.js +56 -0
- package/commandRegistry-NKJIYWXB.js +833 -0
- package/{contentThunks-TFZIJAUK.js → contentThunks-F2BNWOZV.js} +36 -34
- package/{cooldownCommands-REAWYVTY.js → cooldownCommands-ZLEPIB4A.js} +2 -2
- package/{createAgentAutomationAction-FF3MPCIU.js → createAgentAutomationAction-5N2NRNR7.js} +38 -36
- package/createDialogAction-DSEG4L5Y.js +69 -0
- package/{createPageAction-33ZEWWTG.js → createPageAction-K32FYQPE.js} +45 -42
- package/{chunk-LCX62BV6.js → cursorProvider-YGGNMVWX.js} +2496 -9897
- package/{dbSlice-XVWBAGYN.js → dbSlice-Q25STFGJ.js} +11 -10
- package/{deleteFile-YG6QMJLU.js → deleteFile-AJ2EZMHT.js} +7 -6
- package/{dialogCommands-5J56DFWN.js → dialogCommands-LF7PLYAD.js} +49 -45
- package/{dialogSlice-QDMNWIA5.js → dialogSlice-4GBOUTGL.js} +40 -33
- package/{dist-VODFSGNG.js → dist-NJC2W7I4.js} +61 -54
- package/{docCreateCommands-KYI2DC2U.js → docCreateCommands-NQR3S7A4.js} +48 -43
- package/{docDeleteCommands-67REUSRY.js → docDeleteCommands-V3IAQ2HT.js} +49 -44
- package/{docReadCommands-HOSVSBDJ.js → docReadCommands-GDX4CLSU.js} +47 -42
- package/{docStore-TEB62I7A.js → docStore-ZTYSP3YE.js} +16 -12
- package/{docUpdateCommands-EMGCSMXY.js → docUpdateCommands-QMYXWQOU.js} +48 -43
- package/{fetchMessages-EIU7UFFK.js → fetchMessages-I5ICDDHM.js} +5 -5
- package/{getPricing-LUSEABA6.js → getPricing-NMMFT3YI.js} +4 -4
- package/{handleSendMessageAction-ABLLW47K.js → handleSendMessageAction-XFZH2TQS.js} +110 -86
- package/index.js +87 -884
- package/{lib-FRRS27KN.js → lib-PU2G2MSO.js} +523 -191
- package/{localDialogRead-BTFJ4V2I.js → localDialogRead-ZHZ3KXDM.js} +2 -2
- package/{localRuntimeAuthority-6IR6BBXE.js → localRuntimeAuthority-TU6OBC76.js} +9 -6
- package/{localRuntimeDb-SYQGUF4U.js → localRuntimeDb-VKWVOPPI.js} +1 -1
- package/{machineCommands-IX7TOATI.js → machineCommands-KMU3D5I5.js} +91 -81
- package/machineWsRunDispatch-6GJ5YOSW.js +154 -0
- package/{memberThunks-QGFLFVCI.js → memberThunks-R75S3RWP.js} +20 -18
- package/{models-T3SG6MJ5.js → models-ZPU5EDF4.js} +2 -2
- package/oauthTokenStore-ITEB663N.js +23 -0
- package/package.json +1 -1
- package/pdf-U6KSUEZZ.js +26435 -0
- package/processSpawn-SOAV3FEV.js +11 -0
- package/profileConfig-ZGOFORWV.js +43 -0
- package/{providerPresetApply-FG6NZ57W.js → providerPresetApply-TEBH7FDX.js} +7 -7
- package/{providerRegistry-757OWH3O.js → providerRegistry-CBJLNCET.js} +11 -7
- package/{read-COE3ZYR6.js → read-SRQMNO26.js} +7 -6
- package/{readDocTool-5USN4A6V.js → readDocTool-RC3JUNJ2.js} +5 -3
- package/{readlineWorkspace-KZNVDXAK.js → readlineWorkspace-ZMNRQXMC.js} +2704 -2060
- package/{removeDialogAgentAction-IX3MN2ZO.js → removeDialogAgentAction-46XQV5Q7.js} +21 -25
- package/{runAgentClientLoop-7WPY36JC.js → runAgentClientLoop-URRDZ5WG.js} +75 -69
- package/{runtimeDoctorCommands-X6WDBEQX.js → runtimeDoctorCommands-PIVSBZRZ.js} +4 -4
- package/runtimeModeArgs-2Y7BALTO.js +31 -0
- package/{setDialogExtraReferencesAction-X4HCPNZL.js → setDialogExtraReferencesAction-J4V5ADQF.js} +15 -13
- package/{setPrimaryDialogAgentAction-4GMIQLOD.js → setPrimaryDialogAgentAction-UHHV2QZW.js} +22 -26
- package/{chunk-AFUORTY4.js → settingThunks-PPZZZOWN.js} +226 -940
- package/{skillDiscovery-CYUCJMAG.js → skillDiscovery-ZBU76COE.js} +3 -3
- package/{slateUtils-RIZES3PV.js → slateUtils-NGV2NMKM.js} +1 -1
- package/{spaceCommands-NKJVJCQQ.js → spaceCommands-2P2X37MC.js} +51 -46
- package/{streamAgentChatTurn-PCKWJVSK.js → streamAgentChatTurn-YPUYKXDX.js} +206 -105
- package/{switchDialogAgentAction-N52LRPA3.js → switchDialogAgentAction-A2TMVGAE.js} +18 -19
- package/{tableCommands-O6NWHFKV.js → tableCommands-6U4BMLRR.js} +10 -10
- package/{toolApiClient-QVJ6HDNN.js → toolApiClient-EJT6TMFG.js} +3 -3
- package/tools-M5ENXZRJ.js +116 -0
- package/{tuiSummaryLlmCaller-WFRWERGK.js → tuiSummaryLlmCaller-Q65IBDGO.js} +24 -21
- package/{updateCommands-2M74UL7P.js → updateCommands-4EHDLF5J.js} +3 -3
- package/{updateDialogTitleAction-6QWA2RRC.js → updateDialogTitleAction-7PNUE23W.js} +82 -44
- package/action-YVYQEAAV.js +0 -31
- package/agentListCommands-KMS675GZ.js +0 -406
- package/agentRecordCommands-OSWVOMZE.js +0 -128
- package/agentRunCommand-CE5WBXBP.js +0 -155
- package/browser-image-compression-MFDTD7K6.js +0 -1763
- package/chatQueueReduxAdapter-7ZM7QWC6.js +0 -186
- package/chunk-BZPERCRA.js +0 -19009
- package/chunk-M76ECQRD.js +0 -13658
- package/chunk-MEYIRKDN.js +0 -8967
- package/chunk-Q5FJJ6XI.js +0 -103
- package/chunk-S7Z2HMCN.js +0 -25
- package/chunk-Y5GCAMGP.js +0 -9
- package/chunk-YPREESF4.js +0 -225
- package/createDialogAction-I2TXOWUI.js +0 -65
- package/machineWsRunDispatch-OOZS2DG5.js +0 -144
- package/settingSlice-ZQZVUPKK.js +0 -204
- package/syncMapping-N6RGXGBW.js +0 -437
- package/tools-JU7UHNZ4.js +0 -110
- package/{chunk-VMFTFOWF.js → chunk-3AH7LNUB.js} +7 -7
- package/{chunk-X7HHHIOZ.js → chunk-Y6ROFCZO.js} +0 -0
- package/{db-WK5MDZVN.js → db-VQSAOWAO.js} +1 -1
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
resolvePlatformChatCompletionsEndpoint,
|
|
4
4
|
resolvePlatformHostedCredentialProvider,
|
|
5
5
|
resolvePlatformResponsesEndpoint
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7KVZ5LDQ.js";
|
|
7
7
|
import {
|
|
8
8
|
CLI_PROVIDER_VALUES
|
|
9
9
|
} from "./chunk-W7XHHYWM.js";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
CLIENT_VERSION_TOO_OLD_CODE,
|
|
12
12
|
buildClientVersionGateErrorDetail,
|
|
13
13
|
evaluatePlatformHostedClientVersionGate
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-Y3PMXGLH.js";
|
|
15
15
|
import {
|
|
16
16
|
asTrimmedLowercaseString
|
|
17
17
|
} from "./chunk-QSJ3HFEF.js";
|
|
@@ -164,10 +164,28 @@ function resolveProviderAuthHeaderName(args) {
|
|
|
164
164
|
if (/xiaomimimo\.com/i.test(args.endpoint)) return "api-key";
|
|
165
165
|
return "Authorization";
|
|
166
166
|
}
|
|
167
|
+
var OPENCODE_GO_ENDPOINT_RE = /^https?:\/\/opencode\.ai\/zen\/go\//i;
|
|
168
|
+
var OPENCODE_SESSION_HEADER = "x-opencode-session";
|
|
169
|
+
var OPENCODE_USER_AGENT = "nolo-agent/1.0";
|
|
170
|
+
var PROCESS_FALLBACK_SESSION_ID = `nolo-${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
171
|
+
function isOpenCodeGoEndpoint(endpoint) {
|
|
172
|
+
return typeof endpoint === "string" && OPENCODE_GO_ENDPOINT_RE.test(endpoint.trim());
|
|
173
|
+
}
|
|
174
|
+
function buildProviderSessionHeaders(args) {
|
|
175
|
+
if (!isOpenCodeGoEndpoint(args.endpoint)) return {};
|
|
176
|
+
return {
|
|
177
|
+
[OPENCODE_SESSION_HEADER]: args.sessionId?.trim() || PROCESS_FALLBACK_SESSION_ID,
|
|
178
|
+
"User-Agent": OPENCODE_USER_AGENT
|
|
179
|
+
};
|
|
180
|
+
}
|
|
167
181
|
function buildProviderAuthHeaders(args) {
|
|
168
|
-
|
|
182
|
+
const sessionHeaders = buildProviderSessionHeaders(args);
|
|
183
|
+
if (!args.apiKey) return sessionHeaders;
|
|
169
184
|
const headerName = resolveProviderAuthHeaderName(args);
|
|
170
|
-
return
|
|
185
|
+
return {
|
|
186
|
+
...sessionHeaders,
|
|
187
|
+
...headerName.toLowerCase() === "authorization" ? { Authorization: `Bearer ${args.apiKey}` } : { [headerName]: args.apiKey }
|
|
188
|
+
};
|
|
171
189
|
}
|
|
172
190
|
async function buildProviderExecutionPlan(args) {
|
|
173
191
|
const { agentConfig, env } = args;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
loadProfileConfig
|
|
3
|
-
} from "./chunk-BYMV6QZG.js";
|
|
4
|
-
import {
|
|
5
|
-
resolveDefaultSpawn
|
|
6
|
-
} from "./chunk-X7HHHIOZ.js";
|
|
7
1
|
import {
|
|
8
2
|
isCompiledBinary
|
|
9
3
|
} from "./chunk-A7GB5UGH.js";
|
|
10
4
|
import {
|
|
11
5
|
DEFAULT_NOLO_SERVER_URL
|
|
12
6
|
} from "./chunk-OLZBQWZU.js";
|
|
7
|
+
import {
|
|
8
|
+
loadProfileConfig
|
|
9
|
+
} from "./chunk-TNIX77YM.js";
|
|
10
|
+
import {
|
|
11
|
+
resolveDefaultSpawn
|
|
12
|
+
} from "./chunk-Y6ROFCZO.js";
|
|
13
13
|
import {
|
|
14
14
|
normalizeServerOrigin
|
|
15
15
|
} from "./chunk-CF7PE763.js";
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
readFileSync,
|
|
31
31
|
renameSync,
|
|
32
32
|
rmSync,
|
|
33
|
+
statSync,
|
|
33
34
|
writeSync,
|
|
34
35
|
writeFileSync
|
|
35
36
|
} from "node:fs";
|
|
@@ -38,6 +39,7 @@ import { dirname, isAbsolute, join, normalize } from "node:path";
|
|
|
38
39
|
import { randomUUID } from "node:crypto";
|
|
39
40
|
var UPDATE_STATE_FILE = "update-result.json";
|
|
40
41
|
var UPDATE_STALE_MS = 15 * 60 * 1e3;
|
|
42
|
+
var UPDATE_CORRUPT_MAX_AGE_MS = 30 * 60 * 1e3;
|
|
41
43
|
var UPDATE_LOCK_FILE = "update.lock";
|
|
42
44
|
function resolveNoloHome(env, homeDir = homedir()) {
|
|
43
45
|
return env.NOLO_HOME?.trim() || join(homeDir, ".nolo");
|
|
@@ -101,6 +103,39 @@ function readWindowsUpdateState(env = process.env, homeDir = homedir()) {
|
|
|
101
103
|
return null;
|
|
102
104
|
}
|
|
103
105
|
}
|
|
106
|
+
function assessWindowsUpdatePending(state, now, statePath) {
|
|
107
|
+
const startedAtMs = Date.parse(state.startedAt);
|
|
108
|
+
const MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
|
|
109
|
+
if (Number.isFinite(startedAtMs) && startedAtMs - now <= MAX_CLOCK_SKEW_MS) {
|
|
110
|
+
const ageMs = now - startedAtMs;
|
|
111
|
+
const stale = ageMs >= UPDATE_STALE_MS;
|
|
112
|
+
return {
|
|
113
|
+
classification: stale ? "stale" : "active",
|
|
114
|
+
ageMs,
|
|
115
|
+
startedAtUsable: true,
|
|
116
|
+
blocksStartup: !stale
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
let mtimeAge = Number.NaN;
|
|
120
|
+
try {
|
|
121
|
+
mtimeAge = now - statSync(statePath).mtimeMs;
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
if (!Number.isFinite(mtimeAge) || mtimeAge < UPDATE_CORRUPT_MAX_AGE_MS) {
|
|
125
|
+
return {
|
|
126
|
+
classification: "corrupt-unageable",
|
|
127
|
+
ageMs: mtimeAge,
|
|
128
|
+
startedAtUsable: false,
|
|
129
|
+
blocksStartup: true
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
classification: "stale",
|
|
134
|
+
ageMs: mtimeAge,
|
|
135
|
+
startedAtUsable: false,
|
|
136
|
+
blocksStartup: false
|
|
137
|
+
};
|
|
138
|
+
}
|
|
104
139
|
function consumeWindowsUpdateStartupNotice(env = process.env, options = {}) {
|
|
105
140
|
const homeDir = options.homeDir ?? homedir();
|
|
106
141
|
const statePath = resolveWindowsUpdateStatePath(env, homeDir);
|
|
@@ -110,26 +145,35 @@ function consumeWindowsUpdateStartupNotice(env = process.env, options = {}) {
|
|
|
110
145
|
if (options.ownerUpdateId && state.updateId && options.ownerUpdateId === state.updateId) {
|
|
111
146
|
return null;
|
|
112
147
|
}
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
148
|
+
const now = options.now ?? Date.now();
|
|
149
|
+
const assessment = assessWindowsUpdatePending(state, now, statePath);
|
|
150
|
+
if (assessment.blocksStartup) {
|
|
116
151
|
return {
|
|
117
152
|
blocking: true,
|
|
118
|
-
text: `Nolo update
|
|
153
|
+
text: `A Nolo update may still be in progress (${describeUpdatePhase(state)}). Run "nolo update status" for details, or retry shortly.
|
|
119
154
|
Log: ${state.logPath}`
|
|
120
155
|
};
|
|
121
156
|
}
|
|
122
157
|
if (state.lockPath && state.updateId) {
|
|
123
158
|
removeOwnedLock(state.lockPath, state.updateId);
|
|
124
159
|
}
|
|
125
|
-
rmSync(statePath, { force: true });
|
|
126
160
|
return {
|
|
127
161
|
blocking: false,
|
|
128
|
-
text: `The previous Nolo update helper stopped unexpectedly. Verify with "nolo -v"; a manual npm repair may be required.
|
|
162
|
+
text: `The previous Nolo update helper stopped unexpectedly and its lock was released. Verify with "nolo -v" / "nolo update status"; a manual npm repair may be required.
|
|
129
163
|
Log: ${state.logPath}`
|
|
130
164
|
};
|
|
131
165
|
}
|
|
132
|
-
|
|
166
|
+
if (state.noticeAcknowledgedAt) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const acknowledged = {
|
|
170
|
+
...state,
|
|
171
|
+
noticeAcknowledgedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
172
|
+
};
|
|
173
|
+
try {
|
|
174
|
+
writeJsonAtomic(statePath, acknowledged);
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
133
177
|
if (state.status === "success") {
|
|
134
178
|
return {
|
|
135
179
|
blocking: false,
|
|
@@ -144,6 +188,82 @@ ${state.message}` : ""}
|
|
|
144
188
|
Log: ${state.logPath}`
|
|
145
189
|
};
|
|
146
190
|
}
|
|
191
|
+
function describeUpdatePhase(state) {
|
|
192
|
+
if (state.phase) {
|
|
193
|
+
return `phase: ${state.phase}`;
|
|
194
|
+
}
|
|
195
|
+
return "helper running";
|
|
196
|
+
}
|
|
197
|
+
function getWindowsUpdateStatus(env = process.env, options = {}) {
|
|
198
|
+
const homeDir = options.homeDir ?? homedir();
|
|
199
|
+
const state = readWindowsUpdateState(env, homeDir);
|
|
200
|
+
const now = options.now ?? Date.now();
|
|
201
|
+
const version = options.currentVersion ?? "unknown";
|
|
202
|
+
if (!state) {
|
|
203
|
+
return {
|
|
204
|
+
status: "none",
|
|
205
|
+
alive: false,
|
|
206
|
+
text: `No recorded Nolo update.
|
|
207
|
+
Installed version: ${version}`
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const lines = [];
|
|
211
|
+
lines.push(`Update status: ${state.status}`);
|
|
212
|
+
lines.push(`Installed version: ${version}`);
|
|
213
|
+
lines.push(`Channel: ${state.channel}`);
|
|
214
|
+
lines.push(`From version: ${state.currentVersion}`);
|
|
215
|
+
if (state.targetVersion) lines.push(`Target version: ${state.targetVersion}`);
|
|
216
|
+
if (state.phase) lines.push(`Phase: ${state.phase}`);
|
|
217
|
+
lines.push(`Started: ${state.startedAt}`);
|
|
218
|
+
if (state.phaseStartedAt) lines.push(`Phase started: ${state.phaseStartedAt}`);
|
|
219
|
+
if (state.phaseDeadline) lines.push(`Phase deadline: ${state.phaseDeadline}`);
|
|
220
|
+
if (state.finishedAt) lines.push(`Finished: ${state.finishedAt}`);
|
|
221
|
+
if (state.helperPid) lines.push(`Helper pid: ${state.helperPid}`);
|
|
222
|
+
if (state.message) lines.push(`Message: ${state.message}`);
|
|
223
|
+
lines.push(`Log: ${state.logPath}`);
|
|
224
|
+
if (state.status === "pending") {
|
|
225
|
+
const statePath = resolveWindowsUpdateStatePath(env, homeDir);
|
|
226
|
+
const assessment = assessWindowsUpdatePending(state, now, statePath);
|
|
227
|
+
const deadline = state.phaseDeadline ? Date.parse(state.phaseDeadline) : NaN;
|
|
228
|
+
const pastDeadline = Number.isFinite(deadline) && now > deadline;
|
|
229
|
+
if (pastDeadline) {
|
|
230
|
+
lines.push(
|
|
231
|
+
`Phase deadline elapsed: the recorded phase has run past its soft ceiling. This can be a hung helper, but it can also be a slow install \u2014 the lock is NOT auto-released on this evidence alone.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
switch (assessment.classification) {
|
|
235
|
+
case "active":
|
|
236
|
+
lines.push(
|
|
237
|
+
`Diagnosis: update record is fresh (${describeUpdatePhase(state)}). The helper process has not been verified alive; the record simply has not outlived its bound, so startup is still blocked.`
|
|
238
|
+
);
|
|
239
|
+
break;
|
|
240
|
+
case "corrupt-unageable":
|
|
241
|
+
lines.push(
|
|
242
|
+
`Diagnosis: update record has an unreadable start time and cannot be aged reliably. Startup stays conservatively blocked until the record outlives its hard bound; inspect the log to tell a hung helper from a live one.`
|
|
243
|
+
);
|
|
244
|
+
break;
|
|
245
|
+
case "stale":
|
|
246
|
+
lines.push(
|
|
247
|
+
`Diagnosis: update record has outlived its bound; the helper is presumed stopped. The startup guard releases the stale lock on next start, then this record remains here for diagnosis.`
|
|
248
|
+
);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
status: "pending",
|
|
253
|
+
alive: assessment.blocksStartup,
|
|
254
|
+
pendingClassification: assessment.classification,
|
|
255
|
+
text: lines.join("\n")
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (state.status === "success") {
|
|
259
|
+
lines.push(
|
|
260
|
+
state.noticeAcknowledgedAt ? `Diagnosis: update applied and already acknowledged.` : `Diagnosis: update applied; notice pending on next start.`
|
|
261
|
+
);
|
|
262
|
+
} else {
|
|
263
|
+
lines.push(`Diagnosis: update failed; see the log above.`);
|
|
264
|
+
}
|
|
265
|
+
return { status: state.status, alive: false, text: lines.join("\n") };
|
|
266
|
+
}
|
|
147
267
|
function defaultLaunchWindowsUpdateHelper(input) {
|
|
148
268
|
const child = spawn(input.execPath, [input.helperPath, input.payload], {
|
|
149
269
|
detached: true,
|
|
@@ -172,6 +292,19 @@ const writeState = (next) => {
|
|
|
172
292
|
writeFileSync(temp, JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
173
293
|
renameSync(temp, payload.statePath);
|
|
174
294
|
};
|
|
295
|
+
// Record the current helper step with a bounded wall-clock deadline so the
|
|
296
|
+
// startup guard can tell a live phase from a hung one. Deadline is a soft
|
|
297
|
+
// ceiling: a phase that overruns it is reported stale, not killed.
|
|
298
|
+
const setPhase = (base, phase, budgetMs) => {
|
|
299
|
+
const phaseStartedAt = new Date().toISOString();
|
|
300
|
+
const phaseDeadline = new Date(Date.now() + budgetMs).toISOString();
|
|
301
|
+
try {
|
|
302
|
+
writeState({ ...base, status: "pending", phase, phaseStartedAt, phaseDeadline });
|
|
303
|
+
} catch (error) {
|
|
304
|
+
log("Could not persist phase " + phase + ": " + (error && error.message ? error.message : String(error)));
|
|
305
|
+
}
|
|
306
|
+
return { ...base, status: "pending", phase, phaseStartedAt, phaseDeadline };
|
|
307
|
+
};
|
|
175
308
|
const runNpm = (args) => {
|
|
176
309
|
const command = [payload.nodePath, payload.npmCliPath, ...args].join(" ");
|
|
177
310
|
log("> " + command);
|
|
@@ -229,6 +362,27 @@ const isWithin = (root, candidate) => {
|
|
|
229
362
|
const quoteCmd = (value) => '"' + String(value).replace(/"/g, '""') + '"';
|
|
230
363
|
const quotePowerShell = (value) => "'" + String(value).replace(/'/g, "''") + "'";
|
|
231
364
|
const quoteSh = (value) => "'" + String(value).replace(/'/g, "'\\''") + "'";
|
|
365
|
+
|
|
366
|
+
let stagedDirReferenced = false;
|
|
367
|
+
let lockOwnershipLost = false;
|
|
368
|
+
let shimLeftUnrestored = false;
|
|
369
|
+
|
|
370
|
+
const assertLockOwnership = () => {
|
|
371
|
+
try {
|
|
372
|
+
if (!existsSync(payload.lockPath)) {
|
|
373
|
+
throw new Error("Update lock was released or removed; aborting to avoid racing another process");
|
|
374
|
+
}
|
|
375
|
+
const raw = readFileSync(payload.lockPath, "utf8").trim();
|
|
376
|
+
const owner = raw.startsWith("{") ? JSON.parse(raw).updateId : raw;
|
|
377
|
+
if (owner !== payload.updateId) {
|
|
378
|
+
throw new Error("Update lock was reclaimed by " + owner + "; aborting to avoid racing another updater");
|
|
379
|
+
}
|
|
380
|
+
} catch (e) {
|
|
381
|
+
lockOwnershipLost = true;
|
|
382
|
+
throw new Error("Lock ownership check failed: " + (e && e.message ? e.message : String(e)));
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
232
386
|
const switchWindowsShims = (shimDir, entrypoint) => {
|
|
233
387
|
const shims = [
|
|
234
388
|
{ path: join(shimDir, "nolo.cmd"), content: "@ECHO off\r\n" + quoteCmd(payload.nodePath) + " " + quoteCmd(entrypoint) + " %*\r\n" },
|
|
@@ -243,14 +397,45 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
243
397
|
return { ...shim, temp, backup, existed: existsSync(shim.path) };
|
|
244
398
|
});
|
|
245
399
|
const activated = [];
|
|
400
|
+
const attemptRollback = () => {
|
|
401
|
+
let allRestored = true;
|
|
402
|
+
for (const shim of [...activated].reverse()) {
|
|
403
|
+
try { rmSync(shim.path, { force: true }); } catch (e) {
|
|
404
|
+
log("CRITICAL: Failed to remove new shim " + shim.path + ": " + (e && e.message ? e.message : String(e)));
|
|
405
|
+
}
|
|
406
|
+
if (shim.existed) {
|
|
407
|
+
try {
|
|
408
|
+
renameSync(shim.backup, shim.path);
|
|
409
|
+
} catch (e) {
|
|
410
|
+
allRestored = false;
|
|
411
|
+
log("CRITICAL: Failed to restore backup shim for " + shim.path + ": " + (e && e.message ? e.message : String(e)));
|
|
412
|
+
}
|
|
413
|
+
} else if (existsSync(shim.path)) {
|
|
414
|
+
// The delete above did not remove this shim (locked file, ACL, ...).
|
|
415
|
+
// It still points at the staged directory, so the rollback is NOT clean.
|
|
416
|
+
allRestored = false;
|
|
417
|
+
log("CRITICAL: New shim still present after rollback attempt: " + shim.path);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
for (const shim of prepared) try { rmSync(shim.temp, { force: true }); } catch {}
|
|
421
|
+
stagedDirReferenced = !allRestored;
|
|
422
|
+
return allRestored;
|
|
423
|
+
};
|
|
424
|
+
|
|
246
425
|
try {
|
|
247
426
|
for (const shim of prepared) {
|
|
248
427
|
if (shim.existed) renameSync(shim.path, shim.backup);
|
|
249
428
|
try {
|
|
250
429
|
renameSync(shim.temp, shim.path);
|
|
430
|
+
stagedDirReferenced = true;
|
|
251
431
|
} catch (error) {
|
|
252
432
|
if (shim.existed) {
|
|
253
|
-
try {
|
|
433
|
+
try {
|
|
434
|
+
renameSync(shim.backup, shim.path);
|
|
435
|
+
} catch (restoreErr) {
|
|
436
|
+
shimLeftUnrestored = true;
|
|
437
|
+
log("CRITICAL: Failed to restore backup shim for " + shim.path + ": " + (restoreErr && restoreErr.message ? restoreErr.message : String(restoreErr)));
|
|
438
|
+
}
|
|
254
439
|
}
|
|
255
440
|
throw error;
|
|
256
441
|
}
|
|
@@ -258,22 +443,19 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
258
443
|
activated.push(shim);
|
|
259
444
|
}
|
|
260
445
|
} catch (error) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
try { renameSync(shim.backup, shim.path); } catch {}
|
|
446
|
+
if (!attemptRollback() || shimLeftUnrestored) {
|
|
447
|
+
if (error && typeof error === "object") {
|
|
448
|
+
error.message = (error.message ? error.message : String(error)) + "; rollback incomplete";
|
|
265
449
|
}
|
|
450
|
+
log("CRITICAL: Rollback incomplete while switching launch shims");
|
|
266
451
|
}
|
|
267
|
-
for (const shim of prepared) try { rmSync(shim.temp, { force: true }); } catch {}
|
|
268
452
|
throw error;
|
|
269
453
|
}
|
|
270
454
|
return {
|
|
271
455
|
rollback: () => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
try { renameSync(shim.backup, shim.path); } catch {}
|
|
276
|
-
}
|
|
456
|
+
const restored = attemptRollback();
|
|
457
|
+
if (!restored) {
|
|
458
|
+
throw new Error("Rollback incomplete: one or more launch shims could not be restored or removed");
|
|
277
459
|
}
|
|
278
460
|
},
|
|
279
461
|
commit: () => {
|
|
@@ -296,7 +478,9 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
296
478
|
updateId: payload.updateId,
|
|
297
479
|
lockPath: payload.lockPath,
|
|
298
480
|
};
|
|
481
|
+
let progress = base;
|
|
299
482
|
try {
|
|
483
|
+
progress = setPhase(base, "wait-parent-exit", 480 * 250 + 60 * 1000);
|
|
300
484
|
log("Waiting for parent process " + payload.parentPid + " to exit");
|
|
301
485
|
let parentExited = false;
|
|
302
486
|
for (let waited = 0; waited < 480; waited++) {
|
|
@@ -306,6 +490,7 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
306
490
|
if (!parentExited) throw new Error("Timed out waiting for the current Nolo process to exit");
|
|
307
491
|
await delay(250);
|
|
308
492
|
|
|
493
|
+
progress = setPhase(progress, "wait-other-windows", 120 * 1000 + 30 * 1000);
|
|
309
494
|
const otherProcessDeadline = Date.now() + 120 * 1000;
|
|
310
495
|
let otherPids = listOtherNoloPids();
|
|
311
496
|
let checks = 0;
|
|
@@ -319,6 +504,7 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
319
504
|
throw new Error("Other Nolo processes are still running: " + otherPids.join(", "));
|
|
320
505
|
}
|
|
321
506
|
|
|
507
|
+
progress = setPhase(progress, "resolve-prefix", 30 * 1000);
|
|
322
508
|
const prefixResult = runNpm(["prefix", "-g"]);
|
|
323
509
|
if (prefixResult.code !== 0 || !prefixResult.stdout) throw new Error("Could not resolve npm global prefix");
|
|
324
510
|
const prefix = prefixResult.stdout.split(/\r?\n/).at(-1).trim();
|
|
@@ -329,6 +515,7 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
329
515
|
throw new Error("Refusing to update a different npm prefix. Current entrypoint: " + payload.entrypointPath + "; npm prefix: " + prefix);
|
|
330
516
|
}
|
|
331
517
|
|
|
518
|
+
progress = setPhase(progress, "resolve-target-version", 30 * 1000);
|
|
332
519
|
const targetResult = runNpm(["view", "nolo-cli@" + payload.channel, "version"]);
|
|
333
520
|
if (targetResult.code !== 0 || !parseVersion(targetResult.stdout)) {
|
|
334
521
|
throw new Error("Could not resolve nolo-cli@" + payload.channel + " target version");
|
|
@@ -339,10 +526,13 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
339
526
|
}
|
|
340
527
|
|
|
341
528
|
if (compare(targetVersion, payload.currentVersion) > 0) {
|
|
529
|
+
progress = setPhase(progress, "install-target", 5 * 60 * 1000 + 30 * 1000);
|
|
342
530
|
const versionPrefix = join(payload.managedRoot, targetVersion + "-" + payload.updateId);
|
|
343
531
|
uncommittedVersionPrefix = versionPrefix;
|
|
344
532
|
mkdirSync(versionPrefix, { recursive: true });
|
|
345
|
-
|
|
533
|
+
// Install the exact version resolved from the dist-tag above — never the
|
|
534
|
+
// floating channel tag — so the staged package is the verified one.
|
|
535
|
+
const install = runNpm(["install", "-g", "--prefix", versionPrefix, "nolo-cli@" + targetVersion, "--force", "--progress"]);
|
|
346
536
|
if (install.code !== 0) throw new Error("npm install exited with code " + install.code);
|
|
347
537
|
const stagedPackagePath = join(versionPrefix, "node_modules", "nolo-cli", "package.json");
|
|
348
538
|
const staged = JSON.parse(readFileSync(stagedPackagePath, "utf8"));
|
|
@@ -351,6 +541,8 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
351
541
|
}
|
|
352
542
|
const stagedEntrypoint = join(versionPrefix, "node_modules", "nolo-cli", "index.js");
|
|
353
543
|
if (!existsSync(stagedEntrypoint)) throw new Error("Staged nolo entrypoint is missing");
|
|
544
|
+
assertLockOwnership();
|
|
545
|
+
progress = setPhase(progress, "switch-shims", 60 * 1000);
|
|
354
546
|
const switchedShims = switchWindowsShims(prefix, stagedEntrypoint);
|
|
355
547
|
const verify = spawnSync(payload.nodePath, [stagedEntrypoint, "-v"], {
|
|
356
548
|
encoding: "utf8",
|
|
@@ -360,29 +552,51 @@ const switchWindowsShims = (shimDir, entrypoint) => {
|
|
|
360
552
|
timeout: 30 * 1000,
|
|
361
553
|
});
|
|
362
554
|
if (verify.error || verify.status !== 0 || !String(verify.stdout || "").includes(targetVersion)) {
|
|
363
|
-
|
|
555
|
+
let rollbackError = null;
|
|
556
|
+
try {
|
|
557
|
+
switchedShims.rollback();
|
|
558
|
+
} catch (e) {
|
|
559
|
+
rollbackError = e;
|
|
560
|
+
}
|
|
561
|
+
if (rollbackError) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
"The staged CLI failed its post-switch version check; " +
|
|
564
|
+
(rollbackError && rollbackError.message ? rollbackError.message : String(rollbackError)),
|
|
565
|
+
);
|
|
566
|
+
}
|
|
364
567
|
throw new Error("The staged CLI failed its post-switch version check; the previous launch shims were restored");
|
|
365
568
|
}
|
|
569
|
+
// The lock could have been reclaimed (stale timeout / competing updater)
|
|
570
|
+
// during the verify window. Re-check before committing so we never claim
|
|
571
|
+
// success for an update we no longer own. We deliberately do not roll the
|
|
572
|
+
// shims back here: a competing updater may be mid-switch, and nothing is
|
|
573
|
+
// deleted, so leaving our shims in place is recoverable (last writer wins).
|
|
574
|
+
assertLockOwnership();
|
|
366
575
|
switchedShims.commit();
|
|
367
576
|
uncommittedVersionPrefix = null;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
try { rmSync(candidate, { recursive: true, force: true }); } catch {}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
577
|
+
// Previous versions are intentionally retained (unbounded until a
|
|
578
|
+
// reference-aware cleanup exists) because deleting them risks removing a
|
|
579
|
+
// directory that a shim or a rolled-back update still references.
|
|
374
580
|
log("Verified nolo-cli " + targetVersion + " at " + stagedEntrypoint);
|
|
375
581
|
} else {
|
|
376
582
|
log("Already current at " + targetVersion);
|
|
377
583
|
}
|
|
378
|
-
writeState({ ...
|
|
584
|
+
writeState({ ...progress, status: "success", phase: "done", targetVersion, finishedAt: new Date().toISOString() });
|
|
379
585
|
} catch (error) {
|
|
380
586
|
const message = error && error.message ? error.message : String(error);
|
|
381
587
|
log("ERROR: " + message);
|
|
382
588
|
if (uncommittedVersionPrefix) {
|
|
383
|
-
|
|
589
|
+
if (stagedDirReferenced) {
|
|
590
|
+
log("PRESERVING staged directory " + uncommittedVersionPrefix + " because launch shims may still reference it; deleting it would cause MODULE_NOT_FOUND");
|
|
591
|
+
} else {
|
|
592
|
+
try { rmSync(uncommittedVersionPrefix, { recursive: true, force: true }); } catch {}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (lockOwnershipLost) {
|
|
596
|
+
log("Not writing a failed status record: the update lock belongs to another updater");
|
|
597
|
+
} else {
|
|
598
|
+
writeState({ ...progress, status: "failed", finishedAt: new Date().toISOString(), message });
|
|
384
599
|
}
|
|
385
|
-
writeState({ ...base, status: "failed", finishedAt: new Date().toISOString(), message });
|
|
386
600
|
process.exitCode = 1;
|
|
387
601
|
} finally {
|
|
388
602
|
try {
|
|
@@ -679,7 +893,11 @@ async function runShellScript(script, {
|
|
|
679
893
|
const proc = spawn2({
|
|
680
894
|
cmd: ["sh", "-c", script],
|
|
681
895
|
cwd,
|
|
682
|
-
|
|
896
|
+
// Capture mode (TUI sink): ignore stdin — the TUI's own decoder is
|
|
897
|
+
// reading the tty, and a child that reads stdin would race it for keys.
|
|
898
|
+
// The inherit branch is unreachable inside the TUI (useCustomSink is
|
|
899
|
+
// always true there) and stays the correct choice for `nolo update`.
|
|
900
|
+
stdin: useCustomSink ? "ignore" : "inherit",
|
|
683
901
|
stdout: useCustomSink ? "pipe" : "inherit",
|
|
684
902
|
stderr: useCustomSink ? "pipe" : "inherit",
|
|
685
903
|
env
|
|
@@ -900,7 +1118,8 @@ Nolo Agent CLI Installer & Updater
|
|
|
900
1118
|
const useCustomSink = options.output !== void 0;
|
|
901
1119
|
const proc = spawn2({
|
|
902
1120
|
cmd: command,
|
|
903
|
-
|
|
1121
|
+
// Capture mode (TUI sink): ignore stdin — see runShellScript.
|
|
1122
|
+
stdin: useCustomSink ? "ignore" : "inherit",
|
|
904
1123
|
stdout: useCustomSink ? "pipe" : "inherit",
|
|
905
1124
|
stderr: useCustomSink ? "pipe" : "inherit",
|
|
906
1125
|
env
|
|
@@ -936,6 +1155,7 @@ async function runSelfUpdate(outputOrOptions) {
|
|
|
936
1155
|
|
|
937
1156
|
export {
|
|
938
1157
|
consumeWindowsUpdateStartupNotice,
|
|
1158
|
+
getWindowsUpdateStatus,
|
|
939
1159
|
getCliInstallChannel,
|
|
940
1160
|
renderProgressBar,
|
|
941
1161
|
buildNpmSelfUpdateCommand,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
writeOAuthCredential
|
|
3
|
+
} from "./chunk-HNGRBWYF.js";
|
|
1
4
|
import {
|
|
2
5
|
ANTIGRAVITY_AUTH_URL,
|
|
3
6
|
ANTIGRAVITY_CALLBACK_PATH,
|
|
@@ -11,19 +14,16 @@ import {
|
|
|
11
14
|
getAntigravityUserAgent,
|
|
12
15
|
normalizeAntigravityTokenPayload,
|
|
13
16
|
refreshAntigravityToken
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import {
|
|
16
|
-
writeOAuthCredential
|
|
17
|
-
} from "./chunk-ONXOABJR.js";
|
|
17
|
+
} from "./chunk-UA324D4O.js";
|
|
18
18
|
import {
|
|
19
19
|
toErrorMessage
|
|
20
20
|
} from "./chunk-HNMXYYJR.js";
|
|
21
|
-
import {
|
|
22
|
-
asOptionalFiniteNumber
|
|
23
|
-
} from "./chunk-WWC7I6TA.js";
|
|
24
21
|
import {
|
|
25
22
|
isRecord
|
|
26
23
|
} from "./chunk-KUG52IAP.js";
|
|
24
|
+
import {
|
|
25
|
+
asOptionalFiniteNumber
|
|
26
|
+
} from "./chunk-WWC7I6TA.js";
|
|
27
27
|
import {
|
|
28
28
|
asTrimmedString
|
|
29
29
|
} from "./chunk-3EUW3WEO.js";
|
|
@@ -367,6 +367,77 @@ async function refreshAntigravityOAuthToken(credential, deps = {}) {
|
|
|
367
367
|
// packages/cli/oauth/flows/openai-codex.ts
|
|
368
368
|
import { createHash as createHash2 } from "node:crypto";
|
|
369
369
|
|
|
370
|
+
// packages/agent-runtime/oauthRefreshError.ts
|
|
371
|
+
var PERMANENT_REFRESH_CODES = /* @__PURE__ */ new Set([
|
|
372
|
+
"invalid_grant",
|
|
373
|
+
// OpenAI 令牌端点对失效/不存在的 refresh token 返回 401
|
|
374
|
+
// {"error":{"code":"invalid_refresh_token",...}}(2026-10-01 用必然无效的
|
|
375
|
+
// token 实测)。线上 chatgpt 凭据正是因为没认出它,被当成临时的
|
|
376
|
+
// "unauthorized"、每 15 分钟重试一次且永远不提示重新授权。
|
|
377
|
+
"invalid_refresh_token",
|
|
378
|
+
"refresh_token_reused",
|
|
379
|
+
"invalid_client"
|
|
380
|
+
]);
|
|
381
|
+
var SAFE_MESSAGES = {
|
|
382
|
+
invalid_grant: "refresh token \u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u6388\u6743",
|
|
383
|
+
invalid_refresh_token: "refresh token \u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u6388\u6743",
|
|
384
|
+
refresh_token_reused: "refresh token \u5DF2\u88AB\u5176\u4ED6\u526F\u672C\u4F7F\u7528\uFF0C\u8BF7\u91CD\u65B0\u6388\u6743",
|
|
385
|
+
invalid_client: "OAuth \u5BA2\u6237\u7AEF\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u91CD\u65B0\u6388\u6743",
|
|
386
|
+
unauthorized: "\u4E0A\u6E38\u62D2\u7EDD\u4E86\u5237\u65B0\u8BF7\u6C42\uFF08401/403\uFF09",
|
|
387
|
+
rate_limited: "\u4E0A\u6E38\u9650\u6D41\uFF0C\u7A0D\u540E\u91CD\u8BD5",
|
|
388
|
+
http_5xx: "\u4E0A\u6E38\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528",
|
|
389
|
+
network: "\u65E0\u6CD5\u8FDE\u63A5\u4E0A\u6E38",
|
|
390
|
+
bad_response: "\u4E0A\u6E38\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94",
|
|
391
|
+
unknown: "\u5237\u65B0\u5931\u8D25"
|
|
392
|
+
};
|
|
393
|
+
var OAuthRefreshError = class extends Error {
|
|
394
|
+
code;
|
|
395
|
+
status;
|
|
396
|
+
retryAfterMs;
|
|
397
|
+
constructor(code, opts = {}) {
|
|
398
|
+
super(SAFE_MESSAGES[code]);
|
|
399
|
+
this.name = "OAuthRefreshError";
|
|
400
|
+
this.code = code;
|
|
401
|
+
if (opts.status !== void 0) this.status = opts.status;
|
|
402
|
+
if (opts.retryAfterMs !== void 0) this.retryAfterMs = opts.retryAfterMs;
|
|
403
|
+
}
|
|
404
|
+
get permanent() {
|
|
405
|
+
return PERMANENT_REFRESH_CODES.has(this.code);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
function readErrorCode(payload) {
|
|
409
|
+
if (!payload || typeof payload !== "object") return void 0;
|
|
410
|
+
const err = payload.error;
|
|
411
|
+
if (typeof err === "string") return err;
|
|
412
|
+
if (err && typeof err === "object") {
|
|
413
|
+
const code = err.code;
|
|
414
|
+
if (typeof code === "string") return code;
|
|
415
|
+
}
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
function classifyOAuthRefreshHttpFailure(status, payload, headers) {
|
|
419
|
+
const raw = readErrorCode(payload)?.toLowerCase();
|
|
420
|
+
if (raw === "invalid_grant") return new OAuthRefreshError("invalid_grant", { status });
|
|
421
|
+
if (raw === "invalid_refresh_token") {
|
|
422
|
+
return new OAuthRefreshError("invalid_refresh_token", { status });
|
|
423
|
+
}
|
|
424
|
+
if (raw === "refresh_token_reused") {
|
|
425
|
+
return new OAuthRefreshError("refresh_token_reused", { status });
|
|
426
|
+
}
|
|
427
|
+
if (raw === "invalid_client") return new OAuthRefreshError("invalid_client", { status });
|
|
428
|
+
if (status === 429) {
|
|
429
|
+
const seconds = Number(headers?.get("retry-after"));
|
|
430
|
+
return new OAuthRefreshError("rate_limited", {
|
|
431
|
+
status,
|
|
432
|
+
...Number.isFinite(seconds) && seconds > 0 ? { retryAfterMs: seconds * 1e3 } : {}
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (status === 401 || status === 403) return new OAuthRefreshError("unauthorized", { status });
|
|
436
|
+
if (status >= 500) return new OAuthRefreshError("http_5xx", { status });
|
|
437
|
+
return new OAuthRefreshError("unknown", { status });
|
|
438
|
+
}
|
|
439
|
+
var TRANSIENT_MAX_MS = 15 * 6e4;
|
|
440
|
+
|
|
370
441
|
// packages/agent-runtime/openaiCodexOAuth.ts
|
|
371
442
|
var OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
372
443
|
var OPENAI_CODEX_SCOPES = [
|
|
@@ -463,17 +534,14 @@ async function refreshOpenAiCodexToken(credential, deps = {}) {
|
|
|
463
534
|
signal: AbortSignal.timeout(OPENAI_CODEX_TOKEN_REQUEST_TIMEOUT_MS)
|
|
464
535
|
});
|
|
465
536
|
} catch (error) {
|
|
466
|
-
throw new
|
|
467
|
-
`OpenAI Codex token refresh failed: ${toErrorMessage(error)}`
|
|
468
|
-
);
|
|
537
|
+
throw new OAuthRefreshError("network");
|
|
469
538
|
}
|
|
470
539
|
const payload = await response.json().catch(() => ({}));
|
|
471
540
|
if (!response.ok) {
|
|
472
|
-
|
|
473
|
-
throw new Error(`OpenAI Codex token refresh failed: ${detail}`);
|
|
541
|
+
throw classifyOAuthRefreshHttpFailure(response.status, payload, response.headers);
|
|
474
542
|
}
|
|
475
543
|
if (typeof payload.access_token !== "string" || !payload.access_token.trim()) {
|
|
476
|
-
throw new
|
|
544
|
+
throw new OAuthRefreshError("bad_response");
|
|
477
545
|
}
|
|
478
546
|
const now = deps.now?.() ?? Date.now();
|
|
479
547
|
return normalizeOpenAiCodexTokenPayload({
|
|
@@ -1047,11 +1115,10 @@ async function refreshAnthropicToken(credential, deps = {}) {
|
|
|
1047
1115
|
});
|
|
1048
1116
|
const payload = await response.json().catch(() => ({}));
|
|
1049
1117
|
if (!response.ok) {
|
|
1050
|
-
|
|
1051
|
-
throw new Error(`Claude token refresh failed: ${detail}`);
|
|
1118
|
+
throw classifyOAuthRefreshHttpFailure(response.status, payload, response.headers);
|
|
1052
1119
|
}
|
|
1053
1120
|
if (typeof payload.access_token !== "string" || !payload.access_token.trim()) {
|
|
1054
|
-
throw new
|
|
1121
|
+
throw new OAuthRefreshError("bad_response");
|
|
1055
1122
|
}
|
|
1056
1123
|
const now = deps.now?.() ?? Date.now();
|
|
1057
1124
|
return normalizeAnthropicTokenPayload({
|