telstore 0.1.0 → 0.1.1

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 CHANGED
@@ -1,19 +1,19 @@
1
- # telark
1
+ # telstore
2
2
 
3
3
  Split large files into 1.8GB chunks, store them on Telegram, and restore them intact.
4
4
 
5
5
  ## Quick start
6
6
 
7
7
  ```bash
8
- npx telark login # once only
9
- npx telark config chat @my_backups # where backups go, from now on
10
- npx telark data.tar # split it and send it there
11
- npx telark data.tar --to @somewhere # somewhere else, this run only
12
- npx telark config # every setting and where its value comes from
13
- npx telark status # account, destination, unfinished backups
14
- npx telark list # what is already stored in the destination
15
- npx telark restore telark-20260905-7f3a91
16
- npx telark delete telark-20260905-7f3a91 # take it back out of the chat, for good
8
+ npx telstore login # once only
9
+ npx telstore config chat @my_backups # where backups go, from now on
10
+ npx telstore data.tar # split it and send it there
11
+ npx telstore data.tar --to @somewhere # somewhere else, this run only
12
+ npx telstore config # every setting and where its value comes from
13
+ npx telstore status # account, destination, unfinished backups
14
+ npx telstore list # what is already stored in the destination
15
+ npx telstore restore telstore-20260905-7f3a91
16
+ npx telstore delete telstore-20260905-7f3a91 # take it back out of the chat, for good
17
17
  ```
18
18
 
19
19
  ## What you need
@@ -21,19 +21,19 @@ npx telark delete telark-20260905-7f3a91 # take it back out of the chat, for g
21
21
  - Node.js 18 or newer.
22
22
  - An `api_id` and `api_hash` from <https://my.telegram.org> → API development tools. The `login` command asks for both, plus your phone number and the verification code.
23
23
 
24
- telark signs in with your own Telegram account (MTProto), not a bot. That is a hard requirement: the Bot API caps uploads at 50MB per file, while a user account gets 2GB.
24
+ telstore signs in with your own Telegram account (MTProto), not a bot. That is a hard requirement: the Bot API caps uploads at 50MB per file, while a user account gets 2GB.
25
25
 
26
26
  ## Settings and flags
27
27
 
28
- There are two ways to say what telark should do, and they never overlap. **`config` writes;
28
+ There are two ways to say what telstore should do, and they never overlap. **`config` writes;
29
29
  flags do not.** A flag applies to the run you typed it on and changes nothing on disk, so
30
30
  `--to @elsewhere` sends one backup elsewhere without moving the destination for the next one.
31
31
 
32
32
  ```bash
33
- npx telark config # everything, and whether it is yours or a default
34
- npx telark config chat # one value, bare, ready to pipe
35
- npx telark config chunkSize 500MB # change it for good
36
- npx telark config chunkSize --unset # back to the default
33
+ npx telstore config # everything, and whether it is yours or a default
34
+ npx telstore config chat # one value, bare, ready to pipe
35
+ npx telstore config chunkSize 500MB # change it for good
36
+ npx telstore config chunkSize --unset # back to the default
37
37
  ```
38
38
 
39
39
  | Setting | Flag | Default | Meaning |
@@ -63,24 +63,24 @@ follows carries a summary card:
63
63
  🗄 data.tar
64
64
  ━━━━━━━━━━━━━━━
65
65
  💾 21.4 GB · 12 chunks
66
- 🆔 telark-20260905-7f3a91
66
+ 🆔 telstore-20260905-7f3a91
67
67
  📅 2026-09-05 16:40 UTC
68
68
 
69
- ↩ npx telark restore telark-20260905-7f3a91
70
- #telark
69
+ ↩ npx telstore restore telstore-20260905-7f3a91
70
+ #telstore
71
71
  ```
72
72
 
73
- `npx telark list` reads those cards straight out of the chat — one search, no downloads —
73
+ `npx telstore list` reads those cards straight out of the chat — one search, no downloads —
74
74
  and lays them out as a table:
75
75
 
76
76
  ```
77
77
  Destination https://web.telegram.org/k/#@my_backups
78
78
 
79
- BACKUP ID FILE SIZE CHUNKS CREATED
80
- telark-20260905-7f3a91 data.tar 21.4 GB 12 2026-09-05
81
- telark-20260901-9de447 photos.zip 940.3 MB 1 2026-09-01
79
+ BACKUP ID FILE SIZE CHUNKS CREATED
80
+ telstore-20260905-7f3a91 data.tar 21.4 GB 12 2026-09-05
81
+ telstore-20260901-9de447 photos.zip 940.3 MB 1 2026-09-01
82
82
 
