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.
package/LICENSE ADDED
@@ -0,0 +1,168 @@
1
+ Copyright © 2026, anyterm.
2
+
3
+ # PolyForm Shield License 1.0.0
4
+
5
+ <https://polyformproject.org/licenses/shield/1.0.0>
6
+
7
+ ## Acceptance
8
+
9
+ In order to get any license under these terms, you must agree
10
+ to them as both strict obligations and conditions to all your
11
+ licenses.
12
+
13
+ ## Copyright License
14
+
15
+ The licensor grants you a copyright license for the software
16
+ to do everything you might do with the software that would
17
+ otherwise infringe the licensor's copyright in it for any
18
+ permitted purpose. However, you may only distribute the
19
+ software according to [Distribution License](#distribution-license)
20
+ and make changes or new works based on the software according to
21
+ [Changes and New Works License](#changes-and-new-works-license).
22
+
23
+ ## Distribution License
24
+
25
+ The licensor grants you an additional copyright license to
26
+ distribute copies of the software. Your license to distribute
27
+ covers distributing the software with changes and new works
28
+ permitted by [Changes and New Works License](#changes-and-new-works-license).
29
+
30
+ ## Notices
31
+
32
+ You must ensure that anyone who gets a copy of any part of
33
+ the software from you also gets a copy of these terms or the
34
+ URL for them above, as well as copies of any plain-text lines
35
+ beginning with `Required Notice:` that the licensor provided
36
+ with the software.
37
+
38
+ Required Notice: Copyright © 2026, anyterm (https://anyterm.dev)
39
+
40
+ ## Changes and New Works License
41
+
42
+ The licensor grants you an additional copyright license to
43
+ make changes and new works based on the software for any
44
+ permitted purpose.
45
+
46
+ ## Patent License
47
+
48
+ The licensor grants you a patent license for the software
49
+ that covers patent claims the licensor can license, or
50
+ becomes able to license, that you would infringe by using
51
+ the software.
52
+
53
+ ## Noncompete
54
+
55
+ Any purpose is a permitted purpose, except for providing
56
+ any product that competes with the software or any product
57
+ the licensor or any of its affiliates provides using the
58
+ software.
59
+
60
+ ## Competition
61
+
62
+ Goods and services compete even when they provide
63
+ functionality through different kinds of interfaces or for
64
+ different technical platforms. Applications can compete with
65
+ services, libraries with plugins, frameworks with development
66
+ tools, and so on, even if they're written in different
67
+ programming languages or for different computer architectures.
68
+ Goods and services compete even when provided free of charge.
69
+ If you market a product as a practical substitute for the
70
+ software or another product, it definitely competes.
71
+
72
+ ## New Products
73
+
74
+ If you are using the software to provide a product that does
75
+ not compete, but the licensor or any of its affiliates brings
76
+ your product into competition by providing a new version of
77
+ the software or another product using the software, you may
78
+ continue using versions of the software available under these
79
+ terms beforehand to provide your competing product, but not
80
+ any later versions.
81
+
82
+ ## Discontinued Products
83
+
84
+ You may begin using the software to compete with a product
85
+ or service that the licensor or any of its affiliates has
86
+ stopped providing, unless the licensor includes a plain-text
87
+ line beginning with `Licensor Line of Business:` with the
88
+ software that mentions that line of business.
89
+
90
+ Licensor Line of Business: E2E encrypted terminal streaming,
91
+ remote terminal access, and related developer tools provided
92
+ as a hosted service at anyterm.dev
93
+
94
+ ## Sales of Business
95
+
96
+ If the licensor or any of its affiliates sells a line of
97
+ business developing the software or using the software to
98
+ provide a product, the buyer can also enforce
99
+ [Noncompete](#noncompete) for that product.
100
+
101
+ ## Fair Use
102
+
103
+ You may have "fair use" rights for the software under the
104
+ law. These terms do not limit them.
105
+
106
+ ## No Other Rights
107
+
108
+ These terms do not allow you to sublicense or transfer any
109
+ of your licenses to anyone else, or prevent the licensor
110
+ from granting licenses to anyone else. These terms do not
111
+ imply any other licenses.
112
+
113
+ ## Patent Defense
114
+
115
+ If you make any written claim that the software infringes
116
+ or contributes to infringement of any patent, your patent
117
+ license for the software granted under these terms ends
118
+ immediately. If your company makes such a claim, your patent
119
+ license ends immediately for work on behalf of your company.
120
+
121
+ ## Violations
122
+
123
+ The first time you are notified in writing that you have
124
+ violated any of these terms, or done anything with the
125
+ software not covered by your licenses, your licenses can
126
+ nonetheless continue if you come into full compliance with
127
+ these terms, and take practical steps to correct past
128
+ violations, within 32 days of receiving notice. Otherwise,
129
+ all your licenses end immediately.
130
+
131
+ ## No Liability
132
+
133
+ As far as the law allows, the software comes as is, without
134
+ any warranty or condition, and the licensor will not be
135
+ liable to you for any damages arising out of these terms or
136
+ the use or nature of the software, under any kind of legal
137
+ claim.
138
+
139
+ ## Definitions
140
+
141
+ The **licensor** is the individual or entity offering these
142
+ terms, and the **software** is the software the licensor
143
+ makes available under these terms.
144
+
145
+ A **product** can be a good or service, or a combination
146
+ of them.
147
+
148
+ **You** refers to the individual or entity agreeing to
149
+ these terms.
150
+
151
+ **Your company** is any legal entity, sole proprietorship,
152
+ or other kind of organization that you work for, plus all
153
+ its affiliates.
154
+
155
+ **Affiliates** means the other organizations than an
156
+ organization has control over, is under the control of, or
157
+ is under common control with.
158
+
159
+ **Control** means ownership of substantially all the assets
160
+ of an entity, or the power to direct its management and
161
+ policies by vote, contract, or otherwise. Control can be
162
+ direct or indirect.
163
+
164
+ **Your licenses** are all the licenses granted to you for
165
+ the software under these terms.
166
+
167
+ **Use** means anything you do with the software requiring
168
+ one of your licenses.
@@ -0,0 +1,327 @@
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
+ import { createInterface } from "readline/promises";
11
+ var SERVICE = "anyterm";
12
+ var ENV_VAR_MAP = {
13
+ authToken: "ANYTERM_AUTH_TOKEN",
14
+ masterKey: "ANYTERM_MASTER_KEY"
15
+ };
16
+ function hardenConfigPermissions() {
17
+ try {
18
+ if (process.platform !== "win32") {
19
+ fs.chmodSync(config.path, 384);
20
+ }
21
+ } catch {
22
+ }
23
+ }
24
+ var keytar = null;
25
+ var plaintextConfirmed = false;
26
+ async function getKeytar() {
27
+ if (keytar) return keytar;
28
+ try {
29
+ const mod = await import("keytar");
30
+ keytar = mod.default;
31
+ return keytar;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ async function getSecret(key, serverUrl) {
37
+ const envVar = ENV_VAR_MAP[key];
38
+ if (envVar) {
39
+ const envValue = process.env[envVar];
40
+ if (envValue) return envValue;
41
+ }
42
+ const keychainKey = serverUrl ? `${key}:${serverUrl}` : key;
43
+ const kt = await getKeytar();
44
+ if (kt) {
45
+ try {
46
+ const value = await kt.getPassword(SERVICE, keychainKey);
47
+ if (value) return value;
48
+ } catch {
49
+ }
50
+ }
51
+ if (serverUrl) {
52
+ const store = config.store;
53
+ const servers = store["servers"];
54
+ const val2 = servers?.[serverUrl]?.[key];
55
+ return typeof val2 === "string" ? val2 : null;
56
+ }
57
+ const stored = config.store;
58
+ const val = stored[key];
59
+ return typeof val === "string" ? val : null;
60
+ }
61
+ async function setSecret(key, value, serverUrl) {
62
+ const envVar = ENV_VAR_MAP[key];
63
+ if (envVar && process.env[envVar]) return;
64
+ const keychainKey = serverUrl ? `${key}:${serverUrl}` : key;
65
+ const kt = await getKeytar();
66
+ if (kt) {
67
+ try {
68
+ await kt.setPassword(SERVICE, keychainKey, value);
69
+ if (serverUrl) {
70
+ const store = config.store;
71
+ const servers = store["servers"];
72
+ if (servers?.[serverUrl]?.[key]) {
73
+ delete servers[serverUrl][key];
74
+ config.set(
75
+ "servers",
76
+ servers
77
+ );
78
+ }
79
+ } else {
80
+ const stored = config.store;
81
+ if (key in stored) {
82
+ delete stored[key];
83
+ config.store = stored;
84
+ }
85
+ }
86
+ return;
87
+ } catch {
88
+ }
89
+ }
90
+ if (!process.stdin.isTTY) {
91
+ throw new Error(
92
+ "OS keychain unavailable and running in non-interactive mode.\nSet ANYTERM_AUTH_TOKEN and ANYTERM_MASTER_KEY environment variables for headless environments."
93
+ );
94
+ }
95
+ if (!plaintextConfirmed) {
96
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
97
+ try {
98
+ const answer = await rl.question(
99
+ "\x1B[33mOS keychain unavailable. Store credentials in plaintext config file? (y/N): \x1B[0m"
100
+ );
101
+ if (answer.trim().toLowerCase() !== "y") {
102
+ throw new Error(
103
+ "Credentials not saved. Set ANYTERM_AUTH_TOKEN and ANYTERM_MASTER_KEY environment variables, or install a keychain provider."
104
+ );
105
+ }
106
+ plaintextConfirmed = true;
107
+ } finally {
108
+ rl.close();
109
+ }
110
+ }
111
+ if (serverUrl) {
112
+ const store = config.store;
113
+ const servers = store["servers"] ?? {};
114
+ if (!servers[serverUrl]) servers[serverUrl] = {};
115
+ servers[serverUrl][key] = value;
116
+ config.set(
117
+ "servers",
118
+ servers
119
+ );
120
+ } else {
121
+ config.set(
122
+ key,
123
+ value
124
+ );
125
+ }
126
+ hardenConfigPermissions();
127
+ }
128
+ async function deleteSecret(key, serverUrl) {
129
+ const keychainKey = serverUrl ? `${key}:${serverUrl}` : key;
130
+ const kt = await getKeytar();
131
+ if (kt) {
132
+ try {
133
+ await kt.deletePassword(SERVICE, keychainKey);
134
+ } catch {
135
+ }
136
+ }
137
+ if (serverUrl) {
138
+ const store = config.store;
139
+ const servers = store["servers"];
140
+ if (servers?.[serverUrl]?.[key]) {
141
+ delete servers[serverUrl][key];
142
+ config.set(
143
+ "servers",
144
+ servers
145
+ );
146
+ }
147
+ } else {
148
+ const stored = config.store;
149
+ if (key in stored) {
150
+ delete stored[key];
151
+ config.store = stored;
152
+ }
153
+ }
154
+ }
155
+
156
+ // src/config.ts
157
+ var DEFAULT_SERVER_URL = "https://anyterm.dev";
158
+ var CONFIG_VERSION = 2;
159
+ var config = new Conf({
160
+ projectName: "anyterm"
161
+ });
162
+ function normalizeServerUrl(url) {
163
+ const parsed = new URL(url);
164
+ return `${parsed.protocol}//${parsed.host.toLowerCase()}${parsed.pathname.replace(/\/+$/, "")}`;
165
+ }
166
+ function getActiveServer() {
167
+ return config.get("activeServer");
168
+ }
169
+ function setActiveServer(serverUrl) {
170
+ config.set(
171
+ "activeServer",
172
+ serverUrl
173
+ );
174
+ }
175
+ function getServerConfig(serverUrl) {
176
+ const store = config.store;
177
+ return store.servers?.[serverUrl];
178
+ }
179
+ function setServerConfig(serverUrl, data) {
180
+ const store = config.store;
181
+ const servers = store.servers ?? {};
182
+ servers[serverUrl] = { ...servers[serverUrl], ...data };
183
+ config.set("servers", servers);
184
+ }
185
+ function deleteServerConfig(serverUrl) {
186
+ const store = config.store;
187
+ if (store.servers?.[serverUrl]) {
188
+ delete store.servers[serverUrl];
189
+ config.set(
190
+ "servers",
191
+ store.servers
192
+ );
193
+ }
194
+ }
195
+ async function getConfig() {
196
+ const { migrateConfigIfNeeded: migrateConfigIfNeeded2 } = await import("./migrate-OR6DL5TQ.js");
197
+ await migrateConfigIfNeeded2();
198
+ const activeServer = config.get("activeServer");
199
+ if (!activeServer) {
200
+ console.error("Not logged in. Run: anyterm login");
201
+ process.exit(1);
202
+ }
203
+ const serverConf = getServerConfig(activeServer);
204
+ if (!serverConf?.userId) {
205
+ console.error("Not logged in. Run: anyterm login");
206
+ process.exit(1);
207
+ }
208
+ const authToken = await getSecret("authToken", activeServer);
209
+ if (!authToken) {
210
+ console.error("Not logged in. Run: anyterm login");
211
+ process.exit(1);
212
+ }
213
+ const wsUrl = serverConf.wsUrl || activeServer.replace(/^http/, "ws");
214
+ const masterKey = await getSecret("masterKey", activeServer);
215
+ return {
216
+ serverUrl: activeServer,
217
+ wsUrl,
218
+ authToken,
219
+ userId: serverConf.userId,
220
+ publicKey: serverConf.publicKey,
221
+ encryptedPrivateKey: serverConf.encryptedPrivateKey,
222
+ keySalt: serverConf.keySalt,
223
+ masterKey
224
+ };
225
+ }
226
+ function getMachineId() {
227
+ return machineId.machineIdSync().slice(0, 8);
228
+ }
229
+ function getMachineName() {
230
+ return config.get("machineName") || os.hostname();
231
+ }
232
+ function setMachineName(name) {
233
+ config.set(
234
+ "machineName",
235
+ name
236
+ );
237
+ }
238
+
239
+ // src/migrate.ts
240
+ var migrated = false;
241
+ async function migrateConfigIfNeeded() {
242
+ if (migrated) return;
243
+ migrated = true;
244
+ const store = config.store;
245
+ const version = store["configVersion"];
246
+ if (version && version >= CONFIG_VERSION) return;
247
+ const legacyServerUrl = store["serverUrl"];
248
+ if (!legacyServerUrl) {
249
+ config.set(
250
+ "configVersion",
251
+ CONFIG_VERSION
252
+ );
253
+ return;
254
+ }
255
+ const serverConfig = {};
256
+ const perServerKeys = [
257
+ "wsUrl",
258
+ "userId",
259
+ "publicKey",
260
+ "encryptedPrivateKey",
261
+ "keySalt"
262
+ ];
263
+ for (const k of perServerKeys) {
264
+ if (k in store) {
265
+ serverConfig[k] = store[k];
266
+ }
267
+ }
268
+ for (const secretKey of ["authToken", "masterKey"]) {
269
+ const value = await getSecret(secretKey);
270
+ if (value) {
271
+ await setSecret(secretKey, value, legacyServerUrl);
272
+ await deleteSecret(secretKey);
273
+ }
274
+ }
275
+ const servers = store["servers"] ?? {};
276
+ servers[legacyServerUrl] = serverConfig;
277
+ config.set(
278
+ "servers",
279
+ servers
280
+ );
281
+ config.set(
282
+ "activeServer",
283
+ legacyServerUrl
284
+ );
285
+ config.set(
286
+ "configVersion",
287
+ CONFIG_VERSION
288
+ );
289
+ const legacyKeys = [
290
+ "serverUrl",
291
+ "wsUrl",
292
+ "userId",
293
+ "publicKey",
294
+ "encryptedPrivateKey",
295
+ "keySalt",
296
+ "authToken",
297
+ "masterKey"
298
+ ];
299
+ for (const k of legacyKeys) {
300
+ if (k in store) {
301
+ delete store[k];
302
+ }
303
+ }
304
+ config.store = store;
305
+ }
306
+ function _resetMigrationState() {
307
+ migrated = false;
308
+ }
309
+
310
+ export {
311
+ getSecret,
312
+ setSecret,
313
+ deleteSecret,
314
+ migrateConfigIfNeeded,
315
+ _resetMigrationState,
316
+ DEFAULT_SERVER_URL,
317
+ config,
318
+ normalizeServerUrl,
319
+ getActiveServer,
320
+ setActiveServer,
321
+ setServerConfig,
322
+ deleteServerConfig,
323
+ getConfig,
324
+ getMachineId,
325
+ getMachineName,
326
+ setMachineName
327
+ };