capacitor-ota 1.0.0 → 1.0.2

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.
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env npx tsx
2
+ import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
3
+ import { basename, join, resolve } from "path";
4
+ import { homedir, tmpdir } from "os";
5
+ import { execSync } from "child_process";
6
+
7
+ //#region cli/ota.ts
8
+ /**
9
+ * OTA Service CLI
10
+ *
11
+ * Usage:
12
+ * npx tsx cli/ota.ts <command> [options]
13
+ *
14
+ * Commands:
15
+ * login Login and save token
16
+ * apps List all apps
17
+ * versions <app_id> List versions for an app
18
+ * upload <app_id> <path> Upload a new version (directory or zip)
19
+ * delete <version_id> Delete a version
20
+ * rollback <version_id> Rollback to a version
21
+ */
22
+ const CONFIG_PATH = resolve(homedir(), ".ota-cli.json");
23
+ const DEFAULT_URL = "https://ota-service.srvrun.workers.dev";
24
+ function loadConfig() {
25
+ if (existsSync(CONFIG_PATH)) return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
26
+ return { url: DEFAULT_URL };
27
+ }
28
+ function saveConfig(config) {
29
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
30
+ }
31
+ async function api(method, path, body, formData) {
32
+ const config = loadConfig();
33
+ const headers = {};
34
+ if (config.token) headers["Authorization"] = `Bearer ${config.token}`;
35
+ if (body && !formData) headers["Content-Type"] = "application/json";
36
+ const response = await fetch(`${config.url}${path}`, {
37
+ method,
38
+ headers,
39
+ body: formData || (body ? JSON.stringify(body) : void 0)
40
+ });
41
+ if (!response.ok) {
42
+ const error = await response.json().catch(() => ({ message: response.statusText }));
43
+ throw new Error(error.message || `HTTP ${response.status}`);
44
+ }
45
+ return response.json();
46
+ }
47
+ async function login(username, password) {
48
+ const config = loadConfig();
49
+ config.token = (await api("POST", "/api/auth/login", {
50
+ username,
51
+ password
52
+ })).token;
53
+ saveConfig(config);
54
+ console.log("✅ Login successful! Token saved.");
55
+ }
56
+ async function listApps() {
57
+ const apps = await api("GET", "/api/apps");
58
+ if (apps.length === 0) {
59
+ console.log("No apps found.");
60
+ return;
61
+ }
62
+ console.log("\n📱 Apps:\n");
63
+ console.log("ID Name Created");
64
+ console.log("─".repeat(50));
65
+ for (const app of apps) {
66
+ const date = new Date(app.created_at).toLocaleDateString();
67
+ console.log(`${app.id.padEnd(14)} ${app.name.padEnd(14)} ${date}`);
68
+ }
69
+ console.log();
70
+ }
71
+ async function listVersions(appId) {
72
+ const versions = await api("GET", `/api/versions?app_id=${appId}`);
73
+ if (versions.length === 0) {
74
+ console.log("No versions found.");
75
+ return;
76
+ }
77
+ console.log("\n📦 Versions:\n");
78
+ console.log("ID Version Channel Active Downloads Created");
79
+ console.log("─".repeat(65));
80
+ for (const v of versions) {
81
+ const date = new Date(v.created_at).toLocaleDateString();
82
+ const active = v.is_active ? "✅" : " ";
83
+ console.log(`${String(v.id).padEnd(6)}${v.version.padEnd(11)}${v.channel.padEnd(13)}${active.padEnd(8)}${String(v.downloads).padEnd(11)}${date}`);
84
+ }
85
+ console.log();
86
+ }
87
+ function zipDirectory(dirPath) {
88
+ const zipPath = join(mkdtempSync(join(tmpdir(), "ota-")), "bundle.zip");
89
+ execSync(`cd "${dirPath}" && zip -r "${zipPath}" .`, { stdio: "pipe" });
90
+ return zipPath;
91
+ }
92
+ async function upload(appId, inputPath, options) {
93
+ const absolutePath = resolve(inputPath);
94
+ if (!existsSync(absolutePath)) throw new Error(`Path not found: ${absolutePath}`);
95
+ const stat = statSync(absolutePath);
96
+ let zipPath;
97
+ let tempDir = null;
98
+ if (stat.isDirectory()) {
99
+ console.log(`\n📦 Zipping directory: ${basename(absolutePath)}...`);
100
+ zipPath = zipDirectory(absolutePath);
101
+ tempDir = resolve(zipPath, "..");
102
+ } else if (absolutePath.endsWith(".zip")) zipPath = absolutePath;
103
+ else throw new Error("Input must be a directory or a .zip file");
104
+ const fileBuffer = readFileSync(zipPath);
105
+ const blob = new Blob([fileBuffer], { type: "application/zip" });
106
+ const formData = new FormData();
107
+ formData.append("app_id", appId);
108
+ formData.append("version", options.version || "1.0.0");
109
+ formData.append("channel", options.channel || "production");
110
+ formData.append("bundle", blob, "bundle.zip");
111
+ if (options.capacitorAppId) formData.append("capacitor_app_id", options.capacitorAppId);
112
+ if (options.minNativeVersion) formData.append("min_native_version", options.minNativeVersion);
113
+ if (options.releaseNotes) formData.append("release_notes", options.releaseNotes);
114
+ console.log(`📤 Uploading...`);
115
+ const result = await api("POST", "/api/versions/upload", void 0, formData);
116
+ if (tempDir) rmSync(tempDir, {
117
+ recursive: true,
118
+ force: true
119
+ });
120
+ console.log(`\n✅ Upload successful!`);
121
+ console.log(` Version: ${result.version}`);
122
+ console.log(` Checksum: ${result.checksum}\n`);
123
+ }
124
+ async function deleteVersion(versionId) {
125
+ await api("DELETE", `/api/versions/${versionId}`);
126
+ console.log(`✅ Version ${versionId} deleted.`);
127
+ }
128
+ async function rollback(versionId) {
129
+ await api("POST", `/api/versions/${versionId}/rollback`);
130
+ console.log(`✅ Rolled back to version ${versionId}.`);
131
+ }
132
+ async function setUrl(url) {
133
+ const config = loadConfig();
134
+ config.url = url;
135
+ saveConfig(config);
136
+ console.log(`✅ URL set to: ${url}`);
137
+ }
138
+ async function showConfig() {
139
+ const config = loadConfig();
140
+ console.log("\n⚙️ Config:\n");
141
+ console.log(` URL: ${config.url}`);
142
+ console.log(` Token: ${config.token ? "********" : "(not set)"}\n`);
143
+ }
144
+ function printHelp() {
145
+ console.log(`
146
+ OTA Service CLI
147
+
148
+ Usage:
149
+ ota <command> [options]
150
+
151
+ Commands:
152
+ login <username> <password> Login and save token
153
+ apps List all apps
154
+ versions <app_id> List versions for an app
155
+ upload <app_id> <path> Upload a new version (directory or .zip)
156
+ --version, -v <version> Version number (default: 1.0.0)
157
+ --channel, -c <channel> Channel (default: production)
158
+ --capacitor-app-id <id> Capacitor app ID for static JSON
159
+ --min-native <version> Minimum native version
160
+ --notes <text> Release notes
161
+ delete <version_id> Delete a version
162
+ rollback <version_id> Rollback to a version
163
+ config Show current config
164
+ set-url <url> Set API URL
165
+
166
+ Examples:
167
+ ota login admin password123
168
+ ota apps
169
+ ota versions 9fa3a103d8d0
170
+ ota upload 9fa3a103d8d0 ./dist -v 1.0.1 --capacitor-app-id com.example.app
171
+ ota upload 9fa3a103d8d0 ./bundle.zip -v 1.0.2
172
+ ota delete 5
173
+ ota rollback 3
174
+ `);
175
+ }
176
+ function parseArgs(args) {
177
+ const command = args[0] || "help";
178
+ const positional = [];
179
+ const options = {};
180
+ for (let i = 1; i < args.length; i++) {
181
+ const arg = args[i];
182
+ if (arg.startsWith("--")) {
183
+ const key = arg.slice(2);
184
+ options[key] = args[++i] || "";
185
+ } else if (arg.startsWith("-")) {
186
+ const key = arg.slice(1);
187
+ options[key] = args[++i] || "";
188
+ } else positional.push(arg);
189
+ }
190
+ return {
191
+ command,
192
+ positional,
193
+ options
194
+ };
195
+ }
196
+ async function main() {
197
+ const { command, positional, options } = parseArgs(process.argv.slice(2));
198
+ try {
199
+ switch (command) {
200
+ case "login":
201
+ if (positional.length < 2) throw new Error("Usage: ota login <username> <password>");
202
+ await login(positional[0], positional[1]);
203
+ break;
204
+ case "apps":
205
+ await listApps();
206
+ break;
207
+ case "versions":
208
+ if (positional.length < 1) throw new Error("Usage: ota versions <app_id>");
209
+ await listVersions(positional[0]);
210
+ break;
211
+ case "upload":
212
+ if (positional.length < 2) throw new Error("Usage: ota upload <app_id> <path> [options]");
213
+ await upload(positional[0], positional[1], {
214
+ version: options["version"] || options["v"],
215
+ channel: options["channel"] || options["c"],
216
+ capacitorAppId: options["capacitor-app-id"],
217
+ minNativeVersion: options["min-native"],
218
+ releaseNotes: options["notes"]
219
+ });
220
+ break;
221
+ case "delete":
222
+ if (positional.length < 1) throw new Error("Usage: ota delete <version_id>");
223
+ await deleteVersion(positional[0]);
224
+ break;
225
+ case "rollback":
226
+ if (positional.length < 1) throw new Error("Usage: ota rollback <version_id>");
227
+ await rollback(positional[0]);
228
+ break;
229
+ case "config":
230
+ await showConfig();
231
+ break;
232
+ case "set-url":
233
+ if (positional.length < 1) throw new Error("Usage: ota set-url <url>");
234
+ await setUrl(positional[0]);
235
+ break;
236
+ case "help":
237
+ case "--help":
238
+ case "-h":
239
+ printHelp();
240
+ break;
241
+ default:
242
+ console.error(`Unknown command: ${command}`);
243
+ printHelp();
244
+ process.exit(1);
245
+ }
246
+ } catch (error) {
247
+ console.error(`\n❌ Error: ${error instanceof Error ? error.message : error}\n`);
248
+ process.exit(1);
249
+ }
250
+ }
251
+ main();
252
+
253
+ //#endregion
254
+ export { };
@@ -55,7 +55,8 @@ var OtaUpdater = class {
55
55
  if (!isNativePlatform()) return;
56
56
  try {
57
57
  const { CapacitorUpdater } = await import("@capgo/capacitor-updater");
58
- await CapacitorUpdater.notifyAppReady();
58
+ const result = await CapacitorUpdater.notifyAppReady();
59
+ if (result?.bundle?.version) this.state.currentVersion = result.bundle.version;
59
60
  } catch {}
60
61
  }
61
62
  async check() {
package/package.json CHANGED
@@ -1,21 +1,25 @@
1
1
  {
2
2
  "name": "capacitor-ota",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
- "main": "dist/index.mjs",
6
- "types": "dist/index.d.mts",
5
+ "main": "dist/src/index.mjs",
6
+ "types": "dist/src/index.d.mts",
7
7
  "exports": {
8
8
  ".": {
9
- "import": "./dist/index.mjs",
10
- "types": "./dist/index.d.mts"
9
+ "import": "./dist/src/index.mjs",
10
+ "types": "./dist/src/index.d.mts"
11
11
  }
12
12
  },
13
+ "bin": {
14
+ "capacitor-ota": "./dist/cli/ota.mjs",
15
+ "ota": "./dist/cli/ota.mjs"
16
+ },
13
17
  "files": [
14
18
  "dist"
15
19
  ],
16
20
  "scripts": {
17
- "build": "tsdown src/index.ts --format esm --dts",
18
- "dev": "tsdown src/index.ts --format esm --dts --watch",
21
+ "build": "tsdown",
22
+ "dev": "tsdown --watch",
19
23
  "panel:dev": "vite dev --host",
20
24
  "panel:build": "vite build",
21
25
  "panel:preview": "vite preview",
File without changes