obsidian-headless 0.0.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 +174 -0
- package/btime/darwin-arm64/btime.node +0 -0
- package/btime/darwin-x64/btime.node +0 -0
- package/btime/win32-arm64/btime.node +0 -0
- package/btime/win32-ia32/btime.node +0 -0
- package/btime/win32-x64/btime.node +0 -0
- package/cli.js +17 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Obsidian CLI
|
|
2
|
+
|
|
3
|
+
Headless client for [Obsidian](https://obsidian.md) services.
|
|
4
|
+
Sync your vaults from the command line without the desktop app.
|
|
5
|
+
|
|
6
|
+
Requires Node.js 22 or later.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g obsidian-headless
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Authentication
|
|
15
|
+
|
|
16
|
+
Login interactively:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
ob login
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If already logged in, `ob login` displays your account info. To switch accounts, pass `--email` and/or `--password` to log in again.
|
|
23
|
+
|
|
24
|
+
### Environment variable
|
|
25
|
+
|
|
26
|
+
For non-interactive use (CI, scripts, servers), set the `OBSIDIAN_AUTH_TOKEN` environment variable instead of using `ob login`:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
export OBSIDIAN_AUTH_TOKEN="your-auth-token"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
When set, all commands that require authentication will use this token automatically.
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Login
|
|
38
|
+
ob login
|
|
39
|
+
|
|
40
|
+
# List your remote vaults
|
|
41
|
+
ob sync-list-remote
|
|
42
|
+
|
|
43
|
+
# Setup a vault for syncing
|
|
44
|
+
cd ~/vaults/my-vault
|
|
45
|
+
ob sync-setup --vault "My Vault"
|
|
46
|
+
|
|
47
|
+
# Run a one-time sync
|
|
48
|
+
ob sync
|
|
49
|
+
|
|
50
|
+
# Run continuous sync (watches for changes)
|
|
51
|
+
ob sync --continuous
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
### `ob login`
|
|
57
|
+
|
|
58
|
+
Login to your Obsidian account, or display login status if already logged in.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
ob login [--email <email>] [--password <password>] [--mfa <code>]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
All options are interactive when omitted — email and password are prompted, and 2FA is requested automatically if enabled on the account.
|
|
65
|
+
|
|
66
|
+
### `ob logout`
|
|
67
|
+
|
|
68
|
+
Logout and clear stored credentials.
|
|
69
|
+
|
|
70
|
+
### `ob sync-list-remote`
|
|
71
|
+
|
|
72
|
+
List all remote vaults available to your account, including shared vaults.
|
|
73
|
+
|
|
74
|
+
### `ob sync-list-local`
|
|
75
|
+
|
|
76
|
+
List locally configured vaults and their paths.
|
|
77
|
+
|
|
78
|
+
### `ob sync-create-remote`
|
|
79
|
+
|
|
80
|
+
Create a new remote vault.
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
ob sync-create-remote --name "Vault Name" [--encryption <standard|e2ee>] [--password <password>] [--region <region>]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
| Option | Description |
|
|
87
|
+
|---|----------------------------------------------------------|
|
|
88
|
+
| `--name` | Vault name (required) |
|
|
89
|
+
| `--encryption` | `standard` for managed encryption, `e2ee` for end-to-end |
|
|
90
|
+
| `--password` | End-to-end encryption password (prompted if omitted) |
|
|
91
|
+
| `--region` | Server region (automatic if omitted) |
|
|
92
|
+
|
|
93
|
+
### `ob sync-setup`
|
|
94
|
+
|
|
95
|
+
Set up sync between a local vault and a remote vault.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
ob sync-setup --vault <id-or-name> [--path <local-path>] [--password <password>] [--device-name <name>] [--config-dir <name>]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Option | Description |
|
|
102
|
+
|---|-----------------------------------------------------------------|
|
|
103
|
+
| `--vault` | Remote vault ID or name (required) |
|
|
104
|
+
| `--path` | Local directory (default: current directory) |
|
|
105
|
+
| `--password` | E2E encryption password (prompted if omitted) |
|
|
106
|
+
| `--device-name` | Device name to identify this client in the sync version history |
|
|
107
|
+
| `--config-dir` | Config directory name (default: `.obsidian`) |
|
|
108
|
+
|
|
109
|
+
### `ob sync`
|
|
110
|
+
|
|
111
|
+
Run sync for a configured vault.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
ob sync [--path <local-path>] [--continuous]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Option | Description |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `--path` | Local vault path (default: current directory) |
|
|
120
|
+
| `--continuous` | Run continuously, watching for changes |
|
|
121
|
+
|
|
122
|
+
### `ob sync-config`
|
|
123
|
+
|
|
124
|
+
View or change sync settings for a vault.
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
ob sync-config [--path <local-path>] [options]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Run with no options to display the current configuration.
|
|
131
|
+
|
|
132
|
+
| Option | Description |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `--path` | Local vault path (default: current directory) |
|
|
135
|
+
| `--conflict-strategy` | `merge` or `conflict` |
|
|
136
|
+
| `--file-types` | Attachment types to sync: `image`, `audio`, `video`, `pdf`, `unsupported` (comma-separated, empty to clear) |
|
|
137
|
+
| `--configs` | Config categories to sync: `app`, `appearance`, `appearance-data`, `hotkey`, `core-plugin`, `core-plugin-data`, `community-plugin`, `community-plugin-data` (comma-separated, empty to disable config syncing) |
|
|
138
|
+
| `--excluded-folders` | Folders to exclude (comma-separated, empty to clear) |
|
|
139
|
+
| `--device-name` | Device name to identify this client in the sync version history |
|
|
140
|
+
| `--config-dir` | Config directory name (default: `.obsidian`) |
|
|
141
|
+
|
|
142
|
+
### `ob sync-status`
|
|
143
|
+
|
|
144
|
+
Show sync status and configuration for a vault.
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
ob sync-status [--path <local-path>]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### `ob sync-unlink`
|
|
151
|
+
|
|
152
|
+
Disconnect a vault from sync and remove stored credentials.
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
ob sync-unlink [--path <local-path>]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Native modules
|
|
159
|
+
|
|
160
|
+
### btime
|
|
161
|
+
|
|
162
|
+
The `btime` directory contains a prebuilt native N-API addon for setting file creation time (birthtime) on Windows and macOS.
|
|
163
|
+
This is used when downloading files from the server to preserve their original creation timestamps.
|
|
164
|
+
|
|
165
|
+
Since it targets N-API version 3, the compiled `.node` binaries are ABI-stable and work across Node.js versions without recompilation.
|
|
166
|
+
|
|
167
|
+
On Linux, birthtime is not supported — the addon is not included and sync operates normally without it.
|
|
168
|
+
|
|
169
|
+
Prebuilt binaries are included for:
|
|
170
|
+
- `win32-x64`
|
|
171
|
+
- `win32-arm64`
|
|
172
|
+
- `win32-ia32`
|
|
173
|
+
- `darwin-x64`
|
|
174
|
+
- `darwin-arm64`
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/cli.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var wi=Object.create;var pt=Object.defineProperty;var vi=Object.getOwnPropertyDescriptor;var Ei=Object.getOwnPropertyNames;var Si=Object.getPrototypeOf,Di=Object.prototype.hasOwnProperty;var Pi=(s,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ei(e))!Di.call(s,r)&&r!==t&&pt(s,r,{get:()=>e[r],enumerable:!(i=vi(e,r))||i.enumerable});return s};var M=(s,e,t)=>(t=s!=null?wi(Si(s)):{},Pi(e||!s||!s.__esModule?pt(t,"default",{value:s,enumerable:!0}):t,s));var N=M(require("keytar")),$="obsidian-headless",gt="OBSIDIAN_AUTH_TOKEN",I={async getToken(){if(process.env[gt])return process.env[gt];try{return await N.getPassword($,"token")}catch(s){return console.error("Failed to get token from keychain:",s),null}},async setToken(s){try{await N.setPassword($,"token",s)}catch(e){throw new Error(`Failed to store token in keychain: ${e}`)}},async deleteToken(){try{await N.deletePassword($,"token")}catch{}},async getKey(s){let e;try{e=await N.getPassword($,`${s}:key`)}catch{}if(!e)return null;try{return JSON.parse(e)}catch{return null}},async setKey(s,e){try{await N.setPassword($,`${s}:key`,JSON.stringify(e))}catch(t){throw new Error(`Failed to store key in keychain: ${t}`)}},async deleteKey(s){try{await N.deletePassword($,`${s}:key`)}catch{}}};var dt=M(require("node:crypto")),P=M(require("node:fs")),B=M(require("node:os")),D=M(require("node:path")),ke=M(require("node:url"));global.nodeCrypto=dt.default;var ge=null;try{let s=D.default.join(__dirname,"btime",process.platform+"-"+process.arch);ge=require(D.default.join(s,"btime.node")).btime}catch{}function mt(){return ge!==null}function yt(s,e){if(!ge)return;let t=Buffer.alloc(Buffer.byteLength(s,"utf-8")+1);t.write(s,0,t.length-1,"utf-8"),t[t.length-1]=0,ge(t,e)}var Me=["image","audio","video","pdf","unsupported"],G=["image","audio","pdf","video"],Be=["app","appearance","appearance-data","hotkey","core-plugin","core-plugin-data","community-plugin","community-plugin-data"],z=["app","appearance","appearance-data","hotkey","core-plugin","core-plugin-data"];var Ft="obsidian-headless",bt=B.default.platform()==="linux"?D.default.join(process.env.XDG_CONFIG_HOME||D.default.join(B.default.homedir(),".config"),Ft):D.default.join(B.default.homedir(),"."+Ft);function ee(s){return D.default.join(bt,"sync",s)}function wt(s){return D.default.join(ee(s),"state.db")}function vt(s){return D.default.join(ee(s),"sync.log")}function Et(s){return D.default.join(ee(s),"config.json")}function Ue(s){let e=ee(s);return P.default.existsSync(e)||P.default.mkdirSync(e,{recursive:!0,mode:448}),e}function Ve(s){let e=Et(s);if(!P.default.existsSync(e))return null;try{let t=P.default.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function je(s,e){Ue(s);let t=Et(s);P.default.writeFileSync(t,JSON.stringify(e,null,2),{mode:384})}function St(s,e){return D.default.join(s,e,".sync.lock")}function Dt(s){let e=ee(s);P.default.existsSync(e)&&P.default.rmSync(e,{recursive:!0,force:!0})}function qe(){let s=D.default.join(bt,"sync");if(!P.default.existsSync(s))return[];let e=P.default.readdirSync(s,{withFileTypes:!0}),t=[];for(let i of e)if(i.isDirectory()){let r=D.default.join(s,i.name,"config.json");P.default.existsSync(r)&&t.push(i.name)}return t}function te(s){let e=D.default.resolve(s),t=qe();for(let i of t){let r=Ve(i);if(r&&D.default.resolve(r.vaultPath)===e)return r}return null}function Pt(s){let e=s.split(",").map(t=>t.trim().toLowerCase());for(let t of e)if(!Me.includes(t))throw new Error(`Invalid file type: "${t}". Valid values: ${Me.join(", ")}`);return e}function At(s){let e=s.split(",").map(t=>t.trim().toLowerCase());for(let t of e)if(!Be.includes(t))throw new Error(`Invalid config: "${t}". Valid values: ${Be.join(", ")}`);return e}function Ke(s){if(s){if(!s.startsWith(".")||s.includes("/")||s.includes("\\"))throw new Error(`Invalid config directory: "${s}". Must be a dotfolder name (e.g. .obsidian).`);return s}}function We(){let s=B.default.hostname(),e=B.default.platform();return`${s} (${e==="darwin"?"macOS":e==="win32"?"Windows":e==="linux"?"Linux":e})`}function _t(s){let e=D.default.dirname(s);P.default.existsSync(e)||P.default.mkdirSync(e,{recursive:!0});let t=P.default.createWriteStream(s,{flags:"a"}),i=console.log,r=console.warn,n=console.error,o=console.debug;function a(...l){let c=new Date().toISOString(),u=l.map(p=>typeof p=="object"?JSON.stringify(p):String(p)).join(" ");t.write(`[${c}] ${u}
|
|
3
|
+
`)}return console.log=(...l)=>{i(...l),a(...l)},console.warn=(...l)=>{r(...l),a(...l)},console.error=(...l)=>{n(...l),a(...l)},console.debug=(...l)=>{o(...l),a(...l)},()=>{console.log=i,console.warn=r,console.error=n,console.debug=o,t.end()}}var Tt=M(require("better-sqlite3"));var Ai=1,de=class{constructor(e){let t=D.default.dirname(e);P.default.existsSync(t)||P.default.mkdirSync(t,{recursive:!0}),this.db=new Tt.default(e),this.db.pragma("journal_mode = WAL"),this.db.exec("CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT );"),this.db.exec("CREATE TABLE IF NOT EXISTS local_files (path TEXT PRIMARY KEY, data TEXT NOT NULL);"),this.db.exec("CREATE TABLE IF NOT EXISTS server_files (path TEXT PRIMARY KEY, data TEXT NOT NULL);"),this.db.exec("CREATE TABLE IF NOT EXISTS pending_files (uid INTEGER PRIMARY KEY, path TEXT, data TEXT NOT NULL)"),this.stmts={getMeta:this.db.prepare("SELECT value FROM meta WHERE key = ?"),setMeta:this.db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)"),getLocalFile:this.db.prepare("SELECT data FROM local_files WHERE path = ?"),setLocalFile:this.db.prepare("INSERT OR REPLACE INTO local_files (path, data) VALUES (?, ?)"),deleteLocalFile:this.db.prepare("DELETE FROM local_files WHERE path = ?"),getAllLocalFiles:this.db.prepare("SELECT path, data FROM local_files"),getServerFile:this.db.prepare("SELECT data FROM server_files WHERE path = ?"),setServerFile:this.db.prepare("INSERT OR REPLACE INTO server_files (path, data) VALUES (?, ?)"),deleteServerFile:this.db.prepare("DELETE FROM server_files WHERE path = ?"),getAllServerFiles:this.db.prepare("SELECT path, data FROM server_files"),addPendingFile:this.db.prepare("INSERT OR REPLACE INTO pending_files (uid, path, data) VALUES (?, ?, ?)"),deletePendingFile:this.db.prepare("DELETE FROM pending_files WHERE path = ?"),getPendingFiles:this.db.prepare("SELECT data FROM pending_files ORDER BY uid")},this.getMetaValue("schema_version")===null&&this.setMetaValue("schema_version",String(Ai))}getMetaValue(e){let t=this.stmts.getMeta.get(e);return t?t.value:null}setMetaValue(e,t){this.stmts.setMeta.run(e,t)}getVersion(){let e=this.getMetaValue("version");return e?parseInt(e,10):0}setVersion(e){this.setMetaValue("version",String(e))}isInitial(){return this.getMetaValue("initial")!=="false"}setInitial(e){this.setMetaValue("initial",e?"true":"false")}getLocalFile(e){let t=this.stmts.getLocalFile.get(e);if(!t)return null;try{return JSON.parse(t.data)}catch{return null}}setLocalFile(e){this.stmts.setLocalFile.run(e.path,JSON.stringify(e))}deleteLocalFile(e){this.stmts.deleteLocalFile.run(e)}getAllLocalFiles(){let e=this.stmts.getAllLocalFiles.all(),t={};for(let i of e)try{t[i.path]=JSON.parse(i.data)}catch{}return t}getServerFile(e){let t=this.stmts.getServerFile.get(e);if(!t)return null;try{return JSON.parse(t.data)}catch{return null}}setServerFile(e){this.stmts.setServerFile.run(e.path,JSON.stringify(e))}deleteServerFile(e){this.stmts.deleteServerFile.run(e)}getAllServerFiles(){let e=this.stmts.getAllServerFiles.all(),t={};for(let i of e)try{t[i.path]=JSON.parse(i.data)}catch{}return t}addPendingFile(e){this.stmts.addPendingFile.run(e.uid,e.path,JSON.stringify(e))}deletePendingFile(e){this.stmts.deletePendingFile.run(e)}getPendingFiles(){let e=this.stmts.getPendingFiles.all(),t=[];for(let i of e)try{t.push(JSON.parse(i.data))}catch{}return t}close(){this.db.close()}};var ie=()=>typeof activeWindow<"u"?activeWindow:global;function xt(s,e=0,t=!1){let i=null,r=null,n=null,o=0,a=0,l=ie(),c=function(){let d=r,F=n;return r=null,n=null,s.apply(d,F)},u=function(){if(o){let d=Date.now();if(d<o){l=ie(),i=l.setTimeout(u,o-d),o=0;return}}a=0,i=null,c()},p=function(...d){r=this,n=d;let F=Date.now();return i?t?o=a=F+e:l!==ie()&&a<=F&&(l.clearTimeout(i),l=ie(),i=l.setTimeout(u,0)):(l=ie(),a=F+e,i=l.setTimeout(u,e)),p};return p.cancel=function(){return i&&(l.clearTimeout(i),i=null),p},p.run=function(){if(i)return l.clearTimeout(i),i=null,c()},p}var _i=/\u00A0|\u202F/g;function He(s){return s.replace(_i," ")}function O(s){let e=s.lastIndexOf("/");return e===-1?s:s.slice(e+1)}function U(s){let e=s.lastIndexOf("/");return e===-1?"":s.slice(0,e)}function $e(s){for(;s;){if(O(s).startsWith("."))return!0;s=U(s)}return!1}function It(s){let e=O(s),t=e.lastIndexOf(".");return t===-1||t===e.length-1||t===0?e:e.substr(0,t)}function Lt(s){let e=s.lastIndexOf(".");return e===-1||e===s.length-1||e===0?s:s.substr(0,e)}function T(s){let e=s.lastIndexOf(".");return e===-1||e===s.length-1||e===0?"":s.substr(e+1).toLowerCase()}function Q(s){return He(X(s)).normalize("NFC")}function X(s){return s=s.replace(/([\\/])+/g,"/").replace(/(^\/+|\/+$)/g,""),s===""&&(s="/"),s}function re(s){return s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength)}function Ge(s){return Buffer.from(s)}var Ti=5;function xi(s,e){if(e.isFile())return{type:"file",realpath:s,ctime:Math.round(e.birthtimeMs),mtime:Math.round(e.mtimeMs),size:e.size};if(e.isDirectory())return{type:"folder",realpath:s}}function Qe(s){return{ctime:Math.round(s.birthtimeMs),mtime:Math.round(s.mtimeMs),size:s.size}}var Ct=typeof process<"u"?process:null,Ot=Ct?Ct.platform:"",Rt=Ot==="darwin",Nt=Ot==="win32",me=Rt||Nt,ye=class{constructor(e,t,i,r,n,o,a){this.thingsHappening=xt(this.kill.bind(this),60*1e3,!0);this.resourcePathPrefix=o,this.basePath=a,this.fs=e,this.fsPromises=e.promises,this.path=t,this.url=i,this.trash=r,this.files={},this.promise=Promise.resolve(),this.watchers={},this.handler=null,this.btime=n,this.insensitive=Rt||Nt;try{this.testInsensitive()}catch(l){console.error(l)}}testInsensitive(){let{fs:e,path:t,basePath:i}=this,r=t.join(i,".OBSIDIANTEST"),n=t.join(i,".OBSIDIANTEST".toLowerCase());e.existsSync(r)&&e.unlinkSync(r),e.writeFileSync(r,"","utf8"),this.insensitive=e.existsSync(n),e.unlinkSync(r)}getName(){return this.path.basename(this.basePath)}getBasePath(){return this.basePath}async listAll(){this.files={},this.files["/"]={type:"folder",realpath:"/"},await this.listRecursive("")}async listRecursive(e){let t=this.getFullRealPath(e),i=await this.fsPromises.readdir(t);this.thingsHappening();let r=[];for(let n of i)r.push(this.listRecursiveChild(e,n));await Promise.all(r)}async listRecursiveChild(e,t){let i=X(e===""?t:e+"/"+t),r=Q(i);if(this.trigger("raw",r),$e(r))return await this.reconcileDeletion(i,r);try{await this.reconcileFileInternal(i,r)}catch(n){if(n.code==="ENOENT")await this.reconcileDeletion(i,r,!0);else throw n}}mkdir(e){return this.queue(async()=>{let t=this.getFullPath(e);await this.fsPromises.mkdir(t,{recursive:!0}),await this.reconcileInternalFile(e)})}trashSystem(e){return this.queue(async()=>{let t=this.getFullPath(e);return this.trash(t)?(await this.reconcileInternalFile(e),!0):!1})}trashLocal(e){return this.queue(async()=>{let t=this.getFullPath(e),i=this.getFullPath(".trash");await this.fsPromises.mkdir(i,{recursive:!0});let r=this.path.extname(t),n=this.path.basename(t,r),o=this.path.join(i,n+r),a=1;for(;await this._exists(o);)a++,o=this.path.join(i,n+" "+a+r);await this.fsPromises.rename(t,o),await this.reconcileInternalFile(e)})}rmdir(e,t){return this.queue(async()=>{let i=this.getFullPath(e);await this.fsPromises.rm(i,{maxRetries:Ti,recursive:t}),await this.reconcileInternalFile(e)})}read(e){return this.queue(async()=>{let t=this.getFullPath(e);try{return this.fsPromises.readFile(t,"utf8")}catch(i){throw this.queue(()=>this.reconcileInternalFile(e)),i}})}readBinary(e){return this.queue(async()=>{let t=this.getFullPath(e);try{let i=await this.fsPromises.readFile(t);return re(i)}catch(i){throw this.queue(()=>this.reconcileInternalFile(e)),i}})}write(e,t,i){return this.queue(async()=>{let r=this.getFullPath(e);try{await this.fsPromises.writeFile(r,t,"utf8"),await this.applyWriteOptions(r,i)}finally{await this.reconcileInternalFile(e)}})}writeBinary(e,t,i){return this.queue(async()=>{let r=this.getFullPath(e),n=Ge(t);try{await this.fsPromises.writeFile(r,n),await this.applyWriteOptions(r,i)}finally{await this.reconcileInternalFile(e)}})}append(e,t,i){return this.queue(async()=>{let r=this.getFullPath(e);try{await this.fsPromises.appendFile(r,t,"utf8"),await this.applyWriteOptions(r,i)}finally{await this.reconcileInternalFile(e)}})}appendBinary(e,t,i){return this.queue(async()=>{let r=this.getFullPath(e),n=Ge(t);try{await this.fsPromises.appendFile(r,n),await this.applyWriteOptions(r,i)}finally{await this.reconcileInternalFile(e)}})}process(e,t,i){return this.queue(async()=>{let r=this.getFullPath(e),n=await this.fsPromises.readFile(r,"utf8"),o=t(n);if(o===n)return n;try{await this.fsPromises.writeFile(r,o,"utf8"),await this.applyWriteOptions(r,i)}finally{await this.reconcileInternalFile(e)}return o})}async applyWriteOptions(e,t){if(!t)return;let{ctime:i,mtime:r,immediate:n}=t;i&&this.btime(e,i),r&&await this.fsPromises.utimes(e,r/1e3,r/1e3),n&&n()}getResourcePath(e){let t=this.getFullPath(e),i=0,r=this.files[e];r&&r.type==="file"&&(i=r.mtime),i||(i=Date.now());let n=this.url.pathToFileURL(t).href;return n.startsWith("file:///")?n=n.substring(8):n.startsWith("file://")&&(n="%5C%5C"+n.substring(7)),this.resourcePathPrefix+n+"?"+i}getFilePath(e){let t=this.getFullPath(e);return this.url.pathToFileURL(t).toString()}remove(e){return this.queue(async()=>{let t=this.getFullPath(e);await this.fsPromises.unlink(t),await this.reconcileInternalFile(e)})}update(e){return this.queue(async()=>{await this.reconcileInternalFile(e)})}async rename(e,t){e!==t&&await this.queue(async()=>{let i=this.getFullPath(e),r=this.getFullPath(t);if(await this._exists(r,!1)&&!(this.insensitive&&e.toLowerCase()===t.toLowerCase()))throw new Error("Destination file already exists!");let n=this.files[e],o=n?n.realpath:null;if(await this.fsPromises.rename(i,r),!n)return;delete this.files[e];let a=this.getRealPath(t);if(n.realpath=a,this.files[t]=n,this.trigger("renamed",t,e),n.type==="folder"&&this.watchers.hasOwnProperty(e)&&(this.stopWatchPath(e),await this.startWatchPath(t)),n.type==="folder"){for(let c in this.files)if(this.files.hasOwnProperty(c)&&c.startsWith(e+"/")){let u=c.slice(e.length),p=t+u,d=this.files[c];delete this.files[c];let F=d.realpath.slice(o.length);d.realpath=a+F,this.files[p]=d,this.trigger("renamed",p,c)}}})}async copyRecursive(e,t){let i=await this.fsPromises.stat(e);if(i.isFile()&&await this.fsPromises.copyFile(e,t,this.fs.constants.COPYFILE_EXCL),i.isDirectory()){await this.fsPromises.mkdir(t,{recursive:!0});let r=await this.fsPromises.readdir(e);for(let n of r){let o=this.path.join(e,n),a=this.path.join(t,n);await this.copyRecursive(o,a)}}}async copy(e,t){await this.queue(async()=>{let i=this.getFullPath(e),r=this.getFullPath(t);await this.copyRecursive(i,r),await this.reconcileInternalFile(t)})}async exists(e,t){return this.queue(()=>{let i=this.getFullPath(e);return this._exists(i,t)})}async _exists(e,t){try{await this.fsPromises.access(e)}catch{return!1}if(t&&this.insensitive){let i=this.path.dirname(e),r=this.path.basename(e);if((await this.fsPromises.readdir(i)).indexOf(r)===-1)return!1}return!0}async stat(e){return this.queue(async()=>{let t=this.getFullPath(e);try{let i=await this.fsPromises.stat(t);if(i.isFile())return{type:"file",...Qe(i)};if(i.isDirectory())return{type:"folder",...Qe(i)}}catch(i){if(i.code!=="ENOENT")throw i}return null})}async list(e){return this.queue(async()=>{let t=this.getFullPath(e),i=await this.fsPromises.readdir(t),r={folders:[],files:[]};for(let n of i){let o=X(e===""?n:e+"/"+n),a=Q(o),l=await this.fsPromises.stat(this.getFullRealPath(o));l.isFile()&&r.files.push(a),l.isDirectory()&&r.folders.push(a)}return r})}async watch(e){return this.stopWatch(),this.handler=e,await this.startWatchPath("/"),this.queue(()=>this.listAll())}async watchHiddenRecursive(e){if(!me)try{let t=this.getFullRealPath(e),i=await this.fsPromises.readdir(t);await this.startWatchPath(e);for(let r of i)try{let n=X(e===""?r:e+"/"+r),o=this.getFullRealPath(n);(await this.fsPromises.lstat(o)).isDirectory()&&await this.watchHiddenRecursive(n)}catch{}}catch{}}stopWatch(){for(let e in this.watchers)this.watchers.hasOwnProperty(e)&&this.stopWatchPath(e);this.handler=null}async startWatchPath(e){let t=this.getRealPath(e);if(this.watchers[e])return;let i=this.getFullPath(e),r=i;try{r=await this.fsPromises.realpath(i)}catch(o){o.code!=="ENOENT"&&console.error(o)}let n=this.fs.watch(i,{persistent:!1,encoding:"utf8",recursive:me});n.on("change",(o,a)=>{let l=t==="/"?a:t+"/"+a;this.onFileChange(l)}),n.on("error",()=>{this.onFileChange(t)}),this.watchers[e]={watcher:n,resolvedPath:r}}stopWatchPath(e){let t=this.watchers[e];t&&(t.watcher.close(),delete this.watchers[e])}onFileChange(e){e&&(e=X(e),setTimeout(()=>{let t=Q(e);this.queue(()=>this.reconcileFile(e,t,!1))},0))}async reconcileInternalFile(e){return this.reconcileFile(this.getRealPath(e),e)}async reconcileFile(e,t,i=!0){if(this.trigger("raw",t),$e(t))return await this.reconcileDeletion(e,t,i);let r=U(t);r&&r!=="/"&&(this.files[t]||await this.reconcileFile(U(e),r,i));try{let n=this.getFullRealPath(e);if(this.insensitive){let o=this.path.dirname(n),a=this.path.basename(t),l=await this.fsPromises.readdir(o);if(l=l.map(c=>He(c).normalize("NFC")),l.indexOf(a)===-1)return await this.reconcileDeletion(e,t,i)}await this.reconcileFileInternal(e,t)}catch(n){n.code==="ENOENT"?await this.reconcileDeletion(e,t,i):console.error(n)}}async reconcileFileInternal(e,t){let i=this.getFullRealPath(e),r=await this.fsPromises.lstat(i);r.isFile()?await this.reconcileFileCreation(e,t,r):r.isDirectory()?await this.reconcileFolderCreation(e,t):r.isSymbolicLink()&&await this.reconcileSymbolicLinkCreation(e,t)}async reconcileFileCreation(e,t,i){let r=Qe(i),n=this.files[t];if(n)if(n.realpath=e,n.type==="file"){(n.mtime!==Math.round(i.mtimeMs)||n.size!==i.size)&&(n.mtime=Math.round(i.mtimeMs),n.size=i.size,this.trigger("raw",t,t,r),this.trigger("modified",t,t,r));return}else this.removeFile(t);this.files[t]=xi(e,i),this.trigger("file-created",t,t,r)}async reconcileFolderCreation(e,t){this.files.hasOwnProperty(t)?this.files[t].realpath=e:(this.files[t]={type:"folder",realpath:e},this.trigger("folder-created",t),me||await this.startWatchPath(t),await this.listRecursive(e))}async reconcileSymbolicLinkCreation(e,t){let i=this.getFullRealPath(e),r;try{r=await this.fsPromises.realpath(i)}catch{return}let n=this.path.sep,o=this.watchers;if(o.hasOwnProperty(t))o[t].resolvedPath=r;else for(let l in o)if(o.hasOwnProperty(l)&&l!==t){let c=o[l].resolvedPath;if(r===c||c.startsWith(r+n)||r.startsWith(c+n))return}let a=await this.fsPromises.stat(i);a.isFile()?await this.reconcileFileCreation(e,t,a):a.isDirectory()&&(me&&await this.startWatchPath(t),await this.reconcileFolderCreation(e,t))}async reconcileDeletion(e,t,i=!0){if(t==="/"){this.trigger("closed",t);return}this.stopWatchPath(t);let r=this.files[t];if(r){if(!i){setTimeout(()=>{this.queue(()=>this.reconcileFile(e,t))},100);return}if(r.type==="folder")for(let n in this.files)this.files.hasOwnProperty(n)&&n.startsWith(t+"/")&&this.removeFile(n);this.removeFile(t)}}trigger(e,t,i,r){this.handler&&this.handler(e,t,i,r)}getRealPath(e){let t=e;for(;t;){if(this.files.hasOwnProperty(t)){let i=e.substr(t.length);return this.files[t].realpath+i}t=U(t)}return e}getFullPath(e){let t=this.getRealPath(e);return this.getFullRealPath(t)}getFullRealPath(e){return this.path.join(this.basePath,e)}kill(){this.killLastAction&&(this.killLastAction(new Error("File system operation timed out.")),this.killLastAction=null)}queue(e){let t=()=>{let r=new Promise((n,o)=>this.killLastAction=o);return this.thingsHappening(),Promise.race([r,e()])},i=this.promise.then(t,t);return this.promise=i,i}removeFile(e){let t=this.files[e];delete this.files[e],t&&(t.type==="file"?this.trigger("file-removed",e):t.type==="folder"&&this.trigger("folder-removed",e))}static async readLocalFile(e){let t=require;if(t){let i=t("fs");return re(await i.promises.readFile(e))}return null}static async mkdir(e){let t=require;return t?await t("fs").promises.mkdir(e,{recursive:!0}):null}};var kt=/[^a-zA-Z0-9]/,Mt=/\s/,Bt=/[\r\n]/,Ii=/\n\r?\n$/,Li=/^\r?\n\r?\n/;function V(s,e){return s<e?s:e}function j(s,e){return s>e?s:e}var Ci=/\W/;function Oi(s,e){let t=s.length;for(let i=e;i<t;i+=1)if(Ci.test(s[i]))return i;return-1}function Ri(s,e){let t=0,i=-1;for(;i<s.length-1;)if(i=Oi(s,t),i!==-1){if(t!==i){let n=s.substring(t,i);e(n)}let r=s[i];e(r),t=i+1}else{let r=s.substring(t,s.length);e(r),i=s.length;break}}var Fe=class{constructor(){this.diffTimeout=1;this.diffEditCost=4;this.matchThreshold=.5;this.matchDistance=1e3;this.patchDeleteThreshold=.5;this.patchMargin=4;this.matchMaxBits=32}diff_main(e,t,i,r){typeof r>"u"&&(this.diffTimeout<=0?r=Number.MAX_VALUE:r=Date.now()+this.diffTimeout*1e3);let n=r;if(e==null||t==null)throw new Error("Null input. (diff_main)");if(e===t)return e?[[0,e]]:[];typeof i>"u"&&(i=!0);let o=i,a=this.diff_commonPrefix(e,t),l=e.substring(0,a);e=e.substring(a),t=t.substring(a),a=this.diff_commonSuffix(e,t);let c=e.substring(e.length-a);e=e.substring(0,e.length-a),t=t.substring(0,t.length-a);let u=this.diff_compute_(e,t,o,n);return l&&u.unshift([0,l]),c&&u.push([0,c]),this.diff_cleanupMerge(u),u}diff_lineMode(e,t){let{chars1:i,chars2:r,lineArray:n}=this.diff_linesToChars_(e,t),o=this.diff_main(i,r,!1);return this.diff_charsToLines_(o,n),o}diff_wordMode(e,t){let{chars1:i,chars2:r,lineArray:n}=this.diff_wordsToChars_(e,t),o=this.diff_main(i,r,!1);return this.diff_charsToLines_(o,n),o}diff_wordsToChars_(e,t){let i=[],r={};i[0]="";let n=l=>{let c="",u=i.length;return Ri(l,p=>{(r.hasOwnProperty?r.hasOwnProperty(p):r[p]!==void 0)?c+=String.fromCharCode(r[p]):(c+=String.fromCharCode(u),r[p]=u,i[u++]=p)}),c},o=n(e),a=n(t);return{chars1:o,chars2:a,lineArray:i}}diff_commonPrefix(e,t){if(!e||!t||e.charAt(0)!==t.charAt(0))return 0;let i=0,r=V(e.length,t.length),n=r,o=0;for(;i<n;)e.substring(o,n)===t.substring(o,n)?(i=n,o=i):r=n,n=Math.floor((r-i)/2+i);return n}diff_commonSuffix(e,t){if(!e||!t||e.charAt(e.length-1)!==t.charAt(t.length-1))return 0;let i=0,r=V(e.length,t.length),n=r,o=0;for(;i<n;)e.substring(e.length-n,e.length-o)===t.substring(t.length-n,t.length-o)?(i=n,o=i):r=n,n=Math.floor((r-i)/2+i);return n}diff_cleanupSemantic(e){let t=!1,i=[],r=0,n=null,o=0,a=0,l=0,c=0,u=0;for(;o<e.length;)e[o][0]===0?(i[r++]=o,a=c,l=u,c=0,u=0,n=e[o][1]):(e[o][0]===1?c+=e[o][1].length:u+=e[o][1].length,n&&n.length<=j(a,l)&&n.length<=j(c,u)&&(e.splice(i[r-1],0,[-1,n]),e[i[r-1]+1][0]=1,r--,r--,o=r>0?i[r-1]:-1,a=0,l=0,c=0,u=0,n=null,t=!0)),o++;for(t&&this.diff_cleanupMerge(e),this.diff_cleanupSemanticLossless(e),o=1;o<e.length;){if(e[o-1][0]===-1&&e[o][0]===1){let p=e[o-1][1],d=e[o][1],F=this.diff_commonOverlap_(p,d),b=this.diff_commonOverlap_(d,p);F>=b?(F>=p.length/2||F>=d.length/2)&&(e.splice(o,0,[0,d.substring(0,F)]),e[o-1][1]=p.substring(0,p.length-F),e[o+1][1]=d.substring(F),o++):(b>=p.length/2||b>=d.length/2)&&(e.splice(o,0,[0,p.substring(0,b)]),e[o-1][0]=1,e[o-1][1]=d.substring(0,d.length-b),e[o+1][0]=-1,e[o+1][1]=p.substring(b),o++),o++}o++}}diff_cleanupSemanticLossless(e){let t=1;for(;t<e.length-1;){if(e[t-1][0]===0&&e[t+1][0]===0){let i=e[t-1][1],r=e[t][1],n=e[t+1][1],o=this.diff_commonSuffix(i,r);if(o){let p=r.substring(r.length-o);i=i.substring(0,i.length-o),r=p+r.substring(0,r.length-o),n=p+n}let a=i,l=r,c=n,u=this.diff_cleanupSemanticScore_(i,r)+this.diff_cleanupSemanticScore_(r,n);for(;r.charAt(0)===n.charAt(0);){i+=r.charAt(0),r=r.substring(1)+n.charAt(0),n=n.substring(1);let p=this.diff_cleanupSemanticScore_(i,r)+this.diff_cleanupSemanticScore_(r,n);p>=u&&(u=p,a=i,l=r,c=n)}e[t-1][1]!==a&&(a?e[t-1][1]=a:(e.splice(t-1,1),t--),e[t][1]=l,c?e[t+1][1]=c:(e.splice(t+1,1),t--))}t++}}diff_cleanupEfficiency(e){let t=!1,i=[],r=0,n=null,o=0,a=!1,l=!1,c=!1,u=!1;for(;o<e.length;)e[o][0]===0?(e[o][1].length<this.diffEditCost&&(c||u)?(i[r++]=o,a=c,l=u,n=e[o][1]):(r=0,n=null),c=u=!1):(e[o][0]===-1?u=!0:c=!0,n&&(a&&l&&c&&u||n.length<this.diffEditCost/2&&Number(a)+Number(l)+Number(c)+Number(u)===3)&&(e.splice(i[r-1],0,[-1,n]),e[i[r-1]+1][0]=1,r--,n=null,a&&l?(c=u=!0,r=0):(r--,o=r>0?i[r-1]:-1,c=u=!1),t=!0)),o++;t&&this.diff_cleanupMerge(e)}diff_cleanupMerge(e){e.push([0,""]);let t=0,i=0,r=0,n="",o="",a;for(;t<e.length;)switch(e[t][0]){case 1:r++,o+=e[t][1],t++;break;case-1:i++,n+=e[t][1],t++;break;case 0:i+r>1?(i!==0&&r!==0&&(a=this.diff_commonPrefix(o,n),a!==0&&(t-i-r>0&&e[t-i-r-1][0]===0?e[t-i-r-1][1]+=o.substring(0,a):(e.splice(0,0,[0,o.substring(0,a)]),t++),o=o.substring(a),n=n.substring(a)),a=this.diff_commonSuffix(o,n),a!==0&&(e[t][1]=o.substring(o.length-a)+e[t][1],o=o.substring(0,o.length-a),n=n.substring(0,n.length-a))),t-=i+r,e.splice(t,i+r),n.length&&(e.splice(t,0,[-1,n]),t++),o.length&&(e.splice(t,0,[1,o]),t++),t++):t!==0&&e[t-1][0]===0?(e[t-1][1]+=e[t][1],e.splice(t,1)):t++,r=0,i=0,n="",o="";break}e[e.length-1][1]===""&&e.pop();let l=!1;for(t=1;t<e.length-1;)e[t-1][0]===0&&e[t+1][0]===0&&(e[t][1].substring(e[t][1].length-e[t-1][1].length)===e[t-1][1]?(e[t][1]=e[t-1][1]+e[t][1].substring(0,e[t][1].length-e[t-1][1].length),e[t+1][1]=e[t-1][1]+e[t+1][1],e.splice(t-1,1),l=!0):e[t][1].substring(0,e[t+1][1].length)===e[t+1][1]&&(e[t-1][1]+=e[t+1][1],e[t][1]=e[t][1].substring(e[t+1][1].length)+e[t+1][1],e.splice(t+1,1),l=!0)),t++;l&&this.diff_cleanupMerge(e)}diff_xIndex(e,t){let i=0,r=0,n=0,o=0,a;for(a=0;a<e.length&&(e[a][0]!==1&&(i+=e[a][1].length),e[a][0]!==-1&&(r+=e[a][1].length),!(i>t));a++)n=i,o=r;return e.length!==a&&e[a][0]===-1?o:o+(t-n)}diff_prettyHtml(e){let t=[],i=/&/g,r=/</g,n=/>/g,o=/\n/g;for(let a=0;a<e.length;a++){let l=e[a][0],u=e[a][1].replace(i,"&").replace(r,"<").replace(n,">").replace(o,"¶<br>");switch(l){case 1:t[a]='<ins style="background:#e6ffe6;">'+u+"</ins>";break;case-1:t[a]='<del style="background:#ffe6e6;">'+u+"</del>";break;case 0:t[a]="<span>"+u+"</span>";break}}return t.join("")}diff_text1(e){let t=[];for(let i=0;i<e.length;i++)e[i][0]!==1&&(t[i]=e[i][1]);return t.join("")}diff_text2(e){let t=[];for(let i=0;i<e.length;i++)e[i][0]!==-1&&(t[i]=e[i][1]);return t.join("")}diff_levenshtein(e){let t=0,i=0,r=0;for(let n=0;n<e.length;n++){let o=e[n][0],a=e[n][1];switch(o){case 1:i+=a.length;break;case-1:r+=a.length;break;case 0:t+=j(i,r),i=0,r=0;break}}return t+=j(i,r),t}diff_toDelta(e){let t=[];for(let i=0;i<e.length;i++)switch(e[i][0]){case 1:t[i]="+"+encodeURI(e[i][1]);break;case-1:t[i]="-"+e[i][1].length;break;case 0:t[i]="="+e[i][1].length;break}return t.join(" ").replace(/%20/g," ")}diff_fromDelta(e,t){let i=[],r=0,n=0,o=t.split(/\t/g);for(let a=0;a<o.length;a++){let l=o[a].substring(1);switch(o[a].charAt(0)){case"+":try{i[r++]=[1,decodeURI(l)]}catch{throw new Error("Illegal escape in diff_fromDelta: "+l)}break;case"-":case"=":let c=parseInt(l,10);if(isNaN(c)||c<0)throw new Error("Invalid number in diff_fromDelta: "+l);let u=e.substring(n,n+=c);o[a].charAt(0)==="="?i[r++]=[0,u]:i[r++]=[-1,u];break;default:if(o[a])throw new Error("Invalid diff operation in diff_fromDelta: "+o[a])}}if(n!==e.length)throw new Error("Delta length ("+n+") does not equal source text length ("+e.length+")");return i}match_main(e,t,i){if(e==null||t==null||i==null)throw new Error("Null input. (match_main)");return i=j(0,V(i,e.length)),e===t?0:e.length?e.substring(i,i+t.length)===t?i:this.match_bitap_(e,t,i):-1}patch_make(e,t,i){let r,n;if(typeof e=="string"&&typeof t=="string"&&typeof i>"u")r=e,n=this.diff_main(r,t,!0),n.length>2&&(this.diff_cleanupSemantic(n),this.diff_cleanupEfficiency(n));else if(e&&typeof e=="object"&&typeof t>"u"&&typeof i>"u")n=e,r=this.diff_text1(n);else if(typeof e=="string"&&t&&typeof t=="object"&&typeof i>"u")r=e,n=t;else if(typeof e=="string"&&typeof t=="string"&&i&&typeof i=="object")r=e,n=i;else throw new Error("Unknown call format to patch_make");if(n.length===0)return[];let o=[],a=new q,l=0,c=0,u=0,p=r,d=r;for(let F=0;F<n.length;F++){let b=n[F][0],y=n[F][1];switch(!l&&b!==0&&(a.start1=c,a.start2=u),b){case 1:a.diffs[l++]=n[F],a.length2+=y.length,d=d.substring(0,u)+y+d.substring(u);break;case-1:a.length1+=y.length,a.diffs[l++]=n[F],d=d.substring(0,u)+d.substring(u+y.length);break;case 0:y.length<=2*this.patchMargin&&l&&n.length!==F+1?(a.diffs[l++]=n[F],a.length1+=y.length,a.length2+=y.length):y.length>=2*this.patchMargin&&l&&(this.patch_addContext_(a,p),o.push(a),a=new q,l=0,p=d,c=u);break}b!==1&&(c+=y.length),b!==-1&&(u+=y.length)}return l&&(this.patch_addContext_(a,p),o.push(a)),o}patch_deepCopy(e){let t=[];for(let i=0;i<e.length;i++){let r=e[i],n=new q;for(let o=0;o<r.diffs.length;o++)n.diffs[o]=[r.diffs[o][0],r.diffs[o][1]];n.start1=r.start1,n.start2=r.start2,n.length1=r.length1,n.length2=r.length2,t[i]=n}return t}patch_apply(e,t){if(e.length===0)return[t,[]];e=this.patch_deepCopy(e);let i=this.patch_addPadding(e);t=i+t+i,this.patch_splitMax(e);let r=0,n=[];for(let o=0;o<e.length;o++){let a=e[o].start2+r,l=this.diff_text1(e[o].diffs),c,u=-1;if(l.length>this.matchMaxBits?(c=this.match_main(t,l.substring(0,this.matchMaxBits),a),c!==-1&&(u=this.match_main(t,l.substring(l.length-this.matchMaxBits),a+l.length-this.matchMaxBits),(u===-1||c>=u)&&(c=-1))):c=this.match_main(t,l,a),c===-1)n[o]=!1,r-=e[o].length2-e[o].length1;else{n[o]=!0,r=c-a;let p;if(u===-1?p=t.substring(c,c+l.length):p=t.substring(c,u+this.matchMaxBits),l===p)t=t.substring(0,c)+this.diff_text2(e[o].diffs)+t.substring(c+l.length);else{let d=this.diff_main(l,p,!1);if(l.length>this.matchMaxBits&&this.diff_levenshtein(d)/l.length>this.patchDeleteThreshold)n[o]=!1;else{this.diff_cleanupSemanticLossless(d);let F=0,b=0;for(let y=0;y<e[o].diffs.length;y++){let g=e[o].diffs[y];g[0]!==0&&(b=this.diff_xIndex(d,F)),g[0]===1?t=t.substring(0,c+b)+g[1]+t.substring(c+b):g[0]===-1&&(t=t.substring(0,c+b)+t.substring(c+this.diff_xIndex(d,F+g[1].length))),g[0]!==-1&&(F+=g[1].length)}}}}}return t=t.substring(i.length,t.length-i.length),[t,n]}patch_addPadding(e){let t=this.patchMargin,i="";for(let o=1;o<=t;o++)i+=String.fromCharCode(o);for(let o=0;o<e.length;o++)e[o].start1+=t,e[o].start2+=t;let r=e[0],n=r.diffs;if(n.length===0||n[0][0]!==0)n.unshift([0,i]),r.start1-=t,r.start2-=t,r.length1+=t,r.length2+=t;else if(t>n[0][1].length){let o=t-n[0][1].length;n[0][1]=i.substring(n[0][1].length)+n[0][1],r.start1-=o,r.start2-=o,r.length1+=o,r.length2+=o}if(r=e[e.length-1],n=r.diffs,n.length===0||n[n.length-1][0]!==0)n.push([0,i]),r.length1+=t,r.length2+=t;else if(t>n[n.length-1][1].length){let o=t-n[n.length-1][1].length;n[n.length-1][1]+=i.substring(0,o),r.length1+=o,r.length2+=o}return i}patch_splitMax(e){let t=this.matchMaxBits;for(let i=0;i<e.length;i++){if(e[i].length1<=t)continue;let r=e[i];e.splice(i--,1);let n=r.start1,o=r.start2,a="";for(;r.diffs.length!==0;){let l=new q,c=!0;for(l.start1=n-a.length,l.start2=o-a.length,a!==""&&(l.length1=l.length2=a.length,l.diffs.push([0,a]));r.diffs.length!==0&&l.length1<t-this.patchMargin;){let p=r.diffs[0][0],d=r.diffs[0][1];p===1?(l.length2+=d.length,o+=d.length,l.diffs.push(r.diffs.shift()),c=!1):p===-1&&l.diffs.length===1&&l.diffs[0][0]===0&&d.length>2*t?(l.length1+=d.length,n+=d.length,c=!1,l.diffs.push([p,d]),r.diffs.shift()):(d=d.substring(0,t-l.length1-this.patchMargin),l.length1+=d.length,n+=d.length,p===0?(l.length2+=d.length,o+=d.length):c=!1,l.diffs.push([p,d]),d===r.diffs[0][1]?r.diffs.shift():r.diffs[0][1]=r.diffs[0][1].substring(d.length))}a=this.diff_text2(l.diffs),a=a.substring(a.length-this.patchMargin);let u=this.diff_text1(r.diffs).substring(0,this.patchMargin);u!==""&&(l.length1+=u.length,l.length2+=u.length,l.diffs.length!==0&&l.diffs[l.diffs.length-1][0]===0?l.diffs[l.diffs.length-1][1]+=u:l.diffs.push([0,u])),c||e.splice(++i,0,l)}}}patch_toText(e){let t=[];for(let i=0;i<e.length;i++)t[i]=e[i];return t.join("")}patch_fromText(e){let t=[];if(!e)return t;let i=e.split(`
|
|
4
|
+
`),r=0,n=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;for(;r<i.length;){let o=i[r].match(n);if(!o)throw new Error("Invalid patch string: "+i[r]);let a=new q;t.push(a),a.start1=parseInt(o[1],10),o[2]===""?(a.start1--,a.length1=1):o[2]==="0"?a.length1=0:(a.start1--,a.length1=parseInt(o[2],10)),a.start2=parseInt(o[3],10),o[4]===""?(a.start2--,a.length2=1):o[4]==="0"?a.length2=0:(a.start2--,a.length2=parseInt(o[4],10)),r++;let l,c,u;for(;r<i.length;){l=i[r].charAt(0),u=i[r].substring(1);try{c=decodeURI(u)}catch{throw new Error("Illegal escape in patch_fromText: "+u)}if(l==="-")a.diffs.push([-1,c]);else if(l==="+")a.diffs.push([1,c]);else if(l===" ")a.diffs.push([0,c]);else{if(l==="@")break;if(l!=="")throw new Error('Invalid patch mode "'+l+'" in: '+c)}r++}}return t}diff_compute_(e,t,i,r){let n;if(!e)return[[1,t]];if(!t)return[[-1,e]];let o=e.length>t.length?e:t,a=e.length>t.length?t:e,l=o.indexOf(a);if(l!==-1)return n=[[1,o.substring(0,l)],[0,a],[1,o.substring(l+a.length)]],e.length>t.length&&(n[0][0]=-1,n[2][0]=-1),n;if(a.length===1)return[[-1,e],[1,t]];let c=this.diff_halfMatch_(e,t);if(c){let u=c[0],p=c[1],d=c[2],F=c[3],b=c[4],y=this.diff_main(u,d,i,r),g=this.diff_main(p,F,i,r);return y.concat([[0,b]],g)}return i&&e.length>100&&t.length>100?this.diff_lineMode_(e,t,r):this.diff_bisect_(e,t,r)}diff_lineMode_(e,t,i){let r=this.diff_linesToChars_(e,t);e=r.chars1,t=r.chars2;let n=r.lineArray,o=this.diff_main(e,t,!1,i);this.diff_charsToLines_(o,n),this.diff_cleanupSemantic(o),o.push([0,""]);let a=0,l=0,c=0,u="",p="";for(;a<o.length;){switch(o[a][0]){case 1:c++,p+=o[a][1];break;case-1:l++,u+=o[a][1];break;case 0:if(l>=1&&c>=1){o.splice(a-l-c,l+c),a=a-l-c;let d=this.diff_main(u,p,!1,i);for(let F=d.length-1;F>=0;F--)o.splice(a,0,d[F]);a=a+d.length}c=0,l=0,u="",p="";break}a++}return o.pop(),o}diff_bisect_(e,t,i){let r=e.length,n=t.length,o=Math.ceil((r+n)/2),a=o,l=2*o,c=new Array(l),u=new Array(l);for(let h=0;h<l;h++)c[h]=-1,u[h]=-1;c[a+1]=0,u[a+1]=0;let p=r-n,d=p%2!==0,F=0,b=0,y=0,g=0;for(let h=0;h<o&&!(Date.now()>i);h++){for(let f=-h+F;f<=h-b;f+=2){let w=a+f,m;f===-h||f!==h&&c[w-1]<c[w+1]?m=c[w+1]:m=c[w-1]+1;let v=m-f;for(;m<r&&v<n&&e.charAt(m)===t.charAt(v);)m++,v++;if(c[w]=m,m>r)b+=2;else if(v>n)F+=2;else if(d){let E=a+p-f;if(E>=0&&E<l&&u[E]!==-1){let S=r-u[E];if(m>=S)return this.diff_bisectSplit_(e,t,m,v,i)}}}for(let f=-h+y;f<=h-g;f+=2){let w=a+f,m;f===-h||f!==h&&u[w-1]<u[w+1]?m=u[w+1]:m=u[w-1]+1;let v=m-f;for(;m<r&&v<n&&e.charAt(r-m-1)===t.charAt(n-v-1);)m++,v++;if(u[w]=m,m>r)g+=2;else if(v>n)y+=2;else if(!d){let E=a+p-f;if(E>=0&&E<l&&c[E]!==-1){let S=c[E],_=a+S-E;if(m=r-m,S>=m)return this.diff_bisectSplit_(e,t,S,_,i)}}}}return[[-1,e],[1,t]]}diff_bisectSplit_(e,t,i,r,n){let o=e.substring(0,i),a=t.substring(0,r),l=e.substring(i),c=t.substring(r),u=this.diff_main(o,a,!1,n),p=this.diff_main(l,c,!1,n);return u.concat(p)}diff_linesToChars_(e,t){let i=[],r={};i[0]="";let n=this.diff_linesToCharsMunge_(e,i,r,4e4),o=this.diff_linesToCharsMunge_(t,i,r,65535);return{chars1:n,chars2:o,lineArray:i}}diff_linesToCharsMunge_(e,t,i,r){let n="",o=0,a=-1,l=t.length;for(;a<e.length-1;){a=e.indexOf(`
|
|
5
|
+
`,o),a===-1&&(a=e.length-1);let c=e.substring(o,a+1);(i.hasOwnProperty?i.hasOwnProperty(c):i[c]!==void 0)?n+=String.fromCharCode(i[c]):(l===r&&(c=e.substring(o),a=e.length),n+=String.fromCharCode(l),i[c]=l,t[l++]=c),o=a+1}return n}diff_charsToLines_(e,t){for(let i=0;i<e.length;i++){let r=e[i][1],n=[];for(let o=0;o<r.length;o++)n[o]=t[r.charCodeAt(o)];e[i][1]=n.join("")}}diff_commonOverlap_(e,t){let i=e.length,r=t.length;if(i===0||r===0)return 0;i>r?e=e.substring(i-r):i<r&&(t=t.substring(0,i));let n=V(i,r);if(e===t)return n;let o=0,a=1;for(;;){let l=e.substring(n-a),c=t.indexOf(l);if(c===-1)return o;a+=c,(c===0||e.substring(n-a)===t.substring(0,a))&&(o=a,a++)}}diff_halfMatch_(e,t){if(this.diffTimeout<=0)return null;let i=e.length>t.length?e:t,r=e.length>t.length?t:e;if(i.length<4||r.length*2<i.length)return null;let n=this.diff_halfMatchI_(i,r,Math.ceil(i.length/4)),o=this.diff_halfMatchI_(i,r,Math.ceil(i.length/2)),a;if(!n&&!o)return null;o?n?a=n[4].length>o[4].length?n:o:a=o:a=n;let l,c,u,p,d=a[4];return e.length>t.length?(l=a[0],c=a[1],u=a[2],p=a[3]):(u=a[0],p=a[1],l=a[2],c=a[3]),[l,c,u,p,d]}diff_halfMatchI_(e,t,i){let r=e.substring(i,i+Math.floor(e.length/4)),n="",o,a,l,c,u=t.indexOf(r,0);for(;u!==-1;){let p=this.diff_commonPrefix(e.substring(i),t.substring(u)),d=this.diff_commonSuffix(e.substring(0,i),t.substring(0,u));n.length<d+p&&(n=t.substring(u-d,u)+t.substring(u,u+p),o=e.substring(0,i-d),a=e.substring(i+p),l=t.substring(0,u-d),c=t.substring(u+p)),u=t.indexOf(r,u+1)}return n.length*2>=e.length?[o,a,l,c,n]:null}diff_cleanupSemanticScore_(e,t){if(!e||!t)return 6;let i=e.charAt(e.length-1),r=t.charAt(0),n=i.match(kt),o=r.match(kt),a=n&&i.match(Mt),l=o&&r.match(Mt),c=a&&i.match(Bt),u=l&&r.match(Bt),p=c&&e.match(Ii),d=u&&t.match(Li);return p||d?5:c||u?4:n&&!a&&l?3:a||l?2:n||o?1:0}match_bitap_(e,t,i){if(t.length>this.matchMaxBits)throw new Error("Pattern too long for this browser");let r=this.match_alphabet_(t),n=this.matchThreshold,o=e.indexOf(t,i);o!==-1&&(n=V(this.match_bitapScore_(0,o,t,i),n),o=e.lastIndexOf(t,i+t.length),o!==-1&&(n=V(this.match_bitapScore_(0,o,t,i),n)));let a=1<<t.length-1;o=-1;let l,c,u=t.length+e.length,p;for(let d=0;d<t.length;d++){for(l=0,c=u;l<c;)this.match_bitapScore_(d,i+c,t,i)<=n?l=c:u=c,c=Math.floor((u-l)/2+l);u=c;let F=j(1,i-c+1),b=V(i+c,e.length)+t.length,y=Array(b+2);y[b+1]=(1<<d)-1;for(let g=b;g>=F;g--){let h=r[e.charAt(g-1)];if(d===0?y[g]=(y[g+1]<<1|1)&h:y[g]=(y[g+1]<<1|1)&h|((p[g+1]|p[g])<<1|1)|p[g+1],y[g]&a){let f=this.match_bitapScore_(d,g-1,t,i);if(f<=n)if(n=f,o=g-1,o>i)F=j(1,2*i-o);else break}}if(this.match_bitapScore_(d+1,i,t,i)>n)break;p=y}return o}match_bitapScore_(e,t,i,r){let n=e/i.length,o=Math.abs(r-t);return this.matchDistance?n+o/this.matchDistance:o?1:n}match_alphabet_(e){let t={};for(let i=0;i<e.length;i++)t[e.charAt(i)]=0;for(let i=0;i<e.length;i++)t[e.charAt(i)]|=1<<e.length-i-1;return t}patch_addContext_(e,t){if(t.length===0)return;if(e.start2==null)throw Error("patch not initialized");let i=t.substring(e.start2,e.start2+e.length1),r=0;for(;t.indexOf(i)!==t.lastIndexOf(i)&&i.length<this.matchMaxBits-this.patchMargin-this.patchMargin;)r+=this.patchMargin,i=t.substring(e.start2-r,e.start2+e.length1+r);r+=this.patchMargin;let n=t.substring(e.start2-r,e.start2);n&&e.diffs.unshift([0,n]);let o=t.substring(e.start2+e.length1,e.start2+e.length1+r);o&&e.diffs.push([0,o]),e.start1-=n.length,e.start2-=n.length,e.length1+=n.length+o.length,e.length2+=n.length+o.length}},q=class{constructor(){this.diffs=[];this.start1=0;this.start2=0;this.length1=0;this.length2=0}toString(){let e,t;this.length1===0?e=this.start1+",0":this.length1===1?e=this.start1+1:e=this.start1+1+","+this.length1,this.length2===0?t=this.start2+",0":this.length2===1?t=this.start2+1:t=this.start2+1+","+this.length2;let i=["@@ -"+e+" +"+t+` @@
|
|
6
|
+
`],r;for(let n=0;n<this.diffs.length;n++){switch(this.diffs[n][0]){case 1:r="+";break;case-1:r="-";break;case 0:r=" ";break}i[n+1]=r+encodeURI(this.diffs[n][1])+`
|
|
7
|
+
`}return i.join("").replace(/%20/g," ")}};function Ut(s,e,t){let i=new Fe,r=i.diff_main(s,e,!0,0);r.length>2&&(i.diff_cleanupSemantic(r),i.diff_cleanupEfficiency(r));let n=i.patch_make(s,r);return i.patch_apply(n,t)[0]}var be=["bmp","png","jpg","jpeg","gif","svg","webp","avif"],we=["mp3","wav","m4a","3gp","flac","ogg","oga","opus"],ve=["mp4","webm","ogv","mov","mkv"],Ee=["pdf"],Vt=["md"],Xe=["canvas"],jt=["base"];var Pr=Vt.concat(Xe,jt),Ar=[].concat(be,we,ve,Ee,Vt,Xe);var _r=[...be,...we,...ve,...Ee,...jt,...Xe];var Se=class{constructor(e){this.allowTypes=new Set(G);this.allowSpecialFiles=new Set(z);this.ignoreFolders=[];this.filterCache={};this.configDir=e}init(e,t,i){this.filterCache={},this.allowTypes=new Set(e||G),this.allowSpecialFiles=new Set(t||[]),this.ignoreFolders=i||[]}clear(){this.filterCache={},this.allowTypes=new Set(G),this.allowSpecialFiles=new Set(z),this.ignoreFolders=[]}clearCache(){this.filterCache={}}allowSyncFile(e,t){let{filterCache:i}=this;return Object.hasOwn(i,e)?i[e]:i[e]=this._allowSyncFile(e,t)}_allowSyncFile(e,t){for(let n of this.ignoreFolders)if(t&&e===n||e.startsWith(n+"/"))return!1;if(!t&&e.startsWith(this.configDir+"/")){let n=e.substring((this.configDir+"/").length),o=n.split("/");if(o.some(u=>u==="node_modules"||u.startsWith(".")))return!1;let a=O(n),l=T(a),c=null;return n==="workspace.json"||n==="workspace-mobile.json"?!1:(n==="app.json"||n==="types.json"?c="app":n==="appearance.json"?c="appearance":n==="hotkeys.json"?c="hotkey":n==="core-plugins.json"||n==="core-plugins-migration.json"?c="core-plugin":n==="community-plugins.json"?c="community-plugin":o[0]==="themes"&&o.length===3&&(a==="theme.css"||a==="manifest.json")||o[0]==="snippets"&&o.length===2&&l==="css"?c="appearance-data":o.length===1&&l==="json"?c="core-plugin-data":o[0]==="plugins"&&o.length===3&&this.isPluginFile(a)&&(c="community-plugin-data"),c&&this.allowSpecialFiles.has(c))}if(e.startsWith("."))return!1;if(t)return!0;if(e.startsWith("."))return!1;let i=T(O(e));if(i==="md"||i==="canvas"||i==="base")return!0;let{allowTypes:r}=this;return be.includes(i)?r.has("image"):i==="webm"?r.has("audio")||r.has("video"):we.includes(i)?r.has("audio"):ve.includes(i)?r.has("video"):Ee.includes(i)?r.has("pdf"):!!r.has("unsupported")}isPluginFile(e){return e==="manifest.json"||e==="main.js"||e==="styles.css"||e==="data.json"}};var ne=class{constructor(){this.promise=Promise.resolve()}queue(e){let t=this.promise.then(e,e);return this.promise=t,t}};function De(){let s={promise:null,resolve:null,reject:null};return s.promise=new Promise((e,t)=>{s.resolve=e,s.reject=t}),s}var Ye=WebSocket,Kt=URL,Ni=Object.getOwnPropertyDescriptor(Kt.prototype,"hostname").get,ki=String.prototype.endsWith,qt={1e3:"Disconnected",1001:"Going Away",1002:"Protocol Error",1003:"Unsupported Data",1004:"(For future)",1005:"No Status Received",1006:"Disconnected",1007:"Invalid frame payload data",1008:"Policy Violation",1009:"Message too big",1010:"Missing Extension",1011:"Internal Error",1012:"Service Restart",1013:"Try Again Later",1014:"Bad Gateway",1015:"TLS Handshake"};function Mi(s){if(s>=0&&s<=999)return"(Unused)";if(s>=1016){if(s<=1999)return"(For WebSocket standard)";if(s<=2999)return"(For WebSocket extensions)";if(s<=3999)return"(For libraries and frameworks)";if(s<=4999)return"(For applications)"}return qt.hasOwnProperty(s)?qt[s]:"(Unknown)"}var Bi=20*1e3,Ui=10*1e3,Vi=2*60*1e3,Pe=class{constructor(e){this.socket=null;this.queue=new ne;this.notifyQueue=new ne;this.onDisconnect=null;this.perFileMax=199*1024*1024;this.userId=-1;this.justPushed=null;this.encryptionProvider=e}isConnecting(){let{socket:e}=this;return e&&e.readyState===Ye.CONNECTING}isConnected(){let{socket:e}=this;return e&&e.readyState===Ye.OPEN}hasConnection(){let{socket:e}=this;return!!e}async connect(e,t,i,r,n,o,a,l){if(this.hasConnection())return;this.onReady=a,this.onPush=l;let c=this.encryptionProvider.keyHash;return new Promise((u,p)=>{let d=new Kt(e),F=Ni.call(d);if(!ki.call(F,".obsidian.md")&&F!=="127.0.0.1")return p(new Error("Unable to connect to server."));let b=!1,y=h=>{b||(b=!0,this.disconnect(),p(h))},g=this.socket=new Ye(d);g.binaryType="arraybuffer",g.onclose=h=>{h.code===1006?y(new Error("Unable to connect to server.")):y(new Error("Disconnected. Code: "+h.code+" "+Mi(h.code)))},g.onopen=()=>{this.lastMessageTs=Date.now(),this.heartbeat=setInterval(()=>{let h=Date.now()-this.lastMessageTs;if(h>Vi){this.disconnect();return}h>Ui&&this.send({op:"ping"})},Bi),this.send({op:"init",token:t,id:i,keyhash:c,version:r,initial:n,device:o,encryption_version:this.encryptionProvider.encryptionVersion})},g.onmessage=h=>{let f=h.data;if(typeof f!="string")return y(new Error("Server returned binary"));let w;try{w=JSON.parse(f)}catch(m){return console.error(m),y(new Error("Server JSON failed to parse: "+f))}if(w.op!=="pong"){if(w.status==="err"||w.res==="err")return y(new Error("Failed to authenticate: "+w.msg));if(w.res!=="ok")return y(new Error("Did not respond to login request: "+f));if(w.hasOwnProperty("perFileMax")){let m=w.perFileMax;if(!Number.isInteger(m)||isNaN(m)||m<0)return y(new Error("Unexpected value from server for max file size"));this.perFileMax=m}w.userId&&(this.userId=w.userId),u(),b=!0,g.onmessage=this.onMessage.bind(this),g.onclose=this.disconnect.bind(this),g.onerror=this.disconnect.bind(this)}}})}disconnect(){let e=!1;this.socket&&(this.socket.close(),this.socket=null,e=!0),this.heartbeat&&(clearInterval(this.heartbeat),this.heartbeat=null),this.responsePromise&&(this.responsePromise.reject(new Error("Disconnected")),this.responsePromise=null),this.dataPromise&&(this.dataPromise.reject(new Error("Disconnected")),this.dataPromise=null),e&&this.onDisconnect&&this.onDisconnect()}async pull(e){return this.queue.queue(async()=>{this.lastNetworkRequestTs=Date.now();let t=await this.request({op:"pull",uid:e});if(!t.deleted){let i=t.size,r=t.pieces,n=new ArrayBuffer(i),o=0;for(let a=0;a<r;a++){let l=await this.dataResponse();new Uint8Array(n,o,l.byteLength).set(new Uint8Array(l)),o+=l.byteLength}return n.byteLength>0&&(n=await this.encryptionProvider.decrypt(n)),n}return null})}async push(e,t,i,r,n,o,a,l){return this.queue.queue(async()=>{this.lastNetworkRequestTs=Date.now();let c=await this.encryptionProvider.deterministicEncodeStr(e),u=t?await this.encryptionProvider.deterministicEncodeStr(t):null,p=i?"":T(O(e));if(i||r){this.justPushed={path:c,folder:i,deleted:r,mtime:0,hash:""},await this.request({op:"push",path:c,relatedpath:u,extension:p,hash:"",ctime:0,mtime:0,folder:i,deleted:r});return}l.byteLength>0&&(l=await this.encryptionProvider.encrypt(l)),a&&(a=await this.encryptionProvider.deterministicEncodeStr(a));let d=l.byteLength,F=2*1024*1024,b=Math.ceil(d/F);if(this.justPushed={path:c,folder:i,deleted:r,mtime:o,hash:a},(await this.request({op:"push",path:c,relatedpath:u,extension:p,hash:a,ctime:n,mtime:o,folder:i,deleted:r,size:d,pieces:b})).res==="ok"){this.justPushed=null;return}for(let g=0;g<b;g++){let h=g*F,f=Math.min(F,d-h);this.sendBinary(new Uint8Array(l,h,f)),await this.response()}this.justPushed=null})}async listDeleted(){return this.queue.queue(async()=>{let t=(await this.request({op:"deleted",suppressrenames:!0})).items;for(let i of t)i.path=await this.encryptionProvider.deterministicDecodeStr(i.path);return t})}async listHistory(e,t){return this.queue.queue(async()=>{let i=await this.encryptionProvider.deterministicEncodeStr(e),r=await this.request({op:"history",path:i,last:t}),n=r.items;for(let o of n)o.path=await this.encryptionProvider.deterministicDecodeStr(o.path),o.relatedpath=o.relatedpath&&await this.encryptionProvider.deterministicDecodeStr(o.relatedpath);return r})}async restore(e){return this.queue.queue(async()=>this.request({op:"restore",uid:e}))}async getUsernames(){return this.queue.queue(()=>this.request({op:"usernames"}))}async purge(){return this.queue.queue(()=>this.request({op:"purge"}))}async size(){return this.queue.queue(()=>this.request({op:"size"}))}onServerPush(e){let{justPushed:t}=this;t&&t.path===e.path&&t.folder===e.folder&&t.deleted===e.deleted&&t.mtime===e.mtime&&t.hash===e.hash&&(this.justPushed=null,e.wasJustPushed=!0),this.notifyQueue.queue(async()=>{e.path=await this.encryptionProvider.deterministicDecodeStr(e.path),e.hash&&(e.hash=await this.encryptionProvider.deterministicDecodeStr(e.hash)),this.onPush(e)})}response(){return(this.responsePromise=De()).promise}dataResponse(){return(this.dataPromise=De()).promise}async request(e,t=6e4){let i=this.responsePromise=De();this.send(e);let r=new Error("Timeout"),n=setTimeout(()=>i.reject(r),t),o;try{o=await i.promise,clearTimeout(n)}catch(a){throw a===r&&this.disconnect(),a}if(o.err)throw new Error(o.err);return o}onMessage(e){if(this.lastMessageTs=Date.now(),typeof e.data=="string"){let t;try{t=JSON.parse(e.data)}catch{console.log(e.data),this.disconnect();return}let i=t.op;if(i==="pong")return;if(i==="ready"){this.notifyQueue.queue(async()=>{this.onReady(t.version)});return}if(i==="push"){this.onServerPush(t);return}let r=this.responsePromise;r&&(this.responsePromise=null,r.resolve(t))}else{let t=this.dataPromise;t&&(this.dataPromise=null,t.resolve(e.data))}}send(e){this.socket.send(JSON.stringify(e))}sendBinary(e){this.socket.send(e)}};var Ae=class{constructor(e,t,i,r){this.min=e||0,this.max=t||Number.MAX_VALUE,this.base=i||1e3,this.jitter=r||!0,this.count=0,this.nextTs=Date.now()}success(){this.count=0,this.nextTs=Date.now()+this.getTimeout()}fail(){this.count++,this.nextTs=Date.now()+this.getTimeout()}getTimeout(){if(this.count===0)return this.min;let e=this.count-1,t=this.base*Math.pow(2,e);if(this.jitter){let i=.5;t=t*(1-i+i*Math.random())}return t=Math.floor(Math.min(this.max,this.min+t)),t}getNextTs(){return this.nextTs}isReady(){return Date.now()>this.nextTs}};var Wt=crypto,x=Wt.subtle,Je="AES-GCM";function Ht(s){return x.importKey("raw",s,Je,!1,["encrypt","decrypt"])}async function _e(s,e,t){t||(t=Wt.getRandomValues(new Uint8Array(12)));let i=await x.encrypt({name:Je,iv:t},e,s),r=new ArrayBuffer(t.byteLength+i.byteLength),n=new Uint8Array(r);return n.set(new Uint8Array(t),0),n.set(new Uint8Array(i),t.byteLength),r}async function Te(s,e){if(s.byteLength<12)throw new Error("Encrypted data is bad");if(s.byteLength===12)return new ArrayBuffer(0);let t=new Uint8Array(s,0,12),i=new Uint8Array(s,12);return await x.decrypt({name:Je,iv:t},e,i)}function se(s){return s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength)}function L(s){return se(new TextEncoder().encode(s))}function K(s){return new TextDecoder().decode(new Uint8Array(s))}function $t(s){let e=atob(s),t=e.length,i=new Uint8Array(t);for(let r=0;r<t;r++)i[r]=e.charCodeAt(r);return i.buffer}function Gt(s){return ji(new Uint8Array(s))}function ji(s){let e=[],t=s.byteLength;for(let i=0;i<t;i++)e.push(String.fromCharCode(s[i]));return btoa(e.join(""))}async function xe(s){return R(await oe(s))}async function oe(s){return x.digest("SHA-256",new Uint8Array(s))}function Ze(s){let e=s.length/2,t=new ArrayBuffer(e),i=new Uint8Array(t);for(let r=0;r<e;r++)i[r]=parseInt(s.substr(r*2,2),16);return t}function R(s){let e=new Uint8Array(s),t=[];for(let i=0;i<e.length;i++){let r=e[i];t.push((r>>>4).toString(16)),t.push((r&15).toString(16))}return t.join("")}function ze(s){if(s<=0)return"0 B";let e=["B","KB","MB","GB","TB","PB"],t=e.length-1,i=1024;for(let n=0;n<e.length;n++)if(s<Math.pow(i,n+1)){t=n;break}let r=s/Math.pow(i,t);return`${qi(r,t===0?0:2)} ${e[t]}`}function qi(s,e=2,t=".",i=","){let r=s<0?"-":"";s=Math.abs(s);let n=parseInt(s.toFixed(e),10)+"",o=n.length>3?n.length%3:0;return r+(o?n.substr(0,o)+i:"")+n.substr(o).replace(/(\d{3})(?=\d)/g,"$1"+i)+(e?t+Math.abs(s-Math.floor(s)).toFixed(e).slice(2):"")}var Ki=function(){let s=typeof process<"u"?process.platform:"";if(s==="win32")return"win";if(s==="darwin")return"mac";if(s==="linux")return"linux";if(s)return"unknown";let e=navigator.userAgent;return e.indexOf("Win")!==-1?"win":e.indexOf("Mac")!==-1?"mac":e.indexOf("X11")!==-1||e.indexOf("Linux")!==-1?"linux":"unknown"}(),Qt=navigator.userAgent.toLowerCase();var et=Ki==="win";var Gr=/^((?!chrome|android).)*safari/i.test(Qt),Xt=/android/i.test(Qt);var Wi=/[.?*+^$[\]\\(){}|-]/g;function Ie(s){return s.replace(Wi,"\\$&")}var Hi="\\/:"+(Xt?'*?<>"':""),$i='*"\\/<>:|?',Yt="#^[]|",Jt=et?$i:Hi,Gi=Jt.split("").join("\xA0"),zr=Yt.split("").join("\xA0"),Zt=new RegExp("["+Ie(Jt)+"]"),en=new RegExp("["+Ie(Yt)+"]"),Qi=/^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i;function zt(s){try{return Xi(s),!0}catch{return!1}}function Xi(s){if(et){let e=s.charAt(s.length-1);if(e==="."||e===" ")throw new Error("File names cannot end with a dot or a space.");let t=It(s);if(Qi.test(t))throw new Error("File name is forbidden: "+t)}if(s.split("/").some(e=>Zt.test(e)))throw new Error("File name cannot contain any of the following characters: "+Gi)}function ei(s,e="_"){let t=s.trim();if(t&&(t=s.replace(Zt,e),e.length===1)){let i=Ie(e),r=new RegExp(`${i}{2,}`,"g");t=t.replace(r,e)}return t}function Yi(s){return new Promise(e=>setTimeout(e,s))}var Le=class{constructor(e){this.server=null;this.localFiles={};this.serverFiles={};this.newServerFiles=[];this.version=0;this.initial=!0;this.ready=!1;this.syncing=!1;this.loaded=!1;this.backoff=new Ae(0,5*60*1e3,5e3,!0);this.fileRetry={};this.skippedFiles={};this.syncingPath="";this.scanSpecialFiles=!1;this.scanSpecialFileQueue=[];this.resolveStop=null;let{config:t}=e;this.host=t.host,this.token=e.token,this.vaultId=t.vaultId,this.continuous=e.continuous||!1,this.encryption=e.encryption,this.stateStore=e.stateStore,this.conflictStrategy=t.conflictStrategy||"merge",this.deviceName=t.deviceName||"Sync Client",this.adapter=new ye(P.default,D.default,ke.default,()=>!1,yt,"file:///",t.vaultPath),this.filter=new Se(t.configDir),this.filter.init(t.allowTypes||[],t.allowSpecialFiles||[],t.ignoreFolders||[]),this.loadState()}loadState(){this.version=this.stateStore.getVersion(),this.initial=this.stateStore.isInitial(),this.localFiles=this.stateStore.getAllLocalFiles(),this.serverFiles=this.stateStore.getAllServerFiles(),this.newServerFiles=this.stateStore.getPendingFiles()}handlePush(e){if(this.version=e.uid,this.initial&&e.deleted){this.stateStore.setVersion(this.version);return}let t=e.path,i={path:t,size:e.size,hash:e.hash,ctime:e.ctime,mtime:e.mtime,folder:e.folder,deleted:e.deleted,uid:e.uid,device:e.device,user:e.user};this.initial&&(i.initial=!0);let{newServerFiles:r}=this;if(e.wasJustPushed){for(let n=0;n<r.length;n++)r[n].path===t&&(r.splice(n,1),n-=1);this.stateStore.deletePendingFile(t),this.serverFiles[t]=i,this.stateStore.setServerFile(i),this.stateStore.setVersion(this.version);return}r.push(i),this.stateStore.addPendingFile(i),this.stateStore.setVersion(this.version),this.initial||console.debug(`Push: ${i.path} (${i.deleted?"deleted":"updated"})`),this.requestSync()}async sync(){let e=new Promise(i=>this.resolveStop=i),t=setInterval(this.requestSync.bind(this),30*1e3);try{if(!this.loaded){await this.adapter.watch(this.onChange.bind(this));for(let i in this.localFiles)Object.hasOwn(this.adapter.files,i)||(delete this.localFiles[i],this.stateStore.deleteLocalFile(i));this.loaded=!0}await this.requestSync(),await e}finally{clearInterval(t)}}stop(){this.server&&(this.server.disconnect(),this.server=null),this.ready=!1,this.stateStore.close(),this.adapter.stopWatch(),this.adapter.thingsHappening.cancel(),this.resolveStop&&(this.resolveStop(),this.resolveStop=null)}async requestSync(){if(!this.syncing){this.syncing=!0;try{let e=!0;for(;e;){try{this.syncingPath="",e=await this._sync();let i=this.syncingPath;i&&delete this.fileRetry[i]}catch(i){if(console.error("Sync error:",i),this.failedSync(this.syncingPath),e=!1,!this.continuous)throw this.syncing=!1,i}if(!e)return;let t=0;if(this.server){let i=Date.now()-this.server.lastNetworkRequestTs;t=Math.max(0,50-i)}await Yi(t)}}finally{this.syncing=!1}}}canSyncPath(e,t){let{fileRetry:i}=this;for(let r in i)if(i.hasOwnProperty(r)&&i[r].ts>e&&t.startsWith(r))return!1;return!0}canSyncLocalFile(e,t){if(!t.synctime)return!0;let i=t.size,r=i>10*1024,n=i>100*1024,o=r?n?30:20:10;return e-t.synctime>o*1e3}failedSync(e){if(!e)return;let t=Date.now(),i=this.fileRetry[e];i||(i=this.fileRetry[e]={count:0,ts:0});let r=i.count=i.count+1,n=Math.pow(2,r)*5*1e3;n=Math.min(n,5*60*1e3),i.ts=t+n}async getServer(){let{server:e}=this;if(e&&!e.isConnected()&&!e.isConnecting()&&(e.disconnect(),e=this.server=null,this.backoff.fail()),!e){if(!this.backoff.isReady())return this.log("Waiting to connect to server"),null;e=this.server=new Pe(this.encryption)}if(!e.hasConnection()){this.log("Connecting..."),this.ready=!1;try{let t=this.host.startsWith("127.0.0.1")||this.host.startsWith("localhost")?"ws://":"wss://";e.onDisconnect=()=>{this.ready=!1,this.log("Disconnected from server"),this.continuous&&this.requestSync()},await e.connect(t+this.host,this.token,this.vaultId,this.version,this.initial,this.deviceName,i=>{this.ready=!0,this.initial=!1,this.stateStore.setInitial(!1),this.version<i&&(this.version=i,this.stateStore.setVersion(this.version)),this.requestSync()},i=>this.handlePush(i)),this.log("Connection successful. Detecting changes..."),this.backoff.success()}catch(t){if(t.message&&t.message.includes("Your subscription to Obsidian Sync has expired"))throw console.error(t.message),this.stop(),t;if(t.message&&t.message.includes("Vault not found"))throw console.error("The connected remote vault no longer exists."),this.stop(),t;return console.error(t),e.disconnect(),e=this.server=null,this.backoff.fail(),null}}return e}async _sync(){let e=await this.getServer();if(!e)return!1;let t=Date.now(),i=0,r=this.filter.configDir,n=r+"/",{localFiles:o,serverFiles:a,newServerFiles:l,stateStore:c,filter:u,adapter:p}=this;try{let g={},h=this.scanSpecialFileQueue;if(this.scanSpecialFiles){this.scanSpecialFiles=!1,h=this.scanSpecialFileQueue=[];for(let f in o)Object.hasOwn(o,f)&&f.startsWith(".")&&(g[f]=o[f],delete o[f],c.deleteLocalFile(f));if(u.allowSpecialFiles.size>0&&await p.exists(r)){h.push(n+"config");let f=await p.list(r);for(let w of f.files)T(w)==="json"&&h.push(w);if(await p.exists(n+"themes")){let w=await p.list(n+"themes");for(let m of w.folders){let v=await p.list(m);for(let E of v.files){let S=O(E);(S==="manifest.json"||S==="theme.css")&&h.push(E)}}}if(await p.exists(n+"snippets")){let w=await p.list(n+"snippets");for(let m of w.files)T(m)==="css"&&h.push(m)}if(await p.exists(n+"plugins")){let w=await p.list(n+"plugins");for(let m of w.folders){let v=await p.list(m);for(let E of v.files){let S=O(E);u.isPluginFile(S)&&h.push(E)}}}}}for(;h.length>0;){let f=h.pop();if(!await p.exists(f)){delete o[f],c.deleteLocalFile(f);continue}let w=await p.stat(f);if(!w||w.type!=="file"){delete o[f],c.deleteLocalFile(f);continue}let m=o[f];m||(Object.hasOwn(g,f)?m=o[f]=g[f]:m=o[f]={path:f,previouspath:"",ctime:0,mtime:0,size:0,folder:!1,hash:"",synchash:"",synctime:0});let v=Math.ceil(w.mtime),E=Math.ceil(w.ctime),S=w.size;(!m.mtime||m.mtime!==v||m.size!==w.size)&&(m.hash=""),m.mtime=v,m.ctime=E,m.size=S,c.setLocalFile(m)}}catch(g){console.error("Failed to scan config files",g)}if(l.length>0)for(let g=0;g<l.length;g++){let h=l[g],f=h.path,w=a[f],m=o[f],v=(E,S)=>(E||this.log("Accepted",f),m&&(m.synctime=Date.now(),c.setLocalFile(m)),S||(a[f]=h,c.setServerFile(h)),l.splice(g,1),c.deletePendingFile(f),!0);if(!u.allowSyncFile(f,h.folder))return v(!0);if(!zt(f))return this.log("Ignoring remote file name with illegal characters",f,!0),v(!0,!0);if(Q(f).split("/").includes(".."))return v(!0);if(!this.canSyncPath(t,f)){i++;continue}if(this.syncingPath=f,m&&!m.folder&&!m.hash){this.log("Checking",f);let E=await p.readBinary(f);m.hash=await xe(E),c.setLocalFile(m)}if(m&&!m.folder&&!h.folder&&m.hash===h.hash)return v(!0);for(let E=g+1;E<l.length;E++)if(l[E].path===f)return this.log("Skipped",f),v(!0,!0);if(!m)return h.deleted?v(!0):(await this.syncFileDown(e,h),v());if(m.folder&&h.folder){if(h.deleted){try{let E=await p.list(f);if(E.files.length>0||E.folders.length>0)return v(!0)}catch{}this.log("Deleting",f);try{await p.rmdir(f,!0)}catch(E){let S=(E?E.message:"")||"Failed to delete folder";this.log(S,f,!0)}}return v(!0)}if(w&&(m.folder&&w.folder||m.hash===w.hash))return h.deleted?(this.log("Deleting",f),m.folder?await this.adapter.rmdir(f,!0):await this.adapter.remove(f),delete o[f],this.stateStore.deleteLocalFile(f),v()):(await this.syncFileDown(e,h),v());if(!m.folder&&h.folder)try{if((await p.stat(f)).type==="file"){let S=T(O(f)),_=S?f.substr(f.length-S.length-1):"",k=f.substr(0,f.length-_.length)+" (Conflicted copy)"+_;return this.log("Renaming conflicted file",f),await p.rename(f,k),!0}}catch{}if(h.initial&&h.mtime>m.mtime)return await this.syncFileDown(e,h),v();if(!h.initial&&!m.folder&&!h.folder&&!h.deleted&&T(f)==="md"&&h.hash!==m.synchash){this.logMerge("Merging conflicted file",f);let E=await this.adapter.read(f);if(!E)return await this.syncFileDown(e,h),v();let S="";if(w&&!w.deleted)try{S=K(await e.pull(w.uid))}catch(k){return this.logMerge("Merge failed. "+k.toString(),f,!0),v(!0)}let _;try{_=K(await e.pull(h.uid))}catch(k){return this.logMerge("Merge failed. "+k.toString(),f,!0),v(!0)}if(S===_||E===_||!_)return v();if(this.conflictStrategy==="conflict"){let k=new Date().toLocaleString("sv").replace(/[:\- ]/g,"").substring(0,12),mi=T(f),yi=Lt(f),Fi=ei(this.deviceName),bi=`${yi} (Conflicted copy ${Fi} ${k}).${mi}`;try{await p.write(bi,E,{ctime:m.ctime,mtime:m.mtime}),await p.write(f,_,{ctime:h.ctime,mtime:h.mtime}),this.logMerge("Conflicted copy stored",f)}catch{return v()}return v()}if(!S)return Math.abs(Date.now()-m.ctime)<3*60*1e3?(this.log("Downloading "+f),await p.write(f,_,{ctime:h.ctime,mtime:h.mtime}),v()):(h.mtime>m.mtime&&(this.log("Downloading "+f),await p.write(f,_,{ctime:h.ctime,mtime:h.mtime})),v());let H=Ut(S,E,_);return await p.write(f,H),this.logMerge("Merge successful",f),v()}if(!h.folder&&!h.deleted&&h.size>0&&f.startsWith(n)){if(T(f)==="json")try{this.log("Merging conflicted file",f);let E=JSON.parse(await p.read(f));if(E&&!Array.isArray(E)&&typeof E=="object"){let S=JSON.parse(K(await e.pull(h.uid)));if(S&&!Array.isArray(S)&&typeof S=="object"){for(let H in S)Object.hasOwn(S,H)&&(E[H]=S[H]);let _=L(JSON.stringify(E,void 0,2));return await p.writeBinary(f,_),this.log("Merge successful",f),v(!0)}}}catch(E){this.log("Merge failed. "+E.toString(),f,!0)}return await this.syncFileDown(e,h),v()}return this.log("Rejected server change",f),v(!0)}if(!this.ready)return!1;let d=null,F=null;for(let g in a){if(!Object.hasOwn(a,g))continue;let h=a[g];if(!Object.hasOwn(o,g)){if(h.deleted){delete a[g],c.deleteServerFile(g);continue}if(!u.allowSyncFile(g,h.folder))continue;h.folder?(!F||h.path.length>F.path.length)&&(F=h):(!d||h.path.length>d.path.length)&&(d=h)}}if(d){let g=d.path;return this.log("Deleting remote file",g),this.syncingPath=g,await e.push(g,null,!1,!0,0,0,"",null),d.deleted=!0,c.setServerFile(d),!0}if(F){let g=F.path;return this.log("Deleting remote folder",g),this.syncingPath=g,await e.push(g,null,!0,!0,0,0,"",null),F.deleted=!0,c.setServerFile(F),!0}let b=null,y=null;for(let g in o){if(!Object.hasOwn(o,g))continue;let h=o[g],f=a[g];if(!(f&&!f.deleted&&h.folder===f.folder&&h.ctime===f.ctime&&h.mtime===f.mtime&&h.size===f.size&&h.hash&&h.hash===f.hash)&&u.allowSyncFile(g,h.folder)){if(!this.canSyncPath(t,g)){i++;continue}if(process.platform==="linux"&&!h.folder&&f&&f.ctime&&(h.ctime===0||h.ctime>f.ctime)&&!f.folder&&!f.deleted&&(h.ctime=f.ctime),!h.folder&&h.size>e.perFileMax){this.logSkip(`File too large to sync (${ze(h.size)}, max ${ze(e.perFileMax)})`,g);continue}if(!f||f.deleted){if(!this.canSyncLocalFile(t,h)){i++;continue}h.folder?(!b||h.path.length<b.path.length)&&(b=h):(!y||h.size<y.size)&&(y=h);continue}if(!b){if(h.folder){if(!this.canSyncLocalFile(t,h)){i++;continue}f.folder||(!b||h.path.length<b.path.length)&&(b=h);continue}if(f.folder){if(!this.canSyncLocalFile(t,h)){i++;continue}F=f;continue}if(!h.hash||h.hash!==f.hash){if(!this.canSyncLocalFile(t,h)){i++;continue}(!y||h.size<y.size)&&(y=h)}}}}if(b){let g=b.path;return this.syncingPath=g,this.log("Uploading",g),await e.push(g,b.previouspath,!0,!1,0,0,"",null),Object.hasOwn(a,g)?a[g].deleted&&(a[g].deleted=!1):a[g]={uid:0,path:g,size:0,hash:"",ctime:0,mtime:0,folder:!0,deleted:!1,device:this.deviceName},c.setServerFile(a[g]),b.previouspath="",b.synctime=Date.now(),c.setLocalFile(b),!0}if(y){let g=y.path;this.log("New file",g),this.syncingPath=g;let h=a[g],{ctime:f,mtime:w}=y,m=await p.readBinary(g),v=y.hash=await xe(m);return c.setLocalFile(y),h&&!h.deleted&&h.hash===y.hash||(this.log("Uploading file",g),await e.push(g,y.previouspath,!1,!1,f,w,v,m),y.synchash=v,this.log("Upload complete",g),y.previouspath="",y.synctime=Date.now(),c.setLocalFile(y)),!0}return i===0?(this.log("Fully synced"),this.fileRetry={},this.continuous||this.stop(),!1):!0}onChange(e,t,i,r){if(e==="file-created"){let n=this.localFiles[t];n||(n=this.localFiles[t]={path:t,previouspath:"",folder:!1,ctime:0,mtime:0,size:0,hash:"",synctime:0,synchash:""}),r&&((n.mtime!==r.mtime||n.size!==r.size)&&(n.hash=""),n.ctime=r.ctime,n.mtime=r.mtime,n.size=r.size),this.stateStore.setLocalFile(n)}else if(e==="folder-created")this.localFiles[t]||(this.localFiles[t]={path:t,previouspath:"",folder:!0,ctime:0,mtime:0,size:0,hash:"",synctime:0,synchash:""},this.stateStore.setLocalFile(this.localFiles[t]));else if(e==="modified"){let n=this.localFiles[t];n&&r&&((n.mtime!==r.mtime||n.size!==r.size)&&(n.hash=""),n.ctime=r.ctime,n.mtime=r.mtime,n.size=r.size,this.stateStore.setLocalFile(n))}else if(e==="file-removed"||e==="folder-removed")delete this.localFiles[t],this.stateStore.deleteLocalFile(t);else if(e==="renamed"){let n=this.localFiles[i];n&&(delete this.localFiles[i],this.stateStore.deleteLocalFile(i),n.path=t,n.previouspath=i,this.localFiles[t]=n,this.stateStore.setLocalFile(n))}this.loaded&&e!=="raw"&&this.requestSync()}async syncFileDown(e,t){let i=t.path;if(this.log("Downloading",i),t.folder){await this.adapter.mkdir(i),this.log("Downloaded",i);return}let r=await e.pull(t.uid);if(!r)throw new Error("Failed to download file, no data.");let n=U(i);n&&await this.adapter.mkdir(n),await this.adapter.writeBinary(i,r,{ctime:t.ctime,mtime:t.mtime});let o=await xe(r);this.localFiles[i]&&(this.localFiles[i].hash=o,this.localFiles[i].synchash=o,this.stateStore.setLocalFile(this.localFiles[i])),this.log("Downloaded",i)}log(e,t,i=!1){i?t?console.error(e,t):console.error(e):t?console.log(e,t):console.log(e)}logMerge(e,t,i=!1){this.log(e,t,i)}logSkip(e,t){let{skippedFiles:i}=this;(!Object.hasOwn(i,t)||i[t]!==e)&&(i[t]=e,this.log(e,t,!0))}};var gi=require("commander");var ti=fetch,ii="https://"+["api","obsidian","md"].join("."),ae=class s extends Error{constructor(e){super(e.error),this.response=e,this.error=e.error,Object.setPrototypeOf(this,s.prototype)}};async function W(s,e,t){let{preflight:i,headers:r}=t||{};i&&await ti(ii+s,{method:"OPTIONS"}),r||(r={}),r["Content-Type"]="application/json";let n=await(await ti(ii+s,{method:"POST",body:JSON.stringify(e),headers:r})).json();if("error"in n)throw new ae(n);return n}async function tt(s,e,t){return W("/user/signin",{email:s,password:e,mfa:t},{preflight:!0,headers:{Origin:"https://obsidian.md"}})}function it(s){return W("/user/signout",{token:s})}function ri(s){return W("/user/info",{token:s})}function ni(s,e){return W("/vault/regions",{token:s,host:e})}function rt(s,e){return W("/vault/list",{token:s,supported_encryption_version:e})}function si(s,e,t,i,r,n){return W("/vault/create",{token:s,name:e,keyhash:t,salt:i,region:r,encryption_version:n})}function oi(s,e,t,i,r){return W("/vault/access",{token:s,vault_uid:e,keyhash:t,host:i,encryption_version:r})}var le=class s extends Error{constructor(e){super(e),Object.setPrototypeOf(this,s.prototype)}};function ai(s,e,t){return~(s-1)&e|s-1&t}function Ji(s,e){if(s.length!==e.length)return 0;let t=0;for(let i=0;i<s.length;i++)t|=s[i]^e[i];return 1&t-1>>>8}function li(s,e){return s.length===0||e.length===0?!1:Ji(s,e)!==0}function Ce(s){for(let e=0;e<s.length;e++)s[e]=0;return s}function Oe(s,e){for(let t=0;t<e.length;t++)s[t]^=e[t]}var A=class s{static{this.SIZE=16}static{this.R=135}constructor(){this.data=new Uint8Array(s.SIZE)}clear(){Ce(this.data)}clone(){let e=new s;return e.copy(this),e}copy(e){this.data.set(e.data)}dbl(){let e=0;for(let t=s.SIZE-1;t>=0;t--){let i=this.data[t]>>>7&255;this.data[t]=this.data[t]<<1|e,e=i}this.data[s.SIZE-1]^=ai(e,s.R,0),e=0}};var Y=crypto.subtle;var ce=class s{constructor(e){this.key=e}static async importKey(e,t){let i=await Y.deriveKey({name:"HKDF",salt:t,info:new TextEncoder().encode("ObsidianAesSivEnc"),hash:"SHA-256"},e,{name:"AES-CTR",length:256},!1,["encrypt"]);return new s(i)}async encryptCtr(e,t){let i=await Y.encrypt({name:"AES-CTR",counter:e,length:16},this.key,t);return new Uint8Array(i)}};var ue=class s{constructor(e){this._key=e;this._iv=new A;this._emptyPromise=Promise.resolve(this)}static async importKey(e,t){let i=await Y.deriveKey({name:"HKDF",salt:t,info:new TextEncoder().encode("ObsidianAesSivMac"),hash:"SHA-256"},e,{name:"AES-CBC",length:256},!1,["encrypt"]);return new s(i)}clear(){return this}async encryptBlock(e){let t={name:"AES-CBC",iv:this._iv.data},i=await Y.encrypt(t,this._key,e.data);return e.data.set(new Uint8Array(i,0,A.SIZE)),this._emptyPromise}};var fe=class s{constructor(e,t,i){this._cipher=e;this._subkey1=t;this._subkey2=i;this._bufferPos=0;this._finished=!1;this._buffer=new A}static async importKey(e,t){let i=await ue.importKey(e,t),r=new A;await i.encryptBlock(r),r.dbl();let n=r.clone();return n.dbl(),()=>new s(i,r,n)}async update(e){if(this._finished)throw new Error("Cannot update finished CMAC");let t=A.SIZE-this._bufferPos,i=0,r=e.length;if(r>t){for(let n=0;n<t;n++)this._buffer.data[this._bufferPos+n]^=e[n];r-=t,i+=t,await this._cipher.encryptBlock(this._buffer),this._bufferPos=0}for(;r>A.SIZE;){for(let n=0;n<A.SIZE;n++)this._buffer.data[n]^=e[i+n];r-=A.SIZE,i+=A.SIZE,await this._cipher.encryptBlock(this._buffer)}for(let n=0;n<r;n++)this._buffer.data[this._bufferPos++]^=e[i+n];return this}async finish(){if(!this._finished){let e=this._bufferPos<A.SIZE?this._subkey2:this._subkey1;Oe(this._buffer.data,e.data),this._bufferPos<A.SIZE&&(this._buffer.data[this._bufferPos]^=128),await this._cipher.encryptBlock(this._buffer),this._finished=!0}return this._buffer.data}};var he=class s{static async importKey(e,t){let i=await fe.importKey(e,t),r=await ce.importKey(e,t);return new s(i,r)}constructor(e,t){this.cmacFactory=e,this._ctr=t}async seal(e){let t=A.SIZE+e.length,i=new Uint8Array(t),r=await this._s2v(e);return i.set(r),ci(r),i.set(await this._ctr.encryptCtr(r,e),r.length),i}async open(e){if(e.length<A.SIZE)throw new le("AES-SIV: ciphertext is truncated");let t=e.subarray(0,A.SIZE),i=new Uint8Array(A.SIZE);i.set(t),ci(i);let r=await this._ctr.encryptCtr(i,e.subarray(A.SIZE)),n=await this._s2v(r);if(!li(n,t))throw Ce(r),new le("AES-SIV: ciphertext verification failure!");return r}async _s2v(e){let t=this.cmacFactory(),i=new A,r=new A;if(await t.update(i.data),r.data.set(await t.finish()),t=this.cmacFactory(),i.clear(),e.length>=A.SIZE){let n=e.length-A.SIZE;i.data.set(e.subarray(n)),await t.update(e.subarray(0,n))}else i.data.set(e),i.data[e.length]=128,r.dbl();return Oe(i.data,r.data),await t.update(i.data),t.finish()}};function ci(s){s[s.length-8]&=127,s[s.length-4]&=127}var lt=3,ct=3,ui=32,nt=32768,st=8,fi=1,Zi={N:nt,r:st,p:fi,maxmem:128*nt*st*2};async function ut(s,e){s=s.normalize("NFKC"),e=e.normalize("NFKC");let t=require&&require("crypto");if(t){let r=await new Promise((n,o)=>{t.scrypt(Buffer.from(s,"utf8"),Buffer.from(e,"utf8"),ui,Zi,(a,l)=>{a?o(a):n(l)})});return re(r)}let i=await window.scrypt.scrypt(new Uint8Array(L(s)),new Uint8Array(L(e)),nt,st,fi,ui);return se(i)}async function hi(s,e,t){switch(s){case 0:return ot.init(e);case 2:case 3:return at.init(e,t,s);default:throw new Error("Encryption version not supported")}}async function ft(s,e,t){switch(t){case 0:return R(await oe(s));case 2:case 3:let i=await x.importKey("raw",s,"HKDF",!1,["deriveKey"]),r=await x.deriveKey({name:"HKDF",salt:L(e),info:L("ObsidianKeyHash"),hash:"SHA-256"},i,{name:"AES-CBC",length:256},!0,["encrypt"]);return R(await x.exportKey("raw",r));default:throw new Error("Encryption version not supported")}}var ot=class s{constructor(e,t){this.encryptionVersion=0;this.keyHash=e,this.cryptoKey=t}static async init(e){if(e.byteLength!==32)throw new Error("Invalid encryption key");let t=R(await oe(e)),i=await Ht(e);return new s(t,i)}async deterministicEncodeStr(e){let t=L(e),i=await oe(t),r=new Uint8Array(i,0,12);return R(await _e(t,this.cryptoKey,r))}async deterministicDecodeStr(e){let t=Ze(e);return K(await Te(t,this.cryptoKey))}async encrypt(e){return _e(e,this.cryptoKey)}async decrypt(e){return Te(e,this.cryptoKey)}},at=class s{constructor(e,t,i,r){if(this.keyHash=e,this.cryptoKey=t,this.siv=i,r!==2&&r!==3)throw new Error("Invalid encryption version");this.encryptionVersion=r}static async init(e,t,i){if(e.byteLength!==32)throw new Error("Invalid encryption key");let r=await x.importKey("raw",e,"HKDF",!1,["deriveKey"]),n=L(t),o=await x.deriveKey({name:"HKDF",salt:n,info:L("ObsidianKeyHash"),hash:"SHA-256"},r,{name:"AES-CBC",length:256},!0,["encrypt"]),a=R(await x.exportKey("raw",o)),l=await he.importKey(r,n),c=await x.deriveKey({name:"HKDF",salt:new Uint8Array,info:L("ObsidianAesGcm"),hash:"SHA-256"},r,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return new s(a,c,l,i)}async deterministicEncodeStr(e){let t=L(e),i=await this.siv.seal(new Uint8Array(t));return R(se(i))}async deterministicDecodeStr(e){let t=Ze(e),i=await this.siv.open(new Uint8Array(t));return K(se(i))}async encrypt(e){return _e(e,this.cryptoKey)}async decrypt(e){return Te(e,this.cryptoKey)}};var Re=class{constructor(e,t,i){this.refreshTimer=null;this.lockTime=0;this.fs=e,this.path=t,this.lockPath=i}acquire(){let{fs:e,lockPath:t}=this,i=this.path.dirname(t);e.mkdirSync(i,{recursive:!0});try{e.mkdirSync(t)}catch(r){if(r.code!=="EEXIST")throw r;let n;try{n=e.statSync(t).mtimeMs}catch(o){if(o.code==="ENOENT")return this.acquire();throw o}if(Date.now()-n<5e3)throw new J}if(this.lockTime=pi(),this.touch(),!this.verify())throw new J;this.refreshTimer=setInterval(()=>{this.lockTime=pi(),this.touch()},1e3)}release(){this.refreshTimer&&(clearInterval(this.refreshTimer),this.refreshTimer=null);try{this.verify()&&this.fs.rmdirSync(this.lockPath)}catch{}}touch(){try{let e=this.lockTime/1e3;this.fs.utimesSync(this.lockPath,e,e)}catch{}}verify(){try{let e=this.fs.statSync(this.lockPath).mtimeMs;return e===this.lockTime||Math.floor(e/1e3)===Math.floor(this.lockTime/1e3)}catch{return!1}}},J=class extends Error{constructor(){super("Failed to acquire lock.")}};function pi(){let s=Date.now();return s%1e3===0&&(s+=1),s}var C=new gi.Command,Z="ob";C.name(Z).description("Headless client for Obsidian services").version("1.0.0");C.command("login").description("Login to Obsidian account or display login status").option("--email <email>","Email address (prompted if omitted)").option("--password <password>","Password (prompted if omitted)").option("--mfa <code>","MFA code (prompted if omitted)").action(async s=>{try{let e=await I.getToken();if(e&&!s.email&&!s.password){let n=await ri(e);if(n){console.log(`Logged in as ${n.name} (${n.email})`);return}}if(e){try{await it(e)}catch{}await I.deleteToken()}let t=s.email||await pe("Email: ",!0),i=s.password||await pe("Password: "),r;try{r=await tt(t,i,s.mfa||"")}catch(n){if(n instanceof ae&&n.error.includes("2FA code")){if(n.error.includes("2FA code is incorrect"))throw n;let o=await pe("2FA code: ");r=await tt(t,i,o)}else throw n}await I.setToken(r.token),console.log(`Logged in as ${r.name} (${r.email})`)}catch(e){console.error("Login failed:",e.message||e),process.exit(2)}});C.command("logout").description("Logout from Obsidian account").action(async()=>{try{let s=await I.getToken();if(!s){console.log("No account logged in.");return}try{await it(s)}catch{}await I.deleteToken(),console.log("Logged out.")}catch(s){console.error("Logout failed:",s.message||s),process.exit(1)}});C.command("sync-list-remote").description("List available remote vaults").action(async()=>{try{let s=await Ne();console.log("Fetching vaults...");let e=await rt(s,ct);if(e.vaults.length===0&&e.shared.length===0){console.log("No vaults found.");return}console.log(`
|
|
8
|
+
Vaults:`);let t=i=>console.log(` ${i.id} "${i.name}" (${i.region})`);for(let i of e.vaults)t(i);if(e.shared.length>0){console.log(`
|
|
9
|
+
Shared vaults:`);for(let i of e.shared)t(i)}}catch(s){console.error("Failed to list vaults:",s.message||s),process.exit(1)}});C.command("sync-list-local").description("List locally configured vaults").action(()=>{try{let s=qe();if(s.length===0){console.log("No vaults configured.");return}console.log("Configured vaults:");for(let e of s){let t=Ve(e);t&&(console.log(` ${e}`),console.log(` Path: ${t.vaultPath}`),console.log(` Host: ${t.host}`))}}catch(s){console.error("Failed to list vaults:",s.message||s),process.exit(1)}});C.command("sync-create-remote").description("Create a new remote vault").requiredOption("--name <name>","Vault name").option("--encryption <standard|e2ee>","Encryption").option("--password <encryption-password>","End-to-end encryption password (prompt if omitted)").option("--region <region>","Vault region (leave empty for automatic)").action(async s=>{try{let e=await Ne(),t=s.name,i=s.password;!i&&s.encryption!=="standard"&&(i=await pe("End-to-end encryption password (leave empty for standard encryption): "));let r=s.region||"";if(r){let c=(await ni(e)).regions;if(!c.some(u=>u.value===r)){console.error(`Invalid region: "${r}". Available regions:`);for(let u of c)console.error(` ${u.value} (${u.name})`);process.exit(1)}}console.log("Creating vault...");let n,o=null;if(i){n=R(crypto.getRandomValues(new Uint8Array(16)));let l=await ut(i,n);o=await ft(l,n,lt)}let a=await si(e,t,o,n,r,lt);console.log(`
|
|
10
|
+
Vault created successfully!`),console.log(` Vault ID: ${a.id}`),console.log(` Vault name: ${a.name}`),console.log(` Region: ${a.region}`),console.log(` Encryption: ${i?"end-to-end":"managed"}`),console.log(`
|
|
11
|
+
Run '${Z} sync-setup --vault "${a.id}"' to configure sync.`)}catch(e){console.error("Failed to create vault:",e.message||e),process.exit(1)}});C.command("sync-setup").description("Setup sync from a local path to a remote vault").requiredOption("--vault <vault>","Remote vault ID or name").option("--path <local-path>","Local vault path (use current directory if omitted)").option("--password <encryption-password>","End-to-end encryption password (prompted if omitted)").option("--device-name <name>","Device name to identify this client in the sync version history").option("--config-dir <name>","Config directory name (default: .obsidian)").action(async s=>{try{let e=await Ne(),t=s.vault,i=D.default.resolve(s.path||"."),r=s.password,n=s.deviceName||"",o=Ke(s.configDir);console.log("Fetching vault info...");let a=await rt(e,ct),l=[...a.vaults,...a.shared],c=l.find(b=>b.id===t);if(!c){let b=l.filter(y=>y.name===t);if(b.length===1)c=b[0];else if(b.length>1){console.error(`Multiple vaults named "${t}". Use the vault ID instead:`);for(let y of b)console.error(` ${y.id} "${y.name}"`);process.exit(1)}}c||(console.error(`Vault "${t}" not found.`),process.exit(3));let u=c.id;c.password?r=c.password:r||(r=await pe("End-to-end encryption password: ")),r||(console.error("Failed to validate password."),process.exit(2)),console.log("Setting up...");let p=await ut(r,c.salt),d=await ft(p,c.salt,c.encryption_version);try{await oi(e,u,d,c.host,c.encryption_version)}catch{console.error("Failed to validate password."),process.exit(2)}await I.setKey(u,{key:Gt(p),salt:c.salt,version:c.encryption_version});let F={vaultId:u,vaultName:c.name,vaultPath:i,host:c.host,conflictStrategy:"merge",deviceName:n,configDir:o};Ue(u),je(u,F),console.log(`
|
|
12
|
+
Vault configured successfully!`),ht(F),P.default.existsSync(i)?P.default.readdirSync(i).filter(g=>!g.startsWith(".")).length>0&&(console.log(`
|
|
13
|
+
Warning: Your local vault already contains some notes.`),console.log("If you start syncing, notes in your local vault will be merged with notes from your remote vault."),console.log("In case of conflicts, the most recent version of the note will be preserved.")):P.default.mkdirSync(i,{recursive:!0}),di(),console.log(`
|
|
14
|
+
Run '${Z} sync' to start syncing.`)}catch(e){console.error("Setup failed:",e.message||e),process.exit(1)}});C.command("sync-config").description("Change sync configuration for a vault").option("--path <local-path>","Local vault path (use current directory if omitted)").option("--conflict-strategy <strategy>","Conflict resolution strategy (merge or conflict)").option("--excluded-folders <folders>","Folders to exclude, comma-separated (empty string to clear)").option("--file-types <types>","Attachment types to sync, comma-separated: image, audio, video, pdf, unsupported (empty to clear)").option("--configs <settings>","Config categories to sync, comma-separated: app, appearance, appearance-data, hotkey, core-plugin, core-plugin-data, community-plugin, community-plugin-data (empty to clear)").option("--device-name <name>","Device name to identify this client in the sync version history").option("--config-dir <name>","Config directory name (default: .obsidian)").action(s=>{try{let e=D.default.resolve(s.path||"."),t=te(e);t||(console.error(`No sync configuration found for ${e}`),console.error(`Run '${Z} sync-setup' first.`),process.exit(3));let i=!1;s.conflictStrategy!==void 0&&(s.conflictStrategy!=="merge"&&s.conflictStrategy!=="conflict"&&(console.error(`Invalid conflict strategy: "${s.conflictStrategy}". Must be "merge" or "conflict".`),process.exit(1)),t.conflictStrategy=s.conflictStrategy,i=!0),s.excludedFolders!==void 0&&(s.excludedFolders===""?delete t.ignoreFolders:t.ignoreFolders=s.excludedFolders.split(",").map(r=>r.trim()),i=!0),s.fileTypes!==void 0&&(s.fileTypes===""?delete t.allowTypes:t.allowTypes=Pt(s.fileTypes),i=!0),s.configs!==void 0&&(s.configs===""?delete t.allowSpecialFiles:t.allowSpecialFiles=At(s.configs),i=!0),s.deviceName!==void 0&&(t.deviceName=s.deviceName,i=!0),s.configDir!==void 0&&(t.configDir=Ke(s.configDir),i=!0),i?(je(t.vaultId,t),console.log("Configuration updated:")):console.log("No options provided. Current configuration:"),ht(t)}catch(e){console.error("Config update failed:",e.message||e),process.exit(1)}});C.command("sync-status").description("Show sync status for a vault").option("--path <local-path>","Local vault path (use current directory if omitted)").action(async s=>{try{let e=D.default.resolve(s.path||"."),t=te(e);t||(console.error(`No sync configuration found for ${e}`),process.exit(3)),await I.getToken()||console.log("Not logged in."),await I.getKey(t.vaultId)||console.log("End-to-end encryption key missing."),console.log("Sync Configuration:"),ht(t)}catch(e){console.error("Status check failed:",e.message||e),process.exit(1)}});C.command("sync-unlink").description("Disconnect a vault from sync and remove stored credentials").option("--path <local-path>","Local vault path (use current directory if omitted)").action(async s=>{try{let e=D.default.resolve(s.path||"."),t=te(e);t||(console.error(`No sync configuration found for ${e}`),process.exit(3)),await I.deleteKey(t.vaultId),Dt(t.vaultId),console.log(`Sync configuration removed for ${e}`)}catch(e){console.error("Unlink failed:",e.message||e),process.exit(1)}});C.command("sync").description("Sync a vault").option("--path <local-path>","Local vault path (use current directory if omitted)").option("--continuous","Run in continuous sync mode").action(async s=>{try{let e=D.default.resolve(s.path||"."),t=te(e);t||(console.error(`No sync configuration found for ${e}`),console.error(`Run '${Z} sync-setup' first.`),process.exit(3));let i=await Ne(),r=await I.getKey(t.vaultId);r||(console.error("Encryption key not found. Run setup again."),process.exit(2));let n=await hi(r.version,$t(r.key),r.salt),o=wt(t.vaultId),a=new de(o),l=_t(vt(t.vaultId));t.deviceName||(t.deviceName=We()),(!t.configDir||!t.configDir.startsWith("."))&&(t.configDir=".obsidian"),di();let c=new Re(P.default,D.default,St(t.vaultPath,t.configDir));c.acquire();let u;try{u=new Le({config:t,token:i,encryption:n,stateStore:a,continuous:s.continuous});let p=()=>{console.log(`
|
|
15
|
+
Shutting down...`),u.stop(),c.release(),l(),process.exit(0)};process.on("SIGINT",p),process.on("SIGTERM",p),await u.sync()}finally{u?.stop(),c.release(),l()}process.exit(0)}catch(e){e instanceof J&&(console.error("Another sync instance is already running for this vault."),process.exit(1)),console.error("Sync failed:",e),process.exit(1)}});function ht(s){console.log(` Vault: ${s.vaultName} (${s.vaultId})`),console.log(` Local path: ${s.vaultPath}`),console.log(` Conflict strategy: ${s.conflictStrategy}`),console.log(` Device name: ${s.deviceName||We()}`),s.configDir&&console.log(` Config directory: ${s.configDir}`);let{allowTypes:e,allowSpecialFiles:t,ignoreFolders:i}=s;console.log(` File types: ${(e||G).join(", ")}`),console.log(` Configs: ${t?.length===0?"none (config syncing disabled)":(t||z).join(", ")}`),i&&i.length>0&&console.log(` Excluded folders: ${i.join(", ")}`)}function pe(s,e){return new Promise(t=>{if(process.stdin.isTTY){process.stdout.write(s),process.stdin.setRawMode(!0),process.stdin.resume();let i="",r=()=>{process.stdout.clearLine(0),process.stdout.cursorTo(0),process.stdout.write(s+(e?i:"*".repeat(i.length)))},n=o=>{let a=o.toString();a===`
|
|
16
|
+
`||a==="\r"?(process.stdin.setRawMode(!1),process.stdin.pause(),process.stdin.removeListener("data",n),process.stdout.write(`
|
|
17
|
+
`),t(i)):a===""?(process.stdin.setRawMode(!1),process.exit(1)):a==="\x7F"||a==="\b"?(i=i.slice(0,-1),r()):(i+=a,process.stdout.write(e?a:"*".repeat(a.length)))};process.stdin.on("data",n)}else{let i="";process.stdin.setEncoding("utf-8"),process.stdin.on("data",r=>{i+=r}),process.stdin.on("end",()=>{t(i.trimEnd())}),process.stdin.resume()}})}function di(){(process.platform==="win32"||process.platform==="darwin")&&!mt()&&console.warn("Warning: btime native module failed to load. File creation times will not be preserved.")}async function Ne(){let s=await I.getToken();return s||(console.error(`No account logged in. Run "${Z} login" first.`),process.exit(2)),s}C.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "obsidian-headless",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Headless client for Obsidian services",
|
|
5
|
+
"main": "cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ob": "cli.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=22.0.0"
|
|
11
|
+
},
|
|
12
|
+
"os": [
|
|
13
|
+
"darwin",
|
|
14
|
+
"linux",
|
|
15
|
+
"win32"
|
|
16
|
+
],
|
|
17
|
+
"cpu": [
|
|
18
|
+
"x64",
|
|
19
|
+
"arm64"
|
|
20
|
+
],
|
|
21
|
+
"files": [
|
|
22
|
+
"cli.js",
|
|
23
|
+
"btime",
|
|
24
|
+
"package.json",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"obsidian",
|
|
29
|
+
"sync",
|
|
30
|
+
"vault",
|
|
31
|
+
"markdown",
|
|
32
|
+
"notes"
|
|
33
|
+
],
|
|
34
|
+
"homepage": "https://obsidian.md",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/obsidianmd/obsidian-headless"
|
|
38
|
+
},
|
|
39
|
+
"author": "Dynalist Inc.",
|
|
40
|
+
"license": "UNLICENSED",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"better-sqlite3": "12.6.2",
|
|
43
|
+
"commander": "14.0.3",
|
|
44
|
+
"keytar": "7.9.0"
|
|
45
|
+
}
|
|
46
|
+
}
|