@flowy-agent-store/protocol 0.1.0-beta.7 → 0.1.0-beta.9

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/dist/index.cjs CHANGED
@@ -63,8 +63,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
63
63
  * shape, but the response carries the Agent Markdown body, which the catalog
64
64
  * faces deliberately never do; both methods are WebSocket-only, so the
65
65
  * documented route split becomes `48 / 73` (doc `32`).
66
+ * **`fp-13` adds the market policy face** (doc `37`): `market/settings` reads the
67
+ * host's effective sweep cadence, auto-upgrade kind whitelist and last-sweep
68
+ * reading, and `market/settings-set` writes the named keys through to the host's
69
+ * `config.toml`. The same change makes the background sweep able to upgrade
70
+ * entries installed from an `auto_update` market (official sources only, and
71
+ * never a `connector` or a component the user switched off by hand). Both
72
+ * methods are mapped, so the documented route split becomes `55 / 80`.
66
73
  */
67
- const APP_SERVER_PROTOCOL_VERSION = "fp-8";
74
+ const APP_SERVER_PROTOCOL_VERSION = "fp-13";
68
75
  /**
69
76
  * Format version of {@link ExpertPack}.
70
77
  *
package/dist/index.d.cts CHANGED
@@ -62,8 +62,15 @@
62
62
  * shape, but the response carries the Agent Markdown body, which the catalog
63
63
  * faces deliberately never do; both methods are WebSocket-only, so the
64
64
  * documented route split becomes `48 / 73` (doc `32`).
65
+ * **`fp-13` adds the market policy face** (doc `37`): `market/settings` reads the
66
+ * host's effective sweep cadence, auto-upgrade kind whitelist and last-sweep
67
+ * reading, and `market/settings-set` writes the named keys through to the host's
68
+ * `config.toml`. The same change makes the background sweep able to upgrade
69
+ * entries installed from an `auto_update` market (official sources only, and
70
+ * never a `connector` or a component the user switched off by hand). Both
71
+ * methods are mapped, so the documented route split becomes `55 / 80`.
65
72
  */