83
- 2 backups. Restore with: npx telark restore <backup-id>
83
+ 2 backups. Restore with: npx telstore restore <backup-id>
84
84
  ```
85
85
 
86
86
  A backup uploaded before the card existed still gets a row, with dashes where the caption
@@ -88,22 +88,22 @@ says nothing — `list` reports what the chat holds and never fills gaps with gu
88
88
 
89
89
  ## How it works
90
90
 
91
- Every run mints a `backupId`. The file is read directly by offset — no temporary copies — and uploaded as documents named `<backupId>.partNNNN`. Once every chunk is up, telark sends a JSON manifest listing the message id and sha256 of each one. Restore needs only the `backupId`: it finds the manifest in the chat, downloads each chunk to its exact position in a `.partial` file, checks every chunk's sha256 and size, and renames it to the real file only after *all* chunks match.
91
+ Every run mints a `backupId`. The file is read directly by offset — no temporary copies — and uploaded as documents named `<backupId>.partNNNN`. Once every chunk is up, telstore sends a JSON manifest listing the message id and sha256 of each one. Restore needs only the `backupId`: it finds the manifest in the chat, downloads each chunk to its exact position in a `.partial` file, checks every chunk's sha256 and size, and renames it to the real file only after *all* chunks match.
92
92
 
93
- If the connection drops during an **upload**, just run the same command again — progress lives in `~/.telark/state/` and finished chunks are skipped, keeping the same `backupId`. Two things to know about rerunning:
93
+ If the connection drops during an **upload**, just run the same command again — progress lives in `~/.telstore/state/` and finished chunks are skipped, keeping the same `backupId`. Two things to know about rerunning:
94
94
 
95
- - Running again against a destination that differs from the one in the unfinished progress makes telark **refuse to run** rather than silently redirect — one backup cannot be split across two destinations. The error names the chat to pass as `--to` to carry on, or the state file to delete to start a new backup. It reads the same whether the mismatch came from a flag or from your configured `chat`.
95
+ - Running again against a destination that differs from the one in the unfinished progress makes telstore **refuse to run** rather than silently redirect — one backup cannot be split across two destinations. The error names the chat to pass as `--to` to carry on, or the state file to delete to start a new backup. It reads the same whether the mismatch came from a flag or from your configured `chat`.
96
96
  - Running again **without** `--chunk-size` resumes at the size the backup started with, whatever your configured `chunkSize` says today. A setting is what to use when nobody asks for anything; it is not somebody asking.
97
- - Running again **with** a `--chunk-size` that differs from that size makes telark **refuse to run**: the chunks already in the chat were cut that way and cannot be re-cut. Drop the flag to carry on, or delete the state file to start a new backup — which leaves the chunks already sent in the chat with nothing pointing at them.
97
+ - Running again **with** a `--chunk-size` that differs from that size makes telstore **refuse to run**: the chunks already in the chat were cut that way and cannot be re-cut. Drop the flag to carry on, or delete the state file to start a new backup — which leaves the chunks already sent in the chat with nothing pointing at them.
98
98
 
99
- `Ctrl-C` during an upload names the backup it was working on, so `telark status` and a later `restore` have something to go on. telark keeps the **20 most recent** unfinished backups in `~/.telark/state/`; starting a new one past that drops the oldest record and says which id it dropped. Only the local record goes — the chunks that backup sent stay in the chat, searchable by that id, but it can no longer be resumed.
99
+ `Ctrl-C` during an upload names the backup it was working on, so `telstore status` and a later `restore` have something to go on. telstore keeps the **20 most recent** unfinished backups in `~/.telstore/state/`; starting a new one past that drops the oldest record and says which id it dropped. Only the local record goes — the chunks that backup sent stay in the chat, searchable by that id, but it can no longer be resumed.
100
100
 
101
101
  **Restore keeps no state to resume from.** Pressing `Ctrl-C` mid-restore saves nothing — running again starts over.
102
102
 
103
103
  ## Deleting a backup
104
104
 
105
105
  ```bash
106
- npx telark delete telark-20260905-7f3a91
106
+ npx telstore delete telstore-20260905-7f3a91
107
107
  ```
108
108
 
109
109
  It prints what it is about to destroy, asks once, and then removes every chunk message and
@@ -129,14 +129,14 @@ that is usually the one you want gone.
129
129
 
130
130
  ## Limits worth knowing
131
131
 
132
- - A chunk cannot exceed 1950MB: Telegram accepts at most 4000 parts of 512KB per file, an arithmetic ceiling of about 1953MB, and telark stops at 1950MB to leave a safety margin.
132
+ - A chunk cannot exceed 1950MB: Telegram accepts at most 4000 parts of 512KB per file, an arithmetic ceiling of about 1953MB, and telstore stops at 1950MB to leave a safety margin.
133
133
  - The data is **not** encrypted. Don't upload anything you would mind sitting on someone else's infrastructure.
134
- - Deleting a chunk message on Telegram destroys the backup, with no way to recover it. Use `npx telark delete <backup-id>` when that is what you actually want.
134
+ - Deleting a chunk message on Telegram destroys the backup, with no way to recover it. Use `npx telstore delete <backup-id>` when that is what you actually want.
135
135
  - Keep the `backupId`. Without it you have to hunt for the manifest in the chat by hand.
136
136
 
137
137
  ## Where the config lives
138
138
 
139
- `~/.telark/config.json` (mode 600) holds `apiId`, `apiHash` and the session at the top level, with everything `config` manages under `settings`:
139
+ `~/.telstore/config.json` (mode 600) holds `apiId`, `apiHash` and the session at the top level, with everything `config` manages under `settings`:
140
140
 
141
141
  ```json
