fss-link 1.5.7 → 1.6.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 (40) hide show
  1. package/README.md +1 -1
  2. package/bundle/fss-link.js +4785 -3183
  3. package/package.json +4 -1
  4. package/scripts/analyze-session-logs.sh +279 -0
  5. package/scripts/build.js +55 -0
  6. package/scripts/build_package.js +37 -0
  7. package/scripts/build_sandbox.js +195 -0
  8. package/scripts/build_vscode_companion.js +30 -0
  9. package/scripts/check-build-status.js +148 -0
  10. package/scripts/check-publish.js +101 -0
  11. package/scripts/clean.js +55 -0
  12. package/scripts/copy_bundle_assets.js +40 -0
  13. package/scripts/copy_files.js +56 -0
  14. package/scripts/create_alias.sh +39 -0
  15. package/scripts/emergency-kill-all-tests.sh +95 -0
  16. package/scripts/emergency-kill-vitest.sh +95 -0
  17. package/scripts/extract-session-logs.sh +202 -0
  18. package/scripts/generate-git-commit-info.js +71 -0
  19. package/scripts/get-previous-tag.js +213 -0
  20. package/scripts/get-release-version.js +119 -0
  21. package/scripts/index-session-logs.sh +173 -0
  22. package/scripts/install-linux.sh +294 -0
  23. package/scripts/install-macos.sh +343 -0
  24. package/scripts/install-windows.ps1 +427 -0
  25. package/scripts/local_telemetry.js +219 -0
  26. package/scripts/memory-monitor.sh +165 -0
  27. package/scripts/postinstall-message.js +31 -0
  28. package/scripts/prepare-package.js +51 -0
  29. package/scripts/process-session-log.py +302 -0
  30. package/scripts/quick-install.sh +195 -0
  31. package/scripts/sandbox_command.js +126 -0
  32. package/scripts/start.js +76 -0
  33. package/scripts/telemetry.js +85 -0
  34. package/scripts/telemetry_gcp.js +188 -0
  35. package/scripts/telemetry_utils.js +421 -0
  36. package/scripts/test-windows-paths.js +51 -0
  37. package/scripts/tests/get-release-version.test.js +110 -0
  38. package/scripts/tests/test-setup.ts +12 -0
  39. package/scripts/tests/vitest.config.ts +20 -0
  40. package/scripts/version.js +83 -0
