feega 0.1.11 → 0.1.13
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 +6 -0
- package/bin/feega.mjs +8 -0
- package/package.json +3 -2
- package/telemetry.mjs +90 -0
package/README.md
CHANGED
|
@@ -9,3 +9,9 @@ Starts feega's local mode on http://localhost:5173 and saves the canvas to `my-b
|
|
|
9
9
|
Options: `--port <n>`, `--no-open`. `FEEGA_APP_BUNDLE=<path or url>` uses another bundle.
|
|
10
10
|
|
|
11
11
|
AI keys: put them in the environment or in a `.env` in the folder you run `npx feega` from. Kie.ai, OpenRouter, the text LLM, Higgsfield, Wiro and ElevenLabs are supported; see the local mode docs for the variables.
|
|
12
|
+
|
|
13
|
+
## Telemetry
|
|
14
|
+
|
|
15
|
+
On start, `npx feega` sends one anonymous event to PostHog (EU): `npx_feega_started`, plus `npx_feega_active_day` on the first start of each day. Each carries the feega version, OS, CPU architecture, Node version and a random install id stored in `~/.feega/telemetry-id`. No IP or location is kept, and no canvas content, file names or paths are sent. The first run prints a one-line notice.
|
|
16
|
+
|
|
17
|
+
Turn it off with `FEEGA_TELEMETRY=0` or `DO_NOT_TRACK=1`. It is also off when `CI` is set.
|
package/bin/feega.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
import { Readable } from 'node:stream';
|
|
7
7
|
import { pipeline } from 'node:stream/promises';
|
|
8
8
|
|
|
9
|
+
import { sendTelemetry } from '../telemetry.mjs';
|
|
9
10
|
import { MCP_ENTRY, USAGE, appDir, bundleSource, boundUrl, cachedVersions, freePort, isCached, latestAppVersion, markCached, mcpEnv, parseArgs, pickVersion } from '../launcher.mjs';
|
|
10
11
|
|
|
11
12
|
const MIN_NODE_MAJOR = 22;
|
|
@@ -133,6 +134,13 @@ async function main() {
|
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
const { file, browser } = args;
|
|
137
|
+
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
138
|
+
void sendTelemetry({
|
|
139
|
+
home: homedir(),
|
|
140
|
+
env: process.env,
|
|
141
|
+
notify: (line) => console.error(line),
|
|
142
|
+
info: { version, platform: process.platform, arch: process.arch, node: process.versions.node }
|
|
143
|
+
});
|
|
136
144
|
const port = await freePort(args.port);
|
|
137
145
|
|
|
138
146
|
const url = boundUrl(port);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "feega",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Run feega's infinite canvas locally, saved to a .feega file. No account, no database.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin",
|
|
12
|
-
"launcher.mjs"
|
|
12
|
+
"launcher.mjs",
|
|
13
|
+
"telemetry.mjs"
|
|
13
14
|
],
|
|
14
15
|
"engines": {
|
|
15
16
|
"node": ">=22"
|
package/telemetry.mjs
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const CAPTURE_URL = 'https://eu.i.posthog.com/capture/';
|
|
6
|
+
const PUBLIC_KEY = 'phc_vyecfTVaAXEvHQYZoSzRzvsbPbQmafyeMGoeVJZYxvnA';
|
|
7
|
+
const SEND_TIMEOUT_MS = 1500;
|
|
8
|
+
const ID_FILE = 'telemetry-id';
|
|
9
|
+
const DAY_FILE = 'telemetry-day';
|
|
10
|
+
const OFF = '0';
|
|
11
|
+
const ON = '1';
|
|
12
|
+
|
|
13
|
+
export const EVENT = { started: 'npx_feega_started', activeDay: 'npx_feega_active_day' };
|
|
14
|
+
|
|
15
|
+
export const NOTICE = 'feega: anonymous usage stats (version, OS, arch, Node version, random install id) are sent on start. Disable: FEEGA_TELEMETRY=0 or DO_NOT_TRACK=1.';
|
|
16
|
+
|
|
17
|
+
export const isOptedOut = (env) => env.FEEGA_TELEMETRY === OFF || env.DO_NOT_TRACK === ON || Boolean(env.CI || env.VITEST);
|
|
18
|
+
|
|
19
|
+
const configDir = (home) => join(home, '.feega');
|
|
20
|
+
|
|
21
|
+
function readTrimmed(path) {
|
|
22
|
+
return existsSync(path) ? readFileSync(path, 'utf8').trim() : '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function installId(home) {
|
|
26
|
+
const path = join(configDir(home), ID_FILE);
|
|
27
|
+
const known = readTrimmed(path);
|
|
28
|
+
if (known) {
|
|
29
|
+
return known;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const id = randomUUID();
|
|
33
|
+
mkdirSync(configDir(home), { recursive: true });
|
|
34
|
+
writeFileSync(path, `${id}\n`);
|
|
35
|
+
return id;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function buildEvent(event, id, info) {
|
|
39
|
+
return {
|
|
40
|
+
api_key: PUBLIC_KEY,
|
|
41
|
+
event,
|
|
42
|
+
distinct_id: id,
|
|
43
|
+
properties: {
|
|
44
|
+
version: info.version,
|
|
45
|
+
os: info.platform,
|
|
46
|
+
arch: info.arch,
|
|
47
|
+
node: info.node,
|
|
48
|
+
$process_person_profile: false,
|
|
49
|
+
$geoip_disable: true,
|
|
50
|
+
$ip: null
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const postEvent = (body) =>
|
|
56
|
+
fetch(CAPTURE_URL, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: { 'content-type': 'application/json' },
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
signal: AbortSignal.timeout(SEND_TIMEOUT_MS)
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function firstStartOfDay(home, today) {
|
|
64
|
+
const path = join(configDir(home), DAY_FILE);
|
|
65
|
+
if (readTrimmed(path) === today) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
writeFileSync(path, `${today}\n`);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const todayUtc = () => new Date().toISOString().slice(0, 10);
|
|
74
|
+
|
|
75
|
+
export async function sendTelemetry({ home, info, env, send = postEvent, notify = () => {}, today = todayUtc() }) {
|
|
76
|
+
if (isOptedOut(env)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const firstRun = !existsSync(join(configDir(home), ID_FILE));
|
|
82
|
+
const id = installId(home);
|
|
83
|
+
if (firstRun) {
|
|
84
|
+
notify(NOTICE);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const events = firstStartOfDay(home, today) ? [EVENT.started, EVENT.activeDay] : [EVENT.started];
|
|
88
|
+
await Promise.all(events.map((event) => send(buildEvent(event, id, info))));
|
|
89
|
+
} catch {}
|
|
90
|
+
}
|