142
142
  {
@@ -149,4 +149,4 @@ that is usually the one you want gone.
149
149
 
150
150
  Editing it by hand is fine, and a value that cannot be used is named on the next run — with the file and the key, never with a flag you did not type.
151
151
 
152
- `npx telark logout` **only deletes the locally stored session** and keeps the rest — the session is still alive on Telegram's side. To revoke access for good, open Telegram → Settings → Devices (Active sessions) and terminate that session.
152
+ `npx telstore logout` **only deletes the locally stored session** and keeps the rest — the session is still alive on Telegram's side. To revoke access for good, open Telegram → Settings → Devices (Active sessions) and terminate that session.
@@ -70,14 +70,14 @@ async function main() {
70
70
 
71
71
  case 'restore':
72
72
  if (!parsed.args[0]) {
73
- throw new Error('Missing backup id. Example: npx telark restore telark-20260905-7f3a91')
73
+ throw new Error('Missing backup id. Example: npx telstore restore telstore-20260905-7f3a91')
74
74
  }
75
75
  await runRestore(parsed.args[0], parsed.options)
76
76
  return
77
77
 
78
78
  case 'delete':
79
79
  if (!parsed.args[0]) {
80
- throw new Error('Missing backup id. Example: npx telark delete telark-20260905-7f3a91')
80
+ throw new Error('Missing backup id. Example: npx telstore delete telstore-20260905-7f3a91')
81
81
  }
82
82
  await runDelete(parsed.args[0], parsed.options)
83
83
  return
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telstore",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Split large files into chunks and store them on Telegram",
5
5
  "keywords": [
6
6
  "telegram",
@@ -11,19 +11,19 @@
11
11
  "cli",
12
12
  "mtproto"
13
13
  ],
14
- "homepage": "https://github.com/shovity/telark#readme",
14
+ "homepage": "https://github.com/shovity/telstore#readme",
15
15
  "bugs": {
16
- "url": "https://github.com/shovity/telark/issues"
16
+ "url": "https://github.com/shovity/telstore/issues"
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/shovity/telark.git"
20
+ "url": "git+https://github.com/shovity/telstore.git"
21
21
  },
22
22
  "type": "module",
23
23
  "license": "MIT",
24
24
  "author": "shovity",
25
25
  "bin": {
26
- "telark": "bin/telark.js"
26
+ "telstore": "bin/telstore.js"
27
27
  },
28
28
  "files": [
29
29
  "bin",
@@ -39,4 +39,4 @@
39
39
  "dependencies": {
40
40
  "telegram": "^2.26.22"
41
41
  }
42
- }
42
+ }
package/src/caption.js CHANGED
@@ -8,7 +8,7 @@ const DIVIDER = '━'.repeat(15)
8
8
 
9
9
  // The hashtag is what `list` searches for, and it lives on the manifest alone: chunk
10
10
  // captions stay out of that search so a twelve-chunk backup is one hit, not thirteen.
11
- export const MANIFEST_TAG = '#telark'
11
+ export const MANIFEST_TAG = '#telstore'
12
12
 
13
13
  // A file name may legally contain a newline or a tab, and either one would push the
14
14
  // rest of the card down a row and take its shape apart.
@@ -32,7 +32,7 @@ export function manifestCaption({ id, name, size, chunks, createdAt }) {
32
32
  `🆔 ${id}`,
33
33
  `📅 ${utcMinutes(createdAt)}`,
34
34
  '',
35
- `↩ npx telark restore ${id}`,
35
+ `↩ npx telstore restore ${id}`,
36
36
  MANIFEST_TAG,
37
37
  ].join('\n')
38
38
  }
package/src/chat.js CHANGED
@@ -1,4 +1,4 @@
1
- // How telark talks about a destination. None of this touches Telegram — it is string
1
+ // How telstore talks about a destination. None of this touches Telegram — it is string
2
2
  // handling around a target the user typed — so it lives apart from the client that does.
3
3
 
