@blastlauncher/cli 1.0.4 → 3.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -18
- package/dist/browser.d.ts +11 -0
- package/dist/browser.js +49 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +855 -0
- package/dist/headless.d.ts +26 -0
- package/dist/headless.js +162 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +23 -24
- package/CHANGELOG.md +0 -25
- package/build.js +0 -15
- package/dist/cli-linux +0 -4
- package/dist/cli-macos +0 -4
- package/dist/cli-win.exe +0 -0
- package/dist/index.cjs +0 -368
- package/index.js +0 -265
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type BrowserTab } from "./browser.js";
|
|
2
|
+
import type { CapabilityHandler } from "@blastlauncher/engine";
|
|
3
|
+
/** `Application` from the `@raycast/api` declarations, as it crosses the wire. */
|
|
4
|
+
export type Application = {
|
|
5
|
+
name: string;
|
|
6
|
+
path: string;
|
|
7
|
+
localizedName?: string;
|
|
8
|
+
bundleId?: string;
|
|
9
|
+
windowsAppId?: string;
|
|
10
|
+
};
|
|
11
|
+
export type HeadlessCapabilities = {
|
|
12
|
+
aiAnswer?: string | undefined;
|
|
13
|
+
browserTabs?: BrowserTab[] | undefined;
|
|
14
|
+
oauthCode?: string | undefined;
|
|
15
|
+
/** Answers `applications.list`; an empty list when omitted. */
|
|
16
|
+
applications?: Application[] | undefined;
|
|
17
|
+
selectedText?: string | undefined;
|
|
18
|
+
selectedFiles?: string[] | undefined;
|
|
19
|
+
/** Bundle identifier; resolves against applications or a synthetic application. */
|
|
20
|
+
frontmost?: string | undefined;
|
|
21
|
+
/** Answers `clipboard.read` at offset 0; an empty text when omitted. */
|
|
22
|
+
clipboard?: string | undefined;
|
|
23
|
+
};
|
|
24
|
+
/** Parse an `--applications` file: a JSON array of `{ name, path, … }`. */
|
|
25
|
+
export declare function parseApplications(text: string): Application[];
|
|
26
|
+
export declare function createHeadlessHandlers(capabilities?: HeadlessCapabilities): Record<string, CapabilityHandler>;
|
package/dist/headless.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// The capabilities a headless Blast run answers without a desktop client. They
|
|
2
|
+
// read host state or record UI requests: `getApplications` reports the applications the caller supplied
|
|
3
|
+
// (an empty list otherwise), `Clipboard.read` reports the text the caller
|
|
4
|
+
// supplied (empty otherwise, with no history behind it), and opening a
|
|
5
|
+
// preference window or updating command metadata is only recorded. Selection
|
|
6
|
+
// comes from the supplied text and paths; the frontmost app is supplied or neutral.
|
|
7
|
+
// Window management reads are neutral too: one desktop, no windows, and no
|
|
8
|
+
// default application for a path. Everything that would change the machine
|
|
9
|
+
// (`open.*`, `clipboard.paste`, `clipboard.clear`, `window.setBounds`,
|
|
10
|
+
// `mcp.install`) stays denied unless granted.
|
|
11
|
+
import { createBrowserHandlers } from "./browser.js";
|
|
12
|
+
/** Fields other than the required `name` and `path`. */
|
|
13
|
+
const OPTIONAL_FIELDS = ["localizedName", "bundleId", "windowsAppId"];
|
|
14
|
+
/** The offsets the `Clipboard.read` declarations allow into clipboard history. */
|
|
15
|
+
const MAX_OFFSET = 5;
|
|
16
|
+
function readParams(params, api) {
|
|
17
|
+
if (typeof params !== "object" || params === null || Array.isArray(params)) {
|
|
18
|
+
throw new Error(`${api}: params must be an object`);
|
|
19
|
+
}
|
|
20
|
+
return params;
|
|
21
|
+
}
|
|
22
|
+
function toApplication(value, at) {
|
|
23
|
+
if (typeof value !== "object" ||
|
|
24
|
+
value === null ||
|
|
25
|
+
Array.isArray(value) ||
|
|
26
|
+
typeof value.name !== "string" ||
|
|
27
|
+
typeof value.path !== "string") {
|
|
28
|
+
throw new Error(`${at} must be an object with string "name" and "path"`);
|
|
29
|
+
}
|
|
30
|
+
const application = { name: value.name, path: value.path };
|
|
31
|
+
for (const field of OPTIONAL_FIELDS) {
|
|
32
|
+
const fieldValue = value[field];
|
|
33
|
+
if (typeof fieldValue === "string")
|
|
34
|
+
application[field] = fieldValue;
|
|
35
|
+
}
|
|
36
|
+
return application;
|
|
37
|
+
}
|
|
38
|
+
/** The wire form of one application: absent fields stay absent, never undefined. */
|
|
39
|
+
function applicationWire(application) {
|
|
40
|
+
const wire = { name: application.name, path: application.path };
|
|
41
|
+
for (const field of OPTIONAL_FIELDS) {
|
|
42
|
+
const value = application[field];
|
|
43
|
+
if (typeof value === "string")
|
|
44
|
+
wire[field] = value;
|
|
45
|
+
}
|
|
46
|
+
return wire;
|
|
47
|
+
}
|
|
48
|
+
/** Parse an `--applications` file: a JSON array of `{ name, path, … }`. */
|
|
49
|
+
export function parseApplications(text) {
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(text);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(`not JSON (${error instanceof Error ? error.message : String(error)})`, {
|
|
56
|
+
cause: error,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (!Array.isArray(parsed))
|
|
60
|
+
throw new Error("must be a JSON array of applications");
|
|
61
|
+
return parsed.map((value, index) => toApplication(value, `application ${index + 1}`));
|
|
62
|
+
}
|
|
63
|
+
export function createHeadlessHandlers(capabilities = {}) {
|
|
64
|
+
const applications = capabilities.applications ?? [];
|
|
65
|
+
const clipboard = capabilities.clipboard ?? "";
|
|
66
|
+
const frontmost = capabilities.frontmost;
|
|
67
|
+
return {
|
|
68
|
+
...(capabilities.aiAnswer === undefined ? {} : { "ai.ask": () => capabilities.aiAnswer }),
|
|
69
|
+
...(capabilities.browserTabs === undefined
|
|
70
|
+
? {}
|
|
71
|
+
: createBrowserHandlers(capabilities.browserTabs)),
|
|
72
|
+
...(capabilities.oauthCode === undefined
|
|
73
|
+
? {}
|
|
74
|
+
: { "oauth.authorize": () => ({ authorizationCode: capabilities.oauthCode }) }),
|
|
75
|
+
"selection.text": (params) => {
|
|
76
|
+
readParams(params, "selection.text");
|
|
77
|
+
if (!capabilities.selectedText)
|
|
78
|
+
throw new Error("No text selected");
|
|
79
|
+
return capabilities.selectedText;
|
|
80
|
+
},
|
|
81
|
+
"selection.files": (params) => {
|
|
82
|
+
readParams(params, "selection.files");
|
|
83
|
+
return (capabilities.selectedFiles ?? []).map((path) => ({ path }));
|
|
84
|
+
},
|
|
85
|
+
"applications.frontmost": (params) => {
|
|
86
|
+
readParams(params, "applications.frontmost");
|
|
87
|
+
const application = applications.find((app) => frontmost !== undefined && app.bundleId === frontmost);
|
|
88
|
+
return applicationWire(application ?? {
|
|
89
|
+
name: frontmost ?? "Headless",
|
|
90
|
+
path: "",
|
|
91
|
+
...(frontmost === undefined ? {} : { bundleId: frontmost }),
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
"applications.list": (params) => {
|
|
95
|
+
// The supplied list is the whole answer: a headless run cannot narrow it
|
|
96
|
+
// to the applications that can open one path.
|
|
97
|
+
const { path } = readParams(params, "applications.list");
|
|
98
|
+
if (path !== undefined && typeof path !== "string") {
|
|
99
|
+
throw new Error('applications.list: "path" must be a string');
|
|
100
|
+
}
|
|
101
|
+
return applications.map(applicationWire);
|
|
102
|
+
},
|
|
103
|
+
"applications.default": (params) => {
|
|
104
|
+
const { path } = readParams(params, "applications.default");
|
|
105
|
+
if (typeof path !== "string" || path === "") {
|
|
106
|
+
throw new Error('applications.default: "path" must be a non-empty string');
|
|
107
|
+
}
|
|
108
|
+
// No application database exists, so no path has a default application;
|
|
109
|
+
// Raycast rejects in exactly that case.
|
|
110
|
+
throw new Error(`No default application for ${path}`);
|
|
111
|
+
},
|
|
112
|
+
// Window management is client state. Without a client there is one empty
|
|
113
|
+
// desktop, no windows and no active window, so the read-only calls answer
|
|
114
|
+
// neutrally and `window.setBounds` stays denied.
|
|
115
|
+
"window.desktops": (params) => {
|
|
116
|
+
readParams(params, "window.desktops");
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
id: "headless-desktop",
|
|
120
|
+
screenId: "headless-screen",
|
|
121
|
+
active: true,
|
|
122
|
+
type: "User",
|
|
123
|
+
size: { width: 0, height: 0 },
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
},
|
|
127
|
+
"window.list": (params) => {
|
|
128
|
+
readParams(params, "window.list");
|
|
129
|
+
return [];
|
|
130
|
+
},
|
|
131
|
+
"window.active": (params) => {
|
|
132
|
+
readParams(params, "window.active");
|
|
133
|
+
throw new Error("No active window");
|
|
134
|
+
},
|
|
135
|
+
"clipboard.read": (params) => {
|
|
136
|
+
const { offset } = readParams(params, "clipboard.read");
|
|
137
|
+
if (offset !== undefined) {
|
|
138
|
+
if (typeof offset !== "number" ||
|
|
139
|
+
!Number.isInteger(offset) ||
|
|
140
|
+
offset < 0 ||
|
|
141
|
+
offset > MAX_OFFSET) {
|
|
142
|
+
throw new Error(`clipboard.read: "offset" must be an integer between 0 and ${MAX_OFFSET}`);
|
|
143
|
+
}
|
|
144
|
+
// There is no history to read from, so only the current entry has content.
|
|
145
|
+
return { text: offset === 0 ? clipboard : "" };
|
|
146
|
+
}
|
|
147
|
+
return { text: clipboard };
|
|
148
|
+
},
|
|
149
|
+
"command.metadata": (params) => {
|
|
150
|
+
const { subtitle } = readParams(params, "command.metadata");
|
|
151
|
+
if (subtitle !== undefined && subtitle !== null && typeof subtitle !== "string") {
|
|
152
|
+
throw new Error('command.metadata: "subtitle" must be a string or null');
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"preferences.open": (params) => {
|
|
156
|
+
const { scope } = readParams(params, "preferences.open");
|
|
157
|
+
if (scope !== "extension" && scope !== "command") {
|
|
158
|
+
throw new Error('preferences.open: "scope" must be "extension" or "command"');
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blastlauncher/cli",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
"type": "module",
|
|
4
|
+
"exports": {
|
|
5
|
+
".": {
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"import": "./dist/index.js",
|
|
8
|
+
"default": "./dist/index.js"
|
|
9
|
+
}
|
|
8
10
|
},
|
|
9
11
|
"dependencies": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"fast-glob": "^3.2.12",
|
|
14
|
-
"fs-extra": "^11.1.0",
|
|
15
|
-
"semver": "^7.3.8",
|
|
16
|
-
"tempy": "^3.0.0"
|
|
17
|
-
},
|
|
18
|
-
"repository": "https://github.com/BlastLauncher/blast.git",
|
|
19
|
-
"devDependencies": {
|
|
20
|
-
"@types/fs-extra": "^9.0.13",
|
|
21
|
-
"pkg": "^5.8.0"
|
|
12
|
+
"@blastlauncher/engine": "3.0.0-alpha.0",
|
|
13
|
+
"@blastlauncher/protocol": "3.0.0-alpha.0",
|
|
14
|
+
"@blastlauncher/shared-deps": "3.0.0-alpha.0"
|
|
22
15
|
},
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
"version": "3.0.0-alpha.0",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=24.20.0"
|
|
26
20
|
},
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"schema",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"bin": {
|
|
28
|
+
"blast": "./dist/cli.js"
|
|
30
29
|
}
|
|
31
|
-
}
|
|
30
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# @blastlauncher/cli
|
|
2
|
-
|
|
3
|
-
## 1.0.4
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Replace raycast extension publish command
|
|
8
|
-
|
|
9
|
-
## 1.0.3
|
|
10
|
-
|
|
11
|
-
### Patch Changes
|
|
12
|
-
|
|
13
|
-
- Fix package should run build before publish
|
|
14
|
-
|
|
15
|
-
## 1.0.2
|
|
16
|
-
|
|
17
|
-
### Patch Changes
|
|
18
|
-
|
|
19
|
-
- Run build in publish command
|
|
20
|
-
|
|
21
|
-
## 1.0.1
|
|
22
|
-
|
|
23
|
-
### Patch Changes
|
|
24
|
-
|
|
25
|
-
- Initial npm publish
|
package/build.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
-
const esbuild = require("esbuild");
|
|
3
|
-
const { nodeExternalsPlugin } = require("esbuild-node-externals");
|
|
4
|
-
|
|
5
|
-
esbuild.build({
|
|
6
|
-
entryPoints: ["index.js"],
|
|
7
|
-
bundle: true,
|
|
8
|
-
platform: "node",
|
|
9
|
-
outfile: "dist/index.cjs",
|
|
10
|
-
plugins: [
|
|
11
|
-
nodeExternalsPlugin({
|
|
12
|
-
allowList: ["tempy"],
|
|
13
|
-
}),
|
|
14
|
-
],
|
|
15
|
-
});
|
package/dist/cli-linux
DELETED
package/dist/cli-macos
DELETED
package/dist/cli-win.exe
DELETED
|
Binary file
|
package/dist/index.cjs
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
|
|
29
|
-
// ../../node_modules/.pnpm/temp-dir@2.0.0/node_modules/temp-dir/index.js
|
|
30
|
-
var require_temp_dir = __commonJS({
|
|
31
|
-
"../../node_modules/.pnpm/temp-dir@2.0.0/node_modules/temp-dir/index.js"(exports, module2) {
|
|
32
|
-
"use strict";
|
|
33
|
-
var fs3 = require("fs");
|
|
34
|
-
var os = require("os");
|
|
35
|
-
var tempDirectorySymbol = Symbol.for("__RESOLVED_TEMP_DIRECTORY__");
|
|
36
|
-
if (!global[tempDirectorySymbol]) {
|
|
37
|
-
Object.defineProperty(global, tempDirectorySymbol, {
|
|
38
|
-
value: fs3.realpathSync(os.tmpdir())
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
module2.exports = global[tempDirectorySymbol];
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// index.js
|
|
46
|
-
var import_child_process = require("child_process");
|
|
47
|
-
var import_https = __toESM(require("https"));
|
|
48
|
-
var import_path = __toESM(require("path"));
|
|
49
|
-
var import_commander = require("commander");
|
|
50
|
-
var import_esbuild = __toESM(require("esbuild"));
|
|
51
|
-
var import_fs_extra = __toESM(require("fs-extra"));
|
|
52
|
-
var import_semver = __toESM(require("semver"));
|
|
53
|
-
|
|
54
|
-
// ../../node_modules/.pnpm/tempy@3.0.0/node_modules/tempy/index.js
|
|
55
|
-
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
56
|
-
var import_node_path = __toESM(require("node:path"), 1);
|
|
57
|
-
var import_node_stream = __toESM(require("node:stream"), 1);
|
|
58
|
-
var import_node_util = require("node:util");
|
|
59
|
-
|
|
60
|
-
// ../../node_modules/.pnpm/crypto-random-string@4.0.0/node_modules/crypto-random-string/index.js
|
|
61
|
-
var import_util = require("util");
|
|
62
|
-
var import_crypto = __toESM(require("crypto"), 1);
|
|
63
|
-
var randomBytesAsync = (0, import_util.promisify)(import_crypto.default.randomBytes);
|
|
64
|
-
var urlSafeCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~".split("");
|
|
65
|
-
var numericCharacters = "0123456789".split("");
|
|
66
|
-
var distinguishableCharacters = "CDEHKMPRTUWXY012458".split("");
|
|
67
|
-
var asciiPrintableCharacters = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".split("");
|
|
68
|
-
var alphanumericCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
|
|
69
|
-
var generateForCustomCharacters = (length, characters) => {
|
|
70
|
-
const characterCount = characters.length;
|
|
71
|
-
const maxValidSelector = Math.floor(65536 / characterCount) * characterCount - 1;
|
|
72
|
-
const entropyLength = 2 * Math.ceil(1.1 * length);
|
|
73
|
-
let string = "";
|
|
74
|
-
let stringLength = 0;
|
|
75
|
-
while (stringLength < length) {
|
|
76
|
-
const entropy = import_crypto.default.randomBytes(entropyLength);
|
|
77
|
-
let entropyPosition = 0;
|
|
78
|
-
while (entropyPosition < entropyLength && stringLength < length) {
|
|
79
|
-
const entropyValue = entropy.readUInt16LE(entropyPosition);
|
|
80
|
-
entropyPosition += 2;
|
|
81
|
-
if (entropyValue > maxValidSelector) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
string += characters[entropyValue % characterCount];
|
|
85
|
-
stringLength++;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return string;
|
|
89
|
-
};
|
|
90
|
-
var generateForCustomCharactersAsync = async (length, characters) => {
|
|
91
|
-
const characterCount = characters.length;
|
|
92
|
-
const maxValidSelector = Math.floor(65536 / characterCount) * characterCount - 1;
|
|
93
|
-
const entropyLength = 2 * Math.ceil(1.1 * length);
|
|
94
|
-
let string = "";
|
|
95
|
-
let stringLength = 0;
|
|
96
|
-
while (stringLength < length) {
|
|
97
|
-
const entropy = await randomBytesAsync(entropyLength);
|
|
98
|
-
let entropyPosition = 0;
|
|
99
|
-
while (entropyPosition < entropyLength && stringLength < length) {
|
|
100
|
-
const entropyValue = entropy.readUInt16LE(entropyPosition);
|
|
101
|
-
entropyPosition += 2;
|
|
102
|
-
if (entropyValue > maxValidSelector) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
string += characters[entropyValue % characterCount];
|
|
106
|
-
stringLength++;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return string;
|
|
110
|
-
};
|
|
111
|
-
var generateRandomBytes = (byteLength, type, length) => import_crypto.default.randomBytes(byteLength).toString(type).slice(0, length);
|
|
112
|
-
var generateRandomBytesAsync = async (byteLength, type, length) => {
|
|
113
|
-
const buffer = await randomBytesAsync(byteLength);
|
|
114
|
-
return buffer.toString(type).slice(0, length);
|
|
115
|
-
};
|
|
116
|
-
var allowedTypes = /* @__PURE__ */ new Set([
|
|
117
|
-
void 0,
|
|
118
|
-
"hex",
|
|
119
|
-
"base64",
|
|
120
|
-
"url-safe",
|
|
121
|
-
"numeric",
|
|
122
|
-
"distinguishable",
|
|
123
|
-
"ascii-printable",
|
|
124
|
-
"alphanumeric"
|
|
125
|
-
]);
|
|
126
|
-
var createGenerator = (generateForCustomCharacters2, generateRandomBytes2) => ({ length, type, characters }) => {
|
|
127
|
-
if (!(length >= 0 && Number.isFinite(length))) {
|
|
128
|
-
throw new TypeError("Expected a `length` to be a non-negative finite number");
|
|
129
|
-
}
|
|
130
|
-
if (type !== void 0 && characters !== void 0) {
|
|
131
|
-
throw new TypeError("Expected either `type` or `characters`");
|
|
132
|
-
}
|
|
133
|
-
if (characters !== void 0 && typeof characters !== "string") {
|
|
134
|
-
throw new TypeError("Expected `characters` to be string");
|
|
135
|
-
}
|
|
136
|
-
if (!allowedTypes.has(type)) {
|
|
137
|
-
throw new TypeError(`Unknown type: ${type}`);
|
|
138
|
-
}
|
|
139
|
-
if (type === void 0 && characters === void 0) {
|
|
140
|
-
type = "hex";
|
|
141
|
-
}
|
|
142
|
-
if (type === "hex" || type === void 0 && characters === void 0) {
|
|
143
|
-
return generateRandomBytes2(Math.ceil(length * 0.5), "hex", length);
|
|
144
|
-
}
|
|
145
|
-
if (type === "base64") {
|
|
146
|
-
return generateRandomBytes2(Math.ceil(length * 0.75), "base64", length);
|
|
147
|
-
}
|
|
148
|
-
if (type === "url-safe") {
|
|
149
|
-
return generateForCustomCharacters2(length, urlSafeCharacters);
|
|
150
|
-
}
|
|
151
|
-
if (type === "numeric") {
|
|
152
|
-
return generateForCustomCharacters2(length, numericCharacters);
|
|
153
|
-
}
|
|
154
|
-
if (type === "distinguishable") {
|
|
155
|
-
return generateForCustomCharacters2(length, distinguishableCharacters);
|
|
156
|
-
}
|
|
157
|
-
if (type === "ascii-printable") {
|
|
158
|
-
return generateForCustomCharacters2(length, asciiPrintableCharacters);
|
|
159
|
-
}
|
|
160
|
-
if (type === "alphanumeric") {
|
|
161
|
-
return generateForCustomCharacters2(length, alphanumericCharacters);
|
|
162
|
-
}
|
|
163
|
-
if (characters.length === 0) {
|
|
164
|
-
throw new TypeError("Expected `characters` string length to be greater than or equal to 1");
|
|
165
|
-
}
|
|
166
|
-
if (characters.length > 65536) {
|
|
167
|
-
throw new TypeError("Expected `characters` string length to be less or equal to 65536");
|
|
168
|
-
}
|
|
169
|
-
return generateForCustomCharacters2(length, characters.split(""));
|
|
170
|
-
};
|
|
171
|
-
var cryptoRandomString = createGenerator(generateForCustomCharacters, generateRandomBytes);
|
|
172
|
-
cryptoRandomString.async = createGenerator(generateForCustomCharactersAsync, generateRandomBytesAsync);
|
|
173
|
-
var crypto_random_string_default = cryptoRandomString;
|
|
174
|
-
|
|
175
|
-
// ../../node_modules/.pnpm/unique-string@3.0.0/node_modules/unique-string/index.js
|
|
176
|
-
function uniqueString() {
|
|
177
|
-
return crypto_random_string_default({ length: 32 });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// ../../node_modules/.pnpm/tempy@3.0.0/node_modules/tempy/index.js
|
|
181
|
-
var import_temp_dir = __toESM(require_temp_dir(), 1);
|
|
182
|
-
var import_temp_dir2 = __toESM(require_temp_dir(), 1);
|
|
183
|
-
var pipeline = (0, import_node_util.promisify)(import_node_stream.default.pipeline);
|
|
184
|
-
var getPath = (prefix = "") => import_node_path.default.join(import_temp_dir.default, prefix + uniqueString());
|
|
185
|
-
function temporaryFile({ name, extension } = {}) {
|
|
186
|
-
if (name) {
|
|
187
|
-
if (extension !== void 0 && extension !== null) {
|
|
188
|
-
throw new Error("The `name` and `extension` options are mutually exclusive");
|
|
189
|
-
}
|
|
190
|
-
return import_node_path.default.join(temporaryDirectory(), name);
|
|
191
|
-
}
|
|
192
|
-
return getPath() + (extension === void 0 || extension === null ? "" : "." + extension.replace(/^\./, ""));
|
|
193
|
-
}
|
|
194
|
-
function temporaryDirectory({ prefix = "" } = {}) {
|
|
195
|
-
const directory = getPath(prefix);
|
|
196
|
-
import_node_fs.default.mkdirSync(directory);
|
|
197
|
-
return directory;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// index.js
|
|
201
|
-
function getJSON(url) {
|
|
202
|
-
return new Promise((resolve, reject) => {
|
|
203
|
-
import_https.default.get(url, (res) => {
|
|
204
|
-
let body = "";
|
|
205
|
-
res.on("data", (chunk) => {
|
|
206
|
-
body += chunk;
|
|
207
|
-
});
|
|
208
|
-
res.on("end", () => {
|
|
209
|
-
try {
|
|
210
|
-
resolve(JSON.parse(body));
|
|
211
|
-
} catch (e) {
|
|
212
|
-
reject(e);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
}).on("error", (e) => {
|
|
216
|
-
reject(e);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
var PackageVersionHelper = class {
|
|
221
|
-
constructor(npmOrg, registryUrl = "https://registry.npmjs.org") {
|
|
222
|
-
this.npmOrg = npmOrg;
|
|
223
|
-
this.registryUrl = registryUrl;
|
|
224
|
-
}
|
|
225
|
-
async fetchPackageVersions(packageName) {
|
|
226
|
-
let res;
|
|
227
|
-
try {
|
|
228
|
-
console.log(`fetching ${this.registryUrl}/@${this.npmOrg}/${packageName}`);
|
|
229
|
-
res = await getJSON(`${this.registryUrl}/@${this.npmOrg}/${packageName}`);
|
|
230
|
-
if (res.error) {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
return Object.keys(res.versions);
|
|
234
|
-
} catch (e) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
async getLatestVersion(versions) {
|
|
239
|
-
const sortedVersions = versions.sort((a, b) => import_semver.default.compare(a, b));
|
|
240
|
-
return sortedVersions[sortedVersions.length - 1];
|
|
241
|
-
}
|
|
242
|
-
async getPackageVersion(packageName) {
|
|
243
|
-
const versions = await this.fetchPackageVersions(packageName);
|
|
244
|
-
if (!versions) {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
return this.getLatestVersion(versions);
|
|
248
|
-
}
|
|
249
|
-
getPackageName(extensionFolder) {
|
|
250
|
-
const packageJson = JSON.parse(import_fs_extra.default.readFileSync(import_path.default.resolve(extensionFolder, "package.json"), "utf8"));
|
|
251
|
-
let packageName = packageJson.name;
|
|
252
|
-
if (packageName.startsWith("@")) {
|
|
253
|
-
packageName = packageName.split("/")[1];
|
|
254
|
-
}
|
|
255
|
-
packageName = packageName.toLowerCase();
|
|
256
|
-
console.debug(`package name is ${packageName}`);
|
|
257
|
-
return packageName;
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
async function publishExtensions(extensionFolder, npmOrganization, npmRegistry, distDir) {
|
|
261
|
-
const versionHelper = new PackageVersionHelper(npmOrganization, npmRegistry);
|
|
262
|
-
console.log(`
|
|
263
|
-
Entering ${extensionFolder}
|
|
264
|
-
`);
|
|
265
|
-
(0, import_child_process.execSync)(`cd "${extensionFolder}"`);
|
|
266
|
-
if (!distDir) {
|
|
267
|
-
distDir = import_path.default.resolve(extensionFolder, "dist");
|
|
268
|
-
}
|
|
269
|
-
if (!import_fs_extra.default.existsSync(distDir)) {
|
|
270
|
-
import_fs_extra.default.mkdirSync(distDir);
|
|
271
|
-
}
|
|
272
|
-
await buildExtension(extensionFolder, distDir);
|
|
273
|
-
const packageName = versionHelper.getPackageName(extensionFolder);
|
|
274
|
-
let version = await versionHelper.getPackageVersion(packageName);
|
|
275
|
-
if (version) {
|
|
276
|
-
console.info(`current version is ${version}`);
|
|
277
|
-
version = import_semver.default.inc(version, "major");
|
|
278
|
-
} else {
|
|
279
|
-
console.info(`no version found, using 1.0.0`);
|
|
280
|
-
version = "1.0.0";
|
|
281
|
-
}
|
|
282
|
-
updatePackageInfo(distDir, version, npmOrganization);
|
|
283
|
-
console.info(`publishing version ${version}`);
|
|
284
|
-
(0, import_child_process.execSync)(`npm publish --access public`, {
|
|
285
|
-
cwd: distDir
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
function getTSConfigPath(extensionDir) {
|
|
289
|
-
const originalTSconfigPath = import_path.default.resolve(extensionDir, "tsconfig.json");
|
|
290
|
-
const tsconfigJson = JSON.parse(import_fs_extra.default.readFileSync(originalTSconfigPath, "utf8"));
|
|
291
|
-
tsconfigJson.compilerOptions.jsx = "react";
|
|
292
|
-
tsconfigJson.compilerOptions.strict = false;
|
|
293
|
-
const tempTSconfigPath = temporaryFile({ extension: "json" });
|
|
294
|
-
import_fs_extra.default.writeFileSync(tempTSconfigPath, JSON.stringify(tsconfigJson, null, 2));
|
|
295
|
-
return tempTSconfigPath;
|
|
296
|
-
}
|
|
297
|
-
async function buildExtension(extensionDir, outputFolder) {
|
|
298
|
-
const packageJson = JSON.parse(import_fs_extra.default.readFileSync(import_path.default.resolve(extensionDir, "package.json"), "utf8"));
|
|
299
|
-
if (!outputFolder) {
|
|
300
|
-
outputFolder = import_path.default.resolve(extensionDir, "dist");
|
|
301
|
-
}
|
|
302
|
-
const commandNames = packageJson.commands.map((command) => command.name);
|
|
303
|
-
const supportedExts = [".js", ".ts", ".tsx", ".jsx"];
|
|
304
|
-
const tsconfigPath = getTSConfigPath(extensionDir);
|
|
305
|
-
const esbuildConfigs = commandNames.map((commandName) => {
|
|
306
|
-
const files = import_fs_extra.default.readdirSync(import_path.default.join(extensionDir, "src"));
|
|
307
|
-
const entry = files.find((file) => {
|
|
308
|
-
return supportedExts.some((ext) => file === `${commandName}${ext}`);
|
|
309
|
-
});
|
|
310
|
-
if (!entry) {
|
|
311
|
-
throw new Error(`No entry point found for ${commandName}`);
|
|
312
|
-
}
|
|
313
|
-
const entryPath = import_path.default.join(extensionDir, "src", entry);
|
|
314
|
-
const outputPath = import_path.default.join(outputFolder, `${commandName}.js`);
|
|
315
|
-
console.debug(`entry point for ${commandName} is ${entryPath}`);
|
|
316
|
-
return {
|
|
317
|
-
input: entryPath,
|
|
318
|
-
output: outputPath
|
|
319
|
-
};
|
|
320
|
-
});
|
|
321
|
-
console.info(`entry points [${esbuildConfigs.map((config) => config.input).join(" ")}]`);
|
|
322
|
-
for (const config of esbuildConfigs) {
|
|
323
|
-
await import_esbuild.default.build({
|
|
324
|
-
entryPoints: [config.input],
|
|
325
|
-
bundle: true,
|
|
326
|
-
platform: "node",
|
|
327
|
-
outfile: config.output,
|
|
328
|
-
external: ["@raycast/api", "react"],
|
|
329
|
-
jsx: "transform",
|
|
330
|
-
jsxFactory: "_jsx",
|
|
331
|
-
jsxFragment: "_jsxFragment",
|
|
332
|
-
tsconfig: tsconfigPath,
|
|
333
|
-
minify: true
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
import_fs_extra.default.copyFileSync(import_path.default.join(extensionDir, "package.json"), import_path.default.join(outputFolder, "package.json"));
|
|
337
|
-
const assetsDir = import_path.default.join(extensionDir, "assets");
|
|
338
|
-
if (import_fs_extra.default.existsSync(assetsDir)) {
|
|
339
|
-
import_fs_extra.default.copySync(assetsDir, import_path.default.join(outputFolder, "assets"));
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
function updatePackageInfo(targetDir, version, npmOrganization) {
|
|
343
|
-
const packagePath = import_path.default.join(targetDir, "package.json");
|
|
344
|
-
const packageJson = JSON.parse(import_fs_extra.default.readFileSync(packagePath, "utf8"));
|
|
345
|
-
if (!packageJson.name.startsWith(`@${npmOrganization}`)) {
|
|
346
|
-
packageJson.name = `@${npmOrganization}/${packageJson.name}`;
|
|
347
|
-
}
|
|
348
|
-
packageJson.repository = "https://github.com/BlastLauncher/extensions.git";
|
|
349
|
-
packageJson.version = version;
|
|
350
|
-
packageJson.scripts = Object.keys(packageJson.scripts).filter((key) => key !== "publish").reduce((obj, key) => {
|
|
351
|
-
obj[key] = packageJson.scripts[key];
|
|
352
|
-
return obj;
|
|
353
|
-
}, {});
|
|
354
|
-
import_fs_extra.default.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2), "utf8");
|
|
355
|
-
}
|
|
356
|
-
var program = new import_commander.Command();
|
|
357
|
-
program.name("blast-cli").description(`CLI for Blast Launcher
|
|
358
|
-
|
|
359
|
-
Example Usage:
|
|
360
|
-
blast build ./extensions/todo-list -o ./extensions/todo-list/dist
|
|
361
|
-
blast publish ./extensions/todo-list blast-extensions`);
|
|
362
|
-
program.command("publish").description("Publish extensions").argument("<path>", "Path to extensions folder").argument("<organization>", "NPM organization to publish to, e.g. blast-extensions").option("-r, --registry <registry>", "NPM registry to publish to", "https://registry.npmjs.org").option("-o, --output <output>", "Output directory, default to ./dist folder relative to extension path").action((dir, organization, options) => {
|
|
363
|
-
return publishExtensions(dir, organization, options.registry, options.output);
|
|
364
|
-
});
|
|
365
|
-
program.command("build").description("Build extensions").argument("<path>", "Path to extension").option("-o, --output <output>", "Output directory, default to ./dist folder relative to extension path").action((path3, options) => {
|
|
366
|
-
return buildExtension(path3, options.output);
|
|
367
|
-
});
|
|
368
|
-
program.parse();
|