git-daemon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +18 -0
- package/README.md +143 -0
- package/config.schema.json +180 -0
- package/design.md +481 -0
- package/logo.png +0 -0
- package/openapi.yaml +678 -0
- package/package.json +41 -0
- package/src/app.ts +459 -0
- package/src/approvals.ts +35 -0
- package/src/config.ts +104 -0
- package/src/context.ts +64 -0
- package/src/daemon.ts +22 -0
- package/src/deps.ts +134 -0
- package/src/errors.ts +76 -0
- package/src/git.ts +160 -0
- package/src/jobs.ts +194 -0
- package/src/logger.ts +26 -0
- package/src/os.ts +45 -0
- package/src/pairing.ts +52 -0
- package/src/process.ts +55 -0
- package/src/security.ts +80 -0
- package/src/tokens.ts +95 -0
- package/src/tools.ts +45 -0
- package/src/types.ts +111 -0
- package/src/typings/tree-kill.d.ts +9 -0
- package/src/validation.ts +69 -0
- package/src/workspace.ts +83 -0
- package/tests/app.test.ts +122 -0
- package/tsconfig.json +14 -0
- package/vitest.config.ts +8 -0
package/.eslintrc.cjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
env: {
|
|
4
|
+
node: true,
|
|
5
|
+
es2020: true
|
|
6
|
+
},
|
|
7
|
+
parser: "@typescript-eslint/parser",
|
|
8
|
+
plugins: ["@typescript-eslint", "prettier"],
|
|
9
|
+
extends: [
|
|
10
|
+
"eslint:recommended",
|
|
11
|
+
"plugin:@typescript-eslint/recommended",
|
|
12
|
+
"plugin:prettier/recommended"
|
|
13
|
+
],
|
|
14
|
+
rules: {
|
|
15
|
+
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }]
|
|
16
|
+
},
|
|
17
|
+
ignorePatterns: ["dist/", "node_modules/"]
|
|
18
|
+
};
|
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Git Daemon
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Git Daemon is a local Node.js service that exposes a small, authenticated HTTP API for a trusted web UI to perform Git and developer convenience actions on your machine. It is designed to run on `127.0.0.1` only, enforce a strict Origin allowlist, and sandbox all filesystem access to a configured workspace root.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
- Clone, fetch, and read Git status using your system Git credentials
|
|
10
|
+
- Stream long-running job logs via Server-Sent Events (SSE)
|
|
11
|
+
- Open a repo in the OS file browser, terminal, or VS Code (with approvals)
|
|
12
|
+
- Install dependencies with safer defaults (`--ignore-scripts` by default)
|
|
13
|
+
|
|
14
|
+
## Security model (high level)
|
|
15
|
+
|
|
16
|
+
- **Loopback-only**: binds to `127.0.0.1`
|
|
17
|
+
- **Origin allowlist**: every request must include a matching `Origin`
|
|
18
|
+
- **DNS rebinding protections**: verifies `Host` and remote loopback address
|
|
19
|
+
- **Pairing token**: required for all non-public endpoints
|
|
20
|
+
- **Workspace sandbox**: all paths must resolve inside the configured root
|
|
21
|
+
- **Capability approvals**: required for open-terminal/open-vscode/deps install
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
- Node.js (for running the daemon)
|
|
26
|
+
- Git (for clone/fetch/status)
|
|
27
|
+
- Optional: `code` CLI for VS Code, `pnpm`/`yarn` for dependency installs
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Run the daemon
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm run daemon
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The daemon listens on `http://127.0.0.1:8787` by default.
|
|
42
|
+
|
|
43
|
+
## Pairing flow
|
|
44
|
+
|
|
45
|
+
Pairing is required before using protected endpoints.
|
|
46
|
+
|
|
47
|
+
1. Start pairing:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -H "Origin: https://app.example.com" \
|
|
51
|
+
-H "Content-Type: application/json" \
|
|
52
|
+
-d '{"step":"start"}' \
|
|
53
|
+
http://127.0.0.1:8787/v1/pair
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. Confirm pairing with the code:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
curl -H "Origin: https://app.example.com" \
|
|
60
|
+
-H "Content-Type: application/json" \
|
|
61
|
+
-d '{"step":"confirm","code":"<CODE>"}' \
|
|
62
|
+
http://127.0.0.1:8787/v1/pair
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The response includes `accessToken` to use as `Authorization: Bearer <token>`.
|
|
66
|
+
|
|
67
|
+
## Example usage
|
|
68
|
+
|
|
69
|
+
Check meta:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
curl -H "Origin: https://app.example.com" \
|
|
73
|
+
http://127.0.0.1:8787/v1/meta
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Clone a repo (job):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
curl -X POST \
|
|
80
|
+
-H "Origin: https://app.example.com" \
|
|
81
|
+
-H "Authorization: Bearer <TOKEN>" \
|
|
82
|
+
-H "Content-Type: application/json" \
|
|
83
|
+
-d '{"repoUrl":"git@github.com:owner/repo.git","destRelative":"owner/repo"}' \
|
|
84
|
+
http://127.0.0.1:8787/v1/git/clone
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Stream job logs (SSE):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
curl -N \
|
|
91
|
+
-H "Origin: https://app.example.com" \
|
|
92
|
+
-H "Authorization: Bearer <TOKEN>" \
|
|
93
|
+
http://127.0.0.1:8787/v1/jobs/<JOB_ID>/stream
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
Config is stored in OS-specific directories:
|
|
99
|
+
|
|
100
|
+
- macOS: `~/Library/Application Support/Git Daemon`
|
|
101
|
+
- Linux: `~/.config/git-daemon`
|
|
102
|
+
- Windows: `%APPDATA%\\Git Daemon`
|
|
103
|
+
|
|
104
|
+
You can override the config directory with:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
GIT_DAEMON_CONFIG_DIR=/path/to/config npm run daemon
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Key settings live in `config.json`:
|
|
111
|
+
|
|
112
|
+
- `originAllowlist`: array of allowed UI origins
|
|
113
|
+
- `workspaceRoot`: absolute path to the workspace root
|
|
114
|
+
- `deps.defaultSafer`: defaults to `true` for `--ignore-scripts`
|
|
115
|
+
- `jobs.maxConcurrent` and `jobs.timeoutSeconds`
|
|
116
|
+
|
|
117
|
+
Tokens are stored (hashed) in `tokens.json`. Logs are written under the configured `logging.directory` with rotation.
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
Run tests:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npm test
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Lint:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm run lint
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API reference
|
|
134
|
+
|
|
135
|
+
See `openapi.yaml` for the full contract.
|
|
136
|
+
|
|
137
|
+
## UI developer resources
|
|
138
|
+
|
|
139
|
+
This repo already includes the artifacts needed to build or test a UI client:
|
|
140
|
+
|
|
141
|
+
- `openapi.yaml`: full HTTP contract (routes, schemas, error codes).
|
|
142
|
+
- `design.md`: security model, runtime decisions, and behavior expectations.
|
|
143
|
+
- `config.schema.json`: shape of the daemon config (useful for tooling or UI settings screens).
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://example.com/schemas/git-daemon-config.schema.json",
|
|
4
|
+
"title": "Git Daemon Config",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"configVersion",
|
|
9
|
+
"server",
|
|
10
|
+
"originAllowlist",
|
|
11
|
+
"logging",
|
|
12
|
+
"jobs",
|
|
13
|
+
"deps",
|
|
14
|
+
"pairing",
|
|
15
|
+
"approvals"
|
|
16
|
+
],
|
|
17
|
+
"properties": {
|
|
18
|
+
"configVersion": {
|
|
19
|
+
"type": "integer",
|
|
20
|
+
"minimum": 1
|
|
21
|
+
},
|
|
22
|
+
"server": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"required": [
|
|
26
|
+
"host",
|
|
27
|
+
"port"
|
|
28
|
+
],
|
|
29
|
+
"properties": {
|
|
30
|
+
"host": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"default": "127.0.0.1"
|
|
33
|
+
},
|
|
34
|
+
"port": {
|
|
35
|
+
"type": "integer",
|
|
36
|
+
"minimum": 1,
|
|
37
|
+
"maximum": 65535,
|
|
38
|
+
"default": 8787
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"originAllowlist": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"items": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"format": "uri"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"workspaceRoot": {
|
|
51
|
+
"type": [
|
|
52
|
+
"string",
|
|
53
|
+
"null"
|
|
54
|
+
],
|
|
55
|
+
"description": "Absolute path to the workspace root. Null means not yet configured."
|
|
56
|
+
},
|
|
57
|
+
"pairing": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"required": [
|
|
61
|
+
"tokenTtlDays"
|
|
62
|
+
],
|
|
63
|
+
"properties": {
|
|
64
|
+
"tokenTtlDays": {
|
|
65
|
+
"type": "integer",
|
|
66
|
+
"minimum": 1,
|
|
67
|
+
"maximum": 365,
|
|
68
|
+
"default": 30
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"jobs": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"required": [
|
|
76
|
+
"maxConcurrent",
|
|
77
|
+
"timeoutSeconds"
|
|
78
|
+
],
|
|
79
|
+
"properties": {
|
|
80
|
+
"maxConcurrent": {
|
|
81
|
+
"type": "integer",
|
|
82
|
+
"minimum": 1,
|
|
83
|
+
"default": 1
|
|
84
|
+
},
|
|
85
|
+
"timeoutSeconds": {
|
|
86
|
+
"type": "integer",
|
|
87
|
+
"minimum": 60,
|
|
88
|
+
"default": 3600
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"deps": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"required": [
|
|
96
|
+
"defaultSafer"
|
|
97
|
+
],
|
|
98
|
+
"properties": {
|
|
99
|
+
"defaultSafer": {
|
|
100
|
+
"type": "boolean",
|
|
101
|
+
"default": true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"logging": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"additionalProperties": false,
|
|
108
|
+
"required": [
|
|
109
|
+
"directory",
|
|
110
|
+
"maxFiles",
|
|
111
|
+
"maxBytes"
|
|
112
|
+
],
|
|
113
|
+
"properties": {
|
|
114
|
+
"directory": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"default": "logs"
|
|
117
|
+
},
|
|
118
|
+
"maxFiles": {
|
|
119
|
+
"type": "integer",
|
|
120
|
+
"minimum": 1,
|
|
121
|
+
"default": 5
|
|
122
|
+
},
|
|
123
|
+
"maxBytes": {
|
|
124
|
+
"type": "integer",
|
|
125
|
+
"minimum": 1024,
|
|
126
|
+
"default": 5242880
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"approvals": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"additionalProperties": false,
|
|
133
|
+
"required": [
|
|
134
|
+
"entries"
|
|
135
|
+
],
|
|
136
|
+
"properties": {
|
|
137
|
+
"entries": {
|
|
138
|
+
"type": "array",
|
|
139
|
+
"default": [],
|
|
140
|
+
"items": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"additionalProperties": false,
|
|
143
|
+
"required": [
|
|
144
|
+
"origin",
|
|
145
|
+
"repoPath",
|
|
146
|
+
"capabilities",
|
|
147
|
+
"approvedAt"
|
|
148
|
+
],
|
|
149
|
+
"properties": {
|
|
150
|
+
"origin": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"format": "uri"
|
|
153
|
+
},
|
|
154
|
+
"repoPath": {
|
|
155
|
+
"type": "string"
|
|
156
|
+
},
|
|
157
|
+
"capabilities": {
|
|
158
|
+
"type": "array",
|
|
159
|
+
"minItems": 1,
|
|
160
|
+
"uniqueItems": true,
|
|
161
|
+
"items": {
|
|
162
|
+
"type": "string",
|
|
163
|
+
"enum": [
|
|
164
|
+
"open-terminal",
|
|
165
|
+
"open-vscode",
|
|
166
|
+
"deps/install"
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"approvedAt": {
|
|
171
|
+
"type": "string",
|
|
172
|
+
"format": "date-time"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|