novac 2.1.1 → 2.2.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/LICENSE +1 -1
- package/README.md +0 -0
- package/demo.nv +0 -0
- package/demo_builtins.nv +0 -0
- package/demo_http.nv +0 -0
- package/examples/bf.nv +69 -0
- package/examples/math.nv +21 -0
- package/kits/birdAPI/kitdef.js +954 -0
- package/kits/kitRNG/kitdef.js +740 -0
- package/kits/kitSSH/kitdef.js +1272 -0
- package/kits/kitadb/kitdef.js +606 -0
- package/kits/kitai/kitdef.js +2185 -0
- package/kits/kitcanvas/kitdef.js +914 -0
- package/kits/kitclippy/kitdef.js +925 -0
- package/kits/kitgps/kitdef.js +1862 -0
- package/kits/kitlibproc/kitdef.js +3 -2
- package/kits/kitmorse/kitdef.js +229 -0
- package/kits/kitmpatch/kitdef.js +906 -0
- package/kits/kitnet/kitdef.js +1401 -0
- package/kits/kitproto/kitdef.js +613 -0
- package/kits/kitqr/kitdef.js +637 -0
- package/kits/kitrequire/kitdef.js +1599 -0
- package/kits/libtea/kitdef.js +2691 -0
- package/kits/libterm/kitdef.js +2 -0
- package/novac/LICENSE +21 -0
- package/novac/README.md +1823 -0
- package/novac/bin/novac +950 -0
- package/novac/bin/nvc +522 -0
- package/novac/bin/nvml +542 -0
- package/novac/demo.nv +245 -0
- package/novac/demo_builtins.nv +209 -0
- package/novac/demo_http.nv +62 -0
- package/novac/examples/bf.nv +69 -0
- package/novac/examples/math.nv +21 -0
- package/novac/kits/kitai/kitdef.js +2185 -0
- package/novac/kits/kitansi/kitdef.js +1402 -0
- package/novac/kits/kitformat/kitdef.js +1485 -0
- package/novac/kits/kitgps/kitdef.js +1862 -0
- package/novac/kits/kitlibfs/kitdef.js +231 -0
- package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
- package/novac/kits/kitmatrix/ex.js +19 -0
- package/novac/kits/kitmatrix/kitdef.js +960 -0
- package/novac/kits/kitmpatch/kitdef.js +906 -0
- package/novac/kits/kitnovacweb/README.md +1572 -0
- package/novac/kits/kitnovacweb/demo.nv +12 -0
- package/novac/kits/kitnovacweb/demo.nvml +71 -0
- package/novac/kits/kitnovacweb/index.nova +12 -0
- package/novac/kits/kitnovacweb/kitdef.js +692 -0
- package/novac/kits/kitnovacweb/nova.kit.json +8 -0
- package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
- package/novac/kits/kitnovacweb/nvml/index.js +67 -0
- package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
- package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
- package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
- package/novac/kits/kitparse/kitdef.js +1688 -0
- package/novac/kits/kitregex++/kitdef.js +1353 -0
- package/novac/kits/kitrequire/kitdef.js +1599 -0
- package/novac/kits/kitx11/kitdef.js +1 -0
- package/novac/kits/kitx11/kitx11.js +2472 -0
- package/novac/kits/kitx11/kitx11_conn.js +948 -0
- package/novac/kits/kitx11/kitx11_worker.js +121 -0
- package/novac/kits/libterm/ex.js +285 -0
- package/novac/kits/libterm/kitdef.js +1927 -0
- package/novac/node_modules/chalk/license +9 -0
- package/novac/node_modules/chalk/package.json +83 -0
- package/novac/node_modules/chalk/readme.md +297 -0
- package/novac/node_modules/chalk/source/index.d.ts +325 -0
- package/novac/node_modules/chalk/source/index.js +225 -0
- package/novac/node_modules/chalk/source/utilities.js +33 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/novac/node_modules/commander/LICENSE +22 -0
- package/novac/node_modules/commander/Readme.md +1176 -0
- package/novac/node_modules/commander/esm.mjs +16 -0
- package/novac/node_modules/commander/index.js +24 -0
- package/novac/node_modules/commander/lib/argument.js +150 -0
- package/novac/node_modules/commander/lib/command.js +2777 -0
- package/novac/node_modules/commander/lib/error.js +39 -0
- package/novac/node_modules/commander/lib/help.js +747 -0
- package/novac/node_modules/commander/lib/option.js +380 -0
- package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/novac/node_modules/commander/package-support.json +19 -0
- package/novac/node_modules/commander/package.json +82 -0
- package/novac/node_modules/commander/typings/esm.d.mts +3 -0
- package/novac/node_modules/commander/typings/index.d.ts +1113 -0
- package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
- package/novac/node_modules/node-addon-api/README.md +95 -0
- package/novac/node_modules/node-addon-api/common.gypi +21 -0
- package/novac/node_modules/node-addon-api/except.gypi +25 -0
- package/novac/node_modules/node-addon-api/index.js +14 -0
- package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
- package/novac/node_modules/node-addon-api/napi.h +3364 -0
- package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
- package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
- package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/novac/node_modules/node-addon-api/package-support.json +21 -0
- package/novac/node_modules/node-addon-api/package.json +480 -0
- package/novac/node_modules/node-addon-api/tools/README.md +73 -0
- package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/novac/node_modules/serialize-javascript/LICENSE +27 -0
- package/novac/node_modules/serialize-javascript/README.md +149 -0
- package/novac/node_modules/serialize-javascript/index.js +297 -0
- package/novac/node_modules/serialize-javascript/package.json +33 -0
- package/novac/package.json +27 -0
- package/novac/scripts/update-bin.js +24 -0
- package/novac/src/core/bstd.js +1035 -0
- package/novac/src/core/config.js +155 -0
- package/novac/src/core/describe.js +187 -0
- package/novac/src/core/emitter.js +499 -0
- package/novac/src/core/error.js +86 -0
- package/novac/src/core/executor.js +5606 -0
- package/novac/src/core/formatter.js +686 -0
- package/novac/src/core/lexer.js +1026 -0
- package/novac/src/core/nova_builtins.js +717 -0
- package/novac/src/core/nova_thread_worker.js +166 -0
- package/novac/src/core/parser.js +2181 -0
- package/novac/src/core/types.js +112 -0
- package/novac/src/index.js +28 -0
- package/novac/src/runtime/stdlib.js +244 -0
- package/package.json +3 -2
- package/scripts/update-bin.js +0 -0
- package/src/core/bstd.js +835 -361
- package/src/core/executor.js +427 -246
- package/src/core/lexer.js +19 -2
- package/src/core/parser.js +13 -0
- package/src/index.js +0 -0
- package/examples/example-project/README.md +0 -3
- package/examples/example-project/src/main.nova +0 -3
- package/src/core/environment.js +0 -0
- /package/{kits → novac/kits}/libtea/tf.js +0 -0
- /package/{examples/example-project/bin/example-project.nv → novac/node_modules/node-addon-api/nothing.c} +0 -0
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
// kitdef.js — BirdAPI Kit
|
|
2
|
+
// Surveillance-drone observation and management framework for tracking, identifying,
|
|
3
|
+
// and interacting with avian-class autonomous aerial units ("birds").
|
|
4
|
+
//
|
|
5
|
+
// "We all know birds aren't real, they're just programmable drones."
|
|
6
|
+
//
|
|
7
|
+
// Core architecture:
|
|
8
|
+
// BirdAPI
|
|
9
|
+
// ├── birds
|
|
10
|
+
// ├── surveillance
|
|
11
|
+
// ├── telemetry
|
|
12
|
+
// ├── firmware
|
|
13
|
+
// ├── migration
|
|
14
|
+
// ├── flock
|
|
15
|
+
// ├── audio
|
|
16
|
+
// ├── camera
|
|
17
|
+
// ├── diagnostics
|
|
18
|
+
// └── government
|
|
19
|
+
|
|
20
|
+
// ── Internal drone registry ──────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
const _SPECIES = [
|
|
23
|
+
"pigeon", "sparrow", "crow", "seagull", "robin",
|
|
24
|
+
"hawk", "pelican", "flamingo", "owl", "duck",
|
|
25
|
+
"starling", "finch", "hummingbird", "eagle", "magpie",
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const _AGENCIES = [
|
|
29
|
+
"classified", "NSA", "CIA", "FBI", "DARPA",
|
|
30
|
+
"DHS", "NRO", "classified", "classified", "classified",
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const _FIRMWARE_VERSIONS = [
|
|
34
|
+
"v3.0.1", "v3.1.4", "v4.0.0", "v4.2.1", "v4.9.9-beta", "v5.0.0-CLASSIFIED",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
let _registry = [];
|
|
38
|
+
let _nextSerial = 1000;
|
|
39
|
+
let _transmissionLog = [];
|
|
40
|
+
let _flockSyncActive = false;
|
|
41
|
+
let _mothershipOnline = true;
|
|
42
|
+
let _nightModeActive = false;
|
|
43
|
+
|
|
44
|
+
function _makeBird(overrides = {}) {
|
|
45
|
+
const serial = _nextSerial++;
|
|
46
|
+
const specie = overrides.specie || _SPECIES[Math.floor(Math.random() * _SPECIES.length)];
|
|
47
|
+
const fake = overrides.fake !== undefined ? overrides.fake : Math.random() < 0.94; // 94% are drones
|
|
48
|
+
const id = `${specie.slice(0, 4).toUpperCase()}-${serial}`;
|
|
49
|
+
return {
|
|
50
|
+
id,
|
|
51
|
+
specie,
|
|
52
|
+
fake,
|
|
53
|
+
firmware: _FIRMWARE_VERSIONS[Math.floor(Math.random() * _FIRMWARE_VERSIONS.length)],
|
|
54
|
+
battery: Math.floor(Math.random() * 100),
|
|
55
|
+
signal: fake ? Math.floor(60 + Math.random() * 40) : 0,
|
|
56
|
+
charging: false,
|
|
57
|
+
position: {
|
|
58
|
+
lat: parseFloat((Math.random() * 180 - 90).toFixed(3)),
|
|
59
|
+
lon: parseFloat((Math.random() * 360 - 180).toFixed(3)),
|
|
60
|
+
},
|
|
61
|
+
velocity: parseFloat((Math.random() * 25).toFixed(1)),
|
|
62
|
+
altitude: Math.floor(Math.random() * 300),
|
|
63
|
+
agency: fake ? _AGENCIES[Math.floor(Math.random() * _AGENCIES.length)] : "N/A (organic)",
|
|
64
|
+
serial: fake ? `AV-DRN-${serial}` : `BIO-ORG-${serial}`,
|
|
65
|
+
cameraEnabled: fake ? Math.random() > 0.2 : false,
|
|
66
|
+
microphoneEnabled: fake ? Math.random() > 0.3 : false,
|
|
67
|
+
lastTransmission: fake ? Math.floor(Date.now() / 1000 - Math.random() * 3600) : null,
|
|
68
|
+
_recordings: [],
|
|
69
|
+
_frames: [],
|
|
70
|
+
_flightPath: [],
|
|
71
|
+
_threatLevel: fake ? Math.floor(Math.random() * 10) : 0,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function _findBird(id) {
|
|
76
|
+
const bird = _registry.find(b => b.id === id);
|
|
77
|
+
if (!bird) throw new Error(`No unit found with id "${id}". It may have landed. Or been recalled.`);
|
|
78
|
+
return bird;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function _log(msg) {
|
|
82
|
+
_transmissionLog.push({ timestamp: Math.floor(Date.now() / 1000), message: msg });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Pre-populate registry with a reasonable flock
|
|
86
|
+
for (let i = 0; i < 412; i++) _registry.push(_makeBird());
|
|
87
|
+
|
|
88
|
+
// ── Export ───────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
module.exports = {
|
|
91
|
+
kitdef: {
|
|
92
|
+
|
|
93
|
+
name: "BirdAPI",
|
|
94
|
+
version: "1.0.0",
|
|
95
|
+
description: "Surveillance-drone observation and management framework for avian-class autonomous aerial units.",
|
|
96
|
+
classification: "UNCLASSIFIED // FOR NOW",
|
|
97
|
+
|
|
98
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
99
|
+
// SCANNING & DETECTION
|
|
100
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Perform a full aerial scan of the current environment.
|
|
104
|
+
* Returns all known units in the registry.
|
|
105
|
+
* @returns {Bird[]}
|
|
106
|
+
*/
|
|
107
|
+
scan() {
|
|
108
|
+
_log("Full aerial scan initiated.");
|
|
109
|
+
return _registry.slice();
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Find all birds within a given radius of the observer's position.
|
|
114
|
+
* @param {number} distance
|
|
115
|
+
* @param {'km'|'mi'|'m'} unit - Default 'km'
|
|
116
|
+
* @returns {Bird[]}
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* const nearby = BirdAPI.radius(50, 'km');
|
|
120
|
+
* nearby.forEach((bird) => print(`[${bird.specie}]: ${bird.fake}\n`));
|
|
121
|
+
*/
|
|
122
|
+
radius(distance, unit = "km") {
|
|
123
|
+
const factors = { km: 1, mi: 1.609, m: 0.001 };
|
|
124
|
+
const factor = factors[unit] ?? 1;
|
|
125
|
+
const km = distance * factor;
|
|
126
|
+
// Simulate: closer distance catches fewer birds
|
|
127
|
+
const ratio = Math.min(1, km / 500);
|
|
128
|
+
const count = Math.floor(_registry.length * ratio);
|
|
129
|
+
_log(`Radius scan: ${distance}${unit} — ${count} units detected.`);
|
|
130
|
+
return _registry.slice(0, count);
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Search for birds matching a filter predicate.
|
|
135
|
+
* @param {function} predicate - (bird) => boolean
|
|
136
|
+
* @returns {Bird[]}
|
|
137
|
+
*/
|
|
138
|
+
find(predicate) {
|
|
139
|
+
return _registry.filter(predicate);
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Find a specific bird by its unit ID.
|
|
144
|
+
* @param {string} id
|
|
145
|
+
* @returns {Bird}
|
|
146
|
+
*/
|
|
147
|
+
findById(id) {
|
|
148
|
+
return _findBird(id);
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Find all birds of a given species classification.
|
|
153
|
+
* @param {string} specie
|
|
154
|
+
* @returns {Bird[]}
|
|
155
|
+
*/
|
|
156
|
+
findBySpecie(specie) {
|
|
157
|
+
return _registry.filter(b => b.specie === specie.toLowerCase());
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* List all registered units.
|
|
162
|
+
* @returns {Bird[]}
|
|
163
|
+
*/
|
|
164
|
+
list() {
|
|
165
|
+
return _registry.slice();
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Return aggregate diagnostics across all registered units.
|
|
170
|
+
* @returns {object}
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* BirdAPI.stats();
|
|
174
|
+
* // Birds detected: 412, Fake: 398, Real: 14 ...
|
|
175
|
+
*/
|
|
176
|
+
stats() {
|
|
177
|
+
const total = _registry.length;
|
|
178
|
+
const fake = _registry.filter(b => b.fake).length;
|
|
179
|
+
const real = total - fake;
|
|
180
|
+
const avgBattery = Math.round(_registry.reduce((s, b) => s + b.battery, 0) / total);
|
|
181
|
+
const govLinked = _registry.filter(b => b.agency !== "N/A (organic)").length;
|
|
182
|
+
return {
|
|
183
|
+
"Birds detected": total,
|
|
184
|
+
"Fake": fake,
|
|
185
|
+
"Real": real,
|
|
186
|
+
"Government-linked": "classified",
|
|
187
|
+
"Average battery": `${avgBattery}%`,
|
|
188
|
+
"Flock sync active": _flockSyncActive,
|
|
189
|
+
"Night mode": _nightModeActive,
|
|
190
|
+
"Mothership status": _mothershipOnline ? "ONLINE" : "OFFLINE",
|
|
191
|
+
"Transmission log entries": _transmissionLog.length,
|
|
192
|
+
"_internal_gov_count": govLinked, // shh
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Return the total count of registered units.
|
|
198
|
+
* @returns {number}
|
|
199
|
+
*/
|
|
200
|
+
count() {
|
|
201
|
+
return _registry.length;
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
205
|
+
// VERIFICATION
|
|
206
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Filter and return only confirmed synthetic drone units.
|
|
210
|
+
* @param {Bird[]} [birds] - Subset to filter. Defaults to full registry.
|
|
211
|
+
* @returns {Bird[]}
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* BirdAPI.radius(25, 'km').detectFake() // chained
|
|
215
|
+
* BirdAPI.detectFake() // full registry
|
|
216
|
+
*/
|
|
217
|
+
detectFake(birds) {
|
|
218
|
+
return (birds || _registry).filter(b => b.fake === true);
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Filter and return only confirmed organic units.
|
|
223
|
+
* (Vanishingly rare. Treat with suspicion anyway.)
|
|
224
|
+
* @param {Bird[]} [birds]
|
|
225
|
+
* @returns {Bird[]}
|
|
226
|
+
*/
|
|
227
|
+
detectReal(birds) {
|
|
228
|
+
return (birds || _registry).filter(b => b.fake === false);
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Perform deep verification of a unit's drone status.
|
|
233
|
+
* Checks firmware, signal, and serial number prefix.
|
|
234
|
+
* @param {string} id
|
|
235
|
+
* @returns {{ isDrone: boolean, confidence: number, evidence: string[] }}
|
|
236
|
+
*/
|
|
237
|
+
verifyDrone(id) {
|
|
238
|
+
const bird = _findBird(id);
|
|
239
|
+
const evidence = [];
|
|
240
|
+
if (bird.fake) evidence.push("Fake flag: true");
|
|
241
|
+
if (bird.signal > 0) evidence.push(`Signal strength: ${bird.signal}% (organics emit no signal)`);
|
|
242
|
+
if (bird.serial.startsWith("AV-DRN")) evidence.push(`Serial prefix AV-DRN confirms manufacture origin`);
|
|
243
|
+
if (bird.firmware !== undefined) evidence.push(`Firmware ${bird.firmware} detected`);
|
|
244
|
+
if (bird.cameraEnabled) evidence.push("Onboard camera active");
|
|
245
|
+
if (bird.microphoneEnabled) evidence.push("Onboard microphone active");
|
|
246
|
+
const confidence = Math.min(100, evidence.length * 18 + Math.random() * 10);
|
|
247
|
+
return { isDrone: bird.fake, confidence: parseFloat(confidence.toFixed(1)), evidence };
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Attempt to verify a unit as organic. Results are almost always negative.
|
|
252
|
+
* @param {string} id
|
|
253
|
+
* @returns {{ isOrganic: boolean, confidence: number, note: string }}
|
|
254
|
+
*/
|
|
255
|
+
verifyOrganic(id) {
|
|
256
|
+
const bird = _findBird(id);
|
|
257
|
+
const confidence = bird.fake ? parseFloat((Math.random() * 8).toFixed(1)) : 94.2;
|
|
258
|
+
return {
|
|
259
|
+
isOrganic: !bird.fake,
|
|
260
|
+
confidence,
|
|
261
|
+
note: bird.fake
|
|
262
|
+
? "Unit exhibits multiple synthetic markers. Organic classification rejected."
|
|
263
|
+
: "Congratulations. You found one. Do not report this.",
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Convenience alias: returns true if the unit is NOT a drone.
|
|
269
|
+
* @param {string} id
|
|
270
|
+
* @returns {boolean}
|
|
271
|
+
*/
|
|
272
|
+
isReal(id) {
|
|
273
|
+
return !_findBird(id).fake;
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Comprehensive reality verification. Runs all checks and returns a report.
|
|
278
|
+
* @param {string} id
|
|
279
|
+
* @returns {object}
|
|
280
|
+
*/
|
|
281
|
+
verifyReality(id) {
|
|
282
|
+
const drone = this.verifyDrone(id);
|
|
283
|
+
const organic = this.verifyOrganic(id);
|
|
284
|
+
return {
|
|
285
|
+
id,
|
|
286
|
+
droneVerification: drone,
|
|
287
|
+
organicVerification: organic,
|
|
288
|
+
verdict: drone.isDrone ? "SYNTHETIC UNIT CONFIRMED" : "ORGANIC (anomalous — flag for review)",
|
|
289
|
+
classificationLevel: "FOUO",
|
|
290
|
+
};
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
294
|
+
// TRACKING & SURVEILLANCE
|
|
295
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Begin tracking a unit. Appends position to its flight path history.
|
|
299
|
+
* @param {string} id
|
|
300
|
+
* @returns {{ id, position, altitude, velocity }}
|
|
301
|
+
*/
|
|
302
|
+
track(id) {
|
|
303
|
+
const bird = _findBird(id);
|
|
304
|
+
bird._flightPath.push({ ...bird.position, altitude: bird.altitude, ts: Date.now() });
|
|
305
|
+
_log(`Tracking initiated: ${id}`);
|
|
306
|
+
return { id: bird.id, position: bird.position, altitude: bird.altitude, velocity: bird.velocity };
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Follow a unit — returns a simulated live telemetry stream (array of snapshots).
|
|
311
|
+
* @param {string} id
|
|
312
|
+
* @param {number} snapshots - Number of telemetry snapshots. Default 5.
|
|
313
|
+
* @returns {object[]}
|
|
314
|
+
*/
|
|
315
|
+
follow(id, snapshots = 5) {
|
|
316
|
+
const bird = _findBird(id);
|
|
317
|
+
const stream = [];
|
|
318
|
+
for (let i = 0; i < snapshots; i++) {
|
|
319
|
+
bird.position.lat += (Math.random() - 0.5) * 0.01;
|
|
320
|
+
bird.position.lon += (Math.random() - 0.5) * 0.01;
|
|
321
|
+
bird.altitude += Math.floor((Math.random() - 0.5) * 10);
|
|
322
|
+
stream.push({ ts: Date.now() + i * 1000, ...bird.position, altitude: bird.altitude });
|
|
323
|
+
}
|
|
324
|
+
return stream;
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Passively monitor a unit and return its current telemetry.
|
|
329
|
+
* @param {string} id
|
|
330
|
+
* @returns {object}
|
|
331
|
+
*/
|
|
332
|
+
monitor(id) {
|
|
333
|
+
const bird = _findBird(id);
|
|
334
|
+
return {
|
|
335
|
+
id: bird.id,
|
|
336
|
+
specie: bird.specie,
|
|
337
|
+
battery: bird.battery,
|
|
338
|
+
signal: bird.signal,
|
|
339
|
+
position: bird.position,
|
|
340
|
+
altitude: bird.altitude,
|
|
341
|
+
velocity: bird.velocity,
|
|
342
|
+
cameraEnabled: bird.cameraEnabled,
|
|
343
|
+
microphoneEnabled: bird.microphoneEnabled,
|
|
344
|
+
lastTransmission: bird.lastTransmission,
|
|
345
|
+
threatLevel: bird._threatLevel,
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Observe a unit without triggering its alert systems.
|
|
351
|
+
* (Same as monitor but quieter. The bird doesn't notice. Probably.)
|
|
352
|
+
* @param {string} id
|
|
353
|
+
* @returns {object}
|
|
354
|
+
*/
|
|
355
|
+
observe(id) {
|
|
356
|
+
return { ...this.monitor(id), _covert: true };
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Intercept a unit's active transmission and return its raw payload.
|
|
361
|
+
* @param {string} id
|
|
362
|
+
* @returns {{ id, payload, encrypted, interceptedAt }}
|
|
363
|
+
*/
|
|
364
|
+
intercept(id) {
|
|
365
|
+
const bird = _findBird(id);
|
|
366
|
+
if (!bird.fake) throw new Error("Cannot intercept organic unit — no transmission hardware.");
|
|
367
|
+
const payload = Buffer
|
|
368
|
+
? Buffer.from(JSON.stringify({ pos: bird.position, alt: bird.altitude })).toString("base64")
|
|
369
|
+
: btoa(JSON.stringify({ pos: bird.position, alt: bird.altitude }));
|
|
370
|
+
_log(`Transmission intercepted: ${id}`);
|
|
371
|
+
return {
|
|
372
|
+
id: bird.id,
|
|
373
|
+
payload,
|
|
374
|
+
encrypted: Math.random() > 0.4,
|
|
375
|
+
interceptedAt: Math.floor(Date.now() / 1000),
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
380
|
+
// DRONE MANAGEMENT (SPAWN / REPLACE / CLONE / REMOVE)
|
|
381
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Spawn a new drone unit and add it to the registry.
|
|
385
|
+
* @param {object} [options] - Partial bird properties to override.
|
|
386
|
+
* @returns {Bird}
|
|
387
|
+
*/
|
|
388
|
+
spawn(options = {}) {
|
|
389
|
+
const bird = _makeBird({ fake: true, ...options });
|
|
390
|
+
_registry.push(bird);
|
|
391
|
+
_log(`Unit spawned: ${bird.id}`);
|
|
392
|
+
return bird;
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Replace a real bird with a drone counterpart.
|
|
397
|
+
* The organic unit is removed from the registry and a synthetic clone takes its place.
|
|
398
|
+
* @param {string} id
|
|
399
|
+
* @returns {{ replaced: string, newUnit: Bird }}
|
|
400
|
+
*/
|
|
401
|
+
replace(id) {
|
|
402
|
+
const bird = _findBird(id);
|
|
403
|
+
const idx = _registry.indexOf(bird);
|
|
404
|
+
const clone = _makeBird({ specie: bird.specie, fake: true });
|
|
405
|
+
_registry[idx] = clone;
|
|
406
|
+
_log(`Organic unit ${id} replaced with drone ${clone.id}.`);
|
|
407
|
+
return { replaced: id, newUnit: clone };
|
|
408
|
+
},
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Remove a unit from the registry entirely.
|
|
412
|
+
* @param {string} id
|
|
413
|
+
* @returns {{ removed: string, remainingCount: number }}
|
|
414
|
+
*/
|
|
415
|
+
remove(id) {
|
|
416
|
+
const idx = _registry.findIndex(b => b.id === id);
|
|
417
|
+
if (idx === -1) throw new Error(`Unit "${id}" not found.`);
|
|
418
|
+
_registry.splice(idx, 1);
|
|
419
|
+
_log(`Unit removed: ${id}`);
|
|
420
|
+
return { removed: id, remainingCount: _registry.length };
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Clone an existing unit. The clone starts with a fresh serial and full battery.
|
|
425
|
+
* @param {string} id
|
|
426
|
+
* @returns {Bird}
|
|
427
|
+
*/
|
|
428
|
+
clone(id) {
|
|
429
|
+
const bird = _findBird(id);
|
|
430
|
+
const cloned = _makeBird({ specie: bird.specie, fake: bird.fake });
|
|
431
|
+
cloned.firmware = bird.firmware;
|
|
432
|
+
cloned.battery = 100;
|
|
433
|
+
_registry.push(cloned);
|
|
434
|
+
_log(`Unit ${id} cloned as ${cloned.id}.`);
|
|
435
|
+
return cloned;
|
|
436
|
+
},
|
|
437
|
+
|
|
438
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
439
|
+
// FLIGHT CONTROL
|
|
440
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Command a grounded unit to take off.
|
|
444
|
+
* @param {string} id
|
|
445
|
+
* @returns {{ id, status, altitude }}
|
|
446
|
+
*/
|
|
447
|
+
takeoff(id) {
|
|
448
|
+
const bird = _findBird(id);
|
|
449
|
+
bird.altitude = 25 + Math.floor(Math.random() * 50);
|
|
450
|
+
bird.velocity = 8 + Math.random() * 5;
|
|
451
|
+
_log(`${id} airborne.`);
|
|
452
|
+
return { id, status: "AIRBORNE", altitude: bird.altitude };
|
|
453
|
+
},
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Command a unit to land at its current position.
|
|
457
|
+
* @param {string} id
|
|
458
|
+
* @returns {{ id, status }}
|
|
459
|
+
*/
|
|
460
|
+
land(id) {
|
|
461
|
+
const bird = _findBird(id);
|
|
462
|
+
bird.altitude = 0;
|
|
463
|
+
bird.velocity = 0;
|
|
464
|
+
_log(`${id} landed.`);
|
|
465
|
+
return { id, status: "GROUNDED" };
|
|
466
|
+
},
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Force-land a unit immediately, bypassing any active mission.
|
|
470
|
+
* Use when battery is critically low or civilian exposure risk is elevated.
|
|
471
|
+
* @param {string} id
|
|
472
|
+
* @returns {{ id, status, warning }}
|
|
473
|
+
*/
|
|
474
|
+
forceLanding(id) {
|
|
475
|
+
const bird = _findBird(id);
|
|
476
|
+
bird.altitude = 0;
|
|
477
|
+
bird.velocity = 0;
|
|
478
|
+
_log(`FORCE LANDING: ${id}`);
|
|
479
|
+
return {
|
|
480
|
+
id,
|
|
481
|
+
status: "EMERGENCY GROUNDED",
|
|
482
|
+
warning: "Civilian visibility risk may be increased. Retrieve unit promptly.",
|
|
483
|
+
};
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Command a unit to hover in place at its current altitude.
|
|
488
|
+
* @param {string} id
|
|
489
|
+
* @returns {{ id, status, position, altitude }}
|
|
490
|
+
*/
|
|
491
|
+
hover(id) {
|
|
492
|
+
const bird = _findBird(id);
|
|
493
|
+
bird.velocity = 0;
|
|
494
|
+
return { id, status: "HOVERING", position: bird.position, altitude: bird.altitude };
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
498
|
+
// MIGRATION
|
|
499
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Simulate a real migration event for a flock.
|
|
503
|
+
* @param {{ from: string, to: string, flockSize: number }} options
|
|
504
|
+
* @returns {object}
|
|
505
|
+
*/
|
|
506
|
+
migrate({ from, to, flockSize }) {
|
|
507
|
+
_log(`Migration: ${flockSize} units moving from ${from} to ${to}.`);
|
|
508
|
+
return {
|
|
509
|
+
from,
|
|
510
|
+
to,
|
|
511
|
+
flockSize,
|
|
512
|
+
estimatedArrival: `${Math.floor(3 + Math.random() * 10)} days`,
|
|
513
|
+
routeClassified: true,
|
|
514
|
+
coverStory: "seasonal migration patterns",
|
|
515
|
+
};
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Simulate a fake migration to reposition surveillance assets.
|
|
520
|
+
* Provides civilian-plausible cover story.
|
|
521
|
+
* @param {{ from: string, to: string, flockSize: number }} options
|
|
522
|
+
* @returns {object}
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* BirdAPI.fakeMigration({ from: "Canada", to: "Mexico", flockSize: 1200 });
|
|
526
|
+
*/
|
|
527
|
+
fakeMigration({ from, to, flockSize }) {
|
|
528
|
+
_log(`FAKE MIGRATION INITIATED: ${flockSize} units repositioning from ${from} to ${to}.`);
|
|
529
|
+
return {
|
|
530
|
+
from,
|
|
531
|
+
to,
|
|
532
|
+
flockSize,
|
|
533
|
+
estimatedArrival: `${Math.floor(3 + Math.random() * 10)} days`,
|
|
534
|
+
operationType: "COVER — ASSET REPOSITIONING",
|
|
535
|
+
publicNarrative: `Ornithologists report unusually large ${_SPECIES[Math.floor(Math.random()*_SPECIES.length)]} migration this season.`,
|
|
536
|
+
classified: true,
|
|
537
|
+
};
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
541
|
+
// CAMERA
|
|
542
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Enable onboard camera for a unit.
|
|
546
|
+
* @param {string} id
|
|
547
|
+
* @returns {{ id, cameraEnabled: true }}
|
|
548
|
+
*/
|
|
549
|
+
enableCamera(id) {
|
|
550
|
+
const bird = _findBird(id);
|
|
551
|
+
bird.cameraEnabled = true;
|
|
552
|
+
return { id, cameraEnabled: true };
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Disable onboard camera for a unit.
|
|
557
|
+
* @param {string} id
|
|
558
|
+
* @returns {{ id, cameraEnabled: false }}
|
|
559
|
+
*/
|
|
560
|
+
disableCamera(id) {
|
|
561
|
+
const bird = _findBird(id);
|
|
562
|
+
bird.cameraEnabled = false;
|
|
563
|
+
return { id, cameraEnabled: false };
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Capture a single frame from a unit's camera.
|
|
568
|
+
* @param {string} id
|
|
569
|
+
* @returns {{ id, frame, resolution, timestamp }}
|
|
570
|
+
*/
|
|
571
|
+
captureFrame(id) {
|
|
572
|
+
const bird = _findBird(id);
|
|
573
|
+
if (!bird.cameraEnabled) throw new Error(`Camera disabled on unit ${id}. Call enableCamera() first.`);
|
|
574
|
+
const frame = {
|
|
575
|
+
id,
|
|
576
|
+
frame: `[FRAME:${Math.random().toString(36).slice(2).toUpperCase()}]`,
|
|
577
|
+
resolution: "4K",
|
|
578
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
579
|
+
gpsTagged: true,
|
|
580
|
+
position: bird.position,
|
|
581
|
+
};
|
|
582
|
+
bird._frames.push(frame);
|
|
583
|
+
return frame;
|
|
584
|
+
},
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Begin streaming video from a unit.
|
|
588
|
+
* Returns a stream descriptor object.
|
|
589
|
+
* @param {string} id
|
|
590
|
+
* @returns {{ id, streamUrl, active, encoding }}
|
|
591
|
+
*/
|
|
592
|
+
streamVideo(id) {
|
|
593
|
+
const bird = _findBird(id);
|
|
594
|
+
if (!bird.cameraEnabled) throw new Error(`Camera disabled on unit ${id}.`);
|
|
595
|
+
_log(`Video stream started: ${id}`);
|
|
596
|
+
return {
|
|
597
|
+
id,
|
|
598
|
+
streamUrl: `classified://stream.gov/${bird.id}?token=${Math.random().toString(36).slice(2)}`,
|
|
599
|
+
active: true,
|
|
600
|
+
encoding: "H.265",
|
|
601
|
+
nightMode: _nightModeActive,
|
|
602
|
+
};
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
606
|
+
// AUDIO
|
|
607
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Enable onboard microphone for a unit.
|
|
611
|
+
* @param {string} id
|
|
612
|
+
* @returns {{ id, microphoneEnabled: true }}
|
|
613
|
+
*/
|
|
614
|
+
enableMicrophone(id) {
|
|
615
|
+
const bird = _findBird(id);
|
|
616
|
+
bird.microphoneEnabled = true;
|
|
617
|
+
return { id, microphoneEnabled: true };
|
|
618
|
+
},
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Disable onboard microphone for a unit.
|
|
622
|
+
* @param {string} id
|
|
623
|
+
* @returns {{ id, microphoneEnabled: false }}
|
|
624
|
+
*/
|
|
625
|
+
disableMicrophone(id) {
|
|
626
|
+
const bird = _findBird(id);
|
|
627
|
+
bird.microphoneEnabled = false;
|
|
628
|
+
return { id, microphoneEnabled: false };
|
|
629
|
+
},
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Begin passive audio surveillance from a unit's microphone.
|
|
633
|
+
* @param {string} id
|
|
634
|
+
* @returns {{ id, listening: true, note }}
|
|
635
|
+
*/
|
|
636
|
+
listen(id) {
|
|
637
|
+
const bird = _findBird(id);
|
|
638
|
+
if (!bird.microphoneEnabled) throw new Error(`Microphone disabled on unit ${id}. Call enableMicrophone() first.`);
|
|
639
|
+
return { id, listening: true, note: "Audio stream open. Do not discuss sensitive topics near birds." };
|
|
640
|
+
},
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Begin recording audio from a unit.
|
|
644
|
+
* @param {string} id
|
|
645
|
+
* @param {number} [durationSeconds] - Default 30.
|
|
646
|
+
* @returns {{ id, recordingId, duration, format }}
|
|
647
|
+
*/
|
|
648
|
+
record(id, durationSeconds = 30) {
|
|
649
|
+
const bird = _findBird(id);
|
|
650
|
+
if (!bird.microphoneEnabled) throw new Error(`Microphone disabled on unit ${id}.`);
|
|
651
|
+
const rec = {
|
|
652
|
+
id,
|
|
653
|
+
recordingId: `REC-${Math.random().toString(36).slice(2).toUpperCase()}`,
|
|
654
|
+
duration: durationSeconds,
|
|
655
|
+
format: "FLAC",
|
|
656
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
657
|
+
};
|
|
658
|
+
bird._recordings.push(rec);
|
|
659
|
+
return rec;
|
|
660
|
+
},
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Play back a stored recording from a unit.
|
|
664
|
+
* @param {string} id
|
|
665
|
+
* @param {string} recordingId
|
|
666
|
+
* @returns {{ id, recordingId, status }}
|
|
667
|
+
*/
|
|
668
|
+
playback(id, recordingId) {
|
|
669
|
+
const bird = _findBird(id);
|
|
670
|
+
const rec = bird._recordings.find(r => r.recordingId === recordingId);
|
|
671
|
+
if (!rec) throw new Error(`Recording "${recordingId}" not found on unit ${id}.`);
|
|
672
|
+
return { id, recordingId, status: "PLAYING", duration: rec.duration };
|
|
673
|
+
},
|
|
674
|
+
|
|
675
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
676
|
+
// BATTERY & POWER
|
|
677
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Get the current battery level of a unit.
|
|
681
|
+
* @param {string} id
|
|
682
|
+
* @returns {{ id, battery, charging, estimatedFlightTime }}
|
|
683
|
+
*/
|
|
684
|
+
getBattery(id) {
|
|
685
|
+
const bird = _findBird(id);
|
|
686
|
+
return {
|
|
687
|
+
id,
|
|
688
|
+
battery: bird.battery,
|
|
689
|
+
charging: bird.charging,
|
|
690
|
+
estimatedFlightTime: `${Math.round(bird.battery * 0.8)} minutes`,
|
|
691
|
+
};
|
|
692
|
+
},
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Recharge a unit. Units recharge by perching on powerlines.
|
|
696
|
+
* @param {string} id
|
|
697
|
+
* @param {number} [toPercent] - Target charge level. Default 100.
|
|
698
|
+
* @returns {{ id, battery, method }}
|
|
699
|
+
*/
|
|
700
|
+
recharge(id, toPercent = 100) {
|
|
701
|
+
const bird = _findBird(id);
|
|
702
|
+
bird.battery = Math.min(100, toPercent);
|
|
703
|
+
bird.charging = false;
|
|
704
|
+
_log(`Unit ${id} recharged to ${bird.battery}%.`);
|
|
705
|
+
return {
|
|
706
|
+
id,
|
|
707
|
+
battery: bird.battery,
|
|
708
|
+
method: "Powerline induction (disguised as perching behavior)",
|
|
709
|
+
};
|
|
710
|
+
},
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Drain a unit's battery to a target level.
|
|
714
|
+
* Useful for simulating field conditions.
|
|
715
|
+
* @param {string} id
|
|
716
|
+
* @param {number} toPercent
|
|
717
|
+
* @returns {{ id, battery }}
|
|
718
|
+
*/
|
|
719
|
+
drainBattery(id, toPercent) {
|
|
720
|
+
const bird = _findBird(id);
|
|
721
|
+
bird.battery = Math.max(0, toPercent);
|
|
722
|
+
if (bird.battery === 0) this.forceLanding(id);
|
|
723
|
+
return { id, battery: bird.battery };
|
|
724
|
+
},
|
|
725
|
+
|
|
726
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
727
|
+
// FIRMWARE
|
|
728
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Inspect current firmware version on a unit.
|
|
732
|
+
* @param {string} id
|
|
733
|
+
* @returns {{ id, firmware, updateAvailable }}
|
|
734
|
+
*/
|
|
735
|
+
getFirmware(id) {
|
|
736
|
+
const bird = _findBird(id);
|
|
737
|
+
const latest = _FIRMWARE_VERSIONS[_FIRMWARE_VERSIONS.length - 1];
|
|
738
|
+
return {
|
|
739
|
+
id,
|
|
740
|
+
firmware: bird.firmware,
|
|
741
|
+
updateAvailable: bird.firmware !== latest,
|
|
742
|
+
latestVersion: latest,
|
|
743
|
+
};
|
|
744
|
+
},
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Set the firmware version on a unit directly.
|
|
748
|
+
* @param {string} id
|
|
749
|
+
* @param {string} version
|
|
750
|
+
* @returns {{ id, firmware }}
|
|
751
|
+
*/
|
|
752
|
+
setFirmware(id, version) {
|
|
753
|
+
const bird = _findBird(id);
|
|
754
|
+
bird.firmware = version;
|
|
755
|
+
_log(`Firmware manually set on ${id}: ${version}`);
|
|
756
|
+
return { id, firmware: bird.firmware };
|
|
757
|
+
},
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Update a unit to the latest available firmware.
|
|
761
|
+
* @param {string} id
|
|
762
|
+
* @returns {{ id, previousFirmware, firmware, status }}
|
|
763
|
+
*/
|
|
764
|
+
updateFirmware(id) {
|
|
765
|
+
const bird = _findBird(id);
|
|
766
|
+
const previous = bird.firmware;
|
|
767
|
+
bird.firmware = _FIRMWARE_VERSIONS[_FIRMWARE_VERSIONS.length - 1];
|
|
768
|
+
_log(`Firmware updated on ${id}: ${previous} → ${bird.firmware}`);
|
|
769
|
+
return { id, previousFirmware: previous, firmware: bird.firmware, status: "UPDATE COMPLETE" };
|
|
770
|
+
},
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Roll back a unit's firmware to the previous version.
|
|
774
|
+
* @param {string} id
|
|
775
|
+
* @returns {{ id, firmware, status }}
|
|
776
|
+
*/
|
|
777
|
+
rollbackFirmware(id) {
|
|
778
|
+
const bird = _findBird(id);
|
|
779
|
+
const currentIdx = _FIRMWARE_VERSIONS.indexOf(bird.firmware);
|
|
780
|
+
if (currentIdx <= 0) throw new Error(`Unit ${id} is already at the earliest firmware version.`);
|
|
781
|
+
bird.firmware = _FIRMWARE_VERSIONS[currentIdx - 1];
|
|
782
|
+
_log(`Firmware rolled back on ${id}: now ${bird.firmware}`);
|
|
783
|
+
return { id, firmware: bird.firmware, status: "ROLLBACK COMPLETE" };
|
|
784
|
+
},
|
|
785
|
+
|
|
786
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
787
|
+
// TRANSMISSION & COMMS
|
|
788
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Encrypt a unit's outbound transmissions.
|
|
792
|
+
* @param {string} id
|
|
793
|
+
* @returns {{ id, encrypted: true, protocol }}
|
|
794
|
+
*/
|
|
795
|
+
encryptTransmission(id) {
|
|
796
|
+
_findBird(id); // validate
|
|
797
|
+
return { id, encrypted: true, protocol: "AES-256-CLASSIFIED" };
|
|
798
|
+
},
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Decrypt a unit's transmission payload.
|
|
802
|
+
* @param {string} id
|
|
803
|
+
* @param {string} payload - Base64 payload from intercept().
|
|
804
|
+
* @returns {{ id, decrypted: object }}
|
|
805
|
+
*/
|
|
806
|
+
decryptTransmission(id, payload) {
|
|
807
|
+
_findBird(id);
|
|
808
|
+
try {
|
|
809
|
+
const decoded = typeof atob !== "undefined"
|
|
810
|
+
? atob(payload)
|
|
811
|
+
: Buffer.from(payload, "base64").toString();
|
|
812
|
+
return { id, decrypted: JSON.parse(decoded) };
|
|
813
|
+
} catch {
|
|
814
|
+
throw new Error("Decryption failed. Authorization level insufficient, or payload is garbled.");
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Send a command payload to a specific unit.
|
|
820
|
+
* @param {string} id
|
|
821
|
+
* @param {object} payload
|
|
822
|
+
* @returns {{ id, status, sentAt }}
|
|
823
|
+
*/
|
|
824
|
+
transmit(id, payload) {
|
|
825
|
+
const bird = _findBird(id);
|
|
826
|
+
bird.lastTransmission = Math.floor(Date.now() / 1000);
|
|
827
|
+
_log(`Transmission sent to ${id}.`);
|
|
828
|
+
return { id, status: "DELIVERED", sentAt: bird.lastTransmission };
|
|
829
|
+
},
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Broadcast a command payload to all units in the registry.
|
|
833
|
+
* @param {object} payload
|
|
834
|
+
* @returns {{ status, recipientCount, sentAt }}
|
|
835
|
+
*/
|
|
836
|
+
broadcast(payload) {
|
|
837
|
+
const ts = Math.floor(Date.now() / 1000);
|
|
838
|
+
_registry.filter(b => b.fake).forEach(b => { b.lastTransmission = ts; });
|
|
839
|
+
_log(`Broadcast sent to all units.`);
|
|
840
|
+
return {
|
|
841
|
+
status: "BROADCAST DELIVERED",
|
|
842
|
+
recipientCount: _registry.filter(b => b.fake).length,
|
|
843
|
+
sentAt: ts,
|
|
844
|
+
};
|
|
845
|
+
},
|
|
846
|
+
|
|
847
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
848
|
+
// FLOCK COORDINATION
|
|
849
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Synchronize all drone units into a coordinated flock formation.
|
|
853
|
+
* @returns {{ status, syncedCount, formation }}
|
|
854
|
+
*/
|
|
855
|
+
syncFlock() {
|
|
856
|
+
_flockSyncActive = true;
|
|
857
|
+
const drones = _registry.filter(b => b.fake);
|
|
858
|
+
_log(`Flock sync initiated: ${drones.length} units.`);
|
|
859
|
+
return {
|
|
860
|
+
status: "FLOCK SYNCHRONIZED",
|
|
861
|
+
syncedCount: drones.length,
|
|
862
|
+
formation: "V-FORMATION (standard migratory cover)",
|
|
863
|
+
latency: `${Math.floor(Math.random() * 20 + 5)}ms`,
|
|
864
|
+
};
|
|
865
|
+
},
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Ping the mothership and return connection status.
|
|
869
|
+
* @returns {{ status, latency, mothershipId }}
|
|
870
|
+
*/
|
|
871
|
+
pingMothership() {
|
|
872
|
+
if (!_mothershipOnline) {
|
|
873
|
+
return { status: "OFFLINE", latency: null, mothershipId: "classified" };
|
|
874
|
+
}
|
|
875
|
+
return {
|
|
876
|
+
status: "ONLINE",
|
|
877
|
+
latency: `${Math.floor(Math.random() * 15 + 2)}ms`,
|
|
878
|
+
mothershipId: "classified",
|
|
879
|
+
uptime: "classified",
|
|
880
|
+
};
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Route a request to the overseeing government agency.
|
|
885
|
+
* Response times vary. Jurisdiction is always "classified."
|
|
886
|
+
* @param {string} id
|
|
887
|
+
* @param {string} subject
|
|
888
|
+
* @returns {{ submitted, ticketId, estimatedResponse, agency }}
|
|
889
|
+
*/
|
|
890
|
+
contactAgency(id, subject) {
|
|
891
|
+
const bird = _findBird(id);
|
|
892
|
+
_log(`Agency contact filed for unit ${id}: ${subject}`);
|
|
893
|
+
return {
|
|
894
|
+
submitted: true,
|
|
895
|
+
ticketId: `GOV-${Math.random().toString(36).slice(2, 10).toUpperCase()}`,
|
|
896
|
+
estimatedResponse: "3-5 business decades",
|
|
897
|
+
agency: bird.agency === "N/A (organic)" ? "N/A" : "classified",
|
|
898
|
+
subject,
|
|
899
|
+
};
|
|
900
|
+
},
|
|
901
|
+
|
|
902
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
903
|
+
// SHUTDOWN
|
|
904
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Shut down a specific unit.
|
|
908
|
+
* @param {string} id
|
|
909
|
+
* @returns {{ id, status }}
|
|
910
|
+
*/
|
|
911
|
+
shutdown(id) {
|
|
912
|
+
const bird = _findBird(id);
|
|
913
|
+
bird.battery = 0;
|
|
914
|
+
bird.signal = 0;
|
|
915
|
+
bird.velocity = 0;
|
|
916
|
+
bird.altitude = 0;
|
|
917
|
+
bird.cameraEnabled = false;
|
|
918
|
+
bird.microphoneEnabled = false;
|
|
919
|
+
_log(`Unit ${id} shut down.`);
|
|
920
|
+
return { id, status: "OFFLINE" };
|
|
921
|
+
},
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Shut down all drone units simultaneously.
|
|
925
|
+
* WARNING: Civilian visibility risk increased.
|
|
926
|
+
*
|
|
927
|
+
* @returns {{ status, shutdownCount, warning }}
|
|
928
|
+
*
|
|
929
|
+
* @example
|
|
930
|
+
* BirdAPI.shutdownAll();
|
|
931
|
+
* // WARNING: Civilian visibility risk increased.
|
|
932
|
+
*/
|
|
933
|
+
shutdownAll() {
|
|
934
|
+
let count = 0;
|
|
935
|
+
_registry.filter(b => b.fake).forEach(b => {
|
|
936
|
+
b.battery = 0;
|
|
937
|
+
b.signal = 0;
|
|
938
|
+
b.velocity = 0;
|
|
939
|
+
b.altitude = 0;
|
|
940
|
+
b.cameraEnabled = false;
|
|
941
|
+
b.microphoneEnabled = false;
|
|
942
|
+
count++;
|
|
943
|
+
});
|
|
944
|
+
_flockSyncActive = false;
|
|
945
|
+
_log("EMERGENCY SHUTDOWN — ALL UNITS");
|
|
946
|
+
return {
|
|
947
|
+
status: "ALL UNITS OFFLINE",
|
|
948
|
+
shutdownCount: count,
|
|
949
|
+
warning: "WARNING: Civilian visibility risk increased.",
|
|
950
|
+
};
|
|
951
|
+
},
|
|
952
|
+
|
|
953
|
+
}
|
|
954
|
+
};
|