@evalops/maestro 0.10.47 → 0.10.48

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 (72) hide show
  1. package/README.md +4 -0
  2. package/dist/audit/logger.d.ts +1 -1
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +2 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/commands/update.d.ts +12 -0
  7. package/dist/cli/commands/update.d.ts.map +1 -0
  8. package/dist/cli/commands/update.js +148 -0
  9. package/dist/cli/commands/update.js.map +1 -0
  10. package/dist/cli/help.d.ts.map +1 -1
  11. package/dist/cli/help.js +4 -0
  12. package/dist/cli/help.js.map +1 -1
  13. package/dist/cli-tui/commands/command-catalog.js +2 -2
  14. package/dist/cli-tui/commands/command-catalog.js.map +1 -1
  15. package/dist/cli-tui/commands/subcommands/ui-commands.d.ts +1 -1
  16. package/dist/cli-tui/commands/subcommands/ui-commands.js +2 -2
  17. package/dist/cli-tui/commands/subcommands/ui-commands.js.map +1 -1
  18. package/dist/cli-tui/footer.d.ts.map +1 -1
  19. package/dist/cli-tui/footer.js +1 -1
  20. package/dist/cli-tui/footer.js.map +1 -1
  21. package/dist/cli-tui/tui-renderer/ui-state-controller.d.ts +1 -1
  22. package/dist/cli-tui/tui-renderer/ui-state-controller.d.ts.map +1 -1
  23. package/dist/cli-tui/tui-renderer/ui-state-controller.js +5 -11
  24. package/dist/cli-tui/tui-renderer/ui-state-controller.js.map +1 -1
  25. package/dist/cli-tui/tui-renderer.d.ts.map +1 -1
  26. package/dist/cli-tui/tui-renderer.js +1 -1
  27. package/dist/cli-tui/tui-renderer.js.map +1 -1
  28. package/dist/cli-tui/ui-state.js +1 -1
  29. package/dist/cli-tui/ui-state.js.map +1 -1
  30. package/dist/cli-tui/utils/footer-utils.d.ts +1 -1
  31. package/dist/cli-tui/utils/footer-utils.d.ts.map +1 -1
  32. package/dist/cli-tui/utils/footer-utils.js.map +1 -1
  33. package/dist/cli.js +4072 -3450
  34. package/dist/cli.js.map +1 -1
  35. package/dist/codex/app-server-client.d.ts +8 -0
  36. package/dist/codex/app-server-client.d.ts.map +1 -1
  37. package/dist/codex/app-server-client.js +49 -3
  38. package/dist/codex/app-server-client.js.map +1 -1
  39. package/dist/main.d.ts.map +1 -1
  40. package/dist/main.js +5 -0
  41. package/dist/main.js.map +1 -1
  42. package/dist/models/config-loader.d.ts +13 -13
  43. package/dist/node_modules/@evalops/contracts/dist/scenario.d.ts +2 -0
  44. package/dist/node_modules/@evalops/contracts/dist/scenario.d.ts.map +1 -1
  45. package/dist/node_modules/@evalops/contracts/dist/scenario.js.map +1 -1
  46. package/dist/node_modules/@evalops/contracts/package.json +1 -1
  47. package/dist/node_modules/@evalops/tui/package.json +1 -1
  48. package/dist/platform/operating-plane-client.d.ts +3 -3
  49. package/dist/platform/operating-plane-client.d.ts.map +1 -1
  50. package/dist/platform/operating-plane-client.js.map +1 -1
  51. package/dist/platform/operating-plane-summary.js +3 -6
  52. package/dist/platform/operating-plane-summary.js.map +1 -1
  53. package/dist/server/agent-trajectory-scenarios.d.ts.map +1 -1
  54. package/dist/server/agent-trajectory-scenarios.js +29 -6
  55. package/dist/server/agent-trajectory-scenarios.js.map +1 -1
  56. package/dist/server/handlers/ui.d.ts.map +1 -1
  57. package/dist/server/handlers/ui.js +5 -5
  58. package/dist/server/handlers/ui.js.map +1 -1
  59. package/dist/server/stores/ui-store.d.ts.map +1 -1
  60. package/dist/server/stores/ui-store.js +8 -1
  61. package/dist/server/stores/ui-store.js.map +1 -1
  62. package/dist/tools/background/task-types.d.ts +1 -1
  63. package/dist/update/check.d.ts +2 -0
  64. package/dist/update/check.d.ts.map +1 -1
  65. package/dist/update/check.js +100 -41
  66. package/dist/update/check.js.map +1 -1
  67. package/dist/update/startup-refresh.d.ts +56 -0
  68. package/dist/update/startup-refresh.d.ts.map +1 -0
  69. package/dist/update/startup-refresh.js +321 -0
  70. package/dist/update/startup-refresh.js.map +1 -0
  71. package/dist/version.json +2 -2
  72. package/package.json +297 -296
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/update/check.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,iBAAiB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,OAAO,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,qBAAqB;IAC9B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AA4FD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBvE;AAED,wBAAsB,cAAc,CACnC,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,qBAA0B,GACjC,OAAO,CAAC,iBAAiB,CAAC,CA4D5B"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/update/check.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,OAAO,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,qBAAqB;IAC9B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAgBD,eAAO,MAAM,iBAAiB,GAC7B,UAAS,IAAI,CAAC,qBAAqB,EAAE,KAAK,GAAG,MAAM,CAAM,EACzD,MAAK,MAAM,CAAC,UAAwB,KAClC,MAAM,EAgBR,CAAC;AA4FF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBvE;AAED,wBAAsB,cAAc,CACnC,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,qBAA0B,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAoG5B"}
@@ -1,6 +1,37 @@
1
+ import { getPackageName } from "../package-metadata.js";
1
2
  import { withTimeout } from "../utils/async.js";