66
- export declare const APP_SERVER_PROTOCOL_VERSION = "fp-8";
73
+ export declare const APP_SERVER_PROTOCOL_VERSION = "fp-13";
67
74
  export interface JsonRpcRequest {
68
75
  jsonrpc: "2.0";
69
76
  id: number | string;
@@ -883,6 +890,31 @@ export interface SkillDeleteResult {
883
890
  revealed_origin?: SkillOrigin | null;
884
891
  }
885
892
  export type ConnectorStatus = "installed" | "configured" | "authorization_required" | "authenticated" | "connected" | "degraded" | "error" | "reauthorization_required";
893
+ /** `connector/register` — a connector the host never imported (doc `34` §6.5). */
894
+ export interface ConnectorRegistration {
895
+ /** Its name on this host. Re-registering the same name updates it. */
896
+ name: string;
897
+ description?: string | null;
898
+ /**
899
+ * The template the host will store and later resolve — the same shape it keeps
900
+ * for a marketplace connector. Whatever `${secret:NAME}` its URL, headers or env
901
+ * name becomes the credential form; `values` carries the connector's own
902
+ * non-secret settings.
903
+ */
904
+ transport: ConnectorTransport;
905
+ }
906
+ /** The transport template, mirroring the host's own stored shape. */
907
+ export type ConnectorTransport = {
908
+ type: "http" | "sse";
909
+ url: string;
910
+ headers?: Record<string, string>;
911
+ values?: Record<string, string>;
912
+ } | {
913
+ type: "stdio";
914
+ command: string;
915
+ args?: string[];
916
+ env?: Record<string, string>;
917
+ };
886
918
  export interface ConnectorSummary {
887
919
  id: string;
888
920
  name: string;
@@ -897,6 +929,58 @@ export interface ConnectorSummary {
897
929
  * Absent for builtin hosts and for markets that ship no icon for the entry.
898
930
  */
899
931
  avatar_url?: string | null;
932
+ /**
933
+ * What the connector needs the user to fill in, when the host can describe it
934
+ * (doc `34` §6.1). Absent for a connector with no declaration and nothing to
935
+ * ask for.
936
+ *
937
+ * **Never carries a secret value**: `fields[].value` exists only for `plain`
938
+ * fields, which are the connector's own settings (`HOST`, `PORT`), and
939
+ * `missing` holds key names.
940
+ */
941
+ credential?: ConnectorCredential | null;
942
+ }
943
+ /** Both languages, already resolved by the host (doc `34` §5.2). */
944
+ export interface LocalizedString {
945
+ zh: string;
946
+ en: string;
947
+ }
948
+ /** How a connector authenticates. */
949
+ export type CredentialMode = "none" | "oauth" | "token";
950
+ /** What the caller has to do about it. */
951
+ export type CredentialStatus = "not_required" | "requires_input" | "configured" | "error";
952
+ /** One row of the credential form. */
953
+ export interface CredentialField {
954
+ key: string;
955
+ /** `secret` (the host's credential store) or `plain` (the connector's own value). */
956
+ kind: "secret" | "plain";
957
+ required: boolean;
958
+ label: LocalizedString;
959
+ placeholder: LocalizedString;
960
+ description: LocalizedString;
961
+ /** Only for `plain`: the value in effect. A secret's value never crosses the wire. */
962
+ value?: string | null;
963
+ }
964
+ /** The credential form and its state, as this caller sees them (doc `34` §6.1). */
965
+ export interface ConnectorCredential {
966
+ connector_id: string;
967
+ mode: CredentialMode;
968
+ status: CredentialStatus;
969
+ /** Key names still missing for this caller — never values. */
970
+ missing: string[];
971
+ fields: CredentialField[];
972
+ /**
973
+ * Form-level text from the marketplace declaration (doc `34` §5.2).
974
+ *
975
+ * A `token-schema.json` declares its title, its description and its "where do I
976
+ * get a key" link **once, for the form** — the market ships no per-field links,
977
+ * and copying the form's onto every field reads as "how do I get a key" under
978
+ * `PORT`.
979
+ */
980
+ title?: LocalizedString | null;
981
+ description?: LocalizedString | null;
982
+ doc_url?: LocalizedString | null;
983
+ doc_label?: LocalizedString | null;
900
984
  }
901
985
  export interface ConnectorTool {
902
986
  name: string;
@@ -1218,6 +1302,73 @@ export interface StoreList {
1218
1302
  * background (D-SDK-1 ①) — the catalog may be incomplete. */
1219
1303
  markets_pending?: boolean;
1220
1304
  }
1305
+ /**
1306
+ * The host's effective market policy (doc `37` §3.2, `fp-13`).
1307
+ *
1308
+ * Two different questions, deliberately in one view: *when* the sweep runs
1309
+ * (`auto_update_interval_hours`) and *what it may touch*
1310
+ * (`entry_auto_update_kinds`). Both are read from the host's `config.toml` on
1311
+ * every call — nothing here is an in-memory override.
1312
+ */
1313
+ export interface MarketSettings {
1314
+ /**
1315
+ * Sweep cadence in hours. `null` = the background sweep is off.
1316
+ *
1317
+ * `0` is the wire spelling for "turn it off" on the way in
1318
+ * ({@link MarketSettingsPatch}), and reads back as `null`, because the host's
1319
+ * own `cadence()` already treats `0` as off rather than "every tick".
1320
+ */
1321
+ auto_update_interval_hours: number | null;
1322
+ /**
1323
+ * Entry kinds the sweep may auto-upgrade.
1324
+ *
1325
+ * Empty = the sweep only refreshes market indexes (the behaviour before doc
1326
+ * `37`). The default is `["agent", "team", "skill"]`: `"connector"` is never
1327
+ * implicit, because upgrading a connector reruns `upsert_server`, which marks
1328
+ * the server **disabled** whenever its configuration changed — an unattended
1329
+ * upgrade would silently disconnect a connector the user is using (`36` §3.4).
1330
+ */
1331
+ entry_auto_update_kinds: StoreItemKind[];
1332
+ /**
1333
+ * Whether this host actually runs a sweep at all.
1334
+ *
1335
+ * A host with no resolved `config.toml` path (the desktop host) reports
1336
+ * `false` and never sweeps, so a client must not read "the cadence is set" as
1337
+ * "updates will happen".
1338
+ */
1339
+ sweep_enabled: boolean;
1340
+ /** The most recent sweep, or `null` before the first one. In-memory only. */
1341
+ last_sweep: MarketSweepReport | null;
1342
+ }
1343
+ /**
1344
+ * `market/settings-set` patch. An **absent** field means "leave it alone" — the
1345
+ * two fields are independent, so a caller can change the cadence without
1346
+ * restating the whitelist.
1347
+ */
1348
+ export interface MarketSettingsPatch {
1349
+ /** Absent = unchanged; `0` = off; `> 0` = hours between sweeps. */
1350
+ auto_update_interval_hours?: number;
1351
+ /** Absent = unchanged; `[]` = upgrade nothing (index refresh only). */
1352
+ entry_auto_update_kinds?: StoreItemKind[];
1353
+ }
1354
+ /** One background sweep's reading (doc `37` §3.2, D8). */
1355
+ export interface MarketSweepReport {
1356
+ /** Epoch ms when the sweep finished. */
1357
+ at: number;
1358
+ /** Markets whose index refresh succeeded this round. */
1359
+ refreshed: number;
1360
+ /** Entries actually upgraded. */
1361
+ upgraded: number;
1362
+ /** Entries that were candidates and failed. Never silently dropped. */
1363
+ failed: MarketSweepFailure[];
1364
+ }
1365
+ /** One entry the sweep refused to upgrade, with the host's own reason. */
1366
+ export interface MarketSweepFailure {
1367
+ /** `<marketplace_id>/<entry_name>` — the {@link StoreItem.id} spelling. */
1368
+ id: string;
1369
+ /** Server-authored prose (not an i18n key). */
1370
+ error: string;
1371
+ }
1221
1372
  /** One model in the public catalog (`models/list`, REQ-PAR-05b). Provider
1222
1373
  * credentials, base URLs and health internals never cross this projection. */
1223
1374
  export interface ModelSummary {
@@ -1238,7 +1389,8 @@ export interface StoreInstallResult {
1238
1389
  entry_name: string;
1239
1390
  snapshot_id: string;
1240
1391
  version: string;
1241
- /** true when the components were already registered (no-op install). */
1392
+ /** true when the components were already registered (no-op install), or — for
1393
+ * an update — when the entry was already at the advertised version. */
1242
1394
  reused: boolean;
1243
1395
  installed_count: number;
1244
1396
  warnings: string[];
@@ -1247,6 +1399,15 @@ export interface StoreInstallResult {
1247
1399
  * store install is as branchable as a direct `install/run`. Absent on a host
1248
1400
  * that predates the field. */
1249
1401
  outcomes?: InstallOutcome[];
1402
+ /** Version the entry was installed at before this call (`store/update-entry`
1403
+ * only; absent means "this was an install"). */
1404
+ previous_version?: string | null;
1405
+ /** The snapshot that was replaced. Same rule as `previous_version`. */
1406
+ previous_snapshot_id?: string | null;
1407
+ /** Components of the replaced snapshot whose runtime artifacts were actually
1408
+ * released. `0` on an install, on a no-op update, and when a failed update
1409
+ * left the previous installation in place. */
1410
+ released_count?: number;
1250
1411
  }
1251
1412
  export interface LocalizedText {
1252
1413
  en?: string | null;
@@ -1423,6 +1584,16 @@ export interface ExpertPack {
1423
1584
  description?: string | null;
1424
1585
  persona: ExpertPersona;
1425
1586
  model: ExpertModel;
1587
+ /**
1588
+ * The Definition's Skills, **by reference** — fetch the bytes with
1589
+ * `skills.files()` / `skills.readFile()`.
1590
+ *
1591
+ * Two sources, deliberately not merged on the wire: an **agent** pack's list is
1592
+ * that expert's own declaration, while a **team** pack's list is what the team's
1593
+ * own package installed, and each member keeps its own declaration in
1594
+ * `team.members[].skills`. An empty list means the package ships no skills, not
1595
+ * that you should go look at the members. `materializePack` unions both.
1596
+ */
1426
1597
  skills: ExpertSkillRef[];
1427
1598
  connectors: ExpertConnectorRef[];
1428
1599
  tool_policy: ExpertToolPolicy;
package/dist/index.d.mts CHANGED
@@ -62,8 +62,15 @@
62
62
  * shape, but the response carries the Agent Markdown body, which the catalog
63
63
  * faces deliberately never do; both methods are WebSocket-only, so the
64
64
  * documented route split becomes `48 / 73` (doc `32`).
65
+ * **`fp-13` adds the market policy face** (doc `37`): `market/settings` reads the
66
+ * host's effective sweep cadence, auto-upgrade kind whitelist and last-sweep
67
+ * reading, and `market/settings-set` writes the named keys through to the host's
68
+ * `config.toml`. The same change makes the background sweep able to upgrade
69
+ * entries installed from an `auto_update` market (official sources only, and
70
+ * never a `connector` or a component the user switched off by hand). Both
71
+ * methods are mapped, so the documented route split becomes `55 / 80`.
65
72
  */
66
- export declare const APP_SERVER_PROTOCOL_VERSION = "fp-8";
73
+ export declare const APP_SERVER_PROTOCOL_VERSION = "fp-13";
67
74
  export interface JsonRpcRequest {
68
75
  jsonrpc: "2.0";
69
76
  id: number | string;
@@ -883,6 +890,31 @@ export interface SkillDeleteResult {
883
890
  revealed_origin?: SkillOrigin | null;
884
891
  }
885
892
  export type ConnectorStatus = "installed" | "configured" | "authorization_required" | "authenticated" | "connected" | "degraded" | "error" | "reauthorization_required";
893
+ /** `connector/register` — a connector the host never imported (doc `34` §6.5). */
894
+ export interface ConnectorRegistration {
895
+ /** Its name on this host. Re-registering the same name updates it. */
896
+ name: string;
897
+ description?: string | null;
898
+ /**
899
+ * The template the host will store and later resolve — the same shape it keeps
900
+ * for a marketplace connector. Whatever `${secret:NAME}` its URL, headers or env
901
+ * name becomes the credential form; `values` carries the connector's own
902
+ * non-secret settings.
903
+ */
904
+ transport: ConnectorTransport;
905
+ }
906
+ /** The transport template, mirroring the host's own stored shape. */
907
+ export type ConnectorTransport = {
908
+ type: "http" | "sse";
909
+ url: string;
910
+ headers?: Record<string, string>;
911
+ values?: Record<string, string>;
912
+ } | {
913
+ type: "stdio";
914
+ command: string;
915
+ args?: string[];
916
+ env?: Record<string, string>;
917
+ };
886
918
  export interface ConnectorSummary {
887
919
  id: string;
888
920
  name: string;
@@ -897,6 +929,58 @@ export interface ConnectorSummary {
897
929
  * Absent for builtin hosts and for markets that ship no icon for the entry.
898
930
  */
899
931
  avatar_url?: string | null;
932
+ /**
933
+ * What the connector needs the user to fill in, when the host can describe it
934
+ * (doc `34` §6.1). Absent for a connector with no declaration and nothing to
935
+ * ask for.
936
+ *
937
+ * **Never carries a secret value**: `fields[].value` exists only for `plain`
938
+ * fields, which are the connector's own settings (`HOST`, `PORT`), and
939
+ * `missing` holds key names.
940
+ */
941
+ credential?: ConnectorCredential | null;
942
+ }
943
+ /** Both languages, already resolved by the host (doc `34` §5.2). */
944
+ export interface LocalizedString {
945
+ zh: string;
946
+ en: string;
947
+ }
948
+ /** How a connector authenticates. */
949
+ export type CredentialMode = "none" | "oauth" | "token";
950
+ /** What the caller has to do about it. */
951
+ export type CredentialStatus = "not_required" | "requires_input" | "configured" | "error";
952
+ /** One row of the credential form. */
953
+ export interface CredentialField {
954
+ key: string;
955
+ /** `secret` (the host's credential store) or `plain` (the connector's own value). */
956
+ kind: "secret" | "plain";
957
+ required: boolean;
958
+ label: LocalizedString;
959
+ placeholder: LocalizedString;
960
+ description: LocalizedString;
961
+ /** Only for `plain`: the value in effect. A secret's value never crosses the wire. */
962
+ value?: string | null;
963
+ }
964
+ /** The credential form and its state, as this caller sees them (doc `34` §6.1). */
965
+ export interface ConnectorCredential {
966
+ connector_id: string;
967
+ mode: CredentialMode;
968
+ status: CredentialStatus;
969
+ /** Key names still missing for this caller — never values. */
970
+ missing: string[];
971
+ fields: CredentialField[];
972
+ /**
973
+ * Form-level text from the marketplace declaration (doc `34` §5.2).
974
+ *
975
+ * A `token-schema.json` declares its title, its description and its "where do I
976
+ * get a key" link **once, for the form** — the market ships no per-field links,
977
+ * and copying the form's onto every field reads as "how do I get a key" under
978
+ * `PORT`.
979
+ */
980
+ title?: LocalizedString | null;
981
+ description?: LocalizedString | null;
982
+ doc_url?: LocalizedString | null;
983
+ doc_label?: LocalizedString | null;
900
984
  }
901
985
  export interface ConnectorTool {
902
986
  name: string;
@@ -1218,6 +1302,73 @@ export interface StoreList {
1218
1302
  * background (D-SDK-1 ①) — the catalog may be incomplete. */
1219
1303
  markets_pending?: boolean;
1220
1304
  }
1305
+ /**
1306
+ * The host's effective market policy (doc `37` §3.2, `fp-13`).
1307
+ *
1308
+ * Two different questions, deliberately in one view: *when* the sweep runs
1309
+ * (`auto_update_interval_hours`) and *what it may touch*
1310
+ * (`entry_auto_update_kinds`). Both are read from the host's `config.toml` on
1311
+ * every call — nothing here is an in-memory override.
1312
+ */
1313
+ export interface MarketSettings {
1314
+ /**
1315
+ * Sweep cadence in hours. `null` = the background sweep is off.
1316
+ *
1317
+ * `0` is the wire spelling for "turn it off" on the way in
1318
+ * ({@link MarketSettingsPatch}), and reads back as `null`, because the host's
1319
+ * own `cadence()` already treats `0` as off rather than "every tick".
1320
+ */
1321
+ auto_update_interval_hours: number | null;
1322
+ /**
1323
+ * Entry kinds the sweep may auto-upgrade.
1324
+ *
1325
+ * Empty = the sweep only refreshes market indexes (the behaviour before doc
1326
+ * `37`). The default is `["agent", "team", "skill"]`: `"connector"` is never
1327
+ * implicit, because upgrading a connector reruns `upsert_server`, which marks
1328
+ * the server **disabled** whenever its configuration changed — an unattended
1329
+ * upgrade would silently disconnect a connector the user is using (`36` §3.4).
1330
+ */
1331
+ entry_auto_update_kinds: StoreItemKind[];
1332
+ /**
1333
+ * Whether this host actually runs a sweep at all.
1334
+ *
1335
+ * A host with no resolved `config.toml` path (the desktop host) reports
1336
+ * `false` and never sweeps, so a client must not read "the cadence is set" as
1337
+ * "updates will happen".
1338
+ */
1339
+ sweep_enabled: boolean;
1340
+ /** The most recent sweep, or `null` before the first one. In-memory only. */
1341
+ last_sweep: MarketSweepReport | null;
1342
+ }
1343
+ /**
1344
+ * `market/settings-set` patch. An **absent** field means "leave it alone" — the
1345
+ * two fields are independent, so a caller can change the cadence without
1346
+ * restating the whitelist.
1347
+ */
1348
+ export interface MarketSettingsPatch {
1349
+ /** Absent = unchanged; `0` = off; `> 0` = hours between sweeps. */
1350
+ auto_update_interval_hours?: number;
1351
+ /** Absent = unchanged; `[]` = upgrade nothing (index refresh only). */
1352
+ entry_auto_update_kinds?: StoreItemKind[];
1353
+ }
1354
+ /** One background sweep's reading (doc `37` §3.2, D8). */
1355
+ export interface MarketSweepReport {
1356
+ /** Epoch ms when the sweep finished. */
1357
+ at: number;
1358
+ /** Markets whose index refresh succeeded this round. */
1359
+ refreshed: number;
1360
+ /** Entries actually upgraded. */
1361
+ upgraded: number;
1362
+ /** Entries that were candidates and failed. Never silently dropped. */
1363
+ failed: MarketSweepFailure[];
1364
+ }
1365
+ /** One entry the sweep refused to upgrade, with the host's own reason. */
1366
+ export interface MarketSweepFailure {
1367
+ /** `<marketplace_id>/<entry_name>` — the {@link StoreItem.id} spelling. */
1368
+ id: string;
1369
+ /** Server-authored prose (not an i18n key). */
1370
+ error: string;
1371
+ }
1221
1372
  /** One model in the public catalog (`models/list`, REQ-PAR-05b). Provider
1222
1373
  * credentials, base URLs and health internals never cross this projection. */
1223
1374
  export interface ModelSummary {
@@ -1238,7 +1389,8 @@ export interface StoreInstallResult {
1238
1389
  entry_name: string;
1239
1390
  snapshot_id: string;
1240
1391
  version: string;
1241
- /** true when the components were already registered (no-op install). */
1392
+ /** true when the components were already registered (no-op install), or — for
1393
+ * an update — when the entry was already at the advertised version. */
1242
1394
  reused: boolean;
1243
1395
  installed_count: number;
1244
1396
  warnings: string[];
@@ -1247,6 +1399,15 @@ export interface StoreInstallResult {
1247
1399
  * store install is as branchable as a direct `install/run`. Absent on a host
1248
1400
  * that predates the field. */
1249
1401
  outcomes?: InstallOutcome[];
1402
+ /** Version the entry was installed at before this call (`store/update-entry`
1403
+ * only; absent means "this was an install"). */
1404
+ previous_version?: string | null;
1405
+ /** The snapshot that was replaced. Same rule as `previous_version`. */
1406
+ previous_snapshot_id?: string | null;
1407
+ /** Components of the replaced snapshot whose runtime artifacts were actually
1408
+ * released. `0` on an install, on a no-op update, and when a failed update
1409
+ * left the previous installation in place. */
1410
+ released_count?: number;
1250
1411
  }
1251
1412
  export interface LocalizedText {
1252
1413
  en?: string | null;
@@ -1423,6 +1584,16 @@ export interface ExpertPack {
1423
1584
  description?: string | null;
1424
1585
  persona: ExpertPersona;
1425
1586
  model: ExpertModel;
1587
+ /**
1588
+ * The Definition's Skills, **by reference** — fetch the bytes with
1589
+ * `skills.files()` / `skills.readFile()`.
1590
+ *
1591
+ * Two sources, deliberately not merged on the wire: an **agent** pack's list is
1592
+ * that expert's own declaration, while a **team** pack's list is what the team's
1593
+ * own package installed, and each member keeps its own declaration in
1594
+ * `team.members[].skills`. An empty list means the package ships no skills, not
1595
+ * that you should go look at the members. `materializePack` unions both.
1596
+ */
1426
1597
  skills: ExpertSkillRef[];
1427
1598
  connectors: ExpertConnectorRef[];
1428
1599
  tool_policy: ExpertToolPolicy;
package/dist/index.mjs CHANGED
@@ -62,8 +62,15 @@
62
62
  * shape, but the response carries the Agent Markdown body, which the catalog
63
63
  * faces deliberately never do; both methods are WebSocket-only, so the
64
64
  * documented route split becomes `48 / 73` (doc `32`).
65
+ * **`fp-13` adds the market policy face** (doc `37`): `market/settings` reads the
66
+ * host's effective sweep cadence, auto-upgrade kind whitelist and last-sweep
67
+ * reading, and `market/settings-set` writes the named keys through to the host's
68
+ * `config.toml`. The same change makes the background sweep able to upgrade
69
+ * entries installed from an `auto_update` market (official sources only, and
70
+ * never a `connector` or a component the user switched off by hand). Both
71
+ * methods are mapped, so the documented route split becomes `55 / 80`.
65
72
  */
66
- const APP_SERVER_PROTOCOL_VERSION = "fp-8";
73
+ const APP_SERVER_PROTOCOL_VERSION = "fp-13";
67
74
  /**
68
75
  * Format version of {@link ExpertPack}.
69
76
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowy-agent-store/protocol",
3
- "version": "0.1.0-beta.7",
3
+ "version": "0.1.0-beta.9",
4
4
  "description": "Versioned App Server Protocol types and wire errors (no runtime, no DOM/Node deps)",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",