4
4
  export function normalizeChatTarget(input) {
@@ -17,7 +17,7 @@ export function normalizeChatTarget(input) {
17
17
 
18
18
  // Telegram's web client addresses a chat by putting the raw target in the fragment, which
19
19
  // covers both a negative channel id and an @username. Saved Messages is the exception: it
20
- // is reached by the account's own id, which telark does not know, so it gets no link
20
+ // is reached by the account's own id, which telstore does not know, so it gets no link
21
21
  // rather than a guessed one that lands somewhere else.
22
22
  export function chatUrl(chat) {
23
23
  const text = String(chat)
package/src/cli.js CHANGED
@@ -23,22 +23,22 @@ const OPTIONS = {
23
23
  help: { type: 'boolean', short: 'h' },
24
24
  }
25
25
 
26
- export const HELP = `telark — split large files into chunks and store them on Telegram
26
+ export const HELP = `telstore — split large files into chunks and store them on Telegram
27
27
 
28
28
  Usage:
29
- npx telark login Log in to Telegram, only needed once
30
- npx telark <file> Split a file and upload it to Telegram
31
- npx telark list List the backups stored in the destination
32
- npx telark restore <backup-id> Download the chunks and reassemble the file
33
- npx telark delete <backup-id> Remove a backup's chunks and manifest from the chat
34
- npx telark status Show the account, the destination and unfinished backups
35
- npx telark config Show every setting and where its value comes from
36
- npx telark logout Remove the saved session
29
+ npx telstore login Log in to Telegram, only needed once
30
+ npx telstore <file> Split a file and upload it to Telegram
31
+ npx telstore list List the backups stored in the destination
32
+ npx telstore restore <backup-id> Download the chunks and reassemble the file
33
+ npx telstore delete <backup-id> Remove a backup's chunks and manifest from the chat
34
+ npx telstore status Show the account, the destination and unfinished backups
35
+ npx telstore config Show every setting and where its value comes from
36
+ npx telstore logout Remove the saved session
37
37
 
38
38
  Settings:
39
- npx telark config <name> Print one setting's value
40
- npx telark config <name> <value> Change it for good
41
- npx telark config <name> --unset Drop it and fall back to the default
39
+ npx telstore config <name> Print one setting's value
40
+ npx telstore config <name> <value> Change it for good
41
+ npx telstore config <name> --unset Drop it and fall back to the default
42
42
 
43
43
  chat Where backups go: @username, -100123..., or me. No default.
44
44
  chunkSize Size of each chunk, default 1800MB. Examples: 1.8GB, 500MB.
@@ -69,7 +69,7 @@ export function interruptMessage(command, { backupId } = {}) {
69
69
 
70
70
  return (
71
71
  `\n${backup} — run the same command again to continue, ` +
72
- 'or "npx telark status" to see what is left.\n'
72
+ 'or "npx telstore status" to see what is left.\n'
73
73
  )
74
74
  }
75
75
 
@@ -138,13 +138,13 @@ export function route(argv) {
138
138
 
139
139
  const [first, ...rest] = positionals
140
140
 
141
- // `telark --to @chan` with no file used to mean "remember this destination". Flags no
141
+ // `telstore --to @chan` with no file used to mean "remember this destination". Flags no
142
142
  // longer write anything, so that line now asks for a run that has nothing to upload —
143
143
  // say where the destination actually lives instead of printing help at someone who was
144
144
  // perfectly clear about what they wanted.
145
145
  if (first === undefined && values.to && !values.help) {
146
146
  throw new Error(
147
- `Nothing to upload. To change the destination for good, run "npx telark config chat ${values.to}". ` +
147
+ `Nothing to upload. To change the destination for good, run "npx telstore config chat ${values.to}". ` +
148
148
  'To use it for one run, pass --to alongside a file or a command.',
149
149
  )
150
150
  }
package/src/client.js CHANGED
@@ -22,7 +22,7 @@ export function documentFileName(message) {
22
22
  return named?.fileName ?? null
23
23
  }
24
24
 
25
- // The one place telark searches a chat. Both callers want documents and nothing else,
25
+ // The one place telstore searches a chat. Both callers want documents and nothing else,
26
26
  // and getMessages is preferred over a raw Api.messages.Search because it handles offsets,
27
27
  // hashes and pagination itself, so we don't hand-build easily mistyped fields. The raw
28
28
  // message is kept alongside the flat fields because downloading needs it whole.
@@ -42,9 +42,9 @@ export async function searchDocuments(client, peer, { search, limit }) {
42
42
  }))
43
43
  }
44
44
 
45
- // How telark finds a backup's manifest, in one place because restore and delete must not
45
+ // How telstore finds a backup's manifest, in one place because restore and delete must not
46
46
  // disagree about it. The search is by backup id, but the answer is decided by the file name
47
- // telark itself wrote — a caption is text a person can edit, a file name is not.
47
+ // telstore itself wrote — a caption is text a person can edit, a file name is not.
48
48
  export async function findManifestMessage(client, peer, backupId) {
49
49
  const wanted = manifestFileName(backupId)
50
50
  const found = await searchDocuments(client, peer, { search: backupId, limit: 100 })
@@ -56,7 +56,7 @@ export async function readMessageBytes(client, message) {
56
56
  return await client.downloadMedia(message)
57
57
  }
58
58
 
59
- // The one place telark removes messages from a chat, and the mirror of searchDocuments
59
+ // The one place telstore removes messages from a chat, and the mirror of searchDocuments
60
60
  // above. GramJS has its own deleteMessages, and it is the right thing to call — it resolves
61
61
  // the peer and picks between channels.DeleteMessages and messages.DeleteMessages, which is
62
62
  // exactly the choice a fake client would never catch us getting wrong.
@@ -64,7 +64,7 @@ export async function readMessageBytes(client, message) {
64
64
  // What it does on top of that is the problem: it splits the ids into batches of a hundred
65
65
  // and fires every batch at once through Promise.all. A ten-thousand-chunk backup would put
66
66
  // a hundred requests in flight together, none of them under the retry policy or the stall
67
- // deadline that every other network wait in telark carries. Batching here instead keeps
67
+ // deadline that every other network wait in telstore carries. Batching here instead keeps
68
68
  // one request outstanding at a time, under both.
69
69
  //
70
70
  // Telegram does not complain about an id that is no longer there, so sending a batch twice
@@ -123,7 +123,7 @@ export async function closeQuietly(client, disconnect, onWarn) {
123
123
 
124
124
  export function assertLoggedIn(config) {
125
125
  if (!config.session || !config.apiId || !config.apiHash) {
126
- throw new Error('Not logged in — run "npx telark login" first.')
126
+ throw new Error('Not logged in — run "npx telstore login" first.')
127
127
  }
128
128
  }
129
129
 
@@ -139,7 +139,7 @@ export async function connect(config, { verbose = false } = {}) {
139
139
  await client.connect()
140
140
 
141
141
  if (!(await client.isUserAuthorized())) {
142
- throw new Error('Session expired — run "npx telark login".')
142
+ throw new Error('Session expired — run "npx telstore login".')
143
143
  }
144
144
 
145
145
  return client
@@ -12,7 +12,7 @@ const GAP = ' '
12
12
  function unknownKey(name) {
13
13
  if (isManagedByLogin(name)) {
14
14
  return new Error(
15
- `"${name}" is managed by "npx telark login", not by config. ` +
15
+ `"${name}" is managed by "npx telstore login", not by config. ` +
16
16
  `Settings you can change: ${SETTING_KEYS.join(', ')}.`,
17
17
  )
18
18
  }
@@ -37,7 +37,7 @@ function listLines(values, source, stored) {
37
37
  return [key, `${spec.format(value)}${note}`, source(key) === 'settings' ? '' : '(default)']
38
38
  })
39
39
 
40
- // Keys telark does not know are left in the file untouched, but a value that silently
40
+ // Keys telstore does not know are left in the file untouched, but a value that silently
41
41
  // does nothing is worse than one that is refused: show them, so a typo is visible from
42
42
  // the command rather than only from opening the file that also holds the session.
43
43
  const strays = Object.keys(stored).filter((key) => !SETTING_KEYS.includes(key))
@@ -50,8 +50,8 @@ function listLines(values, source, stored) {
50
50
  if (strays.length > 0) {
51
51
  lines.push('')
52
52
  lines.push(
53
- `Ignored, telark does not know these: ${strays.join(', ')}. ` +
54
- 'Remove one with: npx telark config <name> --unset',
53
+ `Ignored, telstore does not know these: ${strays.join(', ')}. ` +
54
+ 'Remove one with: npx telstore config <name> --unset',
55
55
  )
56
56
  }
57
57
 
@@ -65,21 +65,21 @@ export async function runConfig(args = [], options = {}, deps = {}) {
65
65
  if (extra.length > 0) {
66
66
  throw new Error(
67
67
  `Too many arguments: a setting takes one value, but got ${args.length}. ` +
68
- `Did you mean: npx telark config ${name} "${[value, ...extra].join(' ')}"`,
68
+ `Did you mean: npx telstore config ${name} "${[value, ...extra].join(' ')}"`,
69
69
  )
70
70
  }
71
71
 
72
72
  // Both of these would otherwise be obeyed halfway and reported as a success: a listing
73
73
  // that quietly dropped the --unset, or an unset that quietly dropped the value beside it.
74
74
  if (options.unset && name === undefined) {
75
- throw new Error('--unset needs the setting to drop. Try: npx telark config chat --unset')
75
+ throw new Error('--unset needs the setting to drop. Try: npx telstore config chat --unset')
76
76
  }
77
77
 
78
78
  if (options.unset && value !== undefined) {
79
79
  throw new Error(
80
80
  `--unset takes no value, but "${value}" was given. ` +
81
- `Use "npx telark config ${name} --unset" to drop it, ` +
82
- `or "npx telark config ${name} ${value}" to set it.`,
81
+ `Use "npx telstore config ${name} --unset" to drop it, ` +
82
+ `or "npx telstore config ${name} ${value}" to set it.`,
83
83
  )
84
84
  }
85
85
 
@@ -56,7 +56,7 @@ function stateMessageIds(record) {
56
56
  throw new Error(
57
57
  `The record of unfinished backup ${record.state.id} gives ` +
58
58
  `${JSON.stringify(msgId)} as the message id of chunk ${Number(index) + 1}, which ` +
59
- `is not a message id. ${record.file} is damaged, so telark is not deleting ` +
59
+ `is not a message id. ${record.file} is damaged, so telstore is not deleting ` +
60
60
  'anything.',
61
61
  )
62
62
  }
@@ -106,7 +106,7 @@ export async function runDelete(backupId, options = {}, deps = {}) {
106
106
  if (records.length > 1) {
107
107
  throw new Error(
108
108
  `Two local records both claim to be backup ${backupId}: ` +
109
- `${records.map((r) => r.file).join(' and ')}. telark will not guess which one to ` +
109
+ `${records.map((r) => r.file).join(' and ')}. telstore will not guess which one to ` +
110
110
  'drop — remove the wrong one by hand and run again.',
111
111
  )
112
112
  }
@@ -120,7 +120,7 @@ export async function runDelete(backupId, options = {}, deps = {}) {
120
120
  if (!manifestMessage && !record) {
121
121
  throw new Error(
122
122
  `No backup ${backupId} found in ${chatName(chat)}, and no unfinished record of it on ` +
123
- 'this machine. Check the id with "npx telark list", or use --to to point at the ' +
123
+ 'this machine. Check the id with "npx telstore list", or use --to to point at the ' +
124
124
  'right chat.',
125
125
  )
126
126
  }
@@ -131,7 +131,7 @@ export async function runDelete(backupId, options = {}, deps = {}) {
131
131
  if (manifestMessage) {
132
132
  manifest = parseManifestJson(await readMessageBytes(client, manifestMessage))
133
133
 
134
- // The manifest was found by the file name telark itself wrote, and that name is the
134
+ // The manifest was found by the file name telstore itself wrote, and that name is the
135
135
  // id this command was asked about. A body naming a different backup is a file that was
136
136
  // renamed or replaced, and its message ids point at somebody else's chunks — the one
137
137
  // mistake in this whole command that nothing can undo.
@@ -139,7 +139,7 @@ export async function runDelete(backupId, options = {}, deps = {}) {
139
139
  throw new Error(
140
140
  `The manifest named ${manifestFileName(backupId)} describes backup ` +
141
141
  `${JSON.stringify(manifest.id)}, not ${backupId}. Its message ids point at ` +
142
- 'another backup\'s chunks, so telark is not deleting anything.',
142
+ 'another backup\'s chunks, so telstore is not deleting anything.',
143
143
  )
144
144
  }
145
145
 
@@ -36,7 +36,7 @@ function utcDay(unixSeconds) {
36
36
  }
37
37
 
38
38
  // The card is text in a chat, which means a person can edit or predate it. The id is the
39
- // one field restore cannot be wrong about, so it always comes from the file name telark
39
+ // one field restore cannot be wrong about, so it always comes from the file name telstore
40
40
  // wrote; the caption only decorates. A card that cannot be read back leaves the rest
41
41
  // unknown, because inventing it would describe a backup that does not exist.
42
42
  function toRow(message) {
@@ -106,14 +106,14 @@ export async function runList(options = {}, deps = {}) {
106
106
  .map(toRow)
107
107
 
108
108
  if (rows.length === 0) {
109
- log(`No backups found in ${chatName(chat)}. Upload one with: npx telark <file>`)
109
+ log(`No backups found in ${chatName(chat)}. Upload one with: npx telstore <file>`)
110
110
  return rows
111
111
  }
112
112
 
113
113
  for (const line of renderTable(rows)) log(line)
114
114
 
115
115
  log('')
116
- log(`${rows.length} backup${rows.length === 1 ? '' : 's'}. Restore with: npx telark restore <backup-id>`)
116
+ log(`${rows.length} backup${rows.length === 1 ? '' : 's'}. Restore with: npx telstore restore <backup-id>`)
117
117
 
118
118
  return rows
119
119
  }
@@ -78,7 +78,7 @@ export async function runRestore(backupId, options = {}, deps = {}) {
78
78
  if (delayMs > LONG_WAIT_MS) {
79
79
  warn(
80
80
  `\nTelegram wants ${formatDuration(delayMs / 1000)} of waiting before the next part ` +
81
- `(${err.message}). telark is waiting and will carry on by itself, leave it running.\n`,
81
+ `(${err.message}). telstore is waiting and will carry on by itself, leave it running.\n`,
82
82
  )
83
83
  return
84
84
  }
@@ -112,7 +112,7 @@ export async function runRestore(backupId, options = {}, deps = {}) {
112
112
  const partial = `${target}.partial`
113
113
 
114
114
  // Only ENOENT means "no file yet". Treating a permission or I/O error as absence
115
- // would have telark overwrite the user's file without asking.
115
+ // would have telstore overwrite the user's file without asking.
116
116
  let exists = true
117
117
  try {
118
118
  await fs.stat(target)
@@ -78,7 +78,7 @@ export async function runStatus(options = {}, deps = {}) {
78
78
  'Destination',
79
79
  settingsError ??
80
80
  (settings.chat === null
81
- ? 'none set — run "npx telark config chat @my_backups" to set one'
81
+ ? 'none set — run "npx telstore config chat @my_backups" to set one'
82
82
  : describeChat(settings.chat)),
83
83
  ),
84
84
  )
@@ -103,7 +103,7 @@ export async function runUpload(filePath, options = {}, deps = {}) {
103
103
 
104
104
  // The chunks already in the chat were cut at the size this backup started with, and
105
105
  // nothing can re-cut them. Carrying on at a different size would abandon every one of
106
- // them in the chat, where telark can no longer find them — so an unfinished backup
106
+ // them in the chat, where telstore can no longer find them — so an unfinished backup
107
107
  // keeps its own chunk size, and a flag that disagrees is refused rather than obeyed.
108
108
  //
109
109
  // Only a flag is a disagreement. A configured chunkSize says what to use when nobody asks
@@ -164,10 +164,10 @@ export async function runUpload(filePath, options = {}, deps = {}) {
164
164
 
165
165
  // Only a new backup adds to the directory, so this is the one place it can grow.
166
166
  // The report goes out even when the caller asked for silence: this is not narration
167
- // about a transfer, it is telark dropping the only record of someone else's chunks.
167
+ // about a transfer, it is telstore dropping the only record of someone else's chunks.
168
168
  for (const gone of await pruneStates(configDir)) {
169
169
  writeErr(
170
- `\nDropped the record of unfinished backup ${gone.id}: telark keeps the ` +
170
+ `\nDropped the record of unfinished backup ${gone.id}: telstore keeps the ` +
171
171
  `${MAX_STATES} most recent. The chunks it sent are still in ${gone.chat}, ` +
172
172
  'searchable by that id, but that backup can no longer be resumed.\n',
173
173
  )
@@ -185,7 +185,7 @@ export async function runUpload(filePath, options = {}, deps = {}) {
185
185
  if (delayMs > LONG_WAIT_MS) {
186
186
  warn(
187
187
  `\nTelegram wants ${formatDuration(delayMs / 1000)} of waiting before the next send ` +
188
- `(${err.message}). telark is waiting and will carry on by itself, leave it running.\n`,
188
+ `(${err.message}). telstore is waiting and will carry on by itself, leave it running.\n`,
189
189
  )
190
190
  return
191
191
  }
@@ -289,7 +289,7 @@ export async function runUpload(filePath, options = {}, deps = {}) {
289
289
  throw new Error(
290
290
  `${absPath} changed during the upload ` +
291
291
  `(size ${stat.size} → ${after.size}, mtime ${stat.mtimeMs} → ${after.mtimeMs}). ` +
292
- 'This backup mixes old and new data and cannot be trusted — telark is not sending the manifest. ' +
292
+ 'This backup mixes old and new data and cannot be trusted — telstore is not sending the manifest. ' +
293
293
  'Wait until the file settles, then run again to create a new backup.',
294
294
  )
295
295
  }
@@ -316,7 +316,7 @@ export async function runUpload(filePath, options = {}, deps = {}) {
316
316
 
317
317
  await clearState(key, configDir)
318
318
 
319
- log(`\nDone. Restore with:\n npx telark restore ${state.id}`)
319
+ log(`\nDone. Restore with:\n npx telstore restore ${state.id}`)
320
320
 
321
321
  return { id: state.id, chunks: chunks.length }
322
322
  } finally {
package/src/config.js CHANGED
@@ -5,7 +5,7 @@ import path from 'node:path'
5
5
  const FILE_NAME = 'config.json'
6
6
 
7
7
  export function defaultConfigDir() {
8
- return path.join(os.homedir(), '.telark')
8
+ return path.join(os.homedir(), '.telstore')
9
9
  }
10
10
 
11
11
  export function configFile(dir = defaultConfigDir()) {
@@ -19,7 +19,7 @@ function isPlainObject(value) {
19
19
  // `config.json` is hand-editable now that the `config` command invites people into it,
20
20
  // which puts it in the same category as a manifest or a state file: believed only after it
21
21
  // has been checked. A `settings` that is not an object would make every lookup below it
22
- // return undefined, and telark would then run happily on built-in defaults while the
22
+ // return undefined, and telstore would then run happily on built-in defaults while the
23
23
  // user's own choices sat there ignored — so it is named and refused instead.
24
24
  export function checkConfigShape(raw, file) {
25
25
  if (!isPlainObject(raw)) {
@@ -32,7 +32,7 @@ export function checkConfigShape(raw, file) {
32
32
  if (raw.settings !== undefined && !isPlainObject(raw.settings)) {
33
33
  throw new Error(
34
34
  `"settings" in ${file} holds ${Array.isArray(raw.settings) ? 'a list' : typeof raw.settings}, ` +
35
- 'not a group of settings. telark will not guess what was meant — fix that entry, ' +
35
+ 'not a group of settings. telstore will not guess what was meant — fix that entry, ' +
36
36
  'or remove it to fall back to the defaults.',
37
37
  )
38
38
  }
@@ -57,7 +57,7 @@ export async function loadConfig(dir = defaultConfigDir()) {
57
57
  } catch (err) {
58
58
  throw new Error(
59
59
  `Corrupt config file: ${file} is not valid JSON (${err.message}). ` +
60
- 'Fix the syntax to keep your session, or delete the file and run "telark login" again.',
60
+ 'Fix the syntax to keep your session, or delete the file and run "telstore login" again.',
61
61
  )
62
62
  }
63
63
 
package/src/manifest.js CHANGED
@@ -8,7 +8,7 @@ export function newBackupId(now = new Date(), randomHex = () => randomBytes(3).t
8
8
  const yyyy = now.getUTCFullYear()
9
9
  const mm = String(now.getUTCMonth() + 1).padStart(2, '0')
10
10
  const dd = String(now.getUTCDate()).padStart(2, '0')
11
- return `telark-${yyyy}${mm}${dd}-${randomHex()}`
11
+ return `telstore-${yyyy}${mm}${dd}-${randomHex()}`
12
12
  }
13
13
 
14
14
  export function chunkFileName(id, i) {
@@ -72,7 +72,7 @@ export function manifestMessageIds(manifest) {
72
72
  throw new Error(
73
73
  `Manifest gives ${JSON.stringify(msgId)} as the message id of chunk ${index + 1}, ` +
74
74
  'which is not a message id. Deleting from this manifest could remove the wrong ' +
75
- 'messages, so telark is not deleting anything.',
75
+ 'messages, so telstore is not deleting anything.',
76
76
  )
77
77
  }
78
78
 
@@ -85,7 +85,7 @@ export function parseManifest(input) {
85
85
 
86
86
  if (manifest.v !== MANIFEST_VERSION) {
87
87
  throw new Error(
88
- `Manifest uses version ${manifest.v}, this build of telark only understands version ${MANIFEST_VERSION}.`,
88
+ `Manifest uses version ${manifest.v}, this build of telstore only understands version ${MANIFEST_VERSION}.`,
89
89
  )
90
90
  }
91
91
 
@@ -147,7 +147,7 @@ export function parseManifest(input) {
147
147
  // uniform: every chunk is chunkSize, except the last one which is the remainder.
148
148
  // A correct total with individually wrong sizes yields a file with a hole or
149
149
  // extra length while every per-chunk sha256 still matches — silently wrong data,
150
- // precisely what telark must never produce.
150
+ // precisely what telstore must never produce.
151
151
  manifest.chunks.forEach((chunk, index) => {
152
152
  const expected = Math.min(manifest.chunkSize, manifest.size - index * manifest.chunkSize)
153
153
  if (chunk.size !== expected) {
package/src/settings.js CHANGED
@@ -9,7 +9,7 @@ import { formatBytes } from './progress.js'
9
9
 
10
10
  export const DEFAULT_LIMIT = 20
11
11
 
12
- // The three keys telark writes for itself. Naming them separately is what lets the
12
+ // The three keys telstore writes for itself. Naming them separately is what lets the
13
13
  // unknown-key error say "managed by login" instead of listing a session as something the
14
14
  // user forgot to spell correctly.
15
15
  const MANAGED_BY_LOGIN = new Set(['session', 'apiId', 'apiHash'])
@@ -181,7 +181,7 @@ export function resolveSettings(options = {}, config = {}, { file = 'the config
181
181
  export function requireChat(values) {
182
182
  if (values.chat === null || values.chat === undefined) {
183
183
  throw new Error(
184
- 'No destination set — run "npx telark config chat @my_backups" to set one ' +
184
+ 'No destination set — run "npx telstore config chat @my_backups" to set one ' +
185
185
  '("config chat me" for Saved Messages), or pass --to to choose one for this run.',
186
186
  )
187
187
  }
package/src/state.js CHANGED
@@ -120,11 +120,11 @@ export async function listStates(configDir = defaultConfigDir()) {
120
120
  // delete needs the file a record came from, not just its contents — and the name of that
121
121
  // file is a hash of the path, size and mtime *inside* the record, so recomputing it would
122
122
  // be trusting an untrusted file to say where it lives. A hand-edited path yields a key that
123
- // names no file at all, clearState ignores a file that is not there, and telark reports a
123
+ // names no file at all, clearState ignores a file that is not there, and telstore reports a
124
124
  // record dropped that is still sitting on disk. Matching the id inside each file is the one
125
125
  // way that cannot point at the wrong one.
126
126
  //
127
- // Every record claiming the id is returned rather than the first: two of them means telark
127
+ // Every record claiming the id is returned rather than the first: two of them means telstore
128
128
  // cannot know which to drop, and that is the caller's decision to refuse, not ours to make
129
129
  // by picking one.
130
130
  export async function findStates(backupId, configDir = defaultConfigDir()) {
package/src/uploader.js CHANGED
@@ -13,7 +13,7 @@ const read = promisify(readCallback)
13
13
 
14
14
  // Telegram splits its upload API by file size: only files above 10MB may use the
15
15
  // "big" family. GramJS picks the same threshold (LARGE_FILE_THRESHOLD in
16
- // node_modules/telegram/client/uploads.js), and telark follows it.
16
+ // node_modules/telegram/client/uploads.js), and telstore follows it.
17
17
  export const LARGE_FILE_THRESHOLD = 10 * 1024 * 1024
18
18
 
19
19
  async function readExactly(fd, length, position) {