summaryrefslogtreecommitdiff
path: root/archived/projt-launcher/ci/github-script
diff options
context:
space:
mode:
Diffstat (limited to 'archived/projt-launcher/ci/github-script')
-rw-r--r--archived/projt-launcher/ci/github-script/.editorconfig3
-rw-r--r--archived/projt-launcher/ci/github-script/.gitignore2
-rw-r--r--archived/projt-launcher/ci/github-script/.npmrc2
-rw-r--r--archived/projt-launcher/ci/github-script/backport.js688
-rw-r--r--archived/projt-launcher/ci/github-script/commit-types.json70
-rw-r--r--archived/projt-launcher/ci/github-script/commits.js336
-rw-r--r--archived/projt-launcher/ci/github-script/get-teams.js134
-rw-r--r--archived/projt-launcher/ci/github-script/merge.js308
-rw-r--r--archived/projt-launcher/ci/github-script/package-lock.json1721
-rw-r--r--archived/projt-launcher/ci/github-script/package.json19
-rw-r--r--archived/projt-launcher/ci/github-script/prepare.js314
-rw-r--r--archived/projt-launcher/ci/github-script/reviewers.js329
-rw-r--r--archived/projt-launcher/ci/github-script/reviews.js93
-rw-r--r--archived/projt-launcher/ci/github-script/run132
-rw-r--r--archived/projt-launcher/ci/github-script/shell.nix40
-rw-r--r--archived/projt-launcher/ci/github-script/test/commits.test.js42
-rw-r--r--archived/projt-launcher/ci/github-script/withRateLimit.js86
17 files changed, 4319 insertions, 0 deletions
diff --git a/archived/projt-launcher/ci/github-script/.editorconfig b/archived/projt-launcher/ci/github-script/.editorconfig
new file mode 100644
index 0000000000..67d678ef17
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/.editorconfig
@@ -0,0 +1,3 @@
+[run]
+indent_style = space
+indent_size = 2
diff --git a/archived/projt-launcher/ci/github-script/.gitignore b/archived/projt-launcher/ci/github-script/.gitignore
new file mode 100644
index 0000000000..6b8a37657b
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+step-summary.md
diff --git a/archived/projt-launcher/ci/github-script/.npmrc b/archived/projt-launcher/ci/github-script/.npmrc
new file mode 100644
index 0000000000..fb41d64f46
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/.npmrc
@@ -0,0 +1,2 @@
+package-lock-only = true
+save-exact = true
diff --git a/archived/projt-launcher/ci/github-script/backport.js b/archived/projt-launcher/ci/github-script/backport.js
new file mode 100644
index 0000000000..4d63a38875
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/backport.js
@@ -0,0 +1,688 @@
+/**
+ * ProjT Launcher - Backport Handler
+ * Handles backport requests via PR comments.
+ *
+ * Command (single line):
+ * @projt-launcher-bot backport <target...> [--force] [--no-pr]
+ *
+ * Targets:
+ * - release-* branch name (e.g. release-1.2.3)
+ * - latest (highest versioned release-*)
+ * - all (all release-* branches)
+ *
+ * If no targets are provided, it falls back to PR labels:
+ * backport/<branch>, backport/latest, backport/all
+ */
+
+const { execFile } = require('node:child_process')
+const { promisify } = require('node:util')
+
+const execFileAsync = promisify(execFile)
+
+function stripNoise(body = '') {
+ return String(body)
+ .replace(/\r/g, '')
+ .replace(/<!--.*?-->/gms, '')
+ .replace(/(^`{3,})[^`].*?\1/gms, '')
+}
+
+function tokenize(argString) {
+ const tokens = []
+ let i = 0
+ let current = ''
+ let quote = null
+
+ const push = () => {
+ if (current.length > 0) tokens.push(current)
+ current = ''
+ }
+
+ while (i < argString.length) {
+ const ch = argString[i]
+
+ if (quote) {
+ if (ch === quote) {
+ quote = null
+ } else if (ch === '\\' && i + 1 < argString.length) {
+ i++
+ current += argString[i]
+ } else {
+ current += ch
+ }
+ i++
+ continue
+ }
+
+ if (ch === '"' || ch === "'") {
+ quote = ch
+ i++
+ continue
+ }
+
+ if (/\s/.test(ch)) {
+ push()
+ i++
+ while (i < argString.length && /\s/.test(argString[i])) i++
+ continue
+ }
+
+ current += ch
+ i++
+ }
+
+ push()
+ return tokens
+}
+
+function parseBackportCommand(body) {
+ const cleaned = stripNoise(body)
+ const match = cleaned.match(/^@projt-launcher-bot\s+backport\b(.*)$/im)
+ if (!match) return null
+
+ const tokens = tokenize(match[1] ?? '')
+ const targets = []
+ const options = {
+ force: false,
+ noPr: false,
+ }
+
+ for (let idx = 0; idx < tokens.length; idx++) {
+ const t = tokens[idx]
+ if (!t) continue
+
+ if (t === '--force') {
+ options.force = true
+ continue
+ }
+
+ if (t === '--no-pr') {
+ options.noPr = true
+ continue
+ }
+
+ if (t === '--to') {
+ const next = tokens[idx + 1]
+ if (next) {
+ targets.push(next)
+ idx++
+ }
+ continue
+ }
+
+ if (t.startsWith('--to=')) {
+ targets.push(t.slice('--to='.length))
+ continue
+ }
+
+ if (t.startsWith('-')) {
+ continue
+ }
+
+ targets.push(t)
+ }
+
+ return { targets, options }
+}
+
+function parseReleaseVersionTuple(branch) {
+ const m = String(branch).match(/^release-(v?\d+(?:\.\d+){1,2})(?:$|[-_].*)$/i)
+ if (!m) return null
+ const parts = m[1].replace(/^v/i, '').split('.').map((p) => Number(p))
+ while (parts.length < 3) parts.push(0)
+ if (parts.some((n) => Number.isNaN(n))) return null
+ return parts
+}
+
+function compareVersionTuples(a, b) {
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
+ const av = a[i] ?? 0
+ const bv = b[i] ?? 0
+ if (av !== bv) return av - bv
+ }
+ return 0
+}
+
+async function addReaction({ github, node_id, reaction }) {
+ await github.graphql(
+ `mutation($node_id: ID!, $reaction: ReactionContent!) {
+ addReaction(input: { content: $reaction, subjectId: $node_id }) {
+ clientMutationId
+ }
+ }`,
+ { node_id, reaction },
+ )
+}
+
+async function listReleaseBranches({ github, context }) {
+ const branches = await github.paginate(github.rest.repos.listBranches, {
+ ...context.repo,
+ per_page: 100,
+ })
+ return branches.map((b) => b.name).filter((n) => /^release-/.test(n))
+}
+
+async function resolveTargets({ github, context, core, pull_request, requestedTargets }) {
+ const releaseBranches = await listReleaseBranches({ github, context })
+ const releaseSet = new Set(releaseBranches)
+
+ const normalized = (requestedTargets ?? [])
+ .map((t) => String(t).trim())
+ .filter(Boolean)
+
+ const wantsAll = normalized.includes('all')
+ const wantsLatest = normalized.includes('latest')
+
+ const explicit = normalized.filter((t) => t !== 'all' && t !== 'latest')
+
+ const resolved = new Set()
+
+ if (wantsAll) {
+ for (const b of releaseBranches) resolved.add(b)
+ }
+
+ if (wantsLatest) {
+ const candidates = releaseBranches
+ .map((b) => ({ b, v: parseReleaseVersionTuple(b) }))
+ .filter((x) => x.v)
+ .sort((x, y) => compareVersionTuples(x.v, y.v))
+
+ if (candidates.length > 0) {
+ resolved.add(candidates[candidates.length - 1].b)
+ } else {
+ core.warning('No versioned release-* branches found for target "latest"')
+ }
+ }
+
+ for (const t of explicit) {
+ if (releaseSet.has(t)) {
+ resolved.add(t)
+ } else {
+ core.warning(`Ignoring unknown target branch: ${t}`)
+ }
+ }
+
+ // Fallback to PR labels if comment had no targets.
+ if (resolved.size === 0) {
+ const labels = (pull_request.labels ?? []).map((l) => l.name)
+ const labelTargets = []
+ for (const label of labels) {
+ if (!label.startsWith('backport/')) continue
+ labelTargets.push(label.slice('backport/'.length))
+ }
+ if (labelTargets.length > 0) {
+ return resolveTargets({
+ github,
+ context,
+ core,
+ pull_request,
+ requestedTargets: labelTargets,
+ })
+ }
+ }
+
+ return [...resolved]
+}
+
+async function git(args, opts = {}) {
+ const { cwd, core, allowFailure } = opts
+ try {
+ const { stdout, stderr } = await execFileAsync('git', args, { cwd })
+ if (stderr && core) core.info(stderr.trim())
+ return stdout.trim()
+ } catch (e) {
+ if (allowFailure) return null
+ throw e
+ }
+}
+
+async function remoteBranchExists({ cwd, branch }) {
+ try {
+ await execFileAsync('git', ['ls-remote', '--exit-code', '--heads', 'origin', branch], { cwd })
+ return true
+ } catch {
+ return false
+ }
+}
+
+async function getCommitParentCount({ cwd, sha }) {
+ const raw = await git(['cat-file', '-p', sha], { cwd })
+ return raw.split('\n').filter((l) => l.startsWith('parent ')).length
+}
+
+async function createOrReuseBackportPR({
+ github,
+ context,
+ core,
+ targetBranch,
+ backportBranch,
+ originalPR,
+ originalTitle,
+ cherryPickedSha,
+ requestedVia = 'bot comment',
+}) {
+ const head = `${context.repo.owner}:${backportBranch}`
+
+ const { data: prs } = await github.rest.pulls.list({
+ ...context.repo,
+ state: 'all',
+ head,
+ base: targetBranch,
+ per_page: 10,
+ })
+
+ if (prs.length > 0) {
+ return { number: prs[0].number, url: prs[0].html_url, state: prs[0].state, reused: true }
+ }
+
+ const { data: created } = await github.rest.pulls.create({
+ ...context.repo,
+ title: `[Backport ${targetBranch}] ${originalTitle}`,
+ body: [
+ `Automated backport of #${originalPR} to \`${targetBranch}\`.`,
+ ``,
+ `- Original PR: #${originalPR}`,
+ `- Cherry-picked: \`${cherryPickedSha}\``,
+ `- Requested via ${requestedVia}`,
+ ].join('\n'),
+ head: backportBranch,
+ base: targetBranch,
+ maintainer_can_modify: true,
+ })
+
+ try {
+ await github.rest.issues.addLabels({
+ ...context.repo,
+ issue_number: created.number,
+ labels: ['automated-backport'],
+ })
+ } catch (e) {
+ core.warning(`Failed to add label "automated-backport" to #${created.number}: ${e.message}`)
+ }
+
+ return { number: created.number, url: created.html_url, state: created.state, reused: false }
+}
+
+async function performBackport({
+ github,
+ context,
+ core,
+ cwd,
+ pull_request,
+ targetBranch,
+ backportBranch,
+ mergeSha,
+ options,
+ requestedVia,
+}) {
+ const baseBranch = pull_request.base.ref
+
+ if (!options.force) {
+ const exists = await remoteBranchExists({ cwd, branch: backportBranch })
+ if (exists) {
+ return {
+ targetBranch,
+ backportBranch,
+ status: 'skipped',
+ message: `Branch \`${backportBranch}\` already exists (use \`--force\` to rewrite)`,
+ }
+ }
+ }
+
+ await git(['config', 'user.name', 'github-actions[bot]'], { cwd })
+ await git(['config', 'user.email', 'github-actions[bot]@users.noreply.github.com'], { cwd })
+
+ await git(['fetch', 'origin', targetBranch, baseBranch], { cwd })
+ await git(['checkout', '-B', backportBranch, `origin/${targetBranch}`], { cwd })
+
+ const parentCount = await getCommitParentCount({ cwd, sha: mergeSha })
+ const cherryPickArgs = parentCount > 1 ? ['cherry-pick', '-m', '1', mergeSha] : ['cherry-pick', mergeSha]
+
+ try {
+ await git(cherryPickArgs, { cwd })
+ } catch (e) {
+ await git(['cherry-pick', '--abort'], { cwd, allowFailure: true })
+ return {
+ targetBranch,
+ backportBranch,
+ status: 'conflict',
+ message: `Cherry-pick failed with conflicts for \`${targetBranch}\``,
+ }
+ }
+
+ await git(['push', '--force-with-lease', 'origin', backportBranch], { cwd })
+
+ if (options.noPr) {
+ return {
+ targetBranch,
+ backportBranch,
+ status: 'pushed',
+ message: `Pushed \`${backportBranch}\` (PR creation disabled via --no-pr)`,
+ }
+ }
+
+ const pr = await createOrReuseBackportPR({
+ github,
+ context,
+ core,
+ targetBranch,
+ backportBranch,
+ originalPR: pull_request.number,
+ originalTitle: pull_request.title,
+ cherryPickedSha: mergeSha,
+ requestedVia,
+ })
+
+ return {
+ targetBranch,
+ backportBranch,
+ status: 'pr',
+ pr,
+ message: pr.reused
+ ? `Reused backport PR #${pr.number} (${pr.url})`
+ : `Created backport PR #${pr.number} (${pr.url})`,
+ }
+}
+
+async function handleBackportComment({ github, context, core }) {
+ const payload = context.payload
+ const commentBody = payload.comment?.body ?? ''
+ const command = parseBackportCommand(commentBody)
+ if (!command) return false
+
+ if (!payload.issue?.pull_request) {
+ core.info('Backport command ignored: not a pull request')
+ return false
+ }
+
+ const association = payload.comment?.author_association
+ const allowed = new Set(['OWNER', 'MEMBER', 'COLLABORATOR'])
+ if (!allowed.has(String(association))) {
+ core.info(`Backport command ignored: insufficient permissions (${association})`)
+ return false
+ }
+
+ const prNumber = payload.issue.number
+ const { data: pull_request } = await github.rest.pulls.get({
+ ...context.repo,
+ pull_number: prNumber,
+ })
+
+ if (!pull_request.merged) {
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: prNumber,
+ body: 'Backport request ignored: PR is not merged.',
+ })
+ return true
+ }
+
+ const nodeId = payload.comment?.node_id
+ if (nodeId) {
+ try {
+ await addReaction({ github, node_id: nodeId, reaction: 'EYES' })
+ } catch {
+ // ignore
+ }
+ }
+
+ const targets = await resolveTargets({
+ github,
+ context,
+ core,
+ pull_request,
+ requestedTargets: command.targets,
+ })
+
+ if (targets.length === 0) {
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: prNumber,
+ body: [
+ 'Backport failed: no valid targets resolved.',
+ '',
+ 'Use one of:',
+ '- `@projt-launcher-bot backport latest`',
+ '- `@projt-launcher-bot backport all`',
+ '- `@projt-launcher-bot backport release-1.2.3`',
+ ].join('\n'),
+ })
+ if (nodeId) {
+ try {
+ await addReaction({ github, node_id: nodeId, reaction: 'CONFUSED' })
+ } catch {
+ // ignore
+ }
+ }
+ return true
+ }
+
+ const cwd = process.env.GITHUB_WORKSPACE || process.cwd()
+ const mergeSha = pull_request.merge_commit_sha
+ if (!mergeSha) {
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: prNumber,
+ body: 'Backport failed: merge commit SHA is missing for this PR.',
+ })
+ if (nodeId) {
+ try {
+ await addReaction({ github, node_id: nodeId, reaction: 'CONFUSED' })
+ } catch {
+ // ignore
+ }
+ }
+ return true
+ }
+
+ const results = []
+ for (const targetBranch of targets) {
+ const backportBranch = `backport/${targetBranch}/pr-${pull_request.number}`
+ const res = await performBackport({
+ github,
+ context,
+ core,
+ cwd,
+ pull_request,
+ targetBranch,
+ backportBranch,
+ mergeSha,
+ options: command.options,
+ requestedVia: 'bot comment',
+ })
+ results.push(res)
+ }
+
+ const lines = []
+ lines.push('## Backport results')
+ lines.push('')
+ lines.push(`Original PR: #${pull_request.number}`)
+ lines.push(`Cherry-picked: \`${mergeSha}\``)
+ lines.push('')
+ for (const r of results) {
+ if (r.status === 'pr') {
+ lines.push(`- OK \`${r.targetBranch}\`: ${r.message}`)
+ } else if (r.status === 'pushed') {
+ lines.push(`- OK \`${r.targetBranch}\`: ${r.message}`)
+ } else if (r.status === 'skipped') {
+ lines.push(`- SKIP \`${r.targetBranch}\`: ${r.message}`)
+ } else if (r.status === 'conflict') {
+ lines.push(`- FAIL \`${r.targetBranch}\`: ${r.message}`)
+ } else {
+ lines.push(`- WARN \`${r.targetBranch}\`: ${r.message ?? 'unknown status'}`)
+ }
+ }
+
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: prNumber,
+ body: lines.join('\n'),
+ })
+
+ const anyConflict = results.some((r) => r.status === 'conflict')
+ if (nodeId) {
+ try {
+ await addReaction({
+ github,
+ node_id: nodeId,
+ reaction: anyConflict ? 'CONFUSED' : 'ROCKET',
+ })
+ } catch {
+ // ignore
+ }
+ }
+
+ return true
+}
+
+function getBackportLabelTargets(labels = []) {
+ return labels
+ .filter((l) => typeof l === 'string' && l.startsWith('backport/'))
+ .map((l) => l.slice('backport/'.length))
+}
+
+function optionsFromLabels(labelTargets = []) {
+ return {
+ force: labelTargets.includes('force'),
+ noPr: labelTargets.includes('no-pr'),
+ skip: labelTargets.includes('skip'),
+ }
+}
+
+async function upsertBackportSummaryComment({ github, context, pull_number, body }) {
+ const marker = '<!-- projt-bot:backport-summary -->'
+ const fullBody = [marker, body].join('\n')
+
+ const comments = await github.paginate(github.rest.issues.listComments, {
+ ...context.repo,
+ issue_number: pull_number,
+ per_page: 100,
+ })
+
+ const existing = comments.find(
+ (c) => c.user?.login === 'github-actions[bot]' && typeof c.body === 'string' && c.body.includes(marker),
+ )
+
+ if (existing) {
+ await github.rest.issues.updateComment({
+ ...context.repo,
+ comment_id: existing.id,
+ body: fullBody,
+ })
+ } else {
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: pull_number,
+ body: fullBody,
+ })
+ }
+}
+
+async function handleBackportOnClose({ github, context, core }) {
+ const payload = context.payload
+ const pr = payload.pull_request
+ if (!pr) return false
+
+ // Only act when a PR is merged and has backport/* labels.
+ if (!pr.merged) return false
+
+ const labelNames = (pr.labels ?? []).map((l) => l.name)
+ const labelTargets = getBackportLabelTargets(labelNames)
+ if (labelTargets.length === 0) return false
+
+ const opts = optionsFromLabels(labelTargets)
+ if (opts.skip) {
+ core.info('Backport skipped via backport/skip label')
+ return true
+ }
+
+ const requestedTargets = labelTargets.filter((t) => !['force', 'no-pr', 'skip'].includes(t))
+
+ const targets = await resolveTargets({
+ github,
+ context,
+ core,
+ pull_request: pr,
+ requestedTargets,
+ })
+
+ if (targets.length === 0) {
+ await upsertBackportSummaryComment({
+ github,
+ context,
+ pull_number: pr.number,
+ body: [
+ '## Backport results',
+ '',
+ 'No valid targets resolved from backport labels.',
+ '',
+ `Labels: ${labelNames.filter((n) => n.startsWith('backport/')).join(', ')}`,
+ ].join('\n'),
+ })
+ return true
+ }
+
+ const mergeSha = pr.merge_commit_sha
+ if (!mergeSha) {
+ await upsertBackportSummaryComment({
+ github,
+ context,
+ pull_number: pr.number,
+ body: 'Backport failed: merge commit SHA is missing for this PR.',
+ })
+ return true
+ }
+
+ const cwd = process.env.GITHUB_WORKSPACE || process.cwd()
+ const results = []
+ for (const targetBranch of targets) {
+ const backportBranch = `backport/${targetBranch}/pr-${pr.number}`
+ const res = await performBackport({
+ github,
+ context,
+ core,
+ cwd,
+ pull_request: pr,
+ targetBranch,
+ backportBranch,
+ mergeSha,
+ options: { force: opts.force, noPr: opts.noPr },
+ requestedVia: 'labels',
+ })
+ results.push(res)
+ }
+
+ const lines = []
+ lines.push('## Backport results')
+ lines.push('')
+ lines.push(`Original PR: #${pr.number}`)
+ lines.push(`Cherry-picked: \`${mergeSha}\``)
+ lines.push('')
+ for (const r of results) {
+ if (r.status === 'pr') {
+ lines.push(`- OK \`${r.targetBranch}\`: ${r.message}`)
+ } else if (r.status === 'pushed') {
+ lines.push(`- OK \`${r.targetBranch}\`: ${r.message}`)
+ } else if (r.status === 'skipped') {
+ lines.push(`- SKIP \`${r.targetBranch}\`: ${r.message}`)
+ } else if (r.status === 'conflict') {
+ lines.push(`- FAIL \`${r.targetBranch}\`: ${r.message}`)
+ } else {
+ lines.push(`- WARN \`${r.targetBranch}\`: ${r.message ?? 'unknown status'}`)
+ }
+ }
+
+ await upsertBackportSummaryComment({
+ github,
+ context,
+ pull_number: pr.number,
+ body: lines.join('\n'),
+ })
+
+ return true
+}
+
+module.exports = {
+ parseBackportCommand,
+ handleBackportComment,
+ handleBackportOnClose,
+}
diff --git a/archived/projt-launcher/ci/github-script/commit-types.json b/archived/projt-launcher/ci/github-script/commit-types.json
new file mode 100644
index 0000000000..e6b206b1bf
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/commit-types.json
@@ -0,0 +1,70 @@
+{
+ "types": [
+ "deps",
+ "dependencies",
+ "dep",
+ "upgrade",
+ "downgrade",
+ "bump",
+ "release",
+ "hotfix",
+ "security",
+ "vulnerability",
+ "localization",
+ "translation",
+ "i18n",
+ "l10n",
+ "internationalization",
+ "localisation",
+ "config",
+ "configuration",
+ "cleanup",
+ "clean",
+ "maintenance",
+ "infra",
+ "infrastructure",
+ "ops",
+ "operations",
+ "devops",
+ "qa",
+ "ux",
+ "ui",
+ "api",
+ "backend",
+ "frontend",
+ "data",
+ "database",
+ "schema",
+ "samples",
+ "examples",
+ "assets",
+ "content",
+ "docs-build",
+ "docs-ci",
+ "docs-config",
+ "docs-deps",
+ "docs-release",
+ "meta",
+ "init",
+ "prototype",
+ "experiment",
+ "hotpath",
+ "breaking",
+ "deprecate",
+ "compat",
+ "migration",
+ "interop",
+ "benchmark",
+ "profiles",
+ "telemetry",
+ "analytics",
+ "observability",
+ "state",
+ "sync",
+ "validation",
+ "lint",
+ "formatter",
+ "package",
+ "vendor"
+ ]
+}
diff --git a/archived/projt-launcher/ci/github-script/commits.js b/archived/projt-launcher/ci/github-script/commits.js
new file mode 100644
index 0000000000..27dbd61eb6
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/commits.js
@@ -0,0 +1,336 @@
+/**
+ * ProjT Launcher - Commit Validation for Pull Requests
+ * Validates commit messages, structure, and conventions
+ */
+
+const { classify } = require('../supportedBranches.js')
+const withRateLimit = require('./withRateLimit.js')
+const { dismissReviews, postReview } = require('./reviews.js')
+
+const commitTypeConfig = (() => {
+ try {
+ return require('./commit-types.json')
+ } catch (error) {
+ console.warn(`commit validator: could not load commit-types.json (${error.message})`)
+ return {}
+ }
+})()
+
+const parseCommitTypeList = (value) => {
+ if (!value) {
+ return []
+ }
+ return value
+ .split(',')
+ .map((entry) => entry.trim().toLowerCase())
+ .filter(Boolean)
+}
+
+const DEFAULT_COMMIT_TYPES = [
+ 'build',
+ 'chore',
+ 'ci',
+ 'docs',
+ 'feat',
+ 'fix',
+ 'perf',
+ 'refactor',
+ 'revert',
+ 'style',
+ 'test',
+ 'deps',
+]
+
+const EXTENDED_COMMIT_TYPES = [
+ ...(commitTypeConfig.types ?? []),
+]
+
+const ENV_COMMIT_TYPES = parseCommitTypeList(
+ process.env.COMMIT_TYPES ?? process.env.ADDITIONAL_COMMIT_TYPES ?? ''
+)
+
+const COMMIT_TYPES = Array.from(
+ new Set([...DEFAULT_COMMIT_TYPES, ...EXTENDED_COMMIT_TYPES, ...ENV_COMMIT_TYPES])
+)
+
+const COMMIT_TYPE_SET = new Set(COMMIT_TYPES)
+
+// Component scopes for ProjT Launcher
+const VALID_SCOPES = [
+ 'core',
+ 'ui',
+ 'minecraft',
+ 'modplatform',
+ 'modrinth',
+ 'curseforge',
+ 'ftb',
+ 'technic',
+ 'atlauncher',
+ 'auth',
+ 'java',
+ 'news',
+ 'settings',
+ 'skins',
+ 'translations',
+ 'build',
+ 'ci',
+ 'nix',
+ 'vcpkg',
+ 'deps',
+]
+
+/**
+ * Validate commit message format
+ * Expected format: type(scope): description
+ * @param {string} message - Commit message
+ * @returns {object} Validation result
+ */
+function normalizeCommitType(type) {
+ if (!type) {
+ return ''
+ }
+ const trimmed = type.toLowerCase()
+ const legacyMatch = trimmed.match(/^\d+\.(.+)$/)
+ return legacyMatch ? legacyMatch[1] : trimmed
+}
+
+function validateCommitMessage(message) {
+ const firstLine = message.split('\n')[0]
+
+ // Check for conventional commit format
+ const conventionalMatch = firstLine.match(
+ /^(?<type>[\w.-]+)(?:\((?<scope>[\w-]+)\))?!?:\s*(?<description>.+)$/
+ )
+
+ if (!conventionalMatch) {
+ return {
+ valid: false,
+ severity: 'warning',
+ message: `Commit message doesn't follow conventional format: "${firstLine.substring(0, 50)}..."`,
+ }
+ }
+
+ const { type, scope, description } = conventionalMatch.groups
+ const normalizedType = normalizeCommitType(type)
+
+ // Validate type
+ if (!COMMIT_TYPE_SET.has(normalizedType)) {
+ return {
+ valid: false,
+ severity: 'warning',
+ message: `Unknown commit type "${type}". Valid types: ${COMMIT_TYPES.join(', ')}`,
+ }
+ }
+
+ // Validate scope if present
+ if (scope && !VALID_SCOPES.includes(scope.toLowerCase())) {
+ return {
+ valid: false,
+ severity: 'info',
+ message: `Unknown scope "${scope}". Consider using: ${VALID_SCOPES.slice(0, 5).join(', ')}...`,
+ }
+ }
+
+ // Check description length
+ if (description.length < 10) {
+ return {
+ valid: false,
+ severity: 'warning',
+ message: 'Commit description too short (min 10 chars)',
+ }
+ }
+
+ if (firstLine.length > 140) {
+ return {
+ valid: false,
+ severity: 'info',
+ message: 'First line exceeds 140 characters',
+ }
+ }
+
+ return { valid: true }
+}
+
+/**
+ * Check commit for specific patterns
+ * @param {object} commit - Commit object
+ * @returns {object} Check result
+ */
+function checkCommitPatterns(commit) {
+ const message = commit.message
+ const issues = []
+
+ // Check for WIP markers
+ if (message.match(/\bWIP\b/i)) {
+ issues.push({
+ severity: 'warning',
+ message: 'Commit contains WIP marker',
+ })
+ }
+
+ // Check for fixup/squash commits
+ if (message.match(/^(fixup|squash)!/i)) {
+ issues.push({
+ severity: 'info',
+ message: 'Commit is a fixup/squash commit - remember to rebase before merge',
+ })
+ }
+
+ // Check for merge commits
+ if (message.startsWith('Merge ')) {
+ issues.push({
+ severity: 'info',
+ message: 'Merge commit detected - consider rebasing instead',
+ })
+ }
+
+ // Check for large descriptions without body
+ if (message.split('\n').length === 1 && message.length > 100) {
+ issues.push({
+ severity: 'info',
+ message: 'Long commit message without body - consider adding details in commit body',
+ })
+ }
+
+ return issues
+}
+
+/**
+ * Validate all commits in a PR
+ */
+async function run({ github, context, core, dry }) {
+ await withRateLimit({ github, core }, async (stats) => {
+ stats.prs = 1
+
+ const pull_number = context.payload.pull_request.number
+ const base = context.payload.pull_request.base.ref
+ const baseClassification = classify(base)
+
+ // Get all commits in the PR
+ const commits = await github.paginate(github.rest.pulls.listCommits, {
+ ...context.repo,
+ pull_number,
+ })
+
+ core.info(`Validating ${commits.length} commits for PR #${pull_number}`)
+
+ const results = []
+
+ for (const { sha, commit } of commits) {
+ const commitResults = {
+ sha: sha.substring(0, 7),
+ fullSha: sha,
+ author: commit.author.name,
+ message: commit.message.split('\n')[0],
+ issues: [],
+ }
+
+ // Validate commit message format
+ const formatValidation = validateCommitMessage(commit.message)
+ if (!formatValidation.valid) {
+ commitResults.issues.push({
+ severity: formatValidation.severity,
+ message: formatValidation.message,
+ })
+ }
+
+ // Check for commit patterns
+ const patternIssues = checkCommitPatterns(commit)
+ commitResults.issues.push(...patternIssues)
+
+ results.push(commitResults)
+ }
+
+ // Log results
+ let hasErrors = false
+ let hasWarnings = false
+
+ for (const result of results) {
+ core.startGroup(`Commit ${result.sha}`)
+ core.info(`Author: ${result.author}`)
+ core.info(`Message: ${result.message}`)
+
+ if (result.issues.length === 0) {
+ core.info('✓ No issues found')
+ } else {
+ for (const issue of result.issues) {
+ switch (issue.severity) {
+ case 'error':
+ core.error(issue.message)
+ hasErrors = true
+ break
+ case 'warning':
+ core.warning(issue.message)
+ hasWarnings = true
+ break
+ default:
+ core.info(`ℹ ${issue.message}`)
+ }
+ }
+ }
+ core.endGroup()
+ }
+
+ // If all commits are valid, dismiss any previous reviews
+ if (!hasErrors && !hasWarnings) {
+ await dismissReviews({ github, context, dry })
+ core.info('✓ All commits passed validation')
+ return
+ }
+
+ // Generate summary for issues
+ const issueCommits = results.filter(r => r.issues.length > 0)
+
+ if (issueCommits.length > 0) {
+ const body = [
+ '## Commit Validation Issues',
+ '',
+ 'The following commits have issues that should be addressed:',
+ '',
+ ...issueCommits.flatMap(commit => [
+ `### \`${commit.sha}\`: ${commit.message}`,
+ '',
+ ...commit.issues.map(issue => `- **${issue.severity}**: ${issue.message}`),
+ '',
+ ]),
+ '---',
+ '',
+ '### Commit Message Guidelines',
+ '',
+ 'ProjT Launcher uses [Conventional Commits](https://www.conventionalcommits.org/):',
+ '',
+ '```',
+ 'type(scope): description',
+ '',
+ '[optional body]',
+ '',
+ '[optional footer]',
+ '```',
+ '',
+ `**Types**: ${COMMIT_TYPES.join(', ')}`,
+ '',
+ `**Scopes**: ${VALID_SCOPES.slice(0, 8).join(', ')}, ...`,
+ ].join('\n')
+
+ // Post review only for errors/warnings, not info
+ if (hasErrors || hasWarnings) {
+ await postReview({ github, context, core, dry, body })
+ }
+
+ // Write step summary
+ const fs = require('node:fs')
+ if (process.env.GITHUB_STEP_SUMMARY) {
+ fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, body)
+ }
+ }
+
+ if (hasErrors) {
+ throw new Error('Commit validation failed with errors')
+ }
+ })
+}
+
+module.exports = run
+module.exports.validateCommitMessage = validateCommitMessage
+module.exports.checkCommitPatterns = checkCommitPatterns
+module.exports.normalizeCommitType = normalizeCommitType
diff --git a/archived/projt-launcher/ci/github-script/get-teams.js b/archived/projt-launcher/ci/github-script/get-teams.js
new file mode 100644
index 0000000000..c547d5ac62
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/get-teams.js
@@ -0,0 +1,134 @@
+/**
+ * ProjT Launcher - Team Information Fetcher
+ * Fetches team information from GitHub organization for CI purposes
+ */
+
+// Teams to exclude from processing (bots, voters, etc.)
+const excludeTeams = [
+ /^voters.*$/,
+ /^bots?$/,
+]
+
+/**
+ * Main function to fetch team information
+ */
+module.exports = async ({ github, context, core, outFile }) => {
+ const withRateLimit = require('./withRateLimit.js')
+ const { writeFileSync } = require('node:fs')
+
+ const org = context.repo.owner
+ const result = {}
+
+ await withRateLimit({ github, core }, async () => {
+ /**
+ * Convert array of users to object mapping login -> id
+ */
+ function makeUserSet(users) {
+ users.sort((a, b) => (a.login > b.login ? 1 : -1))
+ return users.reduce((acc, user) => {
+ acc[user.login] = user.id
+ return acc
+ }, {})
+ }
+
+ /**
+ * Process teams recursively
+ */
+ async function processTeams(teams) {
+ for (const team of teams) {
+ // Skip excluded teams
+ if (excludeTeams.some((regex) => team.slug.match(regex))) {
+ core.info(`Skipping excluded team: ${team.slug}`)
+ continue
+ }
+
+ core.notice(`Processing team ${team.slug}`)
+
+ try {
+ // Get team members
+ const members = makeUserSet(
+ await github.paginate(github.rest.teams.listMembersInOrg, {
+ org,
+ team_slug: team.slug,
+ role: 'member',
+ }),
+ )
+
+ // Get team maintainers
+ const maintainers = makeUserSet(
+ await github.paginate(github.rest.teams.listMembersInOrg, {
+ org,
+ team_slug: team.slug,
+ role: 'maintainer',
+ }),
+ )
+
+ result[team.slug] = {
+ description: team.description,
+ id: team.id,
+ maintainers,
+ members,
+ name: team.name,
+ }
+ } catch (e) {
+ core.warning(`Failed to fetch team ${team.slug}: ${e.message}`)
+ }
+
+ // Process child teams
+ try {
+ const childTeams = await github.paginate(
+ github.rest.teams.listChildInOrg,
+ {
+ org,
+ team_slug: team.slug,
+ },
+ )
+ await processTeams(childTeams)
+ } catch (e) {
+ // Child teams might not exist or be accessible
+ core.info(`No child teams for ${team.slug}`)
+ }
+ }
+ }
+
+ // Get all teams with access to the repository
+ try {
+ const teams = await github.paginate(github.rest.repos.listTeams, {
+ ...context.repo,
+ })
+
+ core.info(`Found ${teams.length} teams with repository access`)
+ await processTeams(teams)
+ } catch (e) {
+ core.warning(`Could not fetch repository teams: ${e.message}`)
+
+ // Fallback: create minimal team structure
+ result['projt-maintainers'] = {
+ description: 'ProjT Launcher Maintainers',
+ id: 0,
+ maintainers: {},
+ members: {},
+ name: 'ProjT Maintainers',
+ }
+ }
+ })
+
+ // Sort teams alphabetically
+ const sorted = Object.keys(result)
+ .sort()
+ .reduce((acc, key) => {
+ acc[key] = result[key]
+ return acc
+ }, {})
+
+ const json = `${JSON.stringify(sorted, null, 2)}\n`
+
+ if (outFile) {
+ writeFileSync(outFile, json)
+ core.info(`Team information written to ${outFile}`)
+ } else {
+ console.log(json)
+ }
+
+ return sorted
+}
diff --git a/archived/projt-launcher/ci/github-script/merge.js b/archived/projt-launcher/ci/github-script/merge.js
new file mode 100644
index 0000000000..536af0f056
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/merge.js
@@ -0,0 +1,308 @@
+/**
+ * ProjT Launcher - Merge Handler
+ * Handles PR merge operations with validation and queue management
+ */
+
+const { classify } = require('../supportedBranches.js')
+
+// Component definitions for ProjT Launcher
+const COMPONENTS = {
+ core: ['launcher/', 'systeminfo/', 'katabasis/', 'libnbtplusplus/', 'launcherjava/'],
+ ui: ['launcher/ui/', 'launcher/resources/', 'launcher/ui/'],
+ minecraft: ['launcher/minecraft/', 'tomlplusplus/', 'qdcss/'],
+ modplatform: ['launcher/modplatform/'],
+ build: ['CMakeLists.txt', 'cmake/', 'vcpkg.json', 'CMakePresets.json'],
+ docs: ['docs/', 'README.md', 'CONTRIBUTING.md'],
+ ci: ['.github/', 'ci/'],
+}
+
+/**
+ * Get component owners for changed files
+ * @param {Array} files - Changed files
+ * @returns {Set} Component owners
+ */
+function getComponentOwners(files) {
+ const owners = new Set()
+
+ for (const { filename } of files) {
+ for (const [component, paths] of Object.entries(COMPONENTS)) {
+ if (paths.some(path => filename.startsWith(path) || filename === path)) {
+ owners.add(component)
+ }
+ }
+ }
+
+ return owners
+}
+
+/**
+ * Run merge checklist for ProjT Launcher PRs
+ */
+function runChecklist({
+ committers,
+ events,
+ files,
+ pull_request,
+ log,
+ maintainers,
+ user,
+ userIsMaintainer,
+}) {
+ // Check what components are touched
+ const components = getComponentOwners(files)
+
+ // Get eligible reviewers from maintainers
+ const eligible = maintainers && maintainers.length > 0
+ ? new Set(maintainers)
+ : new Set()
+
+ // Get current approvals
+ const approvals = new Set(
+ events
+ .filter(
+ ({ event, state, commit_id }) =>
+ event === 'reviewed' &&
+ state === 'approved' &&
+ // Only approvals for the current head SHA count
+ commit_id === pull_request.head.sha,
+ )
+ .map(({ user }) => user?.id)
+ .filter(Boolean),
+ )
+
+ const checklist = {
+ 'PR targets a development branch (develop, master)':
+ classify(pull_request.base.ref).type.includes('development'),
+
+ 'PR has passing CI checks':
+ pull_request.mergeable_state !== 'blocked',
+
+ 'PR is at least one of:': {
+ 'Approved by a maintainer': committers.intersection(approvals).size > 0,
+ 'Opened by a maintainer': committers.has(pull_request.user.id),
+ 'Part of a backport':
+ pull_request.head.ref.startsWith('backport-') ||
+ pull_request.labels?.some(l => l.name === 'backport'),
+ },
+
+ 'PR has no merge conflicts':
+ pull_request.mergeable === true,
+ }
+
+ if (user) {
+ checklist[`${user.login} is a project maintainer`] = userIsMaintainer
+ if (components.size > 0) {
+ checklist[`${user.login} owns touched components (${Array.from(components).join(', ')})`] =
+ eligible.has(user.id)
+ }
+ } else {
+ checklist['PR has eligible reviewers'] = eligible.size > 0
+ }
+
+ const result = Object.values(checklist).every((v) =>
+ typeof v === 'boolean' ? v : Object.values(v).some(Boolean),
+ )
+
+ log('checklist', JSON.stringify(checklist))
+ log('components', JSON.stringify(Array.from(components)))
+ log('eligible', JSON.stringify(Array.from(eligible)))
+ log('result', result)
+
+ return {
+ checklist,
+ eligible,
+ components,
+ result,
+ }
+}
+
+/**
+ * Check for merge command in comment
+ * Format: @projt-launcher-bot merge
+ */
+function hasMergeCommand(body) {
+ return (body ?? '')
+ .replace(/<!--.*?-->/gms, '')
+ .replace(/(^`{3,})[^`].*?\1/gms, '')
+ .match(/^@projt-launcher-bot\s+merge\s*$/im)
+}
+
+/**
+ * Handle merge comment reaction
+ */
+async function handleMergeComment({ github, body, node_id, reaction }) {
+ if (!hasMergeCommand(body)) return
+
+ await github.graphql(
+ `mutation($node_id: ID!, $reaction: ReactionContent!) {
+ addReaction(input: {
+ content: $reaction,
+ subjectId: $node_id
+ })
+ { clientMutationId }
+ }`,
+ { node_id, reaction },
+ )
+}
+
+/**
+ * Handle merge request for a PR
+ */
+async function handleMerge({
+ github,
+ context,
+ core,
+ log,
+ dry,
+ pull_request,
+ events,
+ maintainers,
+ getTeamMembers,
+ getUser,
+}) {
+ const pull_number = pull_request.number
+
+ // Get list of maintainers (project committers)
+ const committers = new Set(
+ (await getTeamMembers('projt-maintainers')).map(({ id }) => id),
+ )
+
+ // Get changed files
+ const files = (
+ await github.rest.pulls.listFiles({
+ ...context.repo,
+ pull_number,
+ per_page: 100,
+ })
+ ).data
+
+ // Early exit for large PRs
+ if (files.length >= 100) {
+ core.warning('PR touches 100+ files, manual merge required')
+ return false
+ }
+
+ // Only look through comments after the latest push
+ const lastPush = events.findLastIndex(
+ ({ event, sha, commit_id }) =>
+ ['committed', 'head_ref_force_pushed'].includes(event) &&
+ (sha ?? commit_id) === pull_request.head.sha,
+ )
+
+ const comments = events.slice(lastPush + 1).filter(
+ ({ event, body, user, node_id }) =>
+ ['commented', 'reviewed'].includes(event) &&
+ hasMergeCommand(body) &&
+ user &&
+ (dry ||
+ !events.some(
+ ({ event, body }) =>
+ ['commented'].includes(event) &&
+ body.match(new RegExp(`^<!-- comment: ${node_id} -->$`, 'm')),
+ )),
+ )
+
+ /**
+ * Perform the merge
+ */
+ async function merge() {
+ if (dry) {
+ core.info(`Would merge #${pull_number}... (dry run)`)
+ return 'Merge completed (dry run)'
+ }
+
+ // Use merge queue if available, otherwise regular merge
+ try {
+ const resp = await github.graphql(
+ `mutation($node_id: ID!, $sha: GitObjectID) {
+ enqueuePullRequest(input: {
+ expectedHeadOid: $sha,
+ pullRequestId: $node_id
+ })
+ {
+ clientMutationId,
+ mergeQueueEntry { mergeQueue { url } }
+ }
+ }`,
+ { node_id: pull_request.node_id, sha: pull_request.head.sha },
+ )
+ return [
+ `:heavy_check_mark: [Queued](${resp.enqueuePullRequest.mergeQueueEntry.mergeQueue.url}) for merge`,
+ ]
+ } catch (e) {
+ log('Queue merge failed, trying direct merge', e.response?.errors?.[0]?.message)
+ }
+
+ // Fallback to direct merge
+ try {
+ await github.rest.pulls.merge({
+ ...context.repo,
+ pull_number,
+ merge_method: 'squash',
+ sha: pull_request.head.sha,
+ })
+ return [':heavy_check_mark: Merged successfully']
+ } catch (e) {
+ return [`:x: Merge failed: ${e.message}`]
+ }
+ }
+
+ // Process merge commands
+ for (const comment of comments) {
+ const user = await getUser(comment.user.id)
+
+ const { checklist, result } = runChecklist({
+ committers,
+ events,
+ files,
+ pull_request,
+ log,
+ maintainers: maintainers || [],
+ user,
+ userIsMaintainer: committers.has(user.id),
+ })
+
+ const response = []
+
+ if (result) {
+ response.push(...(await merge()))
+ } else {
+ response.push(':x: Cannot merge - checklist not satisfied:')
+ response.push('')
+ response.push('```')
+ response.push(JSON.stringify(checklist, null, 2))
+ response.push('```')
+ }
+
+ if (!dry) {
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: pull_number,
+ body: [
+ `<!-- comment: ${comment.node_id} -->`,
+ '',
+ ...response,
+ ].join('\n'),
+ })
+
+ await handleMergeComment({
+ github,
+ body: comment.body,
+ node_id: comment.node_id,
+ reaction: result ? 'ROCKET' : 'CONFUSED',
+ })
+ } else {
+ core.info(`Response: ${response.join('\n')}`)
+ }
+ }
+
+ return comments.length > 0
+}
+
+module.exports = {
+ runChecklist,
+ hasMergeCommand,
+ handleMergeComment,
+ handleMerge,
+ getComponentOwners,
+}
diff --git a/archived/projt-launcher/ci/github-script/package-lock.json b/archived/projt-launcher/ci/github-script/package-lock.json
new file mode 100644
index 0000000000..62a633e0d6
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/package-lock.json
@@ -0,0 +1,1721 @@
+{
+ "name": "github-script",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "version": "1.0.0",
+ "dependencies": {
+ "@actions/artifact": "6.1.0",
+ "@actions/core": "3.0.0",
+ "@actions/github": "9.0.0",
+ "bottleneck": "2.19.5",
+ "commander": "14.0.3"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@actions/artifact": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-6.1.0.tgz",
+ "integrity": "sha512-oRn9YhKkboXgIq2TQZ9uj6bhkT5ZUzFtnyTQ0tLGBwImaD0GfWShE5R0tPbN25EJmS3tz5sDd2JnVokAOtNrZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@actions/core": "^3.0.0",
+ "@actions/github": "^9.0.0",
+ "@actions/http-client": "^4.0.0",
+ "@azure/storage-blob": "^12.30.0",
+ "@octokit/core": "^7.0.6",
+ "@octokit/plugin-request-log": "^6.0.0",
+ "@octokit/plugin-retry": "^8.0.0",
+ "@octokit/request": "^10.0.7",
+ "@octokit/request-error": "^7.1.0",
+ "@protobuf-ts/plugin": "^2.2.3-alpha.1",
+ "@protobuf-ts/runtime": "^2.9.4",
+ "archiver": "^7.0.1",
+ "jwt-decode": "^4.0.0",
+ "unzip-stream": "^0.3.1"
+ }
+ },
+ "node_modules/@actions/artifact/node_modules/@actions/http-client": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.0.tgz",
+ "integrity": "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==",
+ "license": "MIT",
+ "dependencies": {
+ "tunnel": "^0.0.6",
+ "undici": "^6.23.0"
+ }
+ },
+ "node_modules/@actions/core": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.0.tgz",
+ "integrity": "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg==",
+ "license": "MIT",
+ "dependencies": {
+ "@actions/exec": "^3.0.0",
+ "@actions/http-client": "^4.0.0"
+ }
+ },
+ "node_modules/@actions/core/node_modules/@actions/http-client": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.0.tgz",
+ "integrity": "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==",
+ "license": "MIT",
+ "dependencies": {
+ "tunnel": "^0.0.6",
+ "undici": "^6.23.0"
+ }
+ },
+ "node_modules/@actions/exec": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
+ "integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
+ "license": "MIT",
+ "dependencies": {
+ "@actions/io": "^3.0.2"
+ }
+ },
+ "node_modules/@actions/github": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/github/-/github-9.0.0.tgz",
+ "integrity": "sha512-yJ0RoswsAaKcvkmpCE4XxBRiy/whH2SdTBHWzs0gi4wkqTDhXMChjSdqBz/F4AeiDlP28rQqL33iHb+kjAMX6w==",
+ "license": "MIT",
+ "dependencies": {
+ "@actions/http-client": "^3.0.2",
+ "@octokit/core": "^7.0.6",
+ "@octokit/plugin-paginate-rest": "^14.0.0",
+ "@octokit/plugin-rest-endpoint-methods": "^17.0.0",
+ "@octokit/request": "^10.0.7",
+ "@octokit/request-error": "^7.1.0",
+ "undici": "^6.23.0"
+ }
+ },
+ "node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
+ "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^16.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=6"
+ }
+ },
+ "node_modules/@actions/github/node_modules/@octokit/plugin-rest-endpoint-methods": {
+ "version": "17.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz",
+ "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^16.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=6"
+ }
+ },
+ "node_modules/@actions/http-client": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
+ "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
+ "license": "MIT",
+ "dependencies": {
+ "tunnel": "^0.0.6",
+ "undici": "^6.23.0"
+ }
+ },
+ "node_modules/@actions/io": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
+ "integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
+ "license": "MIT"
+ },
+ "node_modules/@azure/abort-controller": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
+ "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-auth": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz",
+ "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-util": "^1.13.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/core-client": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz",
+ "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.10.0",
+ "@azure/core-rest-pipeline": "^1.22.0",
+ "@azure/core-tracing": "^1.3.0",
+ "@azure/core-util": "^1.13.0",
+ "@azure/logger": "^1.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/core-http-compat": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.1.tgz",
+ "integrity": "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-client": "^1.10.0",
+ "@azure/core-rest-pipeline": "^1.22.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/core-lro": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz",
+ "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-util": "^1.2.0",
+ "@azure/logger": "^1.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-paging": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz",
+ "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/core-rest-pipeline": {
+ "version": "1.22.2",
+ "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz",
+ "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.10.0",
+ "@azure/core-tracing": "^1.3.0",
+ "@azure/core-util": "^1.13.0",
+ "@azure/logger": "^1.3.0",
+ "@typespec/ts-http-runtime": "^0.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/core-tracing": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz",
+ "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/core-util": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz",
+ "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@typespec/ts-http-runtime": "^0.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/core-xml": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.5.0.tgz",
+ "integrity": "sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-xml-parser": "^5.0.7",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/logger": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz",
+ "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@typespec/ts-http-runtime": "^0.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/storage-blob": {
+ "version": "12.30.0",
+ "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.30.0.tgz",
+ "integrity": "sha512-peDCR8blSqhsAKDbpSP/o55S4sheNwSrblvCaHUZ5xUI73XA7ieUGGwrONgD/Fng0EoDe1VOa3fAQ7+WGB3Ocg==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.9.0",
+ "@azure/core-client": "^1.9.3",
+ "@azure/core-http-compat": "^2.2.0",
+ "@azure/core-lro": "^2.2.0",
+ "@azure/core-paging": "^1.6.2",
+ "@azure/core-rest-pipeline": "^1.19.1",
+ "@azure/core-tracing": "^1.2.0",
+ "@azure/core-util": "^1.11.0",
+ "@azure/core-xml": "^1.4.5",
+ "@azure/logger": "^1.1.4",
+ "@azure/storage-common": "^12.2.0",
+ "events": "^3.0.0",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@azure/storage-common": {
+ "version": "12.2.0",
+ "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.2.0.tgz",
+ "integrity": "sha512-YZLxiJ3vBAAnFbG3TFuAMUlxZRexjQX5JDQxOkFGb6e2TpoxH3xyHI6idsMe/QrWtj41U/KoqBxlayzhS+LlwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.9.0",
+ "@azure/core-http-compat": "^2.2.0",
+ "@azure/core-rest-pipeline": "^1.19.1",
+ "@azure/core-tracing": "^1.2.0",
+ "@azure/core-util": "^1.11.0",
+ "@azure/logger": "^1.1.4",
+ "events": "^3.3.0",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@bufbuild/protobuf": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.6.0.tgz",
+ "integrity": "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg==",
+ "license": "(Apache-2.0 AND BSD-3-Clause)"
+ },
+ "node_modules/@bufbuild/protoplugin": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.6.0.tgz",
+ "integrity": "sha512-mfAwI+4GqUtbw/ddfyolEHaAL86ozRIVlOg2A+SVRbjx1CjsMc1YJO+hBSkt/pqfpR+PmWBbZLstHbXP8KGtMQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@bufbuild/protobuf": "2.6.0",
+ "@typescript/vfs": "^1.5.2",
+ "typescript": "5.4.5"
+ }
+ },
+ "node_modules/@bufbuild/protoplugin/node_modules/typescript": {
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@octokit/auth-token": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
+ "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/core": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
+ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/auth-token": "^6.0.0",
+ "@octokit/graphql": "^9.0.3",
+ "@octokit/request": "^10.0.6",
+ "@octokit/request-error": "^7.0.2",
+ "@octokit/types": "^16.0.0",
+ "before-after-hook": "^4.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/endpoint": {
+ "version": "11.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz",
+ "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^16.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/graphql": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz",
+ "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/request": "^10.0.6",
+ "@octokit/types": "^16.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/openapi-types": {
+ "version": "27.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
+ "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
+ "license": "MIT"
+ },
+ "node_modules/@octokit/plugin-request-log": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz",
+ "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=6"
+ }
+ },
+ "node_modules/@octokit/plugin-retry": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.0.3.tgz",
+ "integrity": "sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/request-error": "^7.0.2",
+ "@octokit/types": "^16.0.0",
+ "bottleneck": "^2.15.3"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=7"
+ }
+ },
+ "node_modules/@octokit/request": {
+ "version": "10.0.7",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz",
+ "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/endpoint": "^11.0.2",
+ "@octokit/request-error": "^7.0.2",
+ "@octokit/types": "^16.0.0",
+ "fast-content-type-parse": "^3.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/request-error": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz",
+ "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^16.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/types": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
+ "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^27.0.0"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@protobuf-ts/plugin": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.11.1.tgz",
+ "integrity": "sha512-HyuprDcw0bEEJqkOWe1rnXUP0gwYLij8YhPuZyZk6cJbIgc/Q0IFgoHQxOXNIXAcXM4Sbehh6kjVnCzasElw1A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@bufbuild/protobuf": "^2.4.0",
+ "@bufbuild/protoplugin": "^2.4.0",
+ "@protobuf-ts/protoc": "^2.11.1",
+ "@protobuf-ts/runtime": "^2.11.1",
+ "@protobuf-ts/runtime-rpc": "^2.11.1",
+ "typescript": "^3.9"
+ },
+ "bin": {
+ "protoc-gen-dump": "bin/protoc-gen-dump",
+ "protoc-gen-ts": "bin/protoc-gen-ts"
+ }
+ },
+ "node_modules/@protobuf-ts/protoc": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.11.1.tgz",
+ "integrity": "sha512-mUZJaV0daGO6HUX90o/atzQ6A7bbN2RSuHtdwo8SSF2Qoe3zHwa4IHyCN1evftTeHfLmdz+45qo47sL+5P8nyg==",
+ "license": "Apache-2.0",
+ "bin": {
+ "protoc": "protoc.js"
+ }
+ },
+ "node_modules/@protobuf-ts/runtime": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.11.1.tgz",
+ "integrity": "sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==",
+ "license": "(Apache-2.0 AND BSD-3-Clause)"
+ },
+ "node_modules/@protobuf-ts/runtime-rpc": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.11.1.tgz",
+ "integrity": "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@protobuf-ts/runtime": "^2.11.1"
+ }
+ },
+ "node_modules/@typescript/vfs": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz",
+ "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ }
+ },
+ "node_modules/@typespec/ts-http-runtime": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.2.tgz",
+ "integrity": "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==",
+ "license": "MIT",
+ "dependencies": {
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/archiver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
+ "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.2",
+ "async": "^3.2.4",
+ "buffer-crc32": "^1.0.0",
+ "readable-stream": "^4.0.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^3.0.0",
+ "zip-stream": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
+ "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^10.0.0",
+ "graceful-fs": "^4.2.0",
+ "is-stream": "^2.0.1",
+ "lazystream": "^1.0.0",
+ "lodash": "^4.17.15",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "license": "MIT"
+ },
+ "node_modules/b4a": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
+ "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/bare-events": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz",
+ "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==",
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/before-after-hook": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
+ "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/binary": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+ "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffers": "~0.1.1",
+ "chainsaw": "~0.1.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/bottleneck": {
+ "version": "2.19.5",
+ "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
+ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==",
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
+ "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/buffers": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+ "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==",
+ "engines": {
+ "node": ">=0.2.0"
+ }
+ },
+ "node_modules/chainsaw": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+ "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==",
+ "license": "MIT/X11",
+ "dependencies": {
+ "traverse": ">=0.3.0 <0.4"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/compress-commons": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
+ "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "crc32-stream": "^6.0.0",
+ "is-stream": "^2.0.1",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "license": "MIT"
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz",
+ "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/fast-content-type-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
+ "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "license": "MIT"
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz",
+ "integrity": "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "strnum": "^2.1.2"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/lazystream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "license": "ISC"
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "license": "MIT"
+ },
+ "node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/streamx": {
+ "version": "2.22.1",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz",
+ "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ },
+ "optionalDependencies": {
+ "bare-events": "^2.2.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz",
+ "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/text-decoder": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
+ "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
+ "node_modules/traverse": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+ "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==",
+ "license": "MIT/X11",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tunnel": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/undici": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
+ "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
+ "node_modules/universal-user-agent": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
+ "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
+ "license": "ISC"
+ },
+ "node_modules/unzip-stream": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.4.tgz",
+ "integrity": "sha512-PyofABPVv+d7fL7GOpusx7eRT9YETY2X04PhwbSipdj6bMxVCFJrr+nm0Mxqbf9hUiTin/UsnuFWBXlDZFy0Cw==",
+ "license": "MIT",
+ "dependencies": {
+ "binary": "^0.3.0",
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/zip-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz",
+ "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.0",
+ "compress-commons": "^6.0.2",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ }
+ }
+}
diff --git a/archived/projt-launcher/ci/github-script/package.json b/archived/projt-launcher/ci/github-script/package.json
new file mode 100644
index 0000000000..e4263e2ddf
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "projt-launcher-github-scripts",
+ "version": "1.0.0",
+ "description": "GitHub Actions scripts for ProjT Launcher CI",
+ "private": true,
+ "scripts": {
+ "test": "node test/commits.test.js"
+ },
+ "dependencies": {
+ "@actions/artifact": "6.1.0",
+ "@actions/core": "3.0.0",
+ "@actions/github": "9.0.0",
+ "bottleneck": "2.19.5",
+ "commander": "14.0.3"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+}
diff --git a/archived/projt-launcher/ci/github-script/prepare.js b/archived/projt-launcher/ci/github-script/prepare.js
new file mode 100644
index 0000000000..2c60314f11
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/prepare.js
@@ -0,0 +1,314 @@
+/**
+ * ProjT Launcher - PR Preparation Script
+ * Validates PR structure and prepares merge information
+ */
+
+const { classify } = require('../supportedBranches.js')
+const { postReview } = require('./reviews.js')
+
+const SIGNOFF_MARKER = '<!-- bot:missing-signed-off-by -->'
+
+function stripNoise(body = '') {
+ return String(body)
+ .replace(/\r/g, '')
+ .replace(/<!--.*?-->/gms, '')
+ .replace(/(^`{3,})[^`].*?\1/gms, '')
+}
+
+function hasSignedOffBy(body = '') {
+ const cleaned = stripNoise(body)
+ return /^signed-off-by:\s+.+<[^<>]+>\s*$/im.test(cleaned)
+}
+
+async function dismissSignoffReviews({ github, context, pull_number }) {
+ const reviews = await github.paginate(github.rest.pulls.listReviews, {
+ ...context.repo,
+ pull_number,
+ })
+
+ const signoffReviews = reviews.filter(
+ (r) =>
+ r.user?.login === 'github-actions[bot]' &&
+ r.state === 'CHANGES_REQUESTED' &&
+ typeof r.body === 'string' &&
+ r.body.includes(SIGNOFF_MARKER),
+ )
+
+ for (const review of signoffReviews) {
+ await github.rest.pulls.dismissReview({
+ ...context.repo,
+ pull_number,
+ review_id: review.id,
+ message: 'Signed-off-by found, thank you!',
+ })
+ }
+}
+
+/**
+ * Main PR preparation function
+ * Validates that the PR targets the correct branch and can be merged
+ */
+module.exports = async ({ github, context, core, dry }) => {
+ const payload = context.payload || {}
+ const pull_number =
+ payload?.pull_request?.number ??
+ (Array.isArray(payload?.merge_group?.pull_requests) &&
+ payload.merge_group.pull_requests[0]?.number)
+
+ if (typeof pull_number !== 'number') {
+ core.info('No pull request found on this event; skipping prepare step.')
+ return { ok: true, skipped: true, reason: 'no-pull-request' }
+ }
+
+ // Wait for GitHub to compute merge status
+ for (const retryInterval of [5, 10, 20, 40]) {
+ core.info('Checking whether the pull request can be merged...')
+ const prInfo = (
+ await github.rest.pulls.get({
+ ...context.repo,
+ pull_number,
+ })
+ ).data
+
+ if (prInfo.state !== 'open') {
+ throw new Error('PR is not open anymore.')
+ }
+
+ if (prInfo.mergeable == null) {
+ core.info(
+ `GitHub is still computing merge status, waiting ${retryInterval} seconds...`,
+ )
+ await new Promise((resolve) => setTimeout(resolve, retryInterval * 1000))
+ continue
+ }
+
+ const { base, head, user } = prInfo
+
+ const authorLogin = user?.login ?? ''
+ const isBotAuthor =
+ (user?.type ?? '').toLowerCase() === 'bot' || /\[bot\]$/i.test(authorLogin)
+
+ // Enforce PR template sign-off (Signed-off-by: Name <email>)
+ if (isBotAuthor) {
+ core.info(`Skipping Signed-off-by requirement for bot author: ${authorLogin}`)
+ if (!dry) {
+ await dismissSignoffReviews({ github, context, pull_number })
+ }
+ } else if (!hasSignedOffBy(prInfo.body)) {
+ const body = [
+ SIGNOFF_MARKER,
+ '',
+ '## Missing Signed-off-by',
+ '',
+ 'This repository requires a DCO-style sign-off line in the PR description.',
+ '',
+ 'Add a line like this to the PR description (under “Signed-off-by”):',
+ '',
+ '```',
+ 'Signed-off-by: Your Name <you@example.com>',
+ '```',
+ '',
+ 'After updating the PR description, this check will re-run automatically.',
+ ].join('\n')
+
+ await postReview({ github, context, core, dry, body })
+ throw new Error('Missing Signed-off-by in PR description')
+ } else if (!dry) {
+ await dismissSignoffReviews({ github, context, pull_number })
+ }
+
+ // Classify base branch
+ const baseClassification = classify(base.ref)
+ core.setOutput('base', baseClassification)
+ core.info(`Base branch classification: ${JSON.stringify(baseClassification)}`)
+
+ // Classify head branch
+ const headClassification =
+ base.repo.full_name === head.repo.full_name
+ ? classify(head.ref)
+ : { type: ['wip'] } // PRs from forks are WIP
+ core.setOutput('head', headClassification)
+ core.info(`Head branch classification: ${JSON.stringify(headClassification)}`)
+
+ // Validate base branch targeting
+ if (!baseClassification.type.includes('development') &&
+ !baseClassification.type.includes('release')) {
+ const body = [
+ '## Invalid Target Branch',
+ '',
+ `This PR targets \`${base.ref}\`, which is not a valid target branch.`,
+ '',
+ '### Valid target branches for ProjT Launcher:',
+ '',
+ '| Branch | Purpose |',
+ '|--------|---------|',
+ '| `develop` | Main development branch |',
+ '| `master` / `main` | Stable branch |',
+ '| `release-X.Y.Z` | Release branches |',
+ '',
+ 'Please [change the base branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request) to the appropriate target.',
+ ].join('\n')
+
+ await postReview({ github, context, core, dry, body })
+ throw new Error('PR targets invalid branch.')
+ }
+
+ // Check for release branch targeting from wrong branch
+ if (baseClassification.isRelease) {
+ // For release branches, typically only hotfixes and backports should target them
+ const isBackport = head.ref.startsWith('backport-')
+ const isHotfix = head.ref.startsWith('hotfix-') || head.ref.startsWith('hotfix/')
+
+ if (!isBackport && !isHotfix && headClassification.type.includes('wip')) {
+ const body = [
+ '## Release Branch Warning',
+ '',
+ `This PR targets the release branch \`${base.ref}\`.`,
+ '',
+ 'Release branches should only receive:',
+ '- **Backports** from the development branch',
+ '- **Hotfixes** for critical bugs',
+ '',
+ 'If this is a regular feature/fix, please target `develop` instead.',
+ '',
+ 'If this is intentionally a hotfix, consider naming your branch `hotfix/description`.',
+ ].join('\n')
+
+ await postReview({ github, context, core, dry, body })
+ // This is a warning, not an error
+ core.warning('PR targets release branch from non-hotfix/backport branch')
+ }
+ }
+
+ // Validate feature branches target develop
+ if (headClassification.isFeature &&
+ !['develop'].includes(base.ref)) {
+ const body = [
+ '## Feature Branch Target',
+ '',
+ `Feature branches should typically target \`develop\`, not \`${base.ref}\`.`,
+ '',
+ 'Please verify this is the correct target branch.',
+ ].join('\n')
+
+ core.warning(body)
+ // Don't block, just warn
+ }
+
+ // Process merge state
+ let mergedSha, targetSha
+
+ if (prInfo.mergeable) {
+ core.info('✓ PR can be merged.')
+
+ mergedSha = prInfo.merge_commit_sha
+ targetSha = (
+ await github.rest.repos.getCommit({
+ ...context.repo,
+ ref: prInfo.merge_commit_sha,
+ })
+ ).data.parents[0].sha
+ } else {
+ core.warning('⚠ PR has merge conflicts.')
+
+ mergedSha = head.sha
+ targetSha = (
+ await github.rest.repos.compareCommitsWithBasehead({
+ ...context.repo,
+ basehead: `${base.sha}...${head.sha}`,
+ })
+ ).data.merge_base_commit.sha
+ }
+
+ // Set outputs for downstream jobs
+ core.setOutput('mergedSha', mergedSha)
+ core.setOutput('targetSha', targetSha)
+ core.setOutput('mergeable', prInfo.mergeable)
+ core.setOutput('headSha', head.sha)
+ core.setOutput('baseSha', base.sha)
+
+ // Get changed files for analysis
+ const files = await github.paginate(github.rest.pulls.listFiles, {
+ ...context.repo,
+ pull_number,
+ per_page: 100,
+ })
+
+ // Categorize changes
+ const categories = {
+ source: files.filter(f =>
+ f.filename.startsWith('launcher/')
+ ).length,
+ ui: files.filter(f =>
+ f.filename.includes('/ui/')
+ ).length,
+ build: files.filter(f =>
+ f.filename.includes('CMake') ||
+ f.filename.includes('vcpkg') ||
+ f.filename.endsWith('.cmake')
+ ).length,
+ ci: files.filter(f =>
+ f.filename.startsWith('.github/') ||
+ f.filename.startsWith('ci/')
+ ).length,
+ docs: files.filter(f =>
+ f.filename.startsWith('docs/') ||
+ f.filename.endsWith('.md')
+ ).length,
+ translations: files.filter(f =>
+ f.filename.includes('translations/')
+ ).length,
+ }
+
+ core.info(`Changes summary:`)
+ core.info(` Source files: ${categories.source}`)
+ core.info(` UI files: ${categories.ui}`)
+ core.info(` Build files: ${categories.build}`)
+ core.info(` CI files: ${categories.ci}`)
+ core.info(` Documentation: ${categories.docs}`)
+ core.info(` Translations: ${categories.translations}`)
+
+ core.setOutput('categories', JSON.stringify(categories))
+ core.setOutput('totalFiles', files.length)
+
+ // Write step summary
+ if (process.env.GITHUB_STEP_SUMMARY) {
+ const fs = require('node:fs')
+ const summary = [
+ '## PR Preparation Summary',
+ '',
+ `| Property | Value |`,
+ `|----------|-------|`,
+ `| PR Number | #${pull_number} |`,
+ `| Base Branch | \`${base.ref}\` |`,
+ `| Head Branch | \`${head.ref}\` |`,
+ `| Mergeable | ${prInfo.mergeable ? '✅ Yes' : '❌ No'} |`,
+ `| Total Files | ${files.length} |`,
+ '',
+ '### Change Categories',
+ '',
+ `| Category | Files |`,
+ `|----------|-------|`,
+ ...Object.entries(categories).map(([cat, count]) =>
+ `| ${cat.charAt(0).toUpperCase() + cat.slice(1)} | ${count} |`
+ ),
+ ].join('\n')
+
+ fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary)
+ }
+
+ return {
+ mergeable: prInfo.mergeable,
+ mergedSha,
+ targetSha,
+ headSha: head.sha,
+ baseSha: base.sha,
+ base: baseClassification,
+ head: headClassification,
+ files: files.length,
+ categories,
+ }
+ }
+
+ throw new Error('Timeout waiting for merge status computation')
+}
diff --git a/archived/projt-launcher/ci/github-script/reviewers.js b/archived/projt-launcher/ci/github-script/reviewers.js
new file mode 100644
index 0000000000..9a2fd8df6a
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/reviewers.js
@@ -0,0 +1,329 @@
+/**
+ * ProjT Launcher - Reviewer Assignment
+ * Automatically assigns reviewers based on changed files and CODEOWNERS
+ */
+
+const fs = require('node:fs')
+const path = require('node:path')
+
+function extractMaintainersBlock(source) {
+ const token = 'maintainers ='
+ const start = source.indexOf(token)
+ if (start === -1) {
+ return ''
+ }
+
+ const braceStart = source.indexOf('{', start)
+ if (braceStart === -1) {
+ return ''
+ }
+
+ let depth = 0
+ for (let i = braceStart; i < source.length; i += 1) {
+ const char = source[i]
+ if (char === '{') {
+ depth += 1
+ } else if (char === '}') {
+ depth -= 1
+ if (depth === 0) {
+ return source.slice(braceStart, i + 1)
+ }
+ }
+ }
+ return ''
+}
+
+function parseAreas(areaBlock) {
+ const matches = areaBlock.match(/"([^"]+)"/g) || []
+ return matches.map(entry => entry.replace(/"/g, ''))
+}
+
+function loadMaintainersFromNix() {
+ const maintainersPath = path.join(__dirname, '..', 'eval', 'compare', 'maintainers.nix')
+ try {
+ const source = fs.readFileSync(maintainersPath, 'utf8')
+ const block = extractMaintainersBlock(source)
+ if (!block) {
+ return []
+ }
+
+ const entryRegex = /(\w+)\s*=\s*{([\s\S]*?)\n\s*};/g
+ const maintainers = []
+ let match
+ while ((match = entryRegex.exec(block)) !== null) {
+ const [, , body] = match
+ const githubMatch = body.match(/github\s*=\s*"([^"]+)"/)
+ if (!githubMatch) continue
+ const areasMatch = body.match(/areas\s*=\s*\[([\s\S]*?)\]/)
+ const areas = areasMatch ? parseAreas(areasMatch[1]) : []
+ maintainers.push({
+ handle: githubMatch[1],
+ areas,
+ })
+ }
+ return maintainers
+ } catch (error) {
+ console.warn(`Could not read maintainers from maintainers.nix: ${error.message}`)
+ return []
+ }
+}
+
+const FALLBACK_MAINTAINERS = [
+ {
+ handle: 'YongDo-Hyun',
+ areas: ['all'],
+ },
+ {
+ handle: 'grxtor',
+ areas: ['all'],
+ },
+]
+
+const MAINTAINERS = (() => {
+ const parsed = loadMaintainersFromNix()
+ return parsed.length > 0 ? parsed : FALLBACK_MAINTAINERS
+})()
+
+// File patterns to components mapping
+const FILE_PATTERNS = [
+ { pattern: /^launcher\/ui\//, component: 'ui' },
+ { pattern: /^launcher\/minecraft\//, component: 'minecraft' },
+ { pattern: /^launcher\/modplatform\//, component: 'modplatform' },
+ { pattern: /^launcher\//, component: 'core' },
+ { pattern: /^libraries\//, component: 'core' },
+ { pattern: /^\.github\//, component: 'ci' },
+ { pattern: /^ci\//, component: 'ci' },
+ { pattern: /CMakeLists\.txt$/, component: 'build' },
+ { pattern: /\.cmake$/, component: 'build' },
+ { pattern: /vcpkg/, component: 'build' },
+ { pattern: /^docs\//, component: 'docs' },
+ { pattern: /\.md$/, component: 'docs' },
+ { pattern: /translations\//, component: 'translations' },
+]
+
+const COMPONENTS = Array.from(new Set(FILE_PATTERNS.map(({ component }) => component)))
+
+const getMaintainersForComponent = component => {
+ const assigned = MAINTAINERS.filter(
+ maintainer =>
+ maintainer.areas.includes(component) || maintainer.areas.includes('all')
+ ).map(maintainer => maintainer.handle)
+
+ return assigned.length > 0 ? assigned : MAINTAINERS.map(maintainer => maintainer.handle)
+}
+
+// Component to reviewer mapping for ProjT Launcher
+const COMPONENT_REVIEWERS = Object.fromEntries(
+ COMPONENTS.map(component => [component, getMaintainersForComponent(component)])
+)
+
+/**
+ * Get components affected by file changes
+ * @param {Array} files - List of changed files
+ * @returns {Set} Affected components
+ */
+function getAffectedComponents(files) {
+ const components = new Set()
+
+ for (const file of files) {
+ const filename = file.filename || file
+ for (const { pattern, component } of FILE_PATTERNS) {
+ if (pattern.test(filename)) {
+ components.add(component)
+ break
+ }
+ }
+ }
+
+ return components
+}
+
+/**
+ * Get reviewers for components
+ * @param {Set} components - Affected components
+ * @returns {Set} Reviewers
+ */
+function getReviewersForComponents(components) {
+ const reviewers = new Set()
+
+ for (const component of components) {
+ const componentReviewers = COMPONENT_REVIEWERS[component] || []
+ for (const reviewer of componentReviewers) {
+ reviewers.add(reviewer.toLowerCase())
+ }
+ }
+
+ return reviewers
+}
+
+/**
+ * Handle reviewer assignment for a PR
+ */
+async function handleReviewers({
+ github,
+ context,
+ core,
+ log,
+ dry,
+ pull_request,
+ reviews,
+ maintainers,
+ owners,
+ getTeamMembers,
+ getUser,
+}) {
+ const pull_number = pull_request.number
+
+ // Get currently requested reviewers
+ const requested_reviewers = new Set(
+ pull_request.requested_reviewers.map(({ login }) => login.toLowerCase()),
+ )
+ log?.(
+ 'reviewers - requested_reviewers',
+ Array.from(requested_reviewers).join(', '),
+ )
+
+ // Get existing reviewers (already reviewed)
+ const existing_reviewers = new Set(
+ reviews.map(({ user }) => user?.login.toLowerCase()).filter(Boolean),
+ )
+ log?.(
+ 'reviewers - existing_reviewers',
+ Array.from(existing_reviewers).join(', '),
+ )
+
+ // Guard against too many reviewers from large PRs
+ if (maintainers && maintainers.length > 16) {
+ core.warning('Too many potential reviewers, skipping automatic assignment.')
+ return existing_reviewers.size === 0 && requested_reviewers.size === 0
+ }
+
+ // Build list of potential reviewers
+ const users = new Set()
+
+ // Add maintainers
+ if (maintainers) {
+ for (const id of maintainers) {
+ try {
+ const user = await getUser(id)
+ users.add(user.login.toLowerCase())
+ } catch (e) {
+ core.warning(`Could not resolve user ID ${id}`)
+ }
+ }
+ }
+
+ // Add owners (from CODEOWNERS)
+ if (owners) {
+ for (const handle of owners) {
+ if (handle && !handle.includes('/')) {
+ users.add(handle.toLowerCase())
+ }
+ }
+ }
+
+ log?.('reviewers - users', Array.from(users).join(', '))
+
+ // Handle team-based owners
+ const teams = new Set()
+ if (owners) {
+ for (const handle of owners) {
+ const parts = handle.split('/')
+ if (parts.length === 2 && parts[0] === context.repo.owner) {
+ teams.add(parts[1])
+ }
+ }
+ }
+ log?.('reviewers - teams', Array.from(teams).join(', '))
+
+ // Get team members
+ const team_members = new Set()
+ if (teams.size > 0 && getTeamMembers) {
+ for (const team of teams) {
+ try {
+ const members = await getTeamMembers(team)
+ for (const member of members) {
+ team_members.add(member.login.toLowerCase())
+ }
+ } catch (e) {
+ core.warning(`Could not fetch team ${team}`)
+ }
+ }
+ }
+ log?.('reviewers - team_members', Array.from(team_members).join(', '))
+
+ // Combine all potential reviewers
+ const all_reviewers = new Set([...users, ...team_members])
+
+ // Remove PR author - can't review own PR
+ const author = pull_request.user?.login.toLowerCase()
+ all_reviewers.delete(author)
+
+ log?.('reviewers - all_reviewers', Array.from(all_reviewers).join(', '))
+
+ // Filter to collaborators only
+ const reviewers = []
+ for (const username of all_reviewers) {
+ try {
+ await github.rest.repos.checkCollaborator({
+ ...context.repo,
+ username,
+ })
+ reviewers.push(username)
+ } catch (e) {
+ if (e.status !== 404) throw e
+ core.warning(
+ `User ${username} cannot be requested for review (not a collaborator)`,
+ )
+ }
+ }
+ log?.('reviewers - filtered_reviewers', reviewers.join(', '))
+
+ // Limit reviewers
+ if (reviewers.length > 10) {
+ core.warning(`Too many reviewers (${reviewers.length}), limiting to 10`)
+ reviewers.length = 10
+ }
+
+ // Determine who needs to be requested
+ const new_reviewers = new Set(reviewers)
+ .difference(requested_reviewers)
+ .difference(existing_reviewers)
+
+ log?.(
+ 'reviewers - new_reviewers',
+ Array.from(new_reviewers).join(', '),
+ )
+
+ if (new_reviewers.size === 0) {
+ log?.('Has reviewer changes', 'false (no new reviewers)')
+ } else if (dry) {
+ core.info(
+ `Would request reviewers for #${pull_number}: ${Array.from(new_reviewers).join(', ')} (dry run)`,
+ )
+ } else {
+ await github.rest.pulls.requestReviewers({
+ ...context.repo,
+ pull_number,
+ reviewers: Array.from(new_reviewers),
+ })
+ core.info(
+ `Requested reviewers for #${pull_number}: ${Array.from(new_reviewers).join(', ')}`,
+ )
+ }
+
+ // Return whether "needs-reviewers" label should be set
+ return (
+ new_reviewers.size === 0 &&
+ existing_reviewers.size === 0 &&
+ requested_reviewers.size === 0
+ )
+}
+
+module.exports = {
+ handleReviewers,
+ getAffectedComponents,
+ getReviewersForComponents,
+ COMPONENT_REVIEWERS,
+ FILE_PATTERNS,
+}
diff --git a/archived/projt-launcher/ci/github-script/reviews.js b/archived/projt-launcher/ci/github-script/reviews.js
new file mode 100644
index 0000000000..749ebc7085
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/reviews.js
@@ -0,0 +1,93 @@
+/**
+ * ProjT Launcher - Review Management
+ * Handles GitHub PR review operations
+ */
+
+/**
+ * Dismiss all bot-created reviews on a PR
+ */
+async function dismissReviews({ github, context, dry }) {
+ const pull_number = context.payload.pull_request.number
+
+ if (dry) {
+ return
+ }
+
+ await Promise.all(
+ (
+ await github.paginate(github.rest.pulls.listReviews, {
+ ...context.repo,
+ pull_number,
+ })
+ )
+ .filter((review) => review.user?.login === 'github-actions[bot]')
+ .map(async (review) => {
+ if (review.state === 'CHANGES_REQUESTED') {
+ await github.rest.pulls.dismissReview({
+ ...context.repo,
+ pull_number,
+ review_id: review.id,
+ message: 'All good now, thank you!',
+ })
+ }
+ await github.graphql(
+ `mutation($node_id:ID!) {
+ minimizeComment(input: {
+ classifier: RESOLVED,
+ subjectId: $node_id
+ })
+ { clientMutationId }
+ }`,
+ { node_id: review.node_id },
+ )
+ }),
+ )
+}
+
+async function postReview({ github, context, core, dry, body }) {
+ const pull_number = context.payload.pull_request.number
+
+ const pendingReview = (
+ await github.paginate(github.rest.pulls.listReviews, {
+ ...context.repo,
+ pull_number,
+ })
+ ).find(
+ (review) =>
+ review.user?.login === 'github-actions[bot]' &&
+ // If a review is still pending, we can just update this instead
+ // of posting a new one.
+ (review.state === 'CHANGES_REQUESTED' ||
+ // No need to post a new review, if an older one with the exact
+ // same content had already been dismissed.
+ review.body === body),
+ )
+
+ if (dry) {
+ if (pendingReview)
+ core.info(`pending review found: ${pendingReview.html_url}`)
+ else core.info('no pending review found')
+ core.info(body)
+ } else {
+ if (pendingReview) {
+ await github.rest.pulls.updateReview({
+ ...context.repo,
+ pull_number,
+ review_id: pendingReview.id,
+ body,
+ })
+ } else {
+ await github.rest.pulls.createReview({
+ ...context.repo,
+ pull_number,
+ event: 'REQUEST_CHANGES',
+ body,
+ })
+ }
+ }
+}
+
+module.exports = {
+ dismissReviews,
+ postReview,
+}
diff --git a/archived/projt-launcher/ci/github-script/run b/archived/projt-launcher/ci/github-script/run
new file mode 100644
index 0000000000..4f296f5633
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/run
@@ -0,0 +1,132 @@
+#!/usr/bin/env -S node --import ./run
+/**
+ * ProjT Launcher - CI Script Runner
+ * CLI tool for running CI automation scripts locally
+ */
+import { execSync } from 'node:child_process'
+import { closeSync, mkdtempSync, openSync, rmSync } from 'node:fs'
+import { tmpdir } from 'node:os'
+import { join } from 'node:path'
+import { program } from 'commander'
+import * as core from '@actions/core'
+import { getOctokit } from '@actions/github'
+
+/**
+ * Run a CI action locally
+ */
+async function run(action, owner, repo, pull_number, options = {}) {
+ // Get GitHub token from gh CLI
+ const token = execSync('gh auth token', { encoding: 'utf-8' }).trim()
+ const github = getOctokit(token)
+
+ // Build payload
+ const payload = !pull_number ? {} : {
+ pull_request: (await github.rest.pulls.get({
+ owner,
+ repo,
+ pull_number,
+ })).data
+ }
+
+ process.env['INPUT_GITHUB-TOKEN'] = token
+
+ // Set up step summary file
+ closeSync(openSync('step-summary.md', 'w'))
+ process.env.GITHUB_STEP_SUMMARY = 'step-summary.md'
+
+ await action({
+ github,
+ context: {
+ payload,
+ repo: {
+ owner,
+ repo,
+ },
+ },
+ core,
+ dry: true,
+ ...options,
+ })
+}
+
+program
+ .name('projt-ci')
+ .description('ProjT Launcher CI automation script runner')
+ .version('1.0.0')
+
+program
+ .command('prepare')
+ .description('Prepare and validate a pull request')
+ .argument('<owner>', 'Repository owner (e.g., Project-Tick)')
+ .argument('<repo>', 'Repository name (e.g., ProjT-Launcher)')
+ .argument('<pr>', 'Pull Request number')
+ .option('--no-dry', 'Make actual modifications')
+ .action(async (owner, repo, pr, options) => {
+ const prepare = (await import('./prepare.js')).default
+ await run(prepare, owner, repo, pr, options)
+ })
+
+program
+ .command('commits')
+ .description('Validate commit messages and structure')
+ .argument('<owner>', 'Repository owner')
+ .argument('<repo>', 'Repository name')
+ .argument('<pr>', 'Pull Request number')
+ .option('--no-dry', 'Make actual modifications')
+ .action(async (owner, repo, pr, options) => {
+ const commits = (await import('./commits.js')).default
+ await run(commits, owner, repo, pr, options)
+ })
+
+program
+ .command('get-teams')
+ .description('Fetch team information from GitHub organization')
+ .argument('<owner>', 'Organization/owner name')
+ .argument('<repo>', 'Repository name')
+ .argument('[outFile]', 'Output file path (prints to stdout if omitted)')
+ .action(async (owner, repo, outFile, options) => {
+ const getTeams = (await import('./get-teams.js')).default
+ await run(getTeams, owner, repo, undefined, { ...options, outFile })
+ })
+
+program
+ .command('reviewers')
+ .description('Assign reviewers to a pull request')
+ .argument('<owner>', 'Repository owner')
+ .argument('<repo>', 'Repository name')
+ .argument('<pr>', 'Pull Request number')
+ .option('--no-dry', 'Make actual modifications')
+ .action(async (owner, repo, pr, options) => {
+ const { handleReviewers } = await import('./reviewers.js')
+ const token = execSync('gh auth token', { encoding: 'utf-8' }).trim()
+ const github = getOctokit(token)
+
+ const pull_request = (await github.rest.pulls.get({
+ owner,
+ repo,
+ pull_number: pr,
+ })).data
+
+ const reviews = await github.paginate(github.rest.pulls.listReviews, {
+ owner,
+ repo,
+ pull_number: pr,
+ })
+
+ await handleReviewers({
+ github,
+ context: { repo: { owner, repo } },
+ core,
+ log: console.log,
+ dry: options.dry ?? true,
+ pull_request,
+ reviews,
+ maintainers: [],
+ owners: [],
+ getTeamMembers: async () => [],
+ getUser: async (id) => ({ login: `user-${id}`, id }),
+ })
+ })
+
+// Parse CLI arguments
+await program.parse()
diff --git a/archived/projt-launcher/ci/github-script/shell.nix b/archived/projt-launcher/ci/github-script/shell.nix
new file mode 100644
index 0000000000..788fee1815
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/shell.nix
@@ -0,0 +1,40 @@
+# ProjT Launcher - GitHub Script Development Shell
+# Provides Node.js environment for CI automation scripts
+{
+ system ? builtins.currentSystem,
+ pkgs ? import <nixpkgs> { inherit system; },
+}:
+
+pkgs.mkShell {
+ name = "projt-launcher-github-script";
+
+ packages = with pkgs; [
+ # Node.js for running scripts
+ nodejs_20
+
+ # GitHub CLI for authentication
+ gh
+
+ # Optional: development tools
+ nodePackages.npm
+ ];
+
+ shellHook = ''
+ echo "ProjT Launcher GitHub Script Development Environment"
+ echo ""
+ echo "Available commands:"
+ echo " npm install - Install dependencies"
+ echo " ./run --help - Show available CLI commands"
+ echo " gh auth login - Authenticate with GitHub"
+ echo ""
+
+ # Install npm dependencies if package-lock.json exists
+ if [ -f package-lock.json ] && [ ! -d node_modules ]; then
+ echo "Installing npm dependencies..."
+ npm ci
+ fi
+ '';
+
+ # Environment variables
+ PROJT_CI_ENV = "development";
+}
diff --git a/archived/projt-launcher/ci/github-script/test/commits.test.js b/archived/projt-launcher/ci/github-script/test/commits.test.js
new file mode 100644
index 0000000000..ed1be49682
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/test/commits.test.js
@@ -0,0 +1,42 @@
+#!/usr/bin/env node
+'use strict'
+
+process.env.COMMIT_TYPES = 'customtype'
+
+const assert = require('node:assert/strict')
+const commits = require('../commits.js')
+
+const { validateCommitMessage, normalizeCommitType } = commits
+
+const validMessages = [
+ 'feat(ui): add redesigned settings panel',
+ 'refactor: drop deprecated launcher flag support',
+ 'chore(ci): refresh workflows configuration',
+ '11.feat: support legacy numbered commit type format',
+ '23.deps(deps): bump dependency pins',
+ 'release: publish stable build artifacts',
+ 'customtype: allow env commit type overrides',
+]
+
+for (const message of validMessages) {
+ const result = validateCommitMessage(message)
+ assert.equal(
+ result.valid,
+ true,
+ `Expected commit "${message}" to be valid, got: ${result.message}`
+ )
+}
+
+const invalidType = validateCommitMessage('unknown(scope): add feature that is real enough')
+assert.equal(invalidType.valid, false, 'Expected invalid type to be rejected')
+assert.match(invalidType.message, /Unknown commit type/i)
+
+const shortDescription = validateCommitMessage('feat: short')
+assert.equal(shortDescription.valid, false, 'Expected short description to fail validation')
+assert.match(shortDescription.message, /too short/i)
+
+assert.equal(normalizeCommitType('11.feat'), 'feat')
+assert.equal(normalizeCommitType('23.deps'), 'deps')
+assert.equal(normalizeCommitType('chore'), 'chore')
+
+console.log('commits.js tests passed')
diff --git a/archived/projt-launcher/ci/github-script/withRateLimit.js b/archived/projt-launcher/ci/github-script/withRateLimit.js
new file mode 100644
index 0000000000..e7fcfbb513
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/withRateLimit.js
@@ -0,0 +1,86 @@
+/**
+ * ProjT Launcher - Rate Limit Handler
+ * Manages GitHub API rate limiting for CI scripts
+ */
+
+module.exports = async ({ github, core, maxConcurrent = 1 }, callback) => {
+ let Bottleneck
+ try {
+ Bottleneck = require('bottleneck')
+ } catch (err) {
+ core?.warning?.('bottleneck not installed; running without explicit rate limiting')
+ Bottleneck = class {
+ constructor() {}
+ wrap(fn) {
+ return (...args) => fn(...args)
+ }
+ chain() {
+ return this
+ }
+ schedule(fn, ...args) {
+ return fn(...args)
+ }
+ updateSettings() {}
+ }
+ }
+
+ const stats = {
+ issues: 0,
+ prs: 0,
+ requests: 0,
+ artifacts: 0,
+ }
+
+ // Rate-Limiting and Throttling, see for details:
+ // https://github.com/octokit/octokit.js/issues/1069#throttling
+ // https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api
+ const allLimits = new Bottleneck({
+ // Avoid concurrent requests
+ maxConcurrent,
+ // Will be updated with first `updateReservoir()` call below.
+ reservoir: 0,
+ })
+ // Pause between mutative requests
+ const writeLimits = new Bottleneck({ minTime: 1000 }).chain(allLimits)
+ github.hook.wrap('request', async (request, options) => {
+ // Requests to a different host do not count against the rate limit.
+ if (options.url.startsWith('https://github.com')) return request(options)
+ // Requests to the /rate_limit endpoint do not count against the rate limit.
+ if (options.url === '/rate_limit') return request(options)
+ // Search requests are in a different resource group, which allows 30 requests / minute.
+ // We do less than a handful each run, so not implementing throttling for now.
+ if (options.url.startsWith('/search/')) return request(options)
+ stats.requests++
+ if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method))
+ return writeLimits.schedule(request.bind(null, options))
+ else return allLimits.schedule(request.bind(null, options))
+ })
+
+ async function updateReservoir() {
+ let response
+ try {
+ response = await github.rest.rateLimit.get()
+ } catch (err) {
+ core.error(`Failed updating reservoir:\n${err}`)
+ // Keep retrying on failed rate limit requests instead of exiting the script early.
+ return
+ }
+ // Always keep 1000 spare requests for other jobs to do their regular duty.
+ // They normally use below 100, so 1000 is *plenty* of room to work with.
+ const reservoir = Math.max(0, response.data.resources.core.remaining - 1000)
+ core.info(`Updating reservoir to: ${reservoir}`)
+ allLimits.updateSettings({ reservoir })
+ }
+ await updateReservoir()
+ // Update remaining requests every minute to account for other jobs running in parallel.
+ const reservoirUpdater = setInterval(updateReservoir, 60 * 1000)
+
+ try {
+ await callback(stats)
+ } finally {
+ clearInterval(reservoirUpdater)
+ core.notice(
+ `Processed ${stats.prs} PRs, ${stats.issues} Issues, made ${stats.requests + stats.artifacts} API requests and downloaded ${stats.artifacts} artifacts.`,
+ )
+ }
+}