@@ -0,0 +1,202 @@
1
+ #!/bin/bash
2
+ # FSS Link Session Log Extractor
3
+ # Extracts and organizes session logs from FSS Link checkpoint files
4
+
5
+ set -euo pipefail
6
+
7
+ # Configuration
8
+ FSS_LINK_TMP_DIR="${HOME}/.fss-link/tmp"
9
+ OUTPUT_DIR="/MASTERFOLDER/Projects/fss-link/fss-live-testing/session-logs"
10
+ RAW_DIR="${OUTPUT_DIR}/raw"
11
+ PROCESSED_DIR="${OUTPUT_DIR}/processed"
12
+
13
+ # Colors for output
14
+ RED='\033[0;31m'
15
+ GREEN='\033[0;32m'
16
+ YELLOW='\033[1;33m'
17
+ BLUE='\033[0;34m'
18
+ NC='\033[0m' # No Color
19
+
20
+ # Usage information
21
+ usage() {
22
+ cat << EOF
23
+ Usage: $0 [OPTIONS]
24
+
25
+ Extract FSS Link session logs and prepare them for RAG indexing.
26
+
27
+ OPTIONS:
28
+ -d, --days DAYS Extract logs from last N days (default: 7)
29
+ -a, --all Extract all available logs
30
+ -t, --tool TOOL Filter by tool name (e.g., grep, read, write)
31
+ -s, --session ID Extract specific session by ID
32
+ -l, --list List available sessions
33
+ -h, --help Show this help message
34
+
35
+ EXAMPLES:
36
+ $0 --days 1 # Extract logs from last 24 hours
37
+ $0 --tool grep # Extract only grep-related sessions
38
+ $0 --session 05e09634... # Extract specific session
39
+ $0 --list # List all available sessions
40
+
41
+ OUTPUT:
42
+ Raw logs: ${RAW_DIR}/
43
+ Processed logs: ${PROCESSED_DIR}/
44
+ Analysis: ${OUTPUT_DIR}/analysis/
45
+ EOF
46
+ exit 1
47
+ }
48
+
49
+ # Parse arguments
50
+ DAYS=7
51
+ EXTRACT_ALL=false
52
+ TOOL_FILTER=""
53
+ SESSION_ID=""
54
+ LIST_ONLY=false
55
+
56
+ while [[ $# -gt 0 ]]; do
57
+ case $1 in
58
+ -d|--days)
59
+ DAYS="$2"
60
+ shift 2
61
+ ;;
62
+ -a|--all)
63
+ EXTRACT_ALL=true
64
+ shift
65
+ ;;
66
+ -t|--tool)
67
+ TOOL_FILTER="$2"
68
+ shift 2
69
+ ;;
70
+ -s|--session)
71
+ SESSION_ID="$2"
72
+ shift 2
73
+ ;;
74
+ -l|--list)
75
+ LIST_ONLY=true
76
+ shift
77
+ ;;
78
+ -h|--help)
79
+ usage
80
+ ;;
81
+ *)
82
+ echo -e "${RED}Unknown option: $1${NC}"
83
+ usage
84
+ ;;
85
+ esac
86
+ done
87
+
88
+ # Ensure output directories exist
89
+ mkdir -p "${RAW_DIR}" "${PROCESSED_DIR}" "${OUTPUT_DIR}/analysis"
90
+
91
+ # List available sessions
92
+ list_sessions() {
93
+ echo -e "${BLUE}=== Available FSS Link Sessions ===${NC}\n"
94
+
95
+ if [[ ! -d "${FSS_LINK_TMP_DIR}" ]]; then
96
+ echo -e "${RED}FSS Link tmp directory not found: ${FSS_LINK_TMP_DIR}${NC}"
97
+ exit 1
98
+ fi
99
+
100
+ find "${FSS_LINK_TMP_DIR}" -name "checkpoint*.json" -type f | while read -r checkpoint; do
101
+ session_dir=$(dirname "${checkpoint}")
102
+ session_id=$(basename "${session_dir}")
103
+ file_size=$(du -h "${checkpoint}" | cut -f1)
104
+ mod_time=$(stat -c '%y' "${checkpoint}" 2>/dev/null || stat -f '%Sm' "${checkpoint}")
105
+
106
+ # Try to extract some context from the file
107
+ context=$(jq -r '.messages[2].parts[0].text // "Unknown"' "${checkpoint}" 2>/dev/null | head -c 100)
108
+
109
+ echo -e "${GREEN}Session:${NC} ${session_id}"
110
+ echo -e " ${YELLOW}File:${NC} ${checkpoint}"
111
+ echo -e " ${YELLOW}Size:${NC} ${file_size}"
112
+ echo -e " ${YELLOW}Modified:${NC} ${mod_time}"
113
+ echo -e " ${YELLOW}Context:${NC} ${context}..."
114
+ echo ""
115
+ done
116
+ }
117
+
118
+ # Extract specific session
119
+ extract_session() {
120
+ local session_path="$1"
121
+ local session_id="$2"
122
+ local output_name="$3"
123
+
124
+ echo -e "${BLUE}Extracting session: ${session_id}${NC}"
125
+
126
+ # Copy raw log
127
+ cp "${session_path}" "${RAW_DIR}/${output_name}.json"
128
+ echo -e "${GREEN}✓${NC} Raw log saved: ${RAW_DIR}/${output_name}.json"
129
+
130
+ # Process log with Python script
131
+ python3 /MASTERFOLDER/Projects/fss-link/scripts/process-session-log.py \
132
+ "${session_path}" \
133
+ "${PROCESSED_DIR}/${output_name}.md"
134
+
135
+ if [[ $? -eq 0 ]]; then
136
+ echo -e "${GREEN}✓${NC} Processed log saved: ${PROCESSED_DIR}/${output_name}.md"
137
+ else
138
+ echo -e "${RED}✗${NC} Failed to process session log"
139
+ return 1
140
+ fi
141
+ }
142
+
143
+ # Main execution
144
+ main() {
145
+ if [[ "${LIST_ONLY}" == "true" ]]; then
146
+ list_sessions
147
+ exit 0
148
+ fi
149
+
150
+ echo -e "${BLUE}=== FSS Link Session Log Extractor ===${NC}\n"
151
+
152
+ # Specific session extraction
153
+ if [[ -n "${SESSION_ID}" ]]; then
154
+ session_path="${FSS_LINK_TMP_DIR}/${SESSION_ID}/checkpoint-fileRead.json"
155
+ if [[ ! -f "${session_path}" ]]; then
156
+ echo -e "${RED}Session not found: ${SESSION_ID}${NC}"
157
+ exit 1
158
+ fi
159
+
160
+ timestamp=$(date +%Y%m%d-%H%M%S)
161
+ extract_session "${session_path}" "${SESSION_ID}" "session-${timestamp}"
162
+ exit 0
163
+ fi
164
+
165
+ # Extract sessions based on filters
166
+ local count=0
167
+ find "${FSS_LINK_TMP_DIR}" -name "checkpoint*.json" -type f | while read -r checkpoint; do
168
+ session_dir=$(dirname "${checkpoint}")
169
+ session_id=$(basename "${session_dir}")
170
+
171
+ # Apply time filter
172
+ if [[ "${EXTRACT_ALL}" == "false" ]]; then
173
+ # Check if file was modified in last N days
174
+ if [[ $(find "${checkpoint}" -mtime "-${DAYS}" | wc -l) -eq 0 ]]; then
175
+ continue
176
+ fi
177
+ fi
178
+
179
+ # Apply tool filter
180
+ if [[ -n "${TOOL_FILTER}" ]]; then
181
+ if ! grep -qi "${TOOL_FILTER}" "${checkpoint}" 2>/dev/null; then
182
+ continue
183
+ fi
184
+ fi
185
+
186
+ # Extract this session
187
+ mod_date=$(stat -c '%Y' "${checkpoint}" 2>/dev/null || stat -f '%m' "${checkpoint}")
188
+ formatted_date=$(date -d "@${mod_date}" +%Y%m%d-%H%M%S 2>/dev/null || date -r "${mod_date}" +%Y%m%d-%H%M%S)
189
+ output_name="session-${formatted_date}-${session_id:0:8}"
190
+
191
+ extract_session "${checkpoint}" "${session_id}" "${output_name}"
192
+ ((count++))
193
+ done
194
+
195
+ echo -e "\n${GREEN}Extraction complete: ${count} sessions processed${NC}"
196
+ echo -e "${YELLOW}Next steps:${NC}"
197
+ echo -e " 1. Review processed logs in: ${PROCESSED_DIR}/"
198
+ echo -e " 2. Index with RAG: cd ${OUTPUT_DIR} && rag-index create processed/"
199
+ echo -e " 3. Search logs: rag fss-link-sessions 'your query'"
200
+ }
201
+
202
+ main
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ //
8
+ // Licensed under the Apache License, Version 2.0 (the "License");
9
+ // you may not use this file except in compliance with the License.
10
+ // You may obtain a copy of the License at
11
+ //
12
+ // http://www.apache.org/licenses/LICENSE-2.0
13
+ //
14
+ // Unless required by applicable law or agreed to in writing, software
15
+ // distributed under the License is distributed on an "AS IS" BASIS,
16
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ // See the License for the specific language governing permissions and
18
+ // limitations under the License.
19
+
20
+ import { execSync } from 'child_process';
21
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
22
+ import { dirname, join, relative } from 'path';
23
+ import { fileURLToPath } from 'url';
24
+ import { readPackageUp } from 'read-package-up';
25
+
26
+ const __dirname = dirname(fileURLToPath(import.meta.url));
27
+ const root = join(__dirname, '..');
28
+ const scriptPath = relative(root, fileURLToPath(import.meta.url));
29
+ const generatedCliDir = join(root, 'packages/cli/src/generated');
30
+ const cliGitCommitFile = join(generatedCliDir, 'git-commit.ts');
31
+ const generatedCoreDir = join(root, 'packages/core/src/generated');
32
+ const coreGitCommitFile = join(generatedCoreDir, 'git-commit.ts');
33
+ let gitCommitInfo = 'N/A';
34
+ let cliVersion = 'UNKNOWN';
35
+
36
+ if (!existsSync(generatedCliDir)) {
37
+ mkdirSync(generatedCliDir, { recursive: true });
38
+ }
39
+
40
+ if (!existsSync(generatedCoreDir)) {
41
+ mkdirSync(generatedCoreDir, { recursive: true });
42
+ }
43
+
44
+ try {
45
+ const gitHash = execSync('git rev-parse --short HEAD', {
46
+ encoding: 'utf-8',
47
+ }).trim();
48
+ if (gitHash) {
49
+ gitCommitInfo = gitHash;
50
+ }
51
+
52
+ const result = await readPackageUp();
53
+ cliVersion = result?.packageJson?.version ?? 'UNKNOWN';
54
+ } catch {
55
+ // ignore
56
+ }
57
+
58
+ const fileContent = `/**
59
+ * @license
60
+ * Copyright ${new Date().getFullYear()} Google LLC
61
+ * SPDX-License-Identifier: Apache-2.0
62
+ */
63
+
64
+ // This file is auto-generated by the build script (${scriptPath})
65
+ // Do not edit this file manually.
66
+ export const GIT_COMMIT_INFO = '${gitCommitInfo}';
67
+ export const CLI_VERSION = '${cliVersion}';
68
+ `;
69
+
70
+ writeFileSync(cliGitCommitFile, fileContent);
71
+ writeFileSync(coreGitCommitFile, fileContent);
@@ -0,0 +1,213 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { execSync } from 'child_process';
8
+
9
+ /**
10
+ * Determines the correct previous tag for release notes generation.
11
+ * This function handles the complexity of mixed tag types (regular releases vs nightly releases).
12
+ *
13
+ * @param {string} currentTag - The current release tag (e.g., "v0.1.23")
14
+ * @returns {string|null} - The previous tag to compare against, or null if no suitable tag found
15
+ */
16
+ export function getPreviousTag(currentTag) {
17
+ try {
18
+ // Parse the current tag to understand its type
19
+ const currentTagInfo = parseTag(currentTag);
20
+ if (!currentTagInfo) {
21
+ console.error(`Invalid current tag format: ${currentTag}`);
22
+ return null;
23
+ }
24
+
25
+ // Find the appropriate previous tag based on the current tag type
26
+ let previousTag = null;
27
+
28
+ if (currentTagInfo.isNightly) {
29
+ // For nightly releases, find the last stable release
30
+ previousTag = findLastStableTag(currentTagInfo);
31
+ } else {
32
+ // For stable releases, find the previous stable release
33
+ previousTag = findPreviousStableTag(currentTagInfo);
34
+ }
35
+
36
+ return previousTag;
37
+ } catch (error) {
38
+ console.error('Error getting previous tag:', error.message);
39
+ return null;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Parses a tag string to extract version information and type
45
+ */
46
+ function parseTag(tag) {
47
+ // Remove 'v' prefix if present
48
+ const cleanTag = tag.startsWith('v') ? tag.substring(1) : tag;
49
+
50
+ // Match pattern: X.Y.Z or X.Y.Z-prerelease
51
+ const match = cleanTag.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
52
+ if (!match) {
53
+ return null;
54
+ }
55
+
56
+ const [, major, minor, patch, prerelease] = match;
57
+
58
+ return {
59
+ original: tag,
60
+ major: parseInt(major),
61
+ minor: parseInt(minor),
62
+ patch: parseInt(patch),
63
+ prerelease: prerelease || null,
64
+ isNightly: prerelease && prerelease.startsWith('nightly'),
65
+ isPreview: prerelease && prerelease.startsWith('preview'),
66
+ version: `${major}.${minor}.${patch}`,
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Finds the last stable tag for a nightly release
72
+ * Assumes version numbers are incremental and checks backwards from current version
73
+ */
74
+ function findLastStableTag(currentTagInfo) {
75
+ // For nightly releases, find the stable version of the same version number first
76
+ const baseVersion = `v${currentTagInfo.version}`;
77
+
78
+ // Check if the stable version of the current version exists
79
+ if (tagExists(baseVersion)) {
80
+ return baseVersion;
81
+ }
82
+
83
+ // If not, look for the previous stable versions by decrementing version numbers
84
+ let { major, minor, patch } = currentTagInfo;
85
+
86
+ // Try decrementing patch version first
87
+ while (patch > 0) {
88
+ patch--;
89
+ const candidateTag = `v${major}.${minor}.${patch}`;
90
+ if (tagExists(candidateTag)) {
91
+ return candidateTag;
92
+ }
93
+ }
94
+
95
+ // Try decrementing minor version
96
+ while (minor > 0) {
97
+ minor--;
98
+ patch = 999; // Start from a high patch number and work backwards
99
+ while (patch >= 0) {
100
+ const candidateTag = `v${major}.${minor}.${patch}`;
101
+ if (tagExists(candidateTag)) {
102
+ return candidateTag;
103
+ }
104
+ patch--;
105
+ // Don't check too many patch versions to avoid infinite loops
106
+ if (patch < 0) break;
107
+ }
108
+ }
109
+
110
+ // Try decrementing major version
111
+ while (major > 0) {
112
+ major--;
113
+ minor = 999; // Start from a high minor number and work backwards
114
+ while (minor >= 0) {
115
+ patch = 999;
116
+ while (patch >= 0) {
117
+ const candidateTag = `v${major}.${minor}.${patch}`;
118
+ if (tagExists(candidateTag)) {
119
+ return candidateTag;
120
+ }
121
+ patch--;
122
+ if (patch < 0) break;
123
+ }
124
+ minor--;
125
+ if (minor < 0) break;
126
+ }
127
+ }
128
+
129
+ return null;
130
+ }
131
+
132
+ /**
133
+ * Finds the previous stable tag for a stable release
134
+ * Assumes version numbers are incremental and checks backwards from current version
135
+ */
136
+ function findPreviousStableTag(currentTagInfo) {
137
+ let { major, minor, patch } = currentTagInfo;
138
+
139
+ // Try decrementing patch version first
140
+ while (patch > 0) {
141
+ patch--;
142
+ const candidateTag = `v${major}.${minor}.${patch}`;
143
+ if (tagExists(candidateTag)) {
144
+ return candidateTag;
145
+ }
146
+ }
147
+
148
+ // Try decrementing minor version
149
+ while (minor > 0) {
150
+ minor--;
151
+ patch = 999; // Start from a high patch number and work backwards
152
+ while (patch >= 0) {
153
+ const candidateTag = `v${major}.${minor}.${patch}`;
154
+ if (tagExists(candidateTag)) {
155
+ return candidateTag;
156
+ }
157
+ patch--;
158
+ // Don't check too many patch versions to avoid infinite loops
159
+ if (patch < 0) break;
160
+ }
161
+ }
162
+
163
+ // Try decrementing major version
164
+ while (major > 0) {
165
+ major--;
166
+ minor = 999; // Start from a high minor number and work backwards
167
+ while (minor >= 0) {
168
+ patch = 999;
169
+ while (patch >= 0) {
170
+ const candidateTag = `v${major}.${minor}.${patch}`;
171
+ if (tagExists(candidateTag)) {
172
+ return candidateTag;
173
+ }
174
+ patch--;
175
+ if (patch < 0) break;
176
+ }
177
+ minor--;
178
+ if (minor < 0) break;
179
+ }
180
+ }
181
+
182
+ return null;
183
+ }
184
+
185
+ /**
186
+ * Checks if a git tag exists
187
+ */
188
+ function tagExists(tag) {
189
+ try {
190
+ execSync(`git rev-parse --verify ${tag}`, { stdio: 'ignore' });
191
+ return true;
192
+ } catch {
193
+ return false;
194
+ }
195
+ }
196
+
197
+ // CLI usage
198
+ if (process.argv[1] === new URL(import.meta.url).pathname) {
199
+ const currentTag = process.argv[2];
200
+
201
+ if (!currentTag) {
202
+ console.error('Usage: node get-previous-tag.js <current-tag>');
203
+ process.exit(1);
204
+ }
205
+
206
+ const previousTag = getPreviousTag(currentTag);
207
+ if (previousTag) {
208
+ console.log(previousTag);
209
+ } else {
210
+ console.error('No suitable previous tag found');
211
+ process.exit(1);
212
+ }
213
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { execSync } from 'child_process';
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+
11
+ function getPackageVersion() {
12
+ const packageJsonPath = path.resolve(process.cwd(), 'package.json');
13
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
14
+ return packageJson.version;
15
+ }
16
+
17
+ function incrementPatchVersion(version) {
18
+ const parts = version.split('.');
19
+ const major = parseInt(parts[0]);
20
+ const minor = parseInt(parts[1]);
21
+ const patch = parseInt(parts[2].split('-')[0]); // Handle pre-release versions
22
+ return `${major}.${minor}.${patch + 1}`;
23
+ }
24
+
25
+ function getLatestNightlyCount() {
26
+ try {
27
+ // Try to get the latest nightly tag from git to determine the counter
28
+ const currentVersion = getPackageVersion();
29
+ const nextVersion = incrementPatchVersion(currentVersion);
30
+ const tags = execSync(`git tag -l "v${nextVersion}-nightly.*"`)
31
+ .toString()
32
+ .trim();
33
+
34
+ if (!tags) return 0;
35
+
36
+ const nightlyTags = tags.split('\n').filter(Boolean);
37
+ const counts = nightlyTags.map((tag) => {
38
+ const match = tag.match(/nightly\.(\d+)$/);
39
+ return match ? parseInt(match[1]) : 0;
40
+ });
41
+
42
+ return Math.max(...counts, -1) + 1;
43
+ } catch (_error) {
44
+ // If we can't get tags, start from 0
45
+ return 0;
46
+ }
47
+ }
48
+
49
+ export function getNightlyTagName() {
50
+ const version = getPackageVersion();
51
+ const nextVersion = incrementPatchVersion(version);
52
+ const nightlyCount = getLatestNightlyCount();
53
+
54
+ return `v${nextVersion}-nightly.${nightlyCount}`;
55
+ }
56
+
57
+ export function getReleaseVersion() {
58
+ const isNightly = process.env.IS_NIGHTLY === 'true';
59
+ const manualVersion = process.env.MANUAL_VERSION;
60
+
61
+ let releaseTag;
62
+
63
+ if (isNightly) {
64
+ console.error('Calculating next nightly version...');
65
+ releaseTag = getNightlyTagName();
66
+ } else if (manualVersion) {
67
+ console.error(`Using manual version: ${manualVersion}`);
68
+ releaseTag = manualVersion;
69
+ } else {
70
+ throw new Error(
71
+ 'Error: No version specified and this is not a nightly release.',
72
+ );
73
+ }
74
+
75
+ if (!releaseTag) {
76
+ throw new Error('Error: Version could not be determined.');
77
+ }
78
+
79
+ if (!releaseTag.startsWith('v')) {
80
+ console.error("Version is missing 'v' prefix. Prepending it.");
81
+ releaseTag = `v${releaseTag}`;
82
+ }
83
+
84
+ if (releaseTag.includes('+')) {
85
+ throw new Error(
86
+ 'Error: Versions with build metadata (+) are not supported for releases. Please use a pre-release version (e.g., v1.2.3-alpha.4) instead.',
87
+ );
88
+ }
89
+
90
+ if (!releaseTag.match(/^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$/)) {
91
+ throw new Error(
92
+ 'Error: Version must be in the format vX.Y.Z or vX.Y.Z-prerelease',
93
+ );
94
+ }
95
+
96
+ const releaseVersion = releaseTag.substring(1);
97
+ let npmTag = 'latest';
98
+ if (releaseVersion.includes('-')) {
99
+ const prereleasePart = releaseVersion.split('-')[1];
100
+ npmTag = prereleasePart.split('.')[0];
101
+
102
+ // Ensure nightly releases use 'nightly' tag, not 'latest'
103
+ if (npmTag === 'nightly') {
104
+ npmTag = 'nightly';
105
+ }
106
+ }
107
+
108
+ return { releaseTag, releaseVersion, npmTag };
109
+ }
110
+
111
+ if (process.argv[1] === new URL(import.meta.url).pathname) {
112
+ try {
113
+ const versions = getReleaseVersion();
114
+ console.log(JSON.stringify(versions));
115
+ } catch (error) {
116
+ console.error(error.message);
117
+ process.exit(1);
118
+ }
119
+ }