anyterm 0.1.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.
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config.ts
4
+ import * as os from "os";
5
+ import Conf from "conf";
6
+ import machineId from "node-machine-id";
7
+
8
+ // src/secure-store.ts
9
+ import * as fs from "fs";
10
+ var SERVICE = "anyterm";
11
+ function hardenConfigPermissions() {
12
+ try {
13
+ if (process.platform !== "win32") {
14
+ fs.chmodSync(config.path, 384);
15
+ }
16
+ } catch {
17
+ }
18
+ }
19
+ var keytar = null;
20
+ async function getKeytar() {
21
+ if (keytar) return keytar;
22
+ try {
23
+ const mod = await import("keytar");
24
+ keytar = mod.default;
25
+ return keytar;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ async function getSecret(key, serverUrl) {
31
+ const keychainKey = serverUrl ? `${key}:${serverUrl}` : key;
32
+ const kt = await getKeytar();
33
+ if (kt) {
34
+ try {
35
+ const value = await kt.getPassword(SERVICE, keychainKey);
36
+ if (value) return value;
37
+ } catch {
38
+ }
39
+ }
40
+ if (serverUrl) {
41
+ const store = config.store;
42
+ const servers = store["servers"];
43
+ const val2 = servers?.[serverUrl]?.[key];
44
+ return typeof val2 === "string" ? val2 : null;
45
+ }
46
+ const stored = config.store;
47
+ const val = stored[key];
48
+ return typeof val === "string" ? val : null;
49
+ }
50
+ async function setSecret(key, value, serverUrl) {
51
+ const keychainKey = serverUrl ? `${key}:${serverUrl}` : key;
52
+ const kt = await getKeytar();
53
+ if (kt) {
54
+ try {
55
+ await kt.setPassword(SERVICE, keychainKey, value);
56
+ if (serverUrl) {
57
+ const store = config.store;
58
+ const servers = store["servers"];
59
+ if (servers?.[serverUrl]?.[key]) {
60
+ delete servers[serverUrl][key];
61
+ config.set(
62
+ "servers",
63
+ servers
64
+ );
65
+ }
66
+ } else {
67
+ const stored = config.store;
68
+ if (key in stored) {
69
+ delete stored[key];
70
+ config.store = stored;
71
+ }
72
+ }
73
+ return;
74
+ } catch {
75
+ }
76
+ }
77
+ console.warn(
78
+ "\x1B[33mWarning: OS keychain unavailable. Credentials stored in plaintext config file.\x1B[0m"
79
+ );
80
+ if (serverUrl) {
81
+ const store = config.store;
82
+ const servers = store["servers"] ?? {};
83
+ if (!servers[serverUrl]) servers[serverUrl] = {};
84
+ servers[serverUrl][key] = value;
85
+ config.set(
86
+ "servers",
87
+ servers
88
+ );
89
+ } else {
90
+ config.set(
91
+ key,
92
+ value
93
+ );
94
+ }
95
+ hardenConfigPermissions();
96
+ }
97
+ async function deleteSecret(key, serverUrl) {
98
+ const keychainKey = serverUrl ? `${key}:${serverUrl}` : key;
99
+ const kt = await getKeytar();
100
+ if (kt) {
101
+ try {
102
+ await kt.deletePassword(SERVICE, keychainKey);
103
+ } catch {
104
+ }
105
+ }
106
+ if (serverUrl) {
107
+ const store = config.store;
108
+ const servers = store["servers"];
109
+ if (servers?.[serverUrl]?.[key]) {
110
+ delete servers[serverUrl][key];
111
+ config.set(
112
+ "servers",
113
+ servers
114
+ );
115
+ }
116
+ } else {
117
+ const stored = config.store;
118
+ if (key in stored) {
119
+ delete stored[key];
120
+ config.store = stored;
121
+ }
122
+ }
123
+ }
124
+
125
+ // src/config.ts
126
+ var DEFAULT_SERVER_URL = "https://anyterm.dev";
127
+ var CONFIG_VERSION = 2;
128
+ var config = new Conf({
129
+ projectName: "anyterm"
130
+ });
131
+ function normalizeServerUrl(url) {
132
+ const parsed = new URL(url);
133
+ return `${parsed.protocol}//${parsed.host.toLowerCase()}${parsed.pathname.replace(/\/+$/, "")}`;
134
+ }
135
+ function getActiveServer() {
136
+ return config.get("activeServer");
137
+ }
138
+ function setActiveServer(serverUrl) {
139
+ config.set(
140
+ "activeServer",
141
+ serverUrl
142
+ );
143
+ }
144
+ function getServerConfig(serverUrl) {
145
+ const store = config.store;
146
+ return store.servers?.[serverUrl];
147
+ }
148
+ function setServerConfig(serverUrl, data) {
149
+ const store = config.store;
150
+ const servers = store.servers ?? {};
151
+ servers[serverUrl] = { ...servers[serverUrl], ...data };
152
+ config.set("servers", servers);
153
+ }
154
+ function deleteServerConfig(serverUrl) {
155
+ const store = config.store;
156
+ if (store.servers?.[serverUrl]) {
157
+ delete store.servers[serverUrl];
158
+ config.set(
159
+ "servers",
160
+ store.servers
161
+ );
162
+ }
163
+ }
164
+ async function getConfig() {
165
+ const { migrateConfigIfNeeded: migrateConfigIfNeeded2 } = await import("./migrate-F4NHB5VX.js");
166
+ await migrateConfigIfNeeded2();
167
+ const activeServer = config.get("activeServer");
168
+ if (!activeServer) {
169
+ console.error("Not logged in. Run: anyterm login");
170
+ process.exit(1);
171
+ }
172
+ const serverConf = getServerConfig(activeServer);
173
+ if (!serverConf?.userId) {
174
+ console.error("Not logged in. Run: anyterm login");
175
+ process.exit(1);
176
+ }
177
+ const authToken = await getSecret("authToken", activeServer);
178
+ if (!authToken) {
179
+ console.error("Not logged in. Run: anyterm login");
180
+ process.exit(1);
181
+ }
182
+ const wsUrl = serverConf.wsUrl || activeServer.replace(/^http/, "ws");
183
+ const masterKey = await getSecret("masterKey", activeServer);
184
+ return {
185
+ serverUrl: activeServer,
186
+ wsUrl,
187
+ authToken,
188
+ userId: serverConf.userId,
189
+ publicKey: serverConf.publicKey,
190
+ encryptedPrivateKey: serverConf.encryptedPrivateKey,
191
+ keySalt: serverConf.keySalt,
192
+ masterKey
193
+ };
194
+ }
195
+ function getMachineId() {
196
+ return machineId.machineIdSync().slice(0, 8);
197
+ }
198
+ function getMachineName() {
199
+ return config.get("machineName") || os.hostname();
200
+ }
201
+ function setMachineName(name) {
202
+ config.set(
203
+ "machineName",
204
+ name
205
+ );
206
+ }
207
+
208
+ // src/migrate.ts
209
+ var migrated = false;
210
+ async function migrateConfigIfNeeded() {
211
+ if (migrated) return;
212
+ migrated = true;
213
+ const store = config.store;
214
+ const version = store["configVersion"];
215
+ if (version && version >= CONFIG_VERSION) return;
216
+ const legacyServerUrl = store["serverUrl"];
217
+ if (!legacyServerUrl) {
218
+ config.set(
219
+ "configVersion",
220
+ CONFIG_VERSION
221
+ );
222
+ return;
223
+ }
224
+ const serverConfig = {};
225
+ const perServerKeys = [
226
+ "wsUrl",
227
+ "userId",
228
+ "publicKey",
229
+ "encryptedPrivateKey",
230
+ "keySalt"
231
+ ];
232
+ for (const k of perServerKeys) {
233
+ if (k in store) {
234
+ serverConfig[k] = store[k];
235
+ }
236
+ }
237
+ for (const secretKey of ["authToken", "masterKey"]) {
238
+ const value = await getSecret(secretKey);
239
+ if (value) {
240
+ await setSecret(secretKey, value, legacyServerUrl);
241
+ await deleteSecret(secretKey);
242
+ }
243
+ }
244
+ const servers = store["servers"] ?? {};
245
+ servers[legacyServerUrl] = serverConfig;
246
+ config.set(
247
+ "servers",
248
+ servers
249
+ );
250
+ config.set(
251
+ "activeServer",
252
+ legacyServerUrl
253
+ );
254
+ config.set(
255
+ "configVersion",
256
+ CONFIG_VERSION
257
+ );
258
+ const legacyKeys = [
259
+ "serverUrl",
260
+ "wsUrl",
261
+ "userId",
262
+ "publicKey",
263
+ "encryptedPrivateKey",
264
+ "keySalt",
265
+ "authToken",
266
+ "masterKey"
267
+ ];
268
+ for (const k of legacyKeys) {
269
+ if (k in store) {
270
+ delete store[k];
271
+ }
272
+ }
273
+ config.store = store;
274
+ }
275
+ function _resetMigrationState() {
276
+ migrated = false;
277
+ }
278
+
279
+ export {
280
+ getSecret,
281
+ setSecret,
282
+ deleteSecret,
283
+ migrateConfigIfNeeded,
284
+ _resetMigrationState,
285
+ DEFAULT_SERVER_URL,
286
+ config,
287
+ normalizeServerUrl,
288
+ getActiveServer,
289
+ setActiveServer,
290
+ setServerConfig,
291
+ deleteServerConfig,
292
+ getConfig,
293
+ getMachineId,
294
+ getMachineName,
295
+ setMachineName
296
+ };