2
- const DEFAULT_UPDATE_URL = "https://raw.githubusercontent.com/evalops/hopper/main/public/composer/version.json";
3
+ const DEFAULT_GCS_UPDATE_URL = "https://storage.googleapis.com/evalops-prod-maestro-releases/maestro/version.json";
4
+ const NPM_REGISTRY_BASE_URL = "https://registry.npmjs.org";
3
5
  const DEFAULT_TIMEOUT_MS = 5_000;
6
+ const splitUrlList = (value) => {
7
+ if (!value) {
8
+ return [];
9
+ }
10
+ return value
11
+ .split(/[\n,]/u)
12
+ .map((url) => url.trim())
13
+ .filter(Boolean);
14
+ };
15
+ const npmLatestUrl = (packageName = getPackageName()) => {
16
+ return `${NPM_REGISTRY_BASE_URL}/${encodeURIComponent(packageName)}/latest`;
17
+ };
18
+ export const resolveUpdateUrls = (options = {}, env = process.env) => {
19
+ const explicitUrls = options.urls?.map((url) => url.trim()).filter(Boolean);
20
+ if (explicitUrls && explicitUrls.length > 0) {
21
+ return explicitUrls;
22
+ }
23
+ if (options.url?.trim()) {
24
+ return [options.url.trim()];
25
+ }
26
+ const envUrls = splitUrlList(env.MAESTRO_UPDATE_URLS);
27
+ if (envUrls.length > 0) {
28
+ return envUrls;
29
+ }
30
+ if (env.MAESTRO_UPDATE_URL?.trim()) {
31
+ return [env.MAESTRO_UPDATE_URL.trim()];
32
+ }
33
+ return [DEFAULT_GCS_UPDATE_URL, npmLatestUrl()];
34
+ };
4
35
  const toErrorMessage = (error) => {
5
36
  if (error instanceof Error) {
6
37
  return error.message;
@@ -102,58 +133,86 @@ export function compareVersions(current, latest) {
102
133
  return comparePreRelease(currentPre, latestPre);
103
134
  }
104
135
  export async function checkForUpdate(currentVersion, options = {}) {
105
- const url = options.url ?? process.env.MAESTRO_UPDATE_URL ?? DEFAULT_UPDATE_URL;
136
+ const urls = resolveUpdateUrls(options);
106
137
  const fetchImpl = options.fetch ?? fetch;
107
138
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
108
- try {
109
- const response = await withTimeout(fetchImpl(url, {
110
- headers: { Accept: "application/json" },
111
- }), timeoutMs, `Update check timed out after ${timeoutMs}ms`);
112
- if (!response.ok) {
113
- return {
114
- currentVersion,
115
- isUpdateAvailable: false,
116
- sourceUrl: url,
117
- error: `Update check failed (${response.status} ${response.statusText || ""})`.trim(),
118
- };
119
- }
120
- let payload;
139
+ let lastResult = null;
140
+ let bestUpdateResult = null;
141
+ let bestCurrentResult = null;
142
+ for (const url of urls) {
121
143
  try {
122
- payload = (await response.json());
123
- }
124
- catch (error) {
125
- return {
144
+ const response = await withTimeout(fetchImpl(url, {
145
+ headers: { Accept: "application/json" },
146
+ }), timeoutMs, `Update check timed out after ${timeoutMs}ms`);
147
+ if (!response.ok) {
148
+ lastResult = {
149
+ currentVersion,
150
+ isUpdateAvailable: false,
151
+ sourceUrl: url,
152
+ error: `Update check failed (${response.status} ${response.statusText || ""})`.trim(),
153
+ };
154
+ continue;
155
+ }
156
+ let payload;
157
+ try {
158
+ payload = (await response.json());
159
+ }
160
+ catch (error) {
161
+ lastResult = {
162
+ currentVersion,
163
+ isUpdateAvailable: false,
164
+ sourceUrl: url,
165
+ error: `Invalid update metadata: ${toErrorMessage(error)}`,
166
+ };
167
+ continue;
168
+ }
169
+ if (!payload || typeof payload.version !== "string") {
170
+ lastResult = {
171
+ currentVersion,
172
+ isUpdateAvailable: false,
173
+ sourceUrl: url,
174
+ error: "Update metadata missing version field",
175
+ };
176
+ continue;
177
+ }
178
+ const latestVersion = payload.version.trim();
179
+ const comparison = compareVersions(currentVersion.trim(), latestVersion);
180
+ const result = {
126
181
  currentVersion,
127
- isUpdateAvailable: false,
182
+ latestVersion,
183
+ notes: normalizeNotes(payload.notes),
184
+ isUpdateAvailable: comparison < 0,
128
185
  sourceUrl: url,
129
- error: `Invalid update metadata: ${toErrorMessage(error)}`,
130
186
  };
187
+ lastResult = result;
188
+ if (result.isUpdateAvailable) {
189
+ if (!bestUpdateResult ||
190
+ compareVersions(bestUpdateResult.latestVersion ?? currentVersion, latestVersion) < 0) {
191
+ bestUpdateResult = result;
192
+ }
193
+ continue;
194
+ }
195
+ if (!bestCurrentResult ||
196
+ compareVersions(bestCurrentResult.latestVersion ?? currentVersion, latestVersion) < 0) {
197
+ bestCurrentResult = result;
198
+ }
131
199
  }
132
- if (!payload || typeof payload.version !== "string") {
133
- return {
200
+ catch (error) {
201
+ lastResult = {
134
202
  currentVersion,
135
203
  isUpdateAvailable: false,
136
204
  sourceUrl: url,
137
- error: "Update metadata missing version field",
205
+ error: toErrorMessage(error),
138
206
  };
139
207
  }
140
- const latestVersion = payload.version.trim();
141
- const comparison = compareVersions(currentVersion.trim(), latestVersion);
142
- return {
143
- currentVersion,
144
- latestVersion,
145
- notes: normalizeNotes(payload.notes),
146
- isUpdateAvailable: comparison < 0,
147
- sourceUrl: url,
148
- };
149
- }
150
- catch (error) {
151
- return {
152
- currentVersion,
153
- isUpdateAvailable: false,
154
- sourceUrl: url,
155
- error: toErrorMessage(error),
156
- };
157
208
  }
209
+ return (bestUpdateResult ??
210
+ bestCurrentResult ??
211
+ lastResult ?? {
212
+ currentVersion,
213
+ isUpdateAvailable: false,
214
+ sourceUrl: urls[0] ?? "",
215
+ error: "No update metadata sources configured",
216
+ });
158
217
  }
159
218
  //# sourceMappingURL=check.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/update/check.ts"],"names":[],"mappings":"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","sourcesContent":["import { withTimeout } from \"../utils/async.js\";\n\nconst DEFAULT_UPDATE_URL =\n\t\"https://raw.githubusercontent.com/evalops/hopper/main/public/composer/version.json\";\nconst DEFAULT_TIMEOUT_MS = 5_000;\n\ninterface VersionMetadata {\n\tversion: string;\n\tnotes?: string;\n}\n\nexport interface UpdateCheckResult {\n\tcurrentVersion: string;\n\tlatestVersion?: string;\n\tnotes?: string;\n\tisUpdateAvailable: boolean;\n\tsourceUrl: string;\n\terror?: string;\n}\n\ninterface CheckForUpdateOptions {\n\tfetch?: typeof fetch;\n\ttimeoutMs?: number;\n\turl?: string;\n}\n\nconst toErrorMessage = (error: unknown): string => {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\tif (typeof error === \"string\") {\n\t\treturn error;\n\t}\n\treturn \"Unknown error\";\n};\n\nconst normalizeNotes = (value: unknown): string | undefined => {\n\tif (typeof value !== \"string\") {\n\t\treturn undefined;\n\t}\n\tconst trimmed = value.trim();\n\treturn trimmed.length ? trimmed : undefined;\n};\n\nconst parseNumericSegment = (segment: string): number => {\n\tconst value = Number.parseInt(segment, 10);\n\treturn Number.isNaN(value) ? 0 : value;\n};\n\nconst parseMainSegments = (version: string): number[] => {\n\treturn version.split(\".\").map((segment) => parseNumericSegment(segment));\n};\n\nconst parsePreReleaseSegments = (version: string): string[] => {\n\treturn version ? version.split(\".\") : [];\n};\n\nconst isNumericIdentifier = (segment: string): boolean => {\n\treturn /^[0-9]+$/.test(segment);\n};\n\nconst comparePreRelease = (\n\taSegments: string[],\n\tbSegments: string[],\n): number => {\n\tif (aSegments.length === 0 && bSegments.length === 0) {\n\t\treturn 0;\n\t}\n\tif (aSegments.length === 0) {\n\t\treturn 1;\n\t}\n\tif (bSegments.length === 0) {\n\t\treturn -1;\n\t}\n\tconst length = Math.max(aSegments.length, bSegments.length);\n\tfor (let i = 0; i < length; i++) {\n\t\tconst a = aSegments[i];\n\t\tconst b = bSegments[i];\n\t\tif (a === undefined) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (b === undefined) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (a === b) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst aIsNum = isNumericIdentifier(a);\n\t\tconst bIsNum = isNumericIdentifier(b);\n\t\tif (aIsNum && bIsNum) {\n\t\t\tconst aNum = Number.parseInt(a, 10);\n\t\t\tconst bNum = Number.parseInt(b, 10);\n\t\t\tif (aNum < bNum) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (aNum > bNum) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (aIsNum && !bIsNum) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (!aIsNum && bIsNum) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (a < b) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (a > b) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n};\n\nexport function compareVersions(current: string, latest: string): number {\n\tconst [currentMainPart, currentPreReleasePart = \"\"] = current.split(\"-\", 2);\n\tconst [latestMainPart, latestPreReleasePart = \"\"] = latest.split(\"-\", 2);\n\tconst currentMain = parseMainSegments(currentMainPart!);\n\tconst latestMain = parseMainSegments(latestMainPart!);\n\tconst length = Math.max(currentMain.length, latestMain.length);\n\tfor (let i = 0; i < length; i++) {\n\t\tconst currentValue = currentMain[i] ?? 0;\n\t\tconst latestValue = latestMain[i] ?? 0;\n\t\tif (currentValue < latestValue) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (currentValue > latestValue) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\tconst currentPre = parsePreReleaseSegments(currentPreReleasePart);\n\tconst latestPre = parsePreReleaseSegments(latestPreReleasePart);\n\treturn comparePreRelease(currentPre, latestPre);\n}\n\nexport async function checkForUpdate(\n\tcurrentVersion: string,\n\toptions: CheckForUpdateOptions = {},\n): Promise<UpdateCheckResult> {\n\tconst url =\n\t\toptions.url ?? process.env.MAESTRO_UPDATE_URL ?? DEFAULT_UPDATE_URL;\n\tconst fetchImpl = options.fetch ?? fetch;\n\tconst timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n\ttry {\n\t\tconst response = await withTimeout(\n\t\t\tfetchImpl(url, {\n\t\t\t\theaders: { Accept: \"application/json\" },\n\t\t\t}),\n\t\t\ttimeoutMs,\n\t\t\t`Update check timed out after ${timeoutMs}ms`,\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\tcurrentVersion,\n\t\t\t\tisUpdateAvailable: false,\n\t\t\t\tsourceUrl: url,\n\t\t\t\terror:\n\t\t\t\t\t`Update check failed (${response.status} ${response.statusText || \"\"})`.trim(),\n\t\t\t};\n\t\t}\n\t\tlet payload: VersionMetadata;\n\t\ttry {\n\t\t\tpayload = (await response.json()) as VersionMetadata;\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tcurrentVersion,\n\t\t\t\tisUpdateAvailable: false,\n\t\t\t\tsourceUrl: url,\n\t\t\t\terror: `Invalid update metadata: ${toErrorMessage(error)}`,\n\t\t\t};\n\t\t}\n\t\tif (!payload || typeof payload.version !== \"string\") {\n\t\t\treturn {\n\t\t\t\tcurrentVersion,\n\t\t\t\tisUpdateAvailable: false,\n\t\t\t\tsourceUrl: url,\n\t\t\t\terror: \"Update metadata missing version field\",\n\t\t\t};\n\t\t}\n\t\tconst latestVersion = payload.version.trim();\n\t\tconst comparison = compareVersions(currentVersion.trim(), latestVersion);\n\t\treturn {\n\t\t\tcurrentVersion,\n\t\t\tlatestVersion,\n\t\t\tnotes: normalizeNotes(payload.notes),\n\t\t\tisUpdateAvailable: comparison < 0,\n\t\t\tsourceUrl: url,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tcurrentVersion,\n\t\t\tisUpdateAvailable: false,\n\t\t\tsourceUrl: url,\n\t\t\terror: toErrorMessage(error),\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/update/check.ts"],"names":[],"mappings":"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","sourcesContent":["import { getPackageName } from \"../package-metadata.js\";\nimport { withTimeout } from \"../utils/async.js\";\n\nconst DEFAULT_GCS_UPDATE_URL =\n\t\"https://storage.googleapis.com/evalops-prod-maestro-releases/maestro/version.json\";\nconst NPM_REGISTRY_BASE_URL = \"https://registry.npmjs.org\";\nconst DEFAULT_TIMEOUT_MS = 5_000;\n\ninterface VersionMetadata {\n\tversion: string;\n\tnotes?: string;\n}\n\nexport interface UpdateCheckResult {\n\tcurrentVersion: string;\n\tlatestVersion?: string;\n\tnotes?: string;\n\tisUpdateAvailable: boolean;\n\tsourceUrl: string;\n\terror?: string;\n}\n\ninterface CheckForUpdateOptions {\n\tfetch?: typeof fetch;\n\ttimeoutMs?: number;\n\turl?: string;\n\turls?: string[];\n}\n\nconst splitUrlList = (value: string | undefined): string[] => {\n\tif (!value) {\n\t\treturn [];\n\t}\n\treturn value\n\t\t.split(/[\\n,]/u)\n\t\t.map((url) => url.trim())\n\t\t.filter(Boolean);\n};\n\nconst npmLatestUrl = (packageName = getPackageName()): string => {\n\treturn `${NPM_REGISTRY_BASE_URL}/${encodeURIComponent(packageName)}/latest`;\n};\n\nexport const resolveUpdateUrls = (\n\toptions: Pick<CheckForUpdateOptions, \"url\" | \"urls\"> = {},\n\tenv: NodeJS.ProcessEnv = process.env,\n): string[] => {\n\tconst explicitUrls = options.urls?.map((url) => url.trim()).filter(Boolean);\n\tif (explicitUrls && explicitUrls.length > 0) {\n\t\treturn explicitUrls;\n\t}\n\tif (options.url?.trim()) {\n\t\treturn [options.url.trim()];\n\t}\n\tconst envUrls = splitUrlList(env.MAESTRO_UPDATE_URLS);\n\tif (envUrls.length > 0) {\n\t\treturn envUrls;\n\t}\n\tif (env.MAESTRO_UPDATE_URL?.trim()) {\n\t\treturn [env.MAESTRO_UPDATE_URL.trim()];\n\t}\n\treturn [DEFAULT_GCS_UPDATE_URL, npmLatestUrl()];\n};\n\nconst toErrorMessage = (error: unknown): string => {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\tif (typeof error === \"string\") {\n\t\treturn error;\n\t}\n\treturn \"Unknown error\";\n};\n\nconst normalizeNotes = (value: unknown): string | undefined => {\n\tif (typeof value !== \"string\") {\n\t\treturn undefined;\n\t}\n\tconst trimmed = value.trim();\n\treturn trimmed.length ? trimmed : undefined;\n};\n\nconst parseNumericSegment = (segment: string): number => {\n\tconst value = Number.parseInt(segment, 10);\n\treturn Number.isNaN(value) ? 0 : value;\n};\n\nconst parseMainSegments = (version: string): number[] => {\n\treturn version.split(\".\").map((segment) => parseNumericSegment(segment));\n};\n\nconst parsePreReleaseSegments = (version: string): string[] => {\n\treturn version ? version.split(\".\") : [];\n};\n\nconst isNumericIdentifier = (segment: string): boolean => {\n\treturn /^[0-9]+$/.test(segment);\n};\n\nconst comparePreRelease = (\n\taSegments: string[],\n\tbSegments: string[],\n): number => {\n\tif (aSegments.length === 0 && bSegments.length === 0) {\n\t\treturn 0;\n\t}\n\tif (aSegments.length === 0) {\n\t\treturn 1;\n\t}\n\tif (bSegments.length === 0) {\n\t\treturn -1;\n\t}\n\tconst length = Math.max(aSegments.length, bSegments.length);\n\tfor (let i = 0; i < length; i++) {\n\t\tconst a = aSegments[i];\n\t\tconst b = bSegments[i];\n\t\tif (a === undefined) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (b === undefined) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (a === b) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst aIsNum = isNumericIdentifier(a);\n\t\tconst bIsNum = isNumericIdentifier(b);\n\t\tif (aIsNum && bIsNum) {\n\t\t\tconst aNum = Number.parseInt(a, 10);\n\t\t\tconst bNum = Number.parseInt(b, 10);\n\t\t\tif (aNum < bNum) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (aNum > bNum) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (aIsNum && !bIsNum) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (!aIsNum && bIsNum) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (a < b) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (a > b) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n};\n\nexport function compareVersions(current: string, latest: string): number {\n\tconst [currentMainPart, currentPreReleasePart = \"\"] = current.split(\"-\", 2);\n\tconst [latestMainPart, latestPreReleasePart = \"\"] = latest.split(\"-\", 2);\n\tconst currentMain = parseMainSegments(currentMainPart!);\n\tconst latestMain = parseMainSegments(latestMainPart!);\n\tconst length = Math.max(currentMain.length, latestMain.length);\n\tfor (let i = 0; i < length; i++) {\n\t\tconst currentValue = currentMain[i] ?? 0;\n\t\tconst latestValue = latestMain[i] ?? 0;\n\t\tif (currentValue < latestValue) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (currentValue > latestValue) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\tconst currentPre = parsePreReleaseSegments(currentPreReleasePart);\n\tconst latestPre = parsePreReleaseSegments(latestPreReleasePart);\n\treturn comparePreRelease(currentPre, latestPre);\n}\n\nexport async function checkForUpdate(\n\tcurrentVersion: string,\n\toptions: CheckForUpdateOptions = {},\n): Promise<UpdateCheckResult> {\n\tconst urls = resolveUpdateUrls(options);\n\tconst fetchImpl = options.fetch ?? fetch;\n\tconst timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\tlet lastResult: UpdateCheckResult | null = null;\n\tlet bestUpdateResult: UpdateCheckResult | null = null;\n\tlet bestCurrentResult: UpdateCheckResult | null = null;\n\n\tfor (const url of urls) {\n\t\ttry {\n\t\t\tconst response = await withTimeout(\n\t\t\t\tfetchImpl(url, {\n\t\t\t\t\theaders: { Accept: \"application/json\" },\n\t\t\t\t}),\n\t\t\t\ttimeoutMs,\n\t\t\t\t`Update check timed out after ${timeoutMs}ms`,\n\t\t\t);\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlastResult = {\n\t\t\t\t\tcurrentVersion,\n\t\t\t\t\tisUpdateAvailable: false,\n\t\t\t\t\tsourceUrl: url,\n\t\t\t\t\terror:\n\t\t\t\t\t\t`Update check failed (${response.status} ${response.statusText || \"\"})`.trim(),\n\t\t\t\t};\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet payload: VersionMetadata;\n\t\t\ttry {\n\t\t\t\tpayload = (await response.json()) as VersionMetadata;\n\t\t\t} catch (error) {\n\t\t\t\tlastResult = {\n\t\t\t\t\tcurrentVersion,\n\t\t\t\t\tisUpdateAvailable: false,\n\t\t\t\t\tsourceUrl: url,\n\t\t\t\t\terror: `Invalid update metadata: ${toErrorMessage(error)}`,\n\t\t\t\t};\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!payload || typeof payload.version !== \"string\") {\n\t\t\t\tlastResult = {\n\t\t\t\t\tcurrentVersion,\n\t\t\t\t\tisUpdateAvailable: false,\n\t\t\t\t\tsourceUrl: url,\n\t\t\t\t\terror: \"Update metadata missing version field\",\n\t\t\t\t};\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst latestVersion = payload.version.trim();\n\t\t\tconst comparison = compareVersions(currentVersion.trim(), latestVersion);\n\t\t\tconst result = {\n\t\t\t\tcurrentVersion,\n\t\t\t\tlatestVersion,\n\t\t\t\tnotes: normalizeNotes(payload.notes),\n\t\t\t\tisUpdateAvailable: comparison < 0,\n\t\t\t\tsourceUrl: url,\n\t\t\t};\n\t\t\tlastResult = result;\n\t\t\tif (result.isUpdateAvailable) {\n\t\t\t\tif (\n\t\t\t\t\t!bestUpdateResult ||\n\t\t\t\t\tcompareVersions(\n\t\t\t\t\t\tbestUpdateResult.latestVersion ?? currentVersion,\n\t\t\t\t\t\tlatestVersion,\n\t\t\t\t\t) < 0\n\t\t\t\t) {\n\t\t\t\t\tbestUpdateResult = result;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!bestCurrentResult ||\n\t\t\t\tcompareVersions(\n\t\t\t\t\tbestCurrentResult.latestVersion ?? currentVersion,\n\t\t\t\t\tlatestVersion,\n\t\t\t\t) < 0\n\t\t\t) {\n\t\t\t\tbestCurrentResult = result;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlastResult = {\n\t\t\t\tcurrentVersion,\n\t\t\t\tisUpdateAvailable: false,\n\t\t\t\tsourceUrl: url,\n\t\t\t\terror: toErrorMessage(error),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn (\n\t\tbestUpdateResult ??\n\t\tbestCurrentResult ??\n\t\tlastResult ?? {\n\t\t\tcurrentVersion,\n\t\t\tisUpdateAvailable: false,\n\t\t\tsourceUrl: urls[0] ?? \"\",\n\t\t\terror: \"No update metadata sources configured\",\n\t\t}\n\t);\n}\n"]}
@@ -0,0 +1,56 @@
1
+ import { type UpdateCheckResult } from "./check.js";
2
+ type PackageManager = "npm" | "bun";
3
+ type GlobalInstallContext = {
4
+ packageManager: PackageManager;
5
+ prefix: string;
6
+ };
7
+ export type StartupUpdateOutcome = {
8
+ status: "skipped";
9
+ reason: string;
10
+ } | {
11
+ status: "current";
12
+ check: UpdateCheckResult;
13
+ } | {
14
+ status: "available";
15
+ check: UpdateCheckResult;
16
+ } | {
17
+ status: "failed";
18
+ check?: UpdateCheckResult;
19
+ error: string;
20
+ } | {
21
+ status: "updated";
22
+ check: UpdateCheckResult;
23
+ } | {
24
+ status: "restarted";
25
+ check: UpdateCheckResult;
26
+ exitCode: number;
27
+ };
28
+ type StartupUpdateOptions = {
29
+ args?: string[];
30
+ argv?: string[];
31
+ currentVersion: string;
32
+ env?: NodeJS.ProcessEnv;
33
+ isTty?: boolean;
34
+ now?: number;
35
+ packageName?: string;
36
+ checkForUpdateImpl?: (currentVersion: string, options?: {
37
+ timeoutMs?: number;
38
+ urls?: string[];
39
+ }) => Promise<UpdateCheckResult>;
40
+ checkTimeoutMs?: number;
41
+ globalInstallContexts?: GlobalInstallContext[] | null;
42
+ globalPrefix?: string | null;
43
+ installPackage?: (packageManager: PackageManager, packageName: string, version: string) => {
44
+ status: number | null;
45
+ error?: Error;
46
+ };
47
+ restart?: false | (() => {
48
+ status: number | null;
49
+ error?: Error;
50
+ });
51
+ statePath?: string;
52
+ };
53
+ export declare const isInstalledPackageEntrypoint: (entrypoint: string | undefined, packageName?: string, globalPrefix?: string | null) => boolean;
54
+ export declare function attemptStartupUpdate(options: StartupUpdateOptions): Promise<StartupUpdateOutcome>;
55
+ export {};
56
+ //# sourceMappingURL=startup-refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-refresh.d.ts","sourceRoot":"","sources":["../../src/update/startup-refresh.ts"],"names":[],"mappings":"AAgBA,OAAO,EACN,KAAK,iBAAiB,EAGtB,MAAM,YAAY,CAAC;AAapB,KAAK,cAAc,GAAG,KAAK,GAAG,KAAK,CAAC;AAEpC,KAAK,oBAAoB,GAAG;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAQF,MAAM,MAAM,oBAAoB,GAC7B;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GACjD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,KAAK,oBAAoB,GAAG;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,CACpB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,KAC7C,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,oBAAoB,EAAE,GAAG,IAAI,CAAC;IACtD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,CAChB,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,KACX;QACJ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,KAAK,CAAC,EAAE,KAAK,CAAC;KACd,CAAC;IACF,OAAO,CAAC,EAAE,KAAK,GAAG,CAAC,MAAM;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AA+DF,eAAO,MAAM,4BAA4B,GACxC,YAAY,MAAM,GAAG,SAAS,EAC9B,oBAA8B,EAC9B,eAAe,MAAM,GAAG,IAAI,KAC1B,OAgBF,CAAC;AA+KF,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAmJ/B"}
@@ -0,0 +1,321 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync, } from "node:fs";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
+ import { parseArgs } from "../cli/args.js";
5
+ import { getAgentDir } from "../config/constants.js";
6
+ import { getGlobalInstallCommand, getPackageName, } from "../package-metadata.js";
7
+ import { withTimeout } from "../utils/async.js";
8
+ import { checkForUpdate, resolveUpdateUrls, } from "./check.js";
9
+ const SKIP_ENV = "MAESTRO_SKIP_STARTUP_UPDATE";
10
+ const STARTUP_UPDATE_ENV = "MAESTRO_STARTUP_UPDATE";
11
+ const STARTUP_UPDATE_STATE_ENV = "MAESTRO_STARTUP_UPDATE_STATE";
12
+ const STARTUP_UPDATE_TIMEOUT_ENV = "MAESTRO_STARTUP_UPDATE_TIMEOUT_MS";
13
+ const STARTUP_UPDATE_TTL_ENV = "MAESTRO_STARTUP_UPDATE_RETRY_MS";
14
+ const DEFAULT_INSTALL_TIMEOUT_MS = 60_000;
15
+ const DEFAULT_RETRY_MS = 24 * 60 * 60 * 1_000;
16
+ const DEFAULT_STARTUP_UPDATE_TIMEOUT_MS = 1_500;
17
+ const NPM_PREFIX_TIMEOUT_MS = 2_000;
18
+ const BUN_PREFIX_TIMEOUT_MS = 2_000;
19
+ const OFF_VALUES = new Set(["0", "false", "off", "skip", "disabled"]);
20
+ const CHECK_ONLY_VALUES = new Set(["check", "notice", "notify"]);
21
+ const INSTALLABLE_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
22
+ const envValue = (env, key) => env[key]?.trim().toLowerCase() ?? "";
23
+ const toErrorMessage = (error) => {
24
+ if (error instanceof Error) {
25
+ return error.message;
26
+ }
27
+ if (typeof error === "string") {
28
+ return error;
29
+ }
30
+ return "Unknown error";
31
+ };
32
+ const shouldSkipForArgs = (args) => {
33
+ if (args[0] === "a2a") {
34
+ return "a2a command";
35
+ }
36
+ if (args[0] === "exec") {
37
+ return "non-interactive command";
38
+ }
39
+ if (args[0] === "update") {
40
+ return "manual update command";
41
+ }
42
+ if (args.includes("--version") ||
43
+ args.includes("-v") ||
44
+ args.includes("--help") ||
45
+ args.includes("-h")) {
46
+ return "immediate-exit command";
47
+ }
48
+ if (args.includes("--headless") ||
49
+ args.includes("--json") ||
50
+ args.includes("--rpc") ||
51
+ args.includes("rpc")) {
52
+ return "non-interactive command";
53
+ }
54
+ for (let i = 0; i < args.length; i++) {
55
+ const arg = args[i];
56
+ if (arg === "--mode" && args[i + 1] === "headless") {
57
+ return "headless command";
58
+ }
59
+ if (arg?.startsWith("--mode=") &&
60
+ arg.slice("--mode=".length) === "headless") {
61
+ return "headless command";
62
+ }
63
+ }
64
+ if (parseArgs(args).messages.length > 0) {
65
+ return "single-shot prompt";
66
+ }
67
+ return null;
68
+ };
69
+ export const isInstalledPackageEntrypoint = (entrypoint, packageName = getPackageName(), globalPrefix) => {
70
+ if (!entrypoint) {
71
+ return false;
72
+ }
73
+ if (isPackageEntrypointPath(entrypoint, packageName, globalPrefix)) {
74
+ return true;
75
+ }
76
+ try {
77
+ return isPackageEntrypointPath(realpathSync(entrypoint), packageName, globalPrefix);
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ };
83
+ const isPackageEntrypointPath = (entrypoint, packageName, globalPrefix) => {
84
+ const normalized = entrypoint.replace(/\\/g, "/");
85
+ const packageEntrypoint = `/node_modules/${packageName}/dist/cli.js`;
86
+ if (!normalized.endsWith(packageEntrypoint)) {
87
+ return false;
88
+ }
89
+ if (!globalPrefix) {
90
+ return true;
91
+ }
92
+ const normalizedPrefix = globalPrefix
93
+ .replace(/\\/g, "/")
94
+ .replace(/\/+$/u, "");
95
+ const normalizedPrefixes = new Set([normalizedPrefix]);
96
+ try {
97
+ normalizedPrefixes.add(realpathSync(globalPrefix).replace(/\\/g, "/").replace(/\/+$/u, ""));
98
+ }
99
+ catch {
100
+ // A missing prefix will fail the exact path checks below.
101
+ }
102
+ for (const prefix of normalizedPrefixes) {
103
+ if (normalized === `${prefix}/lib/node_modules/${packageName}/dist/cli.js` ||
104
+ normalized === `${prefix}/node_modules/${packageName}/dist/cli.js`) {
105
+ return true;
106
+ }
107
+ }
108
+ return false;
109
+ };
110
+ const resolveStatePath = (env = process.env, override) => {
111
+ if (override) {
112
+ return override;
113
+ }
114
+ if (env[STARTUP_UPDATE_STATE_ENV]) {
115
+ return resolve(env[STARTUP_UPDATE_STATE_ENV]);
116
+ }
117
+ return resolve(getAgentDir(), "startup-update-state.json");
118
+ };
119
+ const readState = (path) => {
120
+ if (!existsSync(path)) {
121
+ return null;
122
+ }
123
+ try {
124
+ return JSON.parse(readFileSync(path, "utf-8"));
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ };
130
+ const writeState = (path, state) => {
131
+ mkdirSync(dirname(path), { recursive: true });
132
+ writeFileSync(path, JSON.stringify(state, null, 2), "utf-8");
133
+ };
134
+ const retryMsFromEnv = (env) => {
135
+ const parsed = Number.parseInt(env[STARTUP_UPDATE_TTL_ENV] ?? "", 10);
136
+ if (Number.isFinite(parsed) && parsed >= 0) {
137
+ return parsed;
138
+ }
139
+ return DEFAULT_RETRY_MS;
140
+ };
141
+ const startupCheckTimeoutMsFromEnv = (env) => {
142
+ const parsed = Number.parseInt(env[STARTUP_UPDATE_TIMEOUT_ENV] ?? "", 10);
143
+ if (Number.isFinite(parsed) && parsed > 0) {
144
+ return parsed;
145
+ }
146
+ return DEFAULT_STARTUP_UPDATE_TIMEOUT_MS;
147
+ };
148
+ const startupSourceTimeoutMs = (totalTimeoutMs, sourceCount) => Math.max(1, Math.floor(totalTimeoutMs / Math.max(1, sourceCount)));
149
+ const shouldThrottleAttempt = (state, version, now, retryMs) => {
150
+ if (!state ||
151
+ state.version !== version ||
152
+ typeof state.lastAttemptAt !== "number" ||
153
+ state.lastStatus !== "failed") {
154
+ return false;
155
+ }
156
+ return now - state.lastAttemptAt < retryMs;
157
+ };
158
+ const isInstallableVersion = (version) => INSTALLABLE_VERSION.test(version);
159
+ const defaultInstallPackage = (packageManager, packageName, version) => {
160
+ const result = spawnSync(packageManager, ["install", "-g", `${packageName}@${version}`], {
161
+ encoding: "utf-8",
162
+ stdio: "pipe",
163
+ timeout: DEFAULT_INSTALL_TIMEOUT_MS,
164
+ });
165
+ return { status: result.status, error: result.error };
166
+ };
167
+ const resolveNpmGlobalPrefix = (env = process.env) => {
168
+ const result = spawnSync("npm", ["prefix", "-g"], {
169
+ encoding: "utf-8",
170
+ env,
171
+ stdio: ["ignore", "pipe", "ignore"],
172
+ timeout: NPM_PREFIX_TIMEOUT_MS,
173
+ });
174
+ if (result.error || result.status !== 0) {
175
+ return null;
176
+ }
177
+ const prefix = result.stdout.trim();
178
+ return prefix.length > 0 ? prefix : null;
179
+ };
180
+ const resolveBunGlobalPrefix = (env = process.env) => {
181
+ const result = spawnSync("bun", ["pm", "bin", "-g"], {
182
+ encoding: "utf-8",
183
+ env,
184
+ stdio: ["ignore", "pipe", "ignore"],
185
+ timeout: BUN_PREFIX_TIMEOUT_MS,
186
+ });
187
+ if (result.error || result.status !== 0) {
188
+ return null;
189
+ }
190
+ const binDir = result.stdout.trim();
191
+ if (!binDir) {
192
+ return null;
193
+ }
194
+ const bunHome = basename(binDir) === "bin" ? dirname(binDir) : binDir;
195
+ return join(bunHome, "install", "global");
196
+ };
197
+ const resolveGlobalInstallContexts = (env = process.env) => {
198
+ const contexts = [];
199
+ const npmPrefix = resolveNpmGlobalPrefix(env);
200
+ if (npmPrefix) {
201
+ contexts.push({ packageManager: "npm", prefix: npmPrefix });
202
+ }
203
+ const bunPrefix = resolveBunGlobalPrefix(env);
204
+ if (bunPrefix) {
205
+ contexts.push({ packageManager: "bun", prefix: bunPrefix });
206
+ }
207
+ return contexts;
208
+ };
209
+ export async function attemptStartupUpdate(options) {
210
+ const env = options.env ?? process.env;
211
+ const args = options.args ?? process.argv.slice(2);
212
+ const argv = options.argv ?? process.argv;
213
+ const packageName = options.packageName ?? getPackageName();
214
+ const updateMode = envValue(env, STARTUP_UPDATE_ENV);
215
+ const now = options.now ?? Date.now();
216
+ if (env[SKIP_ENV]) {
217
+ return { status: "skipped", reason: `${SKIP_ENV} is set` };
218
+ }
219
+ if (env.CI || env.NODE_ENV === "test") {
220
+ return { status: "skipped", reason: "test or CI environment" };
221
+ }
222
+ if (OFF_VALUES.has(updateMode)) {
223
+ return { status: "skipped", reason: `${STARTUP_UPDATE_ENV} is disabled` };
224
+ }
225
+ if (!(options.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) {
226
+ return { status: "skipped", reason: "non-TTY startup" };
227
+ }
228
+ const argsSkipReason = shouldSkipForArgs(args);
229
+ if (argsSkipReason) {
230
+ return { status: "skipped", reason: argsSkipReason };
231
+ }
232
+ const globalInstallContexts = options.globalInstallContexts ??
233
+ (options.globalPrefix === undefined
234
+ ? resolveGlobalInstallContexts(env)
235
+ : options.globalPrefix
236
+ ? [{ packageManager: "npm", prefix: options.globalPrefix }]
237
+ : []);
238
+ if (globalInstallContexts.length === 0) {
239
+ return {
240
+ status: "skipped",
241
+ reason: options.globalPrefix === null
242
+ ? "npm global prefix unavailable"
243
+ : "global package prefix unavailable",
244
+ };
245
+ }
246
+ const installContext = globalInstallContexts.find((context) => isInstalledPackageEntrypoint(argv[1], packageName, context.prefix));
247
+ if (!installContext) {
248
+ return {
249
+ status: "skipped",
250
+ reason: "not running installed global package",
251
+ };
252
+ }
253
+ const checkTimeoutMs = options.checkTimeoutMs ?? startupCheckTimeoutMsFromEnv(env);
254
+ const updateUrls = resolveUpdateUrls({}, env);
255
+ const checkOptions = {
256
+ timeoutMs: startupSourceTimeoutMs(checkTimeoutMs, updateUrls.length),
257
+ urls: updateUrls,
258
+ };
259
+ const check = await withTimeout((options.checkForUpdateImpl ?? checkForUpdate)(options.currentVersion, checkOptions), checkTimeoutMs, `Startup update check timed out after ${checkTimeoutMs}ms`).catch((error) => ({
260
+ currentVersion: options.currentVersion,
261
+ isUpdateAvailable: false,
262
+ sourceUrl: "",
263
+ error: toErrorMessage(error),
264
+ }));
265
+ if (check.error) {
266
+ return { status: "failed", check, error: check.error };
267
+ }
268
+ if (!check.isUpdateAvailable || !check.latestVersion) {
269
+ return { status: "current", check };
270
+ }
271
+ if (CHECK_ONLY_VALUES.has(updateMode)) {
272
+ return { status: "available", check };
273
+ }
274
+ if (!isInstallableVersion(check.latestVersion)) {
275
+ return {
276
+ status: "failed",
277
+ check,
278
+ error: `Refusing to install unsupported version ${check.latestVersion}`,
279
+ };
280
+ }
281
+ const statePath = resolveStatePath(env, options.statePath);
282
+ if (shouldThrottleAttempt(readState(statePath), check.latestVersion, now, retryMsFromEnv(env))) {
283
+ return { status: "available", check };
284
+ }
285
+ writeState(statePath, {
286
+ version: check.latestVersion,
287
+ lastAttemptAt: now,
288
+ lastStatus: "failed",
289
+ });
290
+ const install = (options.installPackage ?? defaultInstallPackage)(installContext.packageManager, packageName, check.latestVersion);
291
+ if (install.error || install.status !== 0) {
292
+ return {
293
+ status: "failed",
294
+ check,
295
+ error: install.error?.message ??
296
+ `${getGlobalInstallCommand(installContext.packageManager, `${packageName}@${check.latestVersion}`)} exited ${install.status}`,
297
+ };
298
+ }
299
+ writeState(statePath, {
300
+ version: check.latestVersion,
301
+ lastAttemptAt: now,
302
+ lastStatus: "updated",
303
+ });
304
+ if (options.restart === false) {
305
+ return { status: "updated", check };
306
+ }
307
+ const restart = options.restart ??
308
+ (() => {
309
+ const result = spawnSync(process.execPath, argv.slice(1), {
310
+ stdio: "inherit",
311
+ env: { ...env, [SKIP_ENV]: "1" },
312
+ });
313
+ return { status: result.status, error: result.error };
314
+ });
315
+ const restarted = restart();
316
+ if (restarted.error) {
317
+ return { status: "updated", check };
318
+ }
319
+ return { status: "restarted", check, exitCode: restarted.status ?? 0 };
320
+ }
321
+ //# sourceMappingURL=startup-refresh.js.map