omnigateway 0.4.0 → 0.4.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 +20 -17
- package/bin/omni.js +41 -3
- package/gateway.js +41 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -772,29 +772,26 @@ unknown plugin safer. Integrity checking proves you received the bytes the
|
|
|
772
772
|
registry advertised, and nothing about who wrote them or what they do once the
|
|
773
773
|
gateway imports them.
|
|
774
774
|
|
|
775
|
-
|
|
775
|
+
None ship in this repository, deliberately. The first one did, and moving it out
|
|
776
|
+
is what proved the plugin API actually works from outside: while it built as a
|
|
777
|
+
workspace sibling it could reach internal packages no published plugin can, and
|
|
778
|
+
two bugs hid in exactly that gap.
|
|
776
779
|
|
|
777
780
|
| Plugin | What it does |
|
|
778
781
|
| --- | --- |
|
|
779
|
-
| [`pokemon`](
|
|
780
|
-
|
|
781
|
-
It is not installed by default, is not in the npm package, and is not in the
|
|
782
|
-
Docker image. Build it from a checkout and install the result:
|
|
782
|
+
| [`omnigateway-plugin-pokemon`](https://github.com/harismawan/omnigateway-plugin-pokemon) | A Pokémon companion. Each gateway key raises one that hatches, evolves, and graduates into a Pokédex on the tokens that key spends, with a shop that spends a wallet of those same tokens. |
|
|
783
783
|
|
|
784
784
|
```bash
|
|
785
|
-
|
|
786
|
-
omni plugin
|
|
785
|
+
omni plugin install omnigateway-plugin-pokemon
|
|
786
|
+
omni plugin verify pokemon
|
|
787
787
|
omni restart
|
|
788
788
|
```
|
|
789
789
|
|
|
790
|
-
The path ends in `pokemon` because the installer takes the target directory name
|
|
791
|
-
from the source and refuses a manifest whose id disagrees with it — so a plugin
|
|
792
|
-
cannot be installed under a name that is not its own.
|
|
793
|
-
|
|
794
790
|
It needs outbound access to `pokeapi.co` and `raw.githubusercontent.com` for
|
|
795
791
|
species data and sprites, which its manifest declares and `omni plugin verify
|
|
796
|
-
pokemon` prints back. Those assets are Nintendo and Game Freak intellectual
|
|
797
|
-
runtime and never vendored into
|
|
792
|
+
pokemon` prints back. Those assets are Nintendo and Game Freak intellectual
|
|
793
|
+
property, fetched at runtime and never vendored — into that repository, this one,
|
|
794
|
+
or anything either publishes.
|
|
798
795
|
|
|
799
796
|
`verify` is the one to run before restarting a gateway that people are using: it
|
|
800
797
|
reaches the same verdict the next boot will, from the same code, without loading
|
|
@@ -819,14 +816,20 @@ from the path:
|
|
|
819
816
|
|
|
820
817
|
```bash
|
|
821
818
|
# wherever you build — a workstation, CI
|
|
822
|
-
bun run build
|
|
823
|
-
tar -czf
|
|
819
|
+
bun run build # your plugin's own build
|
|
820
|
+
tar -czf my-plugin.tgz -C dist my-plugin
|
|
824
821
|
|
|
825
822
|
# on the host
|
|
826
|
-
scp
|
|
827
|
-
ssh gateway-host 'omni plugin install /tmp/
|
|
823
|
+
scp my-plugin.tgz gateway-host:/tmp/
|
|
824
|
+
ssh gateway-host 'omni plugin install /tmp/my-plugin.tgz && omni plugin verify my-plugin && omni restart'
|
|
828
825
|
```
|
|
829
826
|
|
|
827
|
+
Whatever you pack, the manifest must sit at the **root** of the archive once one
|
|
828
|
+
wrapping directory is stripped. A build that nests it — `dist/my-plugin/omni-plugin.json`
|
|
829
|
+
inside a tarball made from the repository root — is refused with "has no
|
|
830
|
+
omni-plugin.json at its root", and that is the most common way a plugin that
|
|
831
|
+
builds fine turns out not to install.
|
|
832
|
+
|
|
830
833
|
Plaintext `http://` is refused outright and always will be: what arrives over
|
|
831
834
|
that fetch is code the gateway process will `import`, so anyone between you and
|
|
832
835
|
the host would be choosing what the gateway runs. Silently upgrading to `https://`
|
package/bin/omni.js
CHANGED
|
@@ -19991,7 +19991,7 @@ var CORE_TABLES = [
|
|
|
19991
19991
|
"usage_rollup",
|
|
19992
19992
|
"virtual_models"
|
|
19993
19993
|
];
|
|
19994
|
-
var CORE_TABLE_REFERENCE = new RegExp(`\\b(?:${CORE_TABLES.join("|")})\\b
|
|
19994
|
+
var CORE_TABLE_REFERENCE = new RegExp(`\\b(?:${CORE_TABLES.join("|")})\\b`, "i");
|
|
19995
19995
|
var PREFIX3 = "plugin_";
|
|
19996
19996
|
function stripNoise(sql) {
|
|
19997
19997
|
let out = "";
|
|
@@ -19999,7 +19999,25 @@ function stripNoise(sql) {
|
|
|
19999
19999
|
while (i < sql.length) {
|
|
20000
20000
|
const c = sql[i];
|
|
20001
20001
|
const next = sql[i + 1];
|
|
20002
|
-
if (c === "'") {
|
|
20002
|
+
if (c === '"' || c === "`" || c === "[") {
|
|
20003
|
+
const close = c === "[" ? "]" : c;
|
|
20004
|
+
out += c;
|
|
20005
|
+
i += 1;
|
|
20006
|
+
while (i < sql.length) {
|
|
20007
|
+
if (sql[i] === close) {
|
|
20008
|
+
if (close !== "]" && sql[i + 1] === close) {
|
|
20009
|
+
out += close + close;
|
|
20010
|
+
i += 2;
|
|
20011
|
+
continue;
|
|
20012
|
+
}
|
|
20013
|
+
break;
|
|
20014
|
+
}
|
|
20015
|
+
out += sql[i];
|
|
20016
|
+
i += 1;
|
|
20017
|
+
}
|
|
20018
|
+
out += close;
|
|
20019
|
+
i += 1;
|
|
20020
|
+
} else if (c === "'") {
|
|
20003
20021
|
i += 1;
|
|
20004
20022
|
while (i < sql.length) {
|
|
20005
20023
|
if (sql[i] === "'") {
|
|
@@ -20041,6 +20059,7 @@ function assertPluginId(pluginId) {
|
|
|
20041
20059
|
throw new Error(`plugin id ${JSON.stringify(pluginId)} is not a valid identifier`);
|
|
20042
20060
|
}
|
|
20043
20061
|
}
|
|
20062
|
+
var CONNECTION_STATEMENT = /^\s*(pragma|attach|detach|vacuum)\b/i;
|
|
20044
20063
|
function prepare(pluginId, sql) {
|
|
20045
20064
|
assertPluginId(pluginId);
|
|
20046
20065
|
const expanded = sql.replace(PLACEHOLDER, (_match, name) => {
|
|
@@ -20049,12 +20068,29 @@ function prepare(pluginId, sql) {
|
|
|
20049
20068
|
}
|
|
20050
20069
|
return tableFor(pluginId, name);
|
|
20051
20070
|
});
|
|
20052
|
-
const
|
|
20071
|
+
const stripped = stripNoise(expanded);
|
|
20072
|
+
const connection = CONNECTION_STATEMENT.exec(stripped);
|
|
20073
|
+
if (connection !== null) {
|
|
20074
|
+
throw new Error(`plugin sql may not ${connection[1]?.toUpperCase()}: the database handle is the gateway's, ` + "and this would reconfigure it for everything sharing it");
|
|
20075
|
+
}
|
|
20076
|
+
const found = CORE_TABLE_REFERENCE.exec(stripped);
|
|
20053
20077
|
if (found !== null) {
|
|
20054
20078
|
throw new Error(`plugin sql may not reference the core table ${found[0]}`);
|
|
20055
20079
|
}
|
|
20056
20080
|
return expanded;
|
|
20057
20081
|
}
|
|
20082
|
+
function schemaObjects(db) {
|
|
20083
|
+
const rows = db.query("SELECT name FROM sqlite_master").all();
|
|
20084
|
+
return new Set(rows.map((row) => row.name));
|
|
20085
|
+
}
|
|
20086
|
+
function assertOwnNamespace(db, pluginId, before) {
|
|
20087
|
+
const prefix = `${PREFIX3}${pluginId}_`;
|
|
20088
|
+
for (const name of schemaObjects(db)) {
|
|
20089
|
+
if (before.has(name) || name.startsWith(prefix) || name.startsWith("sqlite_"))
|
|
20090
|
+
continue;
|
|
20091
|
+
throw new Error(`plugin migration created ${name}, which is outside this plugin's namespace \u2014 ` + `name it {{\u2026}} so it becomes ${prefix}\u2026`);
|
|
20092
|
+
}
|
|
20093
|
+
}
|
|
20058
20094
|
function toBinding(value, index) {
|
|
20059
20095
|
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || value instanceof Uint8Array) {
|
|
20060
20096
|
return value;
|
|
@@ -20083,8 +20119,10 @@ function createPluginRepo(db) {
|
|
|
20083
20119
|
throw new Error("migration version must be a positive integer");
|
|
20084
20120
|
}
|
|
20085
20121
|
const sql = prepare(pluginId, migration.sql);
|
|
20122
|
+
const before = schemaObjects(db);
|
|
20086
20123
|
db.transaction(() => {
|
|
20087
20124
|
db.run(sql);
|
|
20125
|
+
assertOwnNamespace(db, pluginId, before);
|
|
20088
20126
|
db.run("INSERT INTO plugin_migrations (plugin_id, version, applied_at) VALUES (?,?,?)", [pluginId, migration.version, Date.now()]);
|
|
20089
20127
|
})();
|
|
20090
20128
|
applied.push(migration.version);
|
package/gateway.js
CHANGED
|
@@ -25465,7 +25465,7 @@ var CORE_TABLES = [
|
|
|
25465
25465
|
"usage_rollup",
|
|
25466
25466
|
"virtual_models"
|
|
25467
25467
|
];
|
|
25468
|
-
var CORE_TABLE_REFERENCE = new RegExp(`\\b(?:${CORE_TABLES.join("|")})\\b
|
|
25468
|
+
var CORE_TABLE_REFERENCE = new RegExp(`\\b(?:${CORE_TABLES.join("|")})\\b`, "i");
|
|
25469
25469
|
var PREFIX3 = "plugin_";
|
|
25470
25470
|
function stripNoise(sql) {
|
|
25471
25471
|
let out = "";
|
|
@@ -25473,7 +25473,25 @@ function stripNoise(sql) {
|
|
|
25473
25473
|
while (i < sql.length) {
|
|
25474
25474
|
const c = sql[i];
|
|
25475
25475
|
const next = sql[i + 1];
|
|
25476
|
-
if (c === "'") {
|
|
25476
|
+
if (c === '"' || c === "`" || c === "[") {
|
|
25477
|
+
const close = c === "[" ? "]" : c;
|
|
25478
|
+
out += c;
|
|
25479
|
+
i += 1;
|
|
25480
|
+
while (i < sql.length) {
|
|
25481
|
+
if (sql[i] === close) {
|
|
25482
|
+
if (close !== "]" && sql[i + 1] === close) {
|
|
25483
|
+
out += close + close;
|
|
25484
|
+
i += 2;
|
|
25485
|
+
continue;
|
|
25486
|
+
}
|
|
25487
|
+
break;
|
|
25488
|
+
}
|
|
25489
|
+
out += sql[i];
|
|
25490
|
+
i += 1;
|
|
25491
|
+
}
|
|
25492
|
+
out += close;
|
|
25493
|
+
i += 1;
|
|
25494
|
+
} else if (c === "'") {
|
|
25477
25495
|
i += 1;
|
|
25478
25496
|
while (i < sql.length) {
|
|
25479
25497
|
if (sql[i] === "'") {
|
|
@@ -25515,6 +25533,7 @@ function assertPluginId(pluginId) {
|
|
|
25515
25533
|
throw new Error(`plugin id ${JSON.stringify(pluginId)} is not a valid identifier`);
|
|
25516
25534
|
}
|
|
25517
25535
|
}
|
|
25536
|
+
var CONNECTION_STATEMENT = /^\s*(pragma|attach|detach|vacuum)\b/i;
|
|
25518
25537
|
function prepare(pluginId, sql) {
|
|
25519
25538
|
assertPluginId(pluginId);
|
|
25520
25539
|
const expanded = sql.replace(PLACEHOLDER, (_match, name) => {
|
|
@@ -25523,12 +25542,29 @@ function prepare(pluginId, sql) {
|
|
|
25523
25542
|
}
|
|
25524
25543
|
return tableFor(pluginId, name);
|
|
25525
25544
|
});
|
|
25526
|
-
const
|
|
25545
|
+
const stripped = stripNoise(expanded);
|
|
25546
|
+
const connection = CONNECTION_STATEMENT.exec(stripped);
|
|
25547
|
+
if (connection !== null) {
|
|
25548
|
+
throw new Error(`plugin sql may not ${connection[1]?.toUpperCase()}: the database handle is the gateway's, ` + "and this would reconfigure it for everything sharing it");
|
|
25549
|
+
}
|
|
25550
|
+
const found = CORE_TABLE_REFERENCE.exec(stripped);
|
|
25527
25551
|
if (found !== null) {
|
|
25528
25552
|
throw new Error(`plugin sql may not reference the core table ${found[0]}`);
|
|
25529
25553
|
}
|
|
25530
25554
|
return expanded;
|
|
25531
25555
|
}
|
|
25556
|
+
function schemaObjects(db) {
|
|
25557
|
+
const rows = db.query("SELECT name FROM sqlite_master").all();
|
|
25558
|
+
return new Set(rows.map((row) => row.name));
|
|
25559
|
+
}
|
|
25560
|
+
function assertOwnNamespace(db, pluginId, before) {
|
|
25561
|
+
const prefix = `${PREFIX3}${pluginId}_`;
|
|
25562
|
+
for (const name of schemaObjects(db)) {
|
|
25563
|
+
if (before.has(name) || name.startsWith(prefix) || name.startsWith("sqlite_"))
|
|
25564
|
+
continue;
|
|
25565
|
+
throw new Error(`plugin migration created ${name}, which is outside this plugin's namespace \u2014 ` + `name it {{\u2026}} so it becomes ${prefix}\u2026`);
|
|
25566
|
+
}
|
|
25567
|
+
}
|
|
25532
25568
|
function toBinding(value, index) {
|
|
25533
25569
|
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || value instanceof Uint8Array) {
|
|
25534
25570
|
return value;
|
|
@@ -25557,8 +25593,10 @@ function createPluginRepo(db) {
|
|
|
25557
25593
|
throw new Error("migration version must be a positive integer");
|
|
25558
25594
|
}
|
|
25559
25595
|
const sql = prepare(pluginId, migration.sql);
|
|
25596
|
+
const before = schemaObjects(db);
|
|
25560
25597
|
db.transaction(() => {
|
|
25561
25598
|
db.run(sql);
|
|
25599
|
+
assertOwnNamespace(db, pluginId, before);
|
|
25562
25600
|
db.run("INSERT INTO plugin_migrations (plugin_id, version, applied_at) VALUES (?,?,?)", [pluginId, migration.version, Date.now()]);
|
|
25563
25601
|
})();
|
|
25564
25602
|
applied.push(migration.version);
|
package/package.json
CHANGED