dev-recall 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {dev_recall-0.2.2 → dev_recall-0.2.3}/PKG-INFO +1 -1
- {dev_recall-0.2.2 → dev_recall-0.2.3}/dev_recall.egg-info/PKG-INFO +1 -1
- {dev_recall-0.2.2 → dev_recall-0.2.3}/dev_recall.egg-info/SOURCES.txt +3 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/pyproject.toml +8 -1
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/cli.py +3 -3
- dev_recall-0.2.3/recall/vscode-extension/package.json +42 -0
- dev_recall-0.2.3/recall/vscode-extension/src/extension.ts +238 -0
- dev_recall-0.2.3/recall/vscode-extension/tsconfig.json +14 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/README.md +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/dev_recall.egg-info/dependency_links.txt +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/dev_recall.egg-info/entry_points.txt +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/dev_recall.egg-info/requires.txt +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/dev_recall.egg-info/top_level.txt +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/__init__.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/_hooks.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/__init__.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/ai_chat.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/containers.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/git.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/linux_process.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/linux_session.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/linux_window.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/shell.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/collectors/vscode.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/config.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/daemon.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/daemon_main.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/mcp_server.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/models.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/processor/__init__.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/processor/embedder.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/processor/enricher.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/processor/session.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/query/__init__.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/query/context.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/query/llm.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/query/retriever.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/query/timeparser.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/storage/__init__.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/storage/db.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/recall/storage/vectors.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/setup.cfg +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/tests/test_collectors.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/tests/test_enricher.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/tests/test_query.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/tests/test_session.py +0 -0
- {dev_recall-0.2.2 → dev_recall-0.2.3}/tests/test_storage.py +0 -0
|
@@ -35,6 +35,9 @@ recall/query/timeparser.py
|
|
|
35
35
|
recall/storage/__init__.py
|
|
36
36
|
recall/storage/db.py
|
|
37
37
|
recall/storage/vectors.py
|
|
38
|
+
recall/vscode-extension/package.json
|
|
39
|
+
recall/vscode-extension/tsconfig.json
|
|
40
|
+
recall/vscode-extension/src/extension.ts
|
|
38
41
|
tests/test_collectors.py
|
|
39
42
|
tests/test_enricher.py
|
|
40
43
|
tests/test_query.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dev-recall"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Local-first developer memory layer"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -42,6 +42,13 @@ linux = [
|
|
|
42
42
|
where = ["."]
|
|
43
43
|
include = ["recall*"]
|
|
44
44
|
|
|
45
|
+
[tool.setuptools.package-data]
|
|
46
|
+
recall = ["vscode-extension/**"]
|
|
47
|
+
|
|
48
|
+
# ensures non-.py files are included
|
|
49
|
+
[tool.setuptools]
|
|
50
|
+
include-package-data = true
|
|
51
|
+
|
|
45
52
|
[tool.ruff]
|
|
46
53
|
line-length = 100
|
|
47
54
|
target-version = "py310"
|
|
@@ -12,6 +12,7 @@ import time
|
|
|
12
12
|
from datetime import datetime, timezone
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Optional
|
|
15
|
+
import importlib.resources as pkg_resources
|
|
15
16
|
|
|
16
17
|
import click
|
|
17
18
|
from rich.console import Console
|
|
@@ -66,7 +67,7 @@ _TYPE_STYLES = {
|
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
@click.group()
|
|
69
|
-
@click.version_option()
|
|
70
|
+
@click.version_option(package_name="dev-recall")
|
|
70
71
|
def cli():
|
|
71
72
|
"""Recall — local-first developer memory layer."""
|
|
72
73
|
pass
|
|
@@ -1006,8 +1007,7 @@ def vscode_install():
|
|
|
1006
1007
|
"""Build and install the Recall VS Code extension."""
|
|
1007
1008
|
import shutil
|
|
1008
1009
|
|
|
1009
|
-
ext_dir =
|
|
1010
|
-
# vsce produces name-version.vsix
|
|
1010
|
+
ext_dir = pkg_resources.files("recall") / "vscode-extension" # vsce produces name-version.vsix
|
|
1011
1011
|
vsix_pattern = "recall-vscode-*.vsix"
|
|
1012
1012
|
|
|
1013
1013
|
# Check for npm and code
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "recall-vscode",
|
|
3
|
+
"displayName": "Recall",
|
|
4
|
+
"description": "Developer memory layer — captures workspace activity for dev-recall",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"publisher": "dev-recall",
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.85.0"
|
|
9
|
+
},
|
|
10
|
+
"categories": ["Other"],
|
|
11
|
+
"activationEvents": ["onStartupFinished"],
|
|
12
|
+
"main": "./out/extension.js",
|
|
13
|
+
"contributes": {
|
|
14
|
+
"configuration": {
|
|
15
|
+
"title": "Recall",
|
|
16
|
+
"properties": {
|
|
17
|
+
"recall.port": {
|
|
18
|
+
"type": "number",
|
|
19
|
+
"default": 27182,
|
|
20
|
+
"description": "Port for the Recall daemon HTTP server"
|
|
21
|
+
},
|
|
22
|
+
"recall.enabled": {
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"default": true,
|
|
25
|
+
"description": "Enable/disable the Recall VS Code extension"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"vscode:prepublish": "npm run compile",
|
|
32
|
+
"compile": "tsc -p ./",
|
|
33
|
+
"watch": "tsc -watch -p ./",
|
|
34
|
+
"package": "npm run compile && vsce package"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/vscode": "^1.85.0",
|
|
38
|
+
"@types/node": "^20.0.0",
|
|
39
|
+
"typescript": "^5.0.0",
|
|
40
|
+
"@vscode/vsce": "^3.25.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import * as vscode from 'vscode';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Configuration
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
function getPort(): number {
|
|
10
|
+
// Try reading from config file first, then VS Code setting
|
|
11
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
12
|
+
// Cross-platform config path
|
|
13
|
+
let cfgFile: string;
|
|
14
|
+
if (process.platform === 'darwin') {
|
|
15
|
+
cfgFile = path.join(home, 'Library', 'Application Support', 'dev-recall', 'config.json');
|
|
16
|
+
} else if (process.platform === 'win32') {
|
|
17
|
+
cfgFile = path.join(process.env.APPDATA || '', 'dev-recall', 'config.json');
|
|
18
|
+
} else {
|
|
19
|
+
cfgFile = path.join(home, '.config', 'dev-recall', 'config.json');
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const raw = fs.readFileSync(cfgFile, 'utf-8');
|
|
23
|
+
const cfg = JSON.parse(raw);
|
|
24
|
+
if (cfg.daemon_port) { return cfg.daemon_port; }
|
|
25
|
+
} catch { /* ignore */ }
|
|
26
|
+
|
|
27
|
+
const vsCfg = vscode.workspace.getConfiguration('recall');
|
|
28
|
+
return vsCfg.get<number>('port', 27182);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isEnabled(): boolean {
|
|
32
|
+
return vscode.workspace.getConfiguration('recall').get<boolean>('enabled', true);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// HTTP fire-and-forget
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
async function sendEvent(type: string, data: Record<string, unknown>): Promise<void> {
|
|
40
|
+
if (!isEnabled()) { return; }
|
|
41
|
+
const port = getPort();
|
|
42
|
+
const body = JSON.stringify({ type, ts: new Date().toISOString(), ...data });
|
|
43
|
+
try {
|
|
44
|
+
await fetch(`http://127.0.0.1:${port}/event`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47
|
+
body,
|
|
48
|
+
signal: AbortSignal.timeout(500),
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
// Daemon may not be running — silently ignore
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Active time tracking
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
let lastActivityAt: number = Date.now();
|
|
60
|
+
let activeTimeInterval: ReturnType<typeof setInterval> | undefined;
|
|
61
|
+
|
|
62
|
+
function recordActivity(): void {
|
|
63
|
+
lastActivityAt = Date.now();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Extension activation
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
export function activate(context: vscode.ExtensionContext): void {
|
|
71
|
+
// Report workspace open for each workspace folder
|
|
72
|
+
const folders = vscode.workspace.workspaceFolders;
|
|
73
|
+
if (folders) {
|
|
74
|
+
for (const folder of folders) {
|
|
75
|
+
sendEvent('workspace_open', { workspace: folder.uri.fsPath });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Workspace folders changed
|
|
80
|
+
context.subscriptions.push(
|
|
81
|
+
vscode.workspace.onDidChangeWorkspaceFolders(e => {
|
|
82
|
+
for (const added of e.added) {
|
|
83
|
+
sendEvent('workspace_open', { workspace: added.uri.fsPath });
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// File save
|
|
89
|
+
context.subscriptions.push(
|
|
90
|
+
vscode.workspace.onDidSaveTextDocument(doc => {
|
|
91
|
+
recordActivity();
|
|
92
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(doc.uri);
|
|
93
|
+
sendEvent('file_save', {
|
|
94
|
+
file: doc.uri.fsPath,
|
|
95
|
+
language: doc.languageId,
|
|
96
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
97
|
+
});
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Track activity for active_time reporting
|
|
102
|
+
context.subscriptions.push(
|
|
103
|
+
vscode.window.onDidChangeActiveTextEditor(recordActivity),
|
|
104
|
+
vscode.workspace.onDidChangeTextDocument(recordActivity),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// File lifecycle events
|
|
108
|
+
context.subscriptions.push(
|
|
109
|
+
vscode.workspace.onDidCreateFiles(e => {
|
|
110
|
+
recordActivity();
|
|
111
|
+
for (const uri of e.files) {
|
|
112
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
|
|
113
|
+
sendEvent('file_create', {
|
|
114
|
+
file: uri.fsPath,
|
|
115
|
+
filename: uri.fsPath.split('/').pop() ?? '',
|
|
116
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}),
|
|
120
|
+
vscode.workspace.onDidDeleteFiles(e => {
|
|
121
|
+
recordActivity();
|
|
122
|
+
for (const uri of e.files) {
|
|
123
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
|
|
124
|
+
sendEvent('file_delete', {
|
|
125
|
+
file: uri.fsPath,
|
|
126
|
+
filename: uri.fsPath.split('/').pop() ?? '',
|
|
127
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}),
|
|
131
|
+
vscode.workspace.onDidRenameFiles(e => {
|
|
132
|
+
recordActivity();
|
|
133
|
+
for (const { oldUri, newUri } of e.files) {
|
|
134
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(newUri);
|
|
135
|
+
sendEvent('file_rename', {
|
|
136
|
+
old_file: oldUri.fsPath,
|
|
137
|
+
new_file: newUri.fsPath,
|
|
138
|
+
old_filename: oldUri.fsPath.split('/').pop() ?? '',
|
|
139
|
+
new_filename: newUri.fsPath.split('/').pop() ?? '',
|
|
140
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Debug session events
|
|
147
|
+
context.subscriptions.push(
|
|
148
|
+
vscode.debug.onDidStartDebugSession(session => {
|
|
149
|
+
const workspace = session.workspaceFolder?.uri.fsPath ?? '';
|
|
150
|
+
sendEvent('debug_session_start', {
|
|
151
|
+
name: session.name,
|
|
152
|
+
debug_type: session.type,
|
|
153
|
+
workspace,
|
|
154
|
+
});
|
|
155
|
+
}),
|
|
156
|
+
vscode.debug.onDidTerminateDebugSession(session => {
|
|
157
|
+
const workspace = session.workspaceFolder?.uri.fsPath ?? '';
|
|
158
|
+
sendEvent('debug_session_end', {
|
|
159
|
+
name: session.name,
|
|
160
|
+
debug_type: session.type,
|
|
161
|
+
workspace,
|
|
162
|
+
});
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Test task events (tasks in the Test group)
|
|
167
|
+
context.subscriptions.push(
|
|
168
|
+
vscode.tasks.onDidStartTask(e => {
|
|
169
|
+
if (e.execution.task.group === vscode.TaskGroup.Test) {
|
|
170
|
+
const scope = e.execution.task.scope;
|
|
171
|
+
const workspace = (scope && typeof scope !== 'number')
|
|
172
|
+
? (scope as vscode.WorkspaceFolder).uri.fsPath : '';
|
|
173
|
+
sendEvent('test_run_start', {
|
|
174
|
+
name: e.execution.task.name,
|
|
175
|
+
workspace,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}),
|
|
179
|
+
vscode.tasks.onDidEndTaskProcess(e => {
|
|
180
|
+
if (e.execution.task.group === vscode.TaskGroup.Test) {
|
|
181
|
+
const scope = e.execution.task.scope;
|
|
182
|
+
const workspace = (scope && typeof scope !== 'number')
|
|
183
|
+
? (scope as vscode.WorkspaceFolder).uri.fsPath : '';
|
|
184
|
+
sendEvent('test_run_finish', {
|
|
185
|
+
name: e.execution.task.name,
|
|
186
|
+
exit_code: e.exitCode ?? 0,
|
|
187
|
+
workspace,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Report active time every 5 minutes
|
|
194
|
+
activeTimeInterval = setInterval(() => {
|
|
195
|
+
const secondsSinceActivity = (Date.now() - lastActivityAt) / 1000;
|
|
196
|
+
// Only report if user was active in the last 5 minutes
|
|
197
|
+
if (secondsSinceActivity < 300) {
|
|
198
|
+
const workspace = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '';
|
|
199
|
+
sendEvent('active_time', {
|
|
200
|
+
workspace,
|
|
201
|
+
seconds_active: 300 - secondsSinceActivity,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}, 5 * 60 * 1000);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Extension deactivation
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
export function deactivate(): void {
|
|
212
|
+
if (activeTimeInterval) {
|
|
213
|
+
clearInterval(activeTimeInterval);
|
|
214
|
+
}
|
|
215
|
+
const folders = vscode.workspace.workspaceFolders;
|
|
216
|
+
if (folders) {
|
|
217
|
+
for (const folder of folders) {
|
|
218
|
+
// Fire-and-forget — deactivation is synchronous, best effort
|
|
219
|
+
const port = getPort();
|
|
220
|
+
const body = JSON.stringify({
|
|
221
|
+
type: 'workspace_close',
|
|
222
|
+
ts: new Date().toISOString(),
|
|
223
|
+
workspace: folder.uri.fsPath,
|
|
224
|
+
});
|
|
225
|
+
try {
|
|
226
|
+
// Synchronous best-effort using Node.js http module
|
|
227
|
+
const http = require('http');
|
|
228
|
+
const req = http.request({
|
|
229
|
+
hostname: '127.0.0.1', port, path: '/event', method: 'POST',
|
|
230
|
+
headers: { 'Content-Type': 'application/json' },
|
|
231
|
+
timeout: 200,
|
|
232
|
+
});
|
|
233
|
+
req.write(body);
|
|
234
|
+
req.end();
|
|
235
|
+
} catch { /* ignore */ }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"outDir": "./out",
|
|
6
|
+
"lib": ["ES2020"],
|
|
7
|
+
"sourceMap": true,
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true
|
|
12
|
+
},
|
|
13
|
+
"exclude": ["node_modules", ".vscode-test"]
|
|
14
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|