@decohere/ci-tools 0.0.1-dev.108 → 0.0.1-dev.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,368 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { pathToFileURL } from "node:url";
3
- import { listPublishablePackages } from "./publish-verify.mjs";
4
-
5
- const LIST_ID = process.env.CLICKUP_LIST_ID ?? "901220171084";
6
- const API = "https://api.clickup.com/api/v2";
7
- const GITHUB_API = process.env.GITHUB_API_URL ?? "https://api.github.com";
8
- const DEPLOYMENT_TYPE = 1005;
9
- const TASK_NAME = "deploy(publish): OIDC bootstrap unpublished packages";
10
- const LEGACY_TASK_NAMES = ["chore(publish): OIDC bootstrap unpublished packages"];
11
- const BOOTSTRAP_ENV = "npm-oidc-bootstrap";
12
- const DEFAULT_REPO = "typesmiths/decohere";
13
-
14
- export function taskName() {
15
- return TASK_NAME;
16
- }
17
-
18
- export function bootstrapTaskNames() {
19
- return [TASK_NAME, ...LEGACY_TASK_NAMES];
20
- }
21
-
22
- /** @param {string | undefined} raw */
23
- export function parseUnpublished(raw) {
24
- return String(raw ?? "")
25
- .split(/\s+/)
26
- .map((name) => name.trim())
27
- .filter(Boolean);
28
- }
29
-
30
- /** @param {string | undefined} raw */
31
- export function parseAssigneeIds(raw) {
32
- return String(raw ?? "")
33
- .split(/[,\s]+/)
34
- .map((id) => Number(id.trim()))
35
- .filter((id) => Number.isInteger(id) && id > 0);
36
- }
37
-
38
- /**
39
- * @param {string | undefined} version
40
- */
41
- export function needsPrereleaseTag(version) {
42
- return String(version ?? "").includes("-");
43
- }
44
-
45
- /**
46
- * @param {{ name: string, dir: string, version?: string }[]} packages
47
- * @param {string} [channel]
48
- */
49
- export function firstPublishCommands(packages, channel = "dev") {
50
- if (packages.length === 0) {
51
- return [`npm publish ./<package-dir> --tag ${channel}`];
52
- }
53
- return packages.map((pkg) => {
54
- const tag = needsPrereleaseTag(pkg.version) ? ` --tag ${channel}` : "";
55
- return `npm publish ./projects/${pkg.dir}${tag}`;
56
- });
57
- }
58
-
59
- /**
60
- * @param {unknown} environment
61
- * @returns {string[]}
62
- */
63
- export function extractGitHubReviewerLogins(environment) {
64
- const rules =
65
- environment && typeof environment === "object" ? environment.protection_rules : null;
66
- if (!Array.isArray(rules)) {
67
- return [];
68
- }
69
- const logins = [];
70
- for (const rule of rules) {
71
- const reviewers = rule?.reviewers;
72
- if (!Array.isArray(reviewers)) {
73
- continue;
74
- }
75
- for (const entry of reviewers) {
76
- const login = entry?.reviewer?.login;
77
- if (typeof login === "string" && login.length > 0 && entry?.type !== "Team") {
78
- logins.push(login);
79
- }
80
- }
81
- }
82
- return [...new Set(logins)];
83
- }
84
-
85
- /**
86
- * @param {string[]} logins
87
- * @param {{ id?: number, username?: string, email?: string }[]} members
88
- */
89
- export function mapLoginsToClickUpIds(logins, members) {
90
- const ids = new Set();
91
- for (const login of logins) {
92
- const key = login.toLowerCase();
93
- for (const member of members) {
94
- const username = String(member.username ?? "").toLowerCase();
95
- const email = String(member.email ?? "").toLowerCase();
96
- const local = email.split("@")[0] ?? "";
97
- if (username === key || local === key || email === key) {
98
- const id = Number(member.id);
99
- if (Number.isInteger(id) && id > 0) {
100
- ids.add(id);
101
- }
102
- }
103
- }
104
- }
105
- return [...ids];
106
- }
107
-
108
- /**
109
- * @param {{
110
- * unpublished: string[],
111
- * packages: { name: string, dir: string, version?: string }[],
112
- * repo?: string,
113
- * runUrl?: string,
114
- * channel?: string,
115
- * }} options
116
- */
117
- export function buildDescription(options) {
118
- const repo = options.repo || DEFAULT_REPO;
119
- const names = options.unpublished;
120
- const channel = options.channel || "dev";
121
- const packages = options.packages.filter((pkg) => names.includes(pkg.name));
122
- const [owner, repoName] = repo.split("/");
123
- return [
124
- "## Summary",
125
- "",
126
- "These package names are not on npm yet, so OIDC `npm publish` cannot run until each name exists and has a Trusted Publisher:",
127
- "",
128
- ...names.map((name) => `- \`${name}\``),
129
- "",
130
- "## What to do",
131
- "",
132
- "1. First-publish each name **restricted** (scoped default — do **not** pass `--access public`). From a trusted machine, `npm login` to the org, then from the **repo root**. Use `./` so npm does not treat the path as a GitHub `owner/repo`. `--tag` is required for prerelease versions (`x.y.z-dev.N`). Missing `bin`/`es6` warnings are OK for this name-only bootstrap; CI `forge publish` ships the built tarball later:",
133
- "",
134
- "```bash",
135
- ...firstPublishCommands(packages, channel),
136
- "```",
137
- "",
138
- "2. npm → package settings → Trusted Publisher → GitHub Actions:",
139
- "",
140
- `- Organization: \`${owner ?? "typesmiths"}\``,
141
- `- Repository: \`${repoName ?? "decohere"}\``,
142
- "- Workflow filename: `publish.yml`",
143
- "- Environment: `npm-publish`",
144
- "- Allowed action: `npm publish` (not staged-only)",
145
- "",
146
- "3. Approve the waiting **npm-oidc-bootstrap** deployment on this Publish run. The job re-checks that the names exist, then npm + Docker + tag continue. If reviewers were not configured and the job already failed:",
147
- "",
148
- "```bash",
149
- `gh run list --repo ${repo} --workflow Publish --limit 5`,
150
- `gh run rerun --failed <run-id> --repo ${repo}`,
151
- "```",
152
- "",
153
- options.runUrl ? `- Workflow run: ${options.runUrl}` : null,
154
- ]
155
- .filter((line) => line != null)
156
- .join("\n");
157
- }
158
-
159
- async function clickup(method, path, body) {
160
- const key = process.env.CLICKUP_API_KEY;
161
- if (key == null || key.length === 0) {
162
- throw new Error("Missing CLICKUP_API_KEY");
163
- }
164
- const response = await fetch(`${API}${path}`, {
165
- method,
166
- headers: {
167
- Authorization: key,
168
- "Content-Type": "application/json",
169
- },
170
- body: body == null ? undefined : JSON.stringify(body),
171
- });
172
- const text = await response.text();
173
- if (!response.ok) {
174
- throw new Error(
175
- `ClickUp ${method} ${path} failed (${response.status}): ${text.slice(0, 500)}`,
176
- );
177
- }
178
- return text.length === 0 ? undefined : JSON.parse(text);
179
- }
180
-
181
- async function findTaskByName(names) {
182
- const wanted = new Set(Array.isArray(names) ? names : [names]);
183
- for (let page = 0; page < 10; page += 1) {
184
- const qs = new URLSearchParams({
185
- include_closed: "true",
186
- subtasks: "false",
187
- page: String(page),
188
- });
189
- const data = await clickup("GET", `/list/${LIST_ID}/task?${qs}`);
190
- const tasks = Array.isArray(data) ? data : (data?.tasks ?? []);
191
- const match = tasks.find((task) => wanted.has(task.name));
192
- if (match) {
193
- return match;
194
- }
195
- if (tasks.length < 100) {
196
- break;
197
- }
198
- }
199
- return null;
200
- }
201
-
202
- async function firstOpenStatus() {
203
- const list = await clickup("GET", `/list/${LIST_ID}`);
204
- return (list.statuses ?? []).find((status) => status.type === "open")?.status;
205
- }
206
-
207
- function runUrl() {
208
- if (
209
- process.env.GITHUB_SERVER_URL &&
210
- process.env.GITHUB_REPOSITORY &&
211
- process.env.GITHUB_RUN_ID
212
- ) {
213
- return `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
214
- }
215
- return "";
216
- }
217
-
218
- /**
219
- * @param {{ fetch?: typeof fetch, token?: string, repo?: string, environment?: string }} options
220
- */
221
- export async function githubEnvironmentReviewerLogins(options = {}) {
222
- const token = options.token ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN ?? "";
223
- const repo = options.repo ?? process.env.GITHUB_REPOSITORY ?? "";
224
- const environment = options.environment ?? BOOTSTRAP_ENV;
225
- const fetchFn = options.fetch ?? fetch;
226
- if (!token || !repo) {
227
- return [];
228
- }
229
- const response = await fetchFn(
230
- `${GITHUB_API}/repos/${repo}/environments/${encodeURIComponent(environment)}`,
231
- {
232
- headers: {
233
- Authorization: `Bearer ${token}`,
234
- Accept: "application/vnd.github+json",
235
- "X-GitHub-Api-Version": "2022-11-28",
236
- },
237
- },
238
- );
239
- if (!response.ok) {
240
- process.stderr.write(
241
- `Could not list ${environment} reviewers (${response.status}); assigning from CLICKUP_ASSIGNEE_ID only\n`,
242
- );
243
- return [];
244
- }
245
- return extractGitHubReviewerLogins(await response.json());
246
- }
247
-
248
- export async function clickupWorkspaceMembers() {
249
- const data = await clickup("GET", "/team");
250
- const teams = Array.isArray(data?.teams) ? data.teams : [];
251
- const members = [];
252
- for (const team of teams) {
253
- for (const entry of team.members ?? []) {
254
- const user = entry?.user ?? entry;
255
- if (user?.id) {
256
- members.push({
257
- id: Number(user.id),
258
- username: user.username,
259
- email: user.email,
260
- });
261
- }
262
- }
263
- }
264
- return members;
265
- }
266
-
267
- /**
268
- * @param {{ unpublished: string[], packages: { name: string, dir: string, version?: string }[], assigneeIds: number[], repo?: string, runUrl?: string, channel?: string }} options
269
- */
270
- export async function upsertBootstrapTask(options) {
271
- const name = taskName();
272
- const existing = await findTaskByName(bootstrapTaskNames());
273
- const markdown = buildDescription(options);
274
- const payload = {
275
- name,
276
- markdown_description: markdown,
277
- custom_item_id: DEPLOYMENT_TYPE,
278
- priority: 1,
279
- };
280
- if (options.assigneeIds.length > 0 && !existing) {
281
- payload.assignees = options.assigneeIds;
282
- }
283
-
284
- if (existing) {
285
- const update = {
286
- name,
287
- markdown_description: markdown,
288
- custom_item_id: DEPLOYMENT_TYPE,
289
- priority: 1,
290
- };
291
- if (existing.closed || existing.status?.type === "closed") {
292
- const status = await firstOpenStatus();
293
- if (status) {
294
- update.status = status;
295
- }
296
- }
297
- if (options.assigneeIds.length > 0) {
298
- update.assignees = { add: options.assigneeIds };
299
- }
300
- await clickup("PUT", `/task/${existing.id}`, update);
301
- return { id: existing.id, url: existing.url, created: false };
302
- }
303
- const created = await clickup("POST", `/list/${LIST_ID}/task`, payload);
304
- return { id: created.id, url: created.url, created: true };
305
- }
306
-
307
- export async function runNudge(env = process.env, io = {}) {
308
- const unpublished = parseUnpublished(env.UNPUBLISHED);
309
- if (unpublished.length === 0) {
310
- process.stdout.write("No unpublished package names; skipping ClickUp bootstrap task\n");
311
- return 0;
312
- }
313
-
314
- const root = io.root ?? process.cwd();
315
- const packages = (io.listPackages ?? listPublishablePackages)(root);
316
- const logins = await (io.githubReviewers ?? githubEnvironmentReviewerLogins)({
317
- token: env.GITHUB_TOKEN ?? env.GH_TOKEN,
318
- repo: env.GITHUB_REPOSITORY,
319
- });
320
- const members = await (io.clickupMembers ?? clickupWorkspaceMembers)();
321
- const assigneeIds = [
322
- ...new Set([
323
- ...mapLoginsToClickUpIds(logins, members),
324
- ...parseAssigneeIds(env.CLICKUP_ASSIGNEE_ID),
325
- ]),
326
- ];
327
-
328
- if (logins.length > 0 && mapLoginsToClickUpIds(logins, members).length === 0) {
329
- process.stderr.write(
330
- `GitHub ${BOOTSTRAP_ENV} reviewers (${logins.join(", ")}) did not match a ClickUp member (login ↔ username or email local-part)\n`,
331
- );
332
- }
333
-
334
- const result = await (io.upsert ?? upsertBootstrapTask)({
335
- unpublished,
336
- packages,
337
- assigneeIds,
338
- repo: env.GITHUB_REPOSITORY || DEFAULT_REPO,
339
- runUrl: io.runUrl ?? runUrl(),
340
- channel: env.PUBLISH_CHANNEL || env.CHANNEL || "dev",
341
- });
342
- const url = result.url ?? "";
343
- process.stdout.write(
344
- `${result.created ? "Created" : "Updated"} ClickUp ${result.id} ${url}\n`,
345
- );
346
- if (assigneeIds.length > 0) {
347
- process.stdout.write(`Assigned ClickUp users ${assigneeIds.join(", ")}\n`);
348
- } else {
349
- process.stdout.write(
350
- "No ClickUp assignees (add required reviewers on npm-oidc-bootstrap, or set vars.CLICKUP_ASSIGNEE_ID)\n",
351
- );
352
- }
353
- if (env.GITHUB_OUTPUT) {
354
- const { appendFileSync } = await import("node:fs");
355
- appendFileSync(env.GITHUB_OUTPUT, `task_url=${url}\n`);
356
- }
357
- if (!url) {
358
- throw new Error("ClickUp bootstrap task has no URL");
359
- }
360
- return 0;
361
- }
362
-
363
- const isCli =
364
- process.argv[1] != null && import.meta.url === pathToFileURL(resolve(process.argv[1])).href;
365
-
366
- if (isCli) {
367
- process.exit(await runNudge());
368
- }
@@ -1,209 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import { existsSync, readdirSync, readFileSync } from "node:fs";
3
- import { join, resolve } from "node:path";
4
- import { pathToFileURL } from "node:url";
5
-
6
- /**
7
- * Workspace packages that `forge publish` will upload (not `"private": true`).
8
- * @param {string} root
9
- */
10
- export function listPublishablePackages(root) {
11
- const projectsDir = join(root, "projects");
12
- const packages = [];
13
- for (const dir of readdirSync(projectsDir)) {
14
- const pkgPath = join(projectsDir, dir, "package.json");
15
- if (!existsSync(pkgPath)) {
16
- continue;
17
- }
18
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
19
- if (typeof pkg.name !== "string" || pkg.private === true) {
20
- continue;
21
- }
22
- packages.push({
23
- name: pkg.name,
24
- version: pkg.version,
25
- dir,
26
- });
27
- }
28
- return packages.sort((a, b) => a.name.localeCompare(b.name));
29
- }
30
-
31
- export function dockerRepositories(root) {
32
- const create = JSON.parse(readFileSync(join(root, "projects/create/package.json"), "utf8"));
33
- const devcontainer = JSON.parse(
34
- readFileSync(join(root, "projects/devcontainer/package.json"), "utf8"),
35
- );
36
- return {
37
- create: create.decohere?.dockerImageRepository ?? "ghcr.io/typesmiths/decohere/create",
38
- devcontainer:
39
- devcontainer.decohere?.dockerImageRepository ??
40
- "ghcr.io/typesmiths/decohere/devcontainer",
41
- createVersion: create.version,
42
- workspaceVersion: JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version,
43
- };
44
- }
45
-
46
- /**
47
- * @param {string} spec package or package@version
48
- * @param {{ spawnSync?: typeof spawnSync }} [io]
49
- */
50
- export function npmNameExists(spec, io = {}) {
51
- const spawn = io.spawnSync ?? spawnSync;
52
- const result = spawn("npm", ["view", spec, "name"], {
53
- encoding: "utf8",
54
- stdio: ["ignore", "pipe", "pipe"],
55
- });
56
- return result.status === 0;
57
- }
58
-
59
- /**
60
- * @param {string} spec package@version
61
- * @param {{ spawnSync?: typeof spawnSync }} [io]
62
- */
63
- export function npmVersionExists(spec, io = {}) {
64
- const spawn = io.spawnSync ?? spawnSync;
65
- const result = spawn("npm", ["view", spec, "version"], {
66
- encoding: "utf8",
67
- stdio: ["ignore", "pipe", "pipe"],
68
- });
69
- return result.status === 0;
70
- }
71
-
72
- /**
73
- * Parse `ghcr.io/org/name/image` into GitHub packages API coordinates.
74
- * @param {string} repository
75
- */
76
- export function parseGhcrRepository(repository) {
77
- const withoutHost = repository.replace(/^ghcr\.io\//, "");
78
- const slash = withoutHost.indexOf("/");
79
- if (slash < 0) {
80
- throw new Error(`Not a GHCR repository: ${repository}`);
81
- }
82
- return {
83
- org: withoutHost.slice(0, slash),
84
- packageName: withoutHost.slice(slash + 1),
85
- };
86
- }
87
-
88
- /**
89
- * @param {string} repository ghcr.io/org/pkg
90
- * @param {string} tag
91
- * @param {{ token: string, fetch?: typeof fetch }} io
92
- */
93
- export async function ghcrTagExists(repository, tag, io) {
94
- const { org, packageName } = parseGhcrRepository(repository);
95
- const encoded = encodeURIComponent(packageName);
96
- const headers = {
97
- Authorization: `Bearer ${io.token}`,
98
- Accept: "application/vnd.github+json",
99
- "X-GitHub-Api-Version": "2022-11-28",
100
- };
101
- const fetchImpl = io.fetch ?? fetch;
102
- for (let page = 1; page <= 20; page += 1) {
103
- const url = `https://api.github.com/orgs/${org}/packages/container/${encoded}/versions?per_page=100&page=${page}`;
104
- const response = await fetchImpl(url, { headers });
105
- if (response.status === 404) {
106
- return false;
107
- }
108
- if (!response.ok) {
109
- throw new Error(`GHCR versions ${response.status} for ${repository}`);
110
- }
111
- const versions = await response.json();
112
- if (!Array.isArray(versions) || versions.length === 0) {
113
- return false;
114
- }
115
- for (const version of versions) {
116
- const tags = version.metadata?.container?.tags ?? [];
117
- if (tags.includes(tag)) {
118
- return true;
119
- }
120
- }
121
- if (versions.length < 100) {
122
- return false;
123
- }
124
- }
125
- return false;
126
- }
127
-
128
- /**
129
- * @param {{
130
- * root: string,
131
- * spawnSync?: typeof spawnSync,
132
- * fetch?: typeof fetch,
133
- * githubToken?: string,
134
- * }} options
135
- */
136
- export async function collectPublishVerify(options) {
137
- const packages = listPublishablePackages(options.root);
138
- const unpublished = [];
139
- let publishedVersions = 0;
140
- for (const pkg of packages) {
141
- if (!npmNameExists(pkg.name, options)) {
142
- unpublished.push(pkg.name);
143
- continue;
144
- }
145
- if (npmVersionExists(`${pkg.name}@${pkg.version}`, options)) {
146
- publishedVersions += 1;
147
- }
148
- }
149
-
150
- const images = dockerRepositories(options.root);
151
- const token = options.githubToken ?? "";
152
- let skipDocker = false;
153
- if (token) {
154
- const createExists = await ghcrTagExists(images.create, images.workspaceVersion, {
155
- token,
156
- fetch: options.fetch,
157
- });
158
- const devExists = await ghcrTagExists(images.devcontainer, images.workspaceVersion, {
159
- token,
160
- fetch: options.fetch,
161
- });
162
- skipDocker = createExists && devExists;
163
- }
164
-
165
- return {
166
- unpublished,
167
- skipNpm: packages.length > 0 && publishedVersions === packages.length,
168
- skipDocker,
169
- imageTag: images.workspaceVersion,
170
- createVersion: images.createVersion,
171
- repository: images.create,
172
- devcontainerRepository: images.devcontainer,
173
- packageCount: packages.length,
174
- publishedVersions,
175
- };
176
- }
177
-
178
- export function formatGithubOutput(result) {
179
- return [
180
- `unpublished=${result.unpublished.join(" ")}`,
181
- `skip_npm=${result.skipNpm ? "true" : "false"}`,
182
- `skip_docker=${result.skipDocker ? "true" : "false"}`,
183
- `image_tag=${result.imageTag}`,
184
- `create_version=${result.createVersion}`,
185
- `repository=${result.repository}`,
186
- `devcontainer_repository=${result.devcontainerRepository}`,
187
- ].join("\n");
188
- }
189
-
190
- const isCli =
191
- process.argv[1] != null && import.meta.url === pathToFileURL(resolve(process.argv[1])).href;
192
-
193
- if (isCli) {
194
- const result = await collectPublishVerify({
195
- root: process.cwd(),
196
- githubToken: process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN,
197
- });
198
- const body = formatGithubOutput(result);
199
- process.stdout.write(`${body}\n`);
200
- if (process.env.GITHUB_OUTPUT) {
201
- const { appendFileSync } = await import("node:fs");
202
- appendFileSync(process.env.GITHUB_OUTPUT, `${body}\n`);
203
- }
204
- if (result.unpublished.length) {
205
- process.stderr.write(
206
- `Never-published packages (bootstrap OIDC before PUT): ${result.unpublished.join(", ")}\n`,
207
- );
208
- }
209
- }
@@ -1,70 +0,0 @@
1
- #!/usr/bin/env node
2
- import { spawnSync } from "node:child_process";
3
- import { mkdtempSync, writeFileSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join, resolve } from "node:path";
6
- import { pathToFileURL } from "node:url";
7
-
8
- /**
9
- * Run `npx @decohere/create@version --help` from an empty temp directory.
10
- * Must not use the repo cwd — after `@decohere/cli` is pinned, pnpm installs
11
- * `@decohere/create` into the workspace tree and npm's npx misses the bin.
12
- *
13
- * @param {{
14
- * createVersion: string,
15
- * authToken: string,
16
- * mkdtempSync?: typeof mkdtempSync,
17
- * writeFileSync?: typeof writeFileSync,
18
- * spawnSync?: typeof spawnSync,
19
- * tmpdir?: () => string,
20
- * stdio?: import("node:child_process").StdioOptions,
21
- * }} options
22
- */
23
- export function runCreateLauncherSmoke(options) {
24
- const version = options.createVersion?.trim() ?? "";
25
- const token = options.authToken?.trim() ?? "";
26
- if (!version) {
27
- throw new Error("CREATE_VERSION is required");
28
- }
29
- if (!token) {
30
- throw new Error("NODE_AUTH_TOKEN is required");
31
- }
32
-
33
- const mkdtemp = options.mkdtempSync ?? mkdtempSync;
34
- const writeFile = options.writeFileSync ?? writeFileSync;
35
- const spawn = options.spawnSync ?? spawnSync;
36
- const tmp = options.tmpdir ?? tmpdir;
37
-
38
- const workDir = mkdtemp(join(tmp(), "decohere-create-smoke-"));
39
- const npmrc = join(workDir, ".npmrc");
40
- writeFile(
41
- npmrc,
42
- `@decohere:registry=https://registry.npmjs.org/\n//registry.npmjs.org/:_authToken=${token}\n`,
43
- );
44
-
45
- const result = spawn("npx", ["--yes", `@decohere/create@${version}`, "--help"], {
46
- cwd: workDir,
47
- env: {
48
- ...process.env,
49
- NPM_CONFIG_USERCONFIG: npmrc,
50
- NODE_AUTH_TOKEN: token,
51
- },
52
- stdio: options.stdio ?? "inherit",
53
- });
54
-
55
- return {
56
- workDir,
57
- status: result.status ?? 1,
58
- };
59
- }
60
-
61
- const isCli =
62
- process.argv[1] != null && import.meta.url === pathToFileURL(resolve(process.argv[1])).href;
63
-
64
- if (isCli) {
65
- const { status } = runCreateLauncherSmoke({
66
- createVersion: process.env.CREATE_VERSION ?? "",
67
- authToken: process.env.NODE_AUTH_TOKEN ?? "",
68
- });
69
- process.exit(status);
70
- }