@remnic/connector-bee 9.3.631
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 +21 -0
- package/README.md +90 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +355 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Joshua Warren
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @remnic/connector-bee
|
|
2
|
+
|
|
3
|
+
Bee wearable connector for [Remnic](https://github.com/joshuaswarren/remnic).
|
|
4
|
+
Pulls your Bee bracelet conversations into Remnic's wearable-transcript
|
|
5
|
+
pipeline: cleaned, speaker-labeled, redacted, searchable day
|
|
6
|
+
transcripts — and, under per-source trust gates, memories. Optionally
|
|
7
|
+
imports Bee's own extracted "facts" into the review queue.
|
|
8
|
+
|
|
9
|
+
This is an **à-la-carte optional companion** of `@remnic/core`:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @remnic/connector-bee
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Remnic discovers it at runtime. No further registration is needed.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
> **Heads up:** Bee's original public API (`api.bee.computer` with
|
|
20
|
+
> `x-api-key`) was retired after the Amazon acquisition. This connector
|
|
21
|
+
> targets the current developer surface.
|
|
22
|
+
|
|
23
|
+
### Option A — local proxy (recommended, zero config)
|
|
24
|
+
|
|
25
|
+
1. Enable Developer Mode in the Bee app (Settings → tap **Version**
|
|
26
|
+
5 times), then pair the CLI:
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @beeai/cli
|
|
29
|
+
bee login
|
|
30
|
+
bee proxy # serves the developer API on http://127.0.0.1:8787
|
|
31
|
+
```
|
|
32
|
+
2. Enable the source — no token needed:
|
|
33
|
+
|
|
34
|
+
```jsonc
|
|
35
|
+
{
|
|
36
|
+
"wearables": {
|
|
37
|
+
"enabled": true,
|
|
38
|
+
"sources": {
|
|
39
|
+
"bee": {
|
|
40
|
+
"enabled": true,
|
|
41
|
+
"memoryMode": "smart", // smart (default) | off | review | auto
|
|
42
|
+
"importNativeMemories": "smart" // Bee facts through the same trust pipeline
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Option B — direct API access
|
|
50
|
+
|
|
51
|
+
Set `baseUrl` to the direct host and provide the bearer token from
|
|
52
|
+
`bee login` (`~/.bee/token-prod`) via `BEE_API_TOKEN` (or
|
|
53
|
+
`REMNIC_BEE_API_TOKEN`, or `apiKey` in config). The direct host uses
|
|
54
|
+
Bee's private CA — export `NODE_EXTRA_CA_CERTS` pointing at it.
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
remnic wearables check bee
|
|
60
|
+
remnic wearables sync --source bee --days 7
|
|
61
|
+
remnic wearables transcript --date 2026-06-10 --source bee
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Speaker labels
|
|
65
|
+
|
|
66
|
+
Bee exposes opaque diarization labels ("0", "1", ...) with no wearer
|
|
67
|
+
marker. Map them once and every stored transcript uses the names:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
remnic wearables speakers set bee 0 "Your Name" --self
|
|
71
|
+
remnic wearables speakers set bee 1 "Jane Doe"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Notes
|
|
75
|
+
|
|
76
|
+
- Bee's list API has no date filter; the connector paginates
|
|
77
|
+
newest-first and filters to the requested local day, so backfills
|
|
78
|
+
stay bounded.
|
|
79
|
+
- Conversations still in the `CAPTURING` state are skipped until they
|
|
80
|
+
settle; the next sync picks them up.
|
|
81
|
+
- Default `memoryMode: "smart"`: the LLM judge + per-source trust prior
|
|
82
|
+
+ cross-device corroboration write high-trust facts active, queue
|
|
83
|
+
borderline ones, and drop the rest. Bee-native facts run through the
|
|
84
|
+
same pipeline with a reduced prior.
|
|
85
|
+
|
|
86
|
+
Full documentation: [docs/wearables.md](https://github.com/joshuaswarren/remnic/blob/main/docs/wearables.md).
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { WearableConversation, WearableNativeMemory, WearableConnectorFactoryOptions, WearableSourceConnector, WearableConnectorRegistration } from '@remnic/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal Bee developer API client (raw fetch, no SDK).
|
|
5
|
+
*
|
|
6
|
+
* Contract verified against the official `@beeai/cli` source and
|
|
7
|
+
* docs.bee.computer (2026-06). Two access modes:
|
|
8
|
+
*
|
|
9
|
+
* - **Proxy mode (default):** the official `bee proxy` command serves
|
|
10
|
+
* the full developer API unauthenticated on `http://127.0.0.1:8787`.
|
|
11
|
+
* No token required.
|
|
12
|
+
* - **Direct mode:** `https://app-api-developer.ce.bee.amazon.dev`
|
|
13
|
+
* with `Authorization: Bearer <token>` (token from `bee login`,
|
|
14
|
+
* stored at `~/.bee/token-prod`). The direct host uses Bee's private
|
|
15
|
+
* CA — point `NODE_EXTRA_CA_CERTS` at it when going direct.
|
|
16
|
+
*
|
|
17
|
+
* The legacy pre-acquisition API (`api.bee.computer`, `x-api-key`,
|
|
18
|
+
* page/totalPages) no longer resolves and is intentionally not
|
|
19
|
+
* supported.
|
|
20
|
+
*
|
|
21
|
+
* List endpoints use `limit` + `cursor` pagination with a `next_cursor`
|
|
22
|
+
* in the envelope; timestamps are epoch milliseconds. Tokens are never
|
|
23
|
+
* logged and never appear in thrown error messages.
|
|
24
|
+
*/
|
|
25
|
+
declare const BEE_DEFAULT_BASE_URL = "http://127.0.0.1:8787";
|
|
26
|
+
declare const BEE_DIRECT_BASE_URL = "https://app-api-developer.ce.bee.amazon.dev";
|
|
27
|
+
interface BeeConversationListItem {
|
|
28
|
+
id: number;
|
|
29
|
+
start_time: number;
|
|
30
|
+
end_time?: number | null;
|
|
31
|
+
device_type?: string;
|
|
32
|
+
short_summary?: string | null;
|
|
33
|
+
summary?: string | null;
|
|
34
|
+
state?: string;
|
|
35
|
+
}
|
|
36
|
+
interface BeeUtterance {
|
|
37
|
+
id?: number;
|
|
38
|
+
start?: number | null;
|
|
39
|
+
end?: number | null;
|
|
40
|
+
spoken_at?: number;
|
|
41
|
+
text?: string;
|
|
42
|
+
speaker?: string;
|
|
43
|
+
}
|
|
44
|
+
interface BeeConversationDetail extends BeeConversationListItem {
|
|
45
|
+
transcriptions?: Array<{
|
|
46
|
+
id?: number;
|
|
47
|
+
utterances?: BeeUtterance[];
|
|
48
|
+
}>;
|
|
49
|
+
primary_location?: {
|
|
50
|
+
address?: string | null;
|
|
51
|
+
latitude?: number;
|
|
52
|
+
longitude?: number;
|
|
53
|
+
} | null;
|
|
54
|
+
}
|
|
55
|
+
interface BeeFact {
|
|
56
|
+
id: number;
|
|
57
|
+
text: string;
|
|
58
|
+
tags?: string[];
|
|
59
|
+
created_at?: number;
|
|
60
|
+
confirmed?: boolean;
|
|
61
|
+
}
|
|
62
|
+
interface BeeConversationsPage {
|
|
63
|
+
conversations: BeeConversationListItem[];
|
|
64
|
+
nextCursor: string | null;
|
|
65
|
+
}
|
|
66
|
+
interface BeeFactsPage {
|
|
67
|
+
facts: BeeFact[];
|
|
68
|
+
nextCursor: string | null;
|
|
69
|
+
}
|
|
70
|
+
interface BeeClientOptions {
|
|
71
|
+
/** Bearer token for direct mode. Omit entirely for proxy mode. */
|
|
72
|
+
token?: string;
|
|
73
|
+
/** Defaults to the local `bee proxy` address. */
|
|
74
|
+
baseUrl?: string;
|
|
75
|
+
fetchImpl?: typeof fetch;
|
|
76
|
+
timeoutMs?: number;
|
|
77
|
+
sleep?: (ms: number) => Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
declare class BeeApiError extends Error {
|
|
80
|
+
readonly status?: number | undefined;
|
|
81
|
+
constructor(message: string, status?: number | undefined);
|
|
82
|
+
}
|
|
83
|
+
declare class BeeClient {
|
|
84
|
+
private readonly token;
|
|
85
|
+
private readonly baseUrl;
|
|
86
|
+
private readonly fetchImpl;
|
|
87
|
+
private readonly timeoutMs;
|
|
88
|
+
private readonly sleep;
|
|
89
|
+
constructor(options?: BeeClientOptions);
|
|
90
|
+
get usingLocalProxy(): boolean;
|
|
91
|
+
listConversations(params?: {
|
|
92
|
+
cursor?: string | null;
|
|
93
|
+
limit?: number;
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
}): Promise<BeeConversationsPage>;
|
|
96
|
+
getConversation(id: number, signal?: AbortSignal): Promise<BeeConversationDetail | null>;
|
|
97
|
+
listFacts(params?: {
|
|
98
|
+
cursor?: string | null;
|
|
99
|
+
signal?: AbortSignal;
|
|
100
|
+
}): Promise<BeeFactsPage>;
|
|
101
|
+
verifyAuth(signal?: AbortSignal): Promise<{
|
|
102
|
+
ok: boolean;
|
|
103
|
+
detail?: string;
|
|
104
|
+
}>;
|
|
105
|
+
private requestJson;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* True when the URL points at the local `bee proxy`. Hostname is
|
|
109
|
+
* compared exactly after parsing — a prefix match would also treat
|
|
110
|
+
* hosts like 127.0.0.1.evil.example as local.
|
|
111
|
+
*/
|
|
112
|
+
declare function isLocalProxyUrl(url: string): boolean;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Normalize Bee conversations into Remnic's provider-agnostic
|
|
116
|
+
* `WearableConversation` shape.
|
|
117
|
+
*
|
|
118
|
+
* Bee timestamps are epoch milliseconds; utterance `speaker` values are
|
|
119
|
+
* opaque diarization labels ("0", "1", ...) with no wearer marker — the
|
|
120
|
+
* Remnic speaker registry is how labels become names
|
|
121
|
+
* (`remnic wearables speakers set bee 0 "You" --self`).
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
declare const BEE_SOURCE_ID = "bee";
|
|
125
|
+
declare function conversationToWearable(detail: BeeConversationDetail): WearableConversation;
|
|
126
|
+
declare function factToNativeMemory(fact: BeeFact): WearableNativeMemory;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @remnic/connector-bee — Bee wearable connector.
|
|
130
|
+
*
|
|
131
|
+
* À-la-carte optional companion of @remnic/core (computed-specifier
|
|
132
|
+
* discovery; importing this module self-registers idempotently).
|
|
133
|
+
*
|
|
134
|
+
* Access modes:
|
|
135
|
+
* - default: the local `bee proxy` (http://127.0.0.1:8787, no token)
|
|
136
|
+
* - direct: set `wearables.sources.bee.baseUrl` to the direct host
|
|
137
|
+
* and provide a token via `REMNIC_BEE_API_TOKEN` /
|
|
138
|
+
* `BEE_API_TOKEN` (or `apiKey` in config)
|
|
139
|
+
*
|
|
140
|
+
* Bee's list API has no date filter, so the connector paginates
|
|
141
|
+
* newest-first and filters conversations to the requested local day,
|
|
142
|
+
* stopping once a whole page predates it.
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
declare function resolveBeeToken(configuredToken: string | undefined, env?: NodeJS.ProcessEnv): string | undefined;
|
|
146
|
+
declare function createBeeConnector(options: WearableConnectorFactoryOptions): WearableSourceConnector;
|
|
147
|
+
declare const wearableConnectorRegistration: WearableConnectorRegistration;
|
|
148
|
+
/**
|
|
149
|
+
* Idempotently register the connector with the core registry. Importing
|
|
150
|
+
* this module registers it as a side effect; calling this again is safe
|
|
151
|
+
* (returns false when already registered).
|
|
152
|
+
*/
|
|
153
|
+
declare function ensureBeeConnectorRegistered(): boolean;
|
|
154
|
+
|
|
155
|
+
export { BEE_DEFAULT_BASE_URL, BEE_DIRECT_BASE_URL, BEE_SOURCE_ID, BeeApiError, BeeClient, type BeeClientOptions, type BeeConversationDetail, type BeeConversationListItem, type BeeConversationsPage, type BeeFact, type BeeFactsPage, type BeeUtterance, conversationToWearable, createBeeConnector, ensureBeeConnectorRegistered, factToNativeMemory, isLocalProxyUrl, resolveBeeToken, wearableConnectorRegistration };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// openclaw-engram: Local-first memory plugin
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import {
|
|
5
|
+
dateInTimezone,
|
|
6
|
+
registerWearableConnector,
|
|
7
|
+
getWearableConnector
|
|
8
|
+
} from "@remnic/core";
|
|
9
|
+
|
|
10
|
+
// src/client.ts
|
|
11
|
+
var BEE_DEFAULT_BASE_URL = "http://127.0.0.1:8787";
|
|
12
|
+
var BEE_DIRECT_BASE_URL = "https://app-api-developer.ce.bee.amazon.dev";
|
|
13
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
14
|
+
var MAX_RETRIES = 3;
|
|
15
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
16
|
+
var LIST_PAGE_SIZE = 50;
|
|
17
|
+
var BeeApiError = class extends Error {
|
|
18
|
+
constructor(message, status) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.name = "BeeApiError";
|
|
22
|
+
}
|
|
23
|
+
status;
|
|
24
|
+
};
|
|
25
|
+
var BeeClient = class {
|
|
26
|
+
token;
|
|
27
|
+
baseUrl;
|
|
28
|
+
fetchImpl;
|
|
29
|
+
timeoutMs;
|
|
30
|
+
sleep;
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
this.token = typeof options.token === "string" && options.token.trim().length > 0 ? options.token.trim() : void 0;
|
|
33
|
+
this.baseUrl = stripTrailingSlashes(options.baseUrl ?? BEE_DEFAULT_BASE_URL);
|
|
34
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
35
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
36
|
+
this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
37
|
+
}
|
|
38
|
+
get usingLocalProxy() {
|
|
39
|
+
return isLocalProxyUrl(this.baseUrl);
|
|
40
|
+
}
|
|
41
|
+
async listConversations(params = {}) {
|
|
42
|
+
const search = new URLSearchParams({
|
|
43
|
+
limit: String(params.limit ?? LIST_PAGE_SIZE)
|
|
44
|
+
});
|
|
45
|
+
if (typeof params.cursor === "string" && params.cursor.length > 0) {
|
|
46
|
+
search.set("cursor", params.cursor);
|
|
47
|
+
}
|
|
48
|
+
const payload = await this.requestJson(
|
|
49
|
+
`/v1/conversations?${search.toString()}`,
|
|
50
|
+
params.signal
|
|
51
|
+
);
|
|
52
|
+
const conversations = payload.conversations;
|
|
53
|
+
if (!Array.isArray(conversations)) {
|
|
54
|
+
throw new BeeApiError(
|
|
55
|
+
"Bee API returned an unexpected /v1/conversations shape (missing conversations array)"
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
conversations: conversations.filter(isConversationListItem),
|
|
60
|
+
nextCursor: readNextCursor(payload)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async getConversation(id, signal) {
|
|
64
|
+
let payload;
|
|
65
|
+
try {
|
|
66
|
+
payload = await this.requestJson(`/v1/conversations/${id}`, signal);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (err instanceof BeeApiError && err.status === 404) return null;
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
const detail = payload !== null && typeof payload === "object" && "conversation" in payload ? payload.conversation : payload;
|
|
72
|
+
return isConversationListItem(detail) ? detail : null;
|
|
73
|
+
}
|
|
74
|
+
async listFacts(params = {}) {
|
|
75
|
+
const search = new URLSearchParams({
|
|
76
|
+
limit: String(LIST_PAGE_SIZE),
|
|
77
|
+
confirmed: "true"
|
|
78
|
+
});
|
|
79
|
+
if (typeof params.cursor === "string" && params.cursor.length > 0) {
|
|
80
|
+
search.set("cursor", params.cursor);
|
|
81
|
+
}
|
|
82
|
+
const payload = await this.requestJson(`/v1/facts?${search.toString()}`, params.signal);
|
|
83
|
+
const facts = payload.facts;
|
|
84
|
+
if (!Array.isArray(facts)) {
|
|
85
|
+
throw new BeeApiError(
|
|
86
|
+
"Bee API returned an unexpected /v1/facts shape (missing facts array)"
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
facts: facts.filter(
|
|
91
|
+
(entry) => entry !== null && typeof entry === "object" && typeof entry.id === "number" && typeof entry.text === "string"
|
|
92
|
+
),
|
|
93
|
+
nextCursor: readNextCursor(payload)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async verifyAuth(signal) {
|
|
97
|
+
try {
|
|
98
|
+
await this.requestJson("/v1/me", signal);
|
|
99
|
+
return {
|
|
100
|
+
ok: true,
|
|
101
|
+
detail: this.usingLocalProxy ? "via local bee proxy" : "direct API access"
|
|
102
|
+
};
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof BeeApiError && (err.status === 401 || err.status === 403)) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
detail: this.usingLocalProxy ? "the bee proxy rejected the request \u2014 re-run `bee login` then restart `bee proxy`" : "Bee rejected the token (401/403) \u2014 re-run `bee login` and update BEE_API_TOKEN"
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
detail: err instanceof BeeApiError ? err.message : describeNetworkError(err)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async requestJson(pathAndQuery, signal) {
|
|
117
|
+
let lastError;
|
|
118
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
119
|
+
signal?.throwIfAborted();
|
|
120
|
+
const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
|
|
121
|
+
const combined = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
122
|
+
let response;
|
|
123
|
+
try {
|
|
124
|
+
response = await this.fetchImpl(`${this.baseUrl}${pathAndQuery}`, {
|
|
125
|
+
method: "GET",
|
|
126
|
+
headers: {
|
|
127
|
+
Accept: "application/json",
|
|
128
|
+
...this.token !== void 0 ? { Authorization: `Bearer ${this.token}` } : {}
|
|
129
|
+
},
|
|
130
|
+
signal: combined
|
|
131
|
+
});
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (signal?.aborted) throw err;
|
|
134
|
+
lastError = err;
|
|
135
|
+
if (attempt < MAX_RETRIES) {
|
|
136
|
+
await this.sleep(backoffMs(attempt));
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
throw new BeeApiError(
|
|
140
|
+
`Bee API request failed after ${MAX_RETRIES + 1} attempts: ${describeNetworkError(err)}` + (this.usingLocalProxy ? " \u2014 is `bee proxy` running?" : "")
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (response.status === 429 || response.status >= 500) {
|
|
144
|
+
lastError = new BeeApiError(
|
|
145
|
+
`Bee API responded ${response.status}`,
|
|
146
|
+
response.status
|
|
147
|
+
);
|
|
148
|
+
if (attempt < MAX_RETRIES) {
|
|
149
|
+
await this.sleep(retryDelayMs(response, attempt));
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
throw lastError;
|
|
153
|
+
}
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
throw new BeeApiError(
|
|
156
|
+
`Bee API responded ${response.status} for ${pathAndQuery.split("?")[0]}`,
|
|
157
|
+
response.status
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
return await response.json();
|
|
162
|
+
} catch {
|
|
163
|
+
throw new BeeApiError("Bee API returned a non-JSON body");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
throw lastError instanceof Error ? lastError : new BeeApiError("Bee API request failed");
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
function isConversationListItem(entry) {
|
|
170
|
+
return entry !== null && typeof entry === "object" && typeof entry.id === "number" && typeof entry.start_time === "number";
|
|
171
|
+
}
|
|
172
|
+
function readNextCursor(payload) {
|
|
173
|
+
const cursor = payload.next_cursor;
|
|
174
|
+
if (typeof cursor === "string" && cursor.length > 0) return cursor;
|
|
175
|
+
if (typeof cursor === "number") return String(cursor);
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
function isLocalProxyUrl(url) {
|
|
179
|
+
try {
|
|
180
|
+
const parsed = new URL(url);
|
|
181
|
+
return parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost" || parsed.hostname === "[::1]" || parsed.hostname === "::1";
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function describeNetworkError(err) {
|
|
187
|
+
if (!(err instanceof Error)) return "unexpected non-Error failure";
|
|
188
|
+
const code = err.code;
|
|
189
|
+
return typeof code === "string" && code.length > 0 ? `${err.name} (${code})` : err.name;
|
|
190
|
+
}
|
|
191
|
+
function stripTrailingSlashes(value) {
|
|
192
|
+
let end = value.length;
|
|
193
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end--;
|
|
194
|
+
return value.slice(0, end);
|
|
195
|
+
}
|
|
196
|
+
function backoffMs(attempt) {
|
|
197
|
+
return Math.min(MAX_RETRY_DELAY_MS, 1e3 * 2 ** attempt);
|
|
198
|
+
}
|
|
199
|
+
function retryDelayMs(response, attempt) {
|
|
200
|
+
const headerValue = response.headers.get("retry-after");
|
|
201
|
+
if (headerValue !== null) {
|
|
202
|
+
const parsed = Number(headerValue);
|
|
203
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
204
|
+
return Math.min(MAX_RETRY_DELAY_MS, Math.ceil(parsed * 1e3));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return backoffMs(attempt);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/normalize.ts
|
|
211
|
+
var BEE_SOURCE_ID = "bee";
|
|
212
|
+
function msToIso(ms) {
|
|
213
|
+
if (typeof ms !== "number" || !Number.isFinite(ms) || ms <= 0) return void 0;
|
|
214
|
+
return new Date(ms).toISOString();
|
|
215
|
+
}
|
|
216
|
+
function conversationToWearable(detail) {
|
|
217
|
+
const segments = [];
|
|
218
|
+
for (const transcription of detail.transcriptions ?? []) {
|
|
219
|
+
for (const utterance of transcription.utterances ?? []) {
|
|
220
|
+
const text = typeof utterance.text === "string" ? utterance.text.trim() : "";
|
|
221
|
+
if (text.length === 0) continue;
|
|
222
|
+
const speaker = typeof utterance.speaker === "string" && utterance.speaker.trim().length > 0 ? utterance.speaker.trim() : "unknown";
|
|
223
|
+
segments.push({
|
|
224
|
+
text,
|
|
225
|
+
speakerKey: speaker,
|
|
226
|
+
...msToIso(utterance.spoken_at) !== void 0 ? { startIso: msToIso(utterance.spoken_at) } : {}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
segments.sort((a, b) => {
|
|
231
|
+
const aMs = a.startIso ? Date.parse(a.startIso) : Number.NaN;
|
|
232
|
+
const bMs = b.startIso ? Date.parse(b.startIso) : Number.NaN;
|
|
233
|
+
if (Number.isNaN(aMs) || Number.isNaN(bMs)) return 0;
|
|
234
|
+
if (aMs < bMs) return -1;
|
|
235
|
+
if (aMs > bMs) return 1;
|
|
236
|
+
return 0;
|
|
237
|
+
});
|
|
238
|
+
const title = typeof detail.short_summary === "string" && detail.short_summary.trim().length > 0 ? detail.short_summary.trim().split("\n")[0] : void 0;
|
|
239
|
+
return {
|
|
240
|
+
id: String(detail.id),
|
|
241
|
+
source: BEE_SOURCE_ID,
|
|
242
|
+
...title !== void 0 ? { title } : {},
|
|
243
|
+
...typeof detail.summary === "string" && detail.summary.trim().length > 0 ? { summary: detail.summary.trim() } : {},
|
|
244
|
+
startIso: msToIso(detail.start_time) ?? "",
|
|
245
|
+
...msToIso(detail.end_time ?? void 0) !== void 0 ? { endIso: msToIso(detail.end_time ?? void 0) } : {},
|
|
246
|
+
...typeof detail.primary_location?.address === "string" && detail.primary_location.address.length > 0 ? { location: detail.primary_location.address } : {},
|
|
247
|
+
segments
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function factToNativeMemory(fact) {
|
|
251
|
+
return {
|
|
252
|
+
id: String(fact.id),
|
|
253
|
+
content: fact.text,
|
|
254
|
+
...msToIso(fact.created_at) !== void 0 ? { createdIso: msToIso(fact.created_at) } : {},
|
|
255
|
+
...Array.isArray(fact.tags) && fact.tags.length > 0 ? { tags: fact.tags } : {}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/index.ts
|
|
260
|
+
function resolveBeeToken(configuredToken, env = process.env) {
|
|
261
|
+
if (typeof configuredToken === "string" && configuredToken.trim().length > 0) {
|
|
262
|
+
return configuredToken.trim();
|
|
263
|
+
}
|
|
264
|
+
for (const name of ["REMNIC_BEE_API_TOKEN", "BEE_API_TOKEN"]) {
|
|
265
|
+
const value = env[name];
|
|
266
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
267
|
+
return value.trim();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return void 0;
|
|
271
|
+
}
|
|
272
|
+
function isSyncableState(item) {
|
|
273
|
+
return typeof item.state !== "string" || item.state.toUpperCase() !== "CAPTURING";
|
|
274
|
+
}
|
|
275
|
+
function createBeeConnector(options) {
|
|
276
|
+
let client = null;
|
|
277
|
+
const getClient = () => {
|
|
278
|
+
if (!client) {
|
|
279
|
+
const baseUrl = options.settings.baseUrl ?? BEE_DEFAULT_BASE_URL;
|
|
280
|
+
const token = isLocalProxyUrl(baseUrl) ? void 0 : resolveBeeToken(options.settings.apiKey);
|
|
281
|
+
client = new BeeClient({ token, baseUrl });
|
|
282
|
+
}
|
|
283
|
+
return client;
|
|
284
|
+
};
|
|
285
|
+
return {
|
|
286
|
+
id: BEE_SOURCE_ID,
|
|
287
|
+
displayName: "Bee",
|
|
288
|
+
async verifyAuth(signal) {
|
|
289
|
+
return getClient().verifyAuth(signal);
|
|
290
|
+
},
|
|
291
|
+
async fetchConversations(opts) {
|
|
292
|
+
const page = await getClient().listConversations({
|
|
293
|
+
cursor: opts.cursor,
|
|
294
|
+
signal: opts.signal
|
|
295
|
+
});
|
|
296
|
+
const matching = [];
|
|
297
|
+
let sawOnlyOlder = page.conversations.length > 0;
|
|
298
|
+
for (const item of page.conversations) {
|
|
299
|
+
const localDate = dateInTimezone(new Date(item.start_time), opts.timezone);
|
|
300
|
+
if (localDate === opts.date && isSyncableState(item)) {
|
|
301
|
+
matching.push(item);
|
|
302
|
+
}
|
|
303
|
+
if (localDate >= opts.date) {
|
|
304
|
+
sawOnlyOlder = false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const conversations = [];
|
|
308
|
+
for (const item of matching) {
|
|
309
|
+
const detail = await getClient().getConversation(item.id, opts.signal);
|
|
310
|
+
if (detail === null) continue;
|
|
311
|
+
conversations.push(conversationToWearable(detail));
|
|
312
|
+
}
|
|
313
|
+
const nextCursor = sawOnlyOlder ? null : page.nextCursor;
|
|
314
|
+
return { conversations, nextCursor };
|
|
315
|
+
},
|
|
316
|
+
async fetchNativeMemories(opts) {
|
|
317
|
+
const page = await getClient().listFacts({
|
|
318
|
+
cursor: opts.cursor,
|
|
319
|
+
signal: opts.signal
|
|
320
|
+
});
|
|
321
|
+
return {
|
|
322
|
+
memories: page.facts.map(factToNativeMemory),
|
|
323
|
+
nextCursor: page.nextCursor
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
var wearableConnectorRegistration = {
|
|
329
|
+
id: BEE_SOURCE_ID,
|
|
330
|
+
displayName: "Bee",
|
|
331
|
+
factory: createBeeConnector
|
|
332
|
+
};
|
|
333
|
+
function ensureBeeConnectorRegistered() {
|
|
334
|
+
if (getWearableConnector(BEE_SOURCE_ID) !== void 0) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
registerWearableConnector(wearableConnectorRegistration);
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
ensureBeeConnectorRegistered();
|
|
341
|
+
export {
|
|
342
|
+
BEE_DEFAULT_BASE_URL,
|
|
343
|
+
BEE_DIRECT_BASE_URL,
|
|
344
|
+
BEE_SOURCE_ID,
|
|
345
|
+
BeeApiError,
|
|
346
|
+
BeeClient,
|
|
347
|
+
conversationToWearable,
|
|
348
|
+
createBeeConnector,
|
|
349
|
+
ensureBeeConnectorRegistered,
|
|
350
|
+
factToNativeMemory,
|
|
351
|
+
isLocalProxyUrl,
|
|
352
|
+
resolveBeeToken,
|
|
353
|
+
wearableConnectorRegistration
|
|
354
|
+
};
|
|
355
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/normalize.ts"],"sourcesContent":["/**\n * @remnic/connector-bee — Bee wearable connector.\n *\n * À-la-carte optional companion of @remnic/core (computed-specifier\n * discovery; importing this module self-registers idempotently).\n *\n * Access modes:\n * - default: the local `bee proxy` (http://127.0.0.1:8787, no token)\n * - direct: set `wearables.sources.bee.baseUrl` to the direct host\n * and provide a token via `REMNIC_BEE_API_TOKEN` /\n * `BEE_API_TOKEN` (or `apiKey` in config)\n *\n * Bee's list API has no date filter, so the connector paginates\n * newest-first and filters conversations to the requested local day,\n * stopping once a whole page predates it.\n */\n\nimport {\n dateInTimezone,\n registerWearableConnector,\n getWearableConnector,\n type WearableConnectorFactoryOptions,\n type WearableConnectorRegistration,\n type WearableFetchOptions,\n type WearableFetchPage,\n type WearableNativeMemoryPage,\n type WearableSourceConnector,\n} from \"@remnic/core\";\n\nimport { BeeClient, isLocalProxyUrl, BEE_DEFAULT_BASE_URL, type BeeConversationListItem } from \"./client.js\";\nimport { BEE_SOURCE_ID, conversationToWearable, factToNativeMemory } from \"./normalize.js\";\n\nexport {\n BeeApiError,\n BeeClient,\n BEE_DEFAULT_BASE_URL,\n BEE_DIRECT_BASE_URL,\n isLocalProxyUrl,\n} from \"./client.js\";\nexport type {\n BeeClientOptions,\n BeeConversationDetail,\n BeeConversationListItem,\n BeeConversationsPage,\n BeeFact,\n BeeFactsPage,\n BeeUtterance,\n} from \"./client.js\";\nexport { BEE_SOURCE_ID, conversationToWearable, factToNativeMemory } from \"./normalize.js\";\n\nexport function resolveBeeToken(\n configuredToken: string | undefined,\n env: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n if (typeof configuredToken === \"string\" && configuredToken.trim().length > 0) {\n return configuredToken.trim();\n }\n for (const name of [\"REMNIC_BEE_API_TOKEN\", \"BEE_API_TOKEN\"]) {\n const value = env[name];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n }\n return undefined;\n}\n\n/** Conversations still being recorded are skipped until they settle. */\nfunction isSyncableState(item: BeeConversationListItem): boolean {\n return typeof item.state !== \"string\" || item.state.toUpperCase() !== \"CAPTURING\";\n}\n\nexport function createBeeConnector(\n options: WearableConnectorFactoryOptions,\n): WearableSourceConnector {\n let client: BeeClient | null = null;\n const getClient = (): BeeClient => {\n if (!client) {\n const baseUrl = options.settings.baseUrl ?? BEE_DEFAULT_BASE_URL;\n // The local proxy is unauthenticated by design — never attach a\n // Bearer header there, even when a direct-mode token sits in the\n // environment (it would 401 proxy requests for users who keep\n // BEE_API_TOKEN exported for occasional direct use).\n const token = isLocalProxyUrl(baseUrl)\n ? undefined\n : resolveBeeToken(options.settings.apiKey);\n client = new BeeClient({ token, baseUrl });\n }\n return client;\n };\n\n return {\n id: BEE_SOURCE_ID,\n displayName: \"Bee\",\n async verifyAuth(signal?: AbortSignal) {\n return getClient().verifyAuth(signal);\n },\n async fetchConversations(opts: WearableFetchOptions): Promise<WearableFetchPage> {\n const page = await getClient().listConversations({\n cursor: opts.cursor,\n signal: opts.signal,\n });\n\n const matching: BeeConversationListItem[] = [];\n let sawOnlyOlder = page.conversations.length > 0;\n for (const item of page.conversations) {\n const localDate = dateInTimezone(new Date(item.start_time), opts.timezone);\n if (localDate === opts.date && isSyncableState(item)) {\n matching.push(item);\n }\n if (localDate >= opts.date) {\n sawOnlyOlder = false;\n }\n }\n\n const conversations = [];\n for (const item of matching) {\n const detail = await getClient().getConversation(item.id, opts.signal);\n if (detail === null) continue;\n conversations.push(conversationToWearable(detail));\n }\n\n // Stop paginating once an entire (newest-first) page predates the\n // requested day; anything deeper is older still.\n const nextCursor = sawOnlyOlder ? null : page.nextCursor;\n return { conversations, nextCursor };\n },\n async fetchNativeMemories(opts: {\n cursor?: string | null;\n signal?: AbortSignal;\n }): Promise<WearableNativeMemoryPage> {\n const page = await getClient().listFacts({\n cursor: opts.cursor,\n signal: opts.signal,\n });\n return {\n memories: page.facts.map(factToNativeMemory),\n nextCursor: page.nextCursor,\n };\n },\n };\n}\n\nexport const wearableConnectorRegistration: WearableConnectorRegistration = {\n id: BEE_SOURCE_ID,\n displayName: \"Bee\",\n factory: createBeeConnector,\n};\n\n/**\n * Idempotently register the connector with the core registry. Importing\n * this module registers it as a side effect; calling this again is safe\n * (returns false when already registered).\n */\nexport function ensureBeeConnectorRegistered(): boolean {\n if (getWearableConnector(BEE_SOURCE_ID) !== undefined) {\n return false;\n }\n registerWearableConnector(wearableConnectorRegistration);\n return true;\n}\n\nensureBeeConnectorRegistered();\n","/**\n * Minimal Bee developer API client (raw fetch, no SDK).\n *\n * Contract verified against the official `@beeai/cli` source and\n * docs.bee.computer (2026-06). Two access modes:\n *\n * - **Proxy mode (default):** the official `bee proxy` command serves\n * the full developer API unauthenticated on `http://127.0.0.1:8787`.\n * No token required.\n * - **Direct mode:** `https://app-api-developer.ce.bee.amazon.dev`\n * with `Authorization: Bearer <token>` (token from `bee login`,\n * stored at `~/.bee/token-prod`). The direct host uses Bee's private\n * CA — point `NODE_EXTRA_CA_CERTS` at it when going direct.\n *\n * The legacy pre-acquisition API (`api.bee.computer`, `x-api-key`,\n * page/totalPages) no longer resolves and is intentionally not\n * supported.\n *\n * List endpoints use `limit` + `cursor` pagination with a `next_cursor`\n * in the envelope; timestamps are epoch milliseconds. Tokens are never\n * logged and never appear in thrown error messages.\n */\n\nexport const BEE_DEFAULT_BASE_URL = \"http://127.0.0.1:8787\";\nexport const BEE_DIRECT_BASE_URL = \"https://app-api-developer.ce.bee.amazon.dev\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_RETRIES = 3;\nconst MAX_RETRY_DELAY_MS = 30_000;\nconst LIST_PAGE_SIZE = 50;\n\nexport interface BeeConversationListItem {\n id: number;\n start_time: number;\n end_time?: number | null;\n device_type?: string;\n short_summary?: string | null;\n summary?: string | null;\n state?: string;\n}\n\nexport interface BeeUtterance {\n id?: number;\n start?: number | null;\n end?: number | null;\n spoken_at?: number;\n text?: string;\n speaker?: string;\n}\n\nexport interface BeeConversationDetail extends BeeConversationListItem {\n transcriptions?: Array<{\n id?: number;\n utterances?: BeeUtterance[];\n }>;\n primary_location?: {\n address?: string | null;\n latitude?: number;\n longitude?: number;\n } | null;\n}\n\nexport interface BeeFact {\n id: number;\n text: string;\n tags?: string[];\n created_at?: number;\n confirmed?: boolean;\n}\n\nexport interface BeeConversationsPage {\n conversations: BeeConversationListItem[];\n nextCursor: string | null;\n}\n\nexport interface BeeFactsPage {\n facts: BeeFact[];\n nextCursor: string | null;\n}\n\nexport interface BeeClientOptions {\n /** Bearer token for direct mode. Omit entirely for proxy mode. */\n token?: string;\n /** Defaults to the local `bee proxy` address. */\n baseUrl?: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport class BeeApiError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n ) {\n super(message);\n this.name = \"BeeApiError\";\n }\n}\n\nexport class BeeClient {\n private readonly token: string | undefined;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(options: BeeClientOptions = {}) {\n this.token =\n typeof options.token === \"string\" && options.token.trim().length > 0\n ? options.token.trim()\n : undefined;\n this.baseUrl = stripTrailingSlashes(options.baseUrl ?? BEE_DEFAULT_BASE_URL);\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.sleep =\n options.sleep ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));\n }\n\n get usingLocalProxy(): boolean {\n return isLocalProxyUrl(this.baseUrl);\n }\n\n async listConversations(params: {\n cursor?: string | null;\n limit?: number;\n signal?: AbortSignal;\n } = {}): Promise<BeeConversationsPage> {\n const search = new URLSearchParams({\n limit: String(params.limit ?? LIST_PAGE_SIZE),\n });\n if (typeof params.cursor === \"string\" && params.cursor.length > 0) {\n search.set(\"cursor\", params.cursor);\n }\n const payload = await this.requestJson(\n `/v1/conversations?${search.toString()}`,\n params.signal,\n );\n const conversations = (payload as { conversations?: unknown }).conversations;\n if (!Array.isArray(conversations)) {\n throw new BeeApiError(\n \"Bee API returned an unexpected /v1/conversations shape (missing conversations array)\",\n );\n }\n return {\n conversations: conversations.filter(isConversationListItem),\n nextCursor: readNextCursor(payload),\n };\n }\n\n async getConversation(\n id: number,\n signal?: AbortSignal,\n ): Promise<BeeConversationDetail | null> {\n let payload: unknown;\n try {\n payload = await this.requestJson(`/v1/conversations/${id}`, signal);\n } catch (err) {\n // A conversation can be deleted between the list and the detail\n // fetch — a 404 skips that conversation instead of aborting the\n // whole day sync. Other failures (auth, 5xx after retries) still\n // throw so transient outages retry the sync rather than silently\n // dropping data.\n if (err instanceof BeeApiError && err.status === 404) return null;\n throw err;\n }\n // Current API returns the detail at the top level; the legacy shape\n // wrapped it as {conversation: {...}}. Accept both.\n const detail =\n payload !== null &&\n typeof payload === \"object\" &&\n \"conversation\" in (payload as Record<string, unknown>)\n ? (payload as { conversation: unknown }).conversation\n : payload;\n return isConversationListItem(detail) ? (detail as BeeConversationDetail) : null;\n }\n\n async listFacts(params: {\n cursor?: string | null;\n signal?: AbortSignal;\n } = {}): Promise<BeeFactsPage> {\n const search = new URLSearchParams({\n limit: String(LIST_PAGE_SIZE),\n confirmed: \"true\",\n });\n if (typeof params.cursor === \"string\" && params.cursor.length > 0) {\n search.set(\"cursor\", params.cursor);\n }\n const payload = await this.requestJson(`/v1/facts?${search.toString()}`, params.signal);\n const facts = (payload as { facts?: unknown }).facts;\n if (!Array.isArray(facts)) {\n throw new BeeApiError(\n \"Bee API returned an unexpected /v1/facts shape (missing facts array)\",\n );\n }\n return {\n facts: facts.filter(\n (entry): entry is BeeFact =>\n entry !== null &&\n typeof entry === \"object\" &&\n typeof (entry as { id?: unknown }).id === \"number\" &&\n typeof (entry as { text?: unknown }).text === \"string\",\n ),\n nextCursor: readNextCursor(payload),\n };\n }\n\n async verifyAuth(signal?: AbortSignal): Promise<{ ok: boolean; detail?: string }> {\n try {\n await this.requestJson(\"/v1/me\", signal);\n return {\n ok: true,\n detail: this.usingLocalProxy ? \"via local bee proxy\" : \"direct API access\",\n };\n } catch (err) {\n if (err instanceof BeeApiError && (err.status === 401 || err.status === 403)) {\n return {\n ok: false,\n detail: this.usingLocalProxy\n ? \"the bee proxy rejected the request — re-run `bee login` then restart `bee proxy`\"\n : \"Bee rejected the token (401/403) — re-run `bee login` and update BEE_API_TOKEN\",\n };\n }\n // BeeApiError messages are our own constructed strings (status\n // codes + endpoint role; network failures already carry the\n // `bee proxy` hint from requestJson) — keep them actionable.\n // Only foreign errors are reduced to name + code.\n return {\n ok: false,\n detail: err instanceof BeeApiError ? err.message : describeNetworkError(err),\n };\n }\n }\n\n private async requestJson(pathAndQuery: string, signal?: AbortSignal): Promise<unknown> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n signal?.throwIfAborted();\n const timeoutSignal = AbortSignal.timeout(this.timeoutMs);\n const combined = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n let response: Response;\n try {\n response = await this.fetchImpl(`${this.baseUrl}${pathAndQuery}`, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n ...(this.token !== undefined\n ? { Authorization: `Bearer ${this.token}` }\n : {}),\n },\n signal: combined,\n });\n } catch (err) {\n if (signal?.aborted) throw err;\n lastError = err;\n if (attempt < MAX_RETRIES) {\n await this.sleep(backoffMs(attempt));\n continue;\n }\n throw new BeeApiError(\n `Bee API request failed after ${MAX_RETRIES + 1} attempts: ${describeNetworkError(err)}` +\n (this.usingLocalProxy ? \" — is `bee proxy` running?\" : \"\"),\n );\n }\n\n if (response.status === 429 || response.status >= 500) {\n lastError = new BeeApiError(\n `Bee API responded ${response.status}`,\n response.status,\n );\n if (attempt < MAX_RETRIES) {\n await this.sleep(retryDelayMs(response, attempt));\n continue;\n }\n throw lastError;\n }\n if (!response.ok) {\n throw new BeeApiError(\n `Bee API responded ${response.status} for ${pathAndQuery.split(\"?\")[0]}`,\n response.status,\n );\n }\n try {\n return await response.json();\n } catch {\n throw new BeeApiError(\"Bee API returned a non-JSON body\");\n }\n }\n throw lastError instanceof Error\n ? lastError\n : new BeeApiError(\"Bee API request failed\");\n }\n}\n\nfunction isConversationListItem(entry: unknown): entry is BeeConversationListItem {\n return (\n entry !== null &&\n typeof entry === \"object\" &&\n typeof (entry as { id?: unknown }).id === \"number\" &&\n typeof (entry as { start_time?: unknown }).start_time === \"number\"\n );\n}\n\nfunction readNextCursor(payload: unknown): string | null {\n const cursor = (payload as { next_cursor?: unknown }).next_cursor;\n if (typeof cursor === \"string\" && cursor.length > 0) return cursor;\n if (typeof cursor === \"number\") return String(cursor);\n return null;\n}\n\n/**\n * True when the URL points at the local `bee proxy`. Hostname is\n * compared exactly after parsing — a prefix match would also treat\n * hosts like 127.0.0.1.evil.example as local.\n */\nexport function isLocalProxyUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return (\n parsed.hostname === \"127.0.0.1\" ||\n parsed.hostname === \"localhost\" ||\n parsed.hostname === \"[::1]\" ||\n parsed.hostname === \"::1\"\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Network/timeout failures wrap Node error text that can carry loader\n * paths or stack fragments; sync errors reach MCP clients verbatim, so\n * only the error name + code survive.\n */\nfunction describeNetworkError(err: unknown): string {\n if (!(err instanceof Error)) return \"unexpected non-Error failure\";\n const code = (err as NodeJS.ErrnoException).code;\n return typeof code === \"string\" && code.length > 0 ? `${err.name} (${code})` : err.name;\n}\n\n/** Loop instead of `/\\/+$/` — CodeQL js/polynomial-redos on user-set URLs. */\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 0x2f) end--;\n return value.slice(0, end);\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(MAX_RETRY_DELAY_MS, 1_000 * 2 ** attempt);\n}\n\nfunction retryDelayMs(response: Response, attempt: number): number {\n const headerValue = response.headers.get(\"retry-after\");\n if (headerValue !== null) {\n const parsed = Number(headerValue);\n if (Number.isFinite(parsed) && parsed > 0) {\n return Math.min(MAX_RETRY_DELAY_MS, Math.ceil(parsed * 1_000));\n }\n }\n return backoffMs(attempt);\n}\n","/**\n * Normalize Bee conversations into Remnic's provider-agnostic\n * `WearableConversation` shape.\n *\n * Bee timestamps are epoch milliseconds; utterance `speaker` values are\n * opaque diarization labels (\"0\", \"1\", ...) with no wearer marker — the\n * Remnic speaker registry is how labels become names\n * (`remnic wearables speakers set bee 0 \"You\" --self`).\n */\n\nimport type {\n WearableConversation,\n WearableNativeMemory,\n WearableTranscriptSegment,\n} from \"@remnic/core\";\n\nimport type { BeeConversationDetail, BeeFact } from \"./client.js\";\n\nexport const BEE_SOURCE_ID = \"bee\";\n\nfunction msToIso(ms: number | null | undefined): string | undefined {\n if (typeof ms !== \"number\" || !Number.isFinite(ms) || ms <= 0) return undefined;\n return new Date(ms).toISOString();\n}\n\nexport function conversationToWearable(\n detail: BeeConversationDetail,\n): WearableConversation {\n const segments: WearableTranscriptSegment[] = [];\n for (const transcription of detail.transcriptions ?? []) {\n for (const utterance of transcription.utterances ?? []) {\n const text = typeof utterance.text === \"string\" ? utterance.text.trim() : \"\";\n if (text.length === 0) continue;\n const speaker =\n typeof utterance.speaker === \"string\" && utterance.speaker.trim().length > 0\n ? utterance.speaker.trim()\n : \"unknown\";\n segments.push({\n text,\n speakerKey: speaker,\n ...(msToIso(utterance.spoken_at) !== undefined\n ? { startIso: msToIso(utterance.spoken_at) }\n : {}),\n });\n }\n }\n // Bee nests utterances per transcription block; order within a block\n // is chronological but blocks can interleave — sort stably by time\n // when timestamps exist.\n segments.sort((a, b) => {\n const aMs = a.startIso ? Date.parse(a.startIso) : Number.NaN;\n const bMs = b.startIso ? Date.parse(b.startIso) : Number.NaN;\n if (Number.isNaN(aMs) || Number.isNaN(bMs)) return 0;\n if (aMs < bMs) return -1;\n if (aMs > bMs) return 1;\n return 0;\n });\n\n const title =\n typeof detail.short_summary === \"string\" && detail.short_summary.trim().length > 0\n ? detail.short_summary.trim().split(\"\\n\")[0]\n : undefined;\n\n return {\n id: String(detail.id),\n source: BEE_SOURCE_ID,\n ...(title !== undefined ? { title } : {}),\n ...(typeof detail.summary === \"string\" && detail.summary.trim().length > 0\n ? { summary: detail.summary.trim() }\n : {}),\n startIso: msToIso(detail.start_time) ?? \"\",\n ...(msToIso(detail.end_time ?? undefined) !== undefined\n ? { endIso: msToIso(detail.end_time ?? undefined) }\n : {}),\n ...(typeof detail.primary_location?.address === \"string\" &&\n detail.primary_location.address.length > 0\n ? { location: detail.primary_location.address }\n : {}),\n segments,\n };\n}\n\nexport function factToNativeMemory(fact: BeeFact): WearableNativeMemory {\n return {\n id: String(fact.id),\n content: fact.text,\n ...(msToIso(fact.created_at) !== undefined\n ? { createdIso: msToIso(fact.created_at) }\n : {}),\n ...(Array.isArray(fact.tags) && fact.tags.length > 0 ? { tags: fact.tags } : {}),\n };\n}\n"],"mappings":";;;AAiBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAOK;;;ACJA,IAAM,uBAAuB;AAC7B,IAAM,sBAAsB;AAEnC,IAAM,qBAAqB;AAC3B,IAAM,cAAc;AACpB,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB;AA6DhB,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YACE,SACS,QACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAEO,IAAM,YAAN,MAAgB;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,UAA4B,CAAC,GAAG;AAC1C,SAAK,QACH,OAAO,QAAQ,UAAU,YAAY,QAAQ,MAAM,KAAK,EAAE,SAAS,IAC/D,QAAQ,MAAM,KAAK,IACnB;AACN,SAAK,UAAU,qBAAqB,QAAQ,WAAW,oBAAoB;AAC3E,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,QACH,QAAQ,UAAU,CAAC,OAAe,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACtF;AAAA,EAEA,IAAI,kBAA2B;AAC7B,WAAO,gBAAgB,KAAK,OAAO;AAAA,EACrC;AAAA,EAEA,MAAM,kBAAkB,SAIpB,CAAC,GAAkC;AACrC,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,OAAO,OAAO,OAAO,SAAS,cAAc;AAAA,IAC9C,CAAC;AACD,QAAI,OAAO,OAAO,WAAW,YAAY,OAAO,OAAO,SAAS,GAAG;AACjE,aAAO,IAAI,UAAU,OAAO,MAAM;AAAA,IACpC;AACA,UAAM,UAAU,MAAM,KAAK;AAAA,MACzB,qBAAqB,OAAO,SAAS,CAAC;AAAA,MACtC,OAAO;AAAA,IACT;AACA,UAAM,gBAAiB,QAAwC;AAC/D,QAAI,CAAC,MAAM,QAAQ,aAAa,GAAG;AACjC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,eAAe,cAAc,OAAO,sBAAsB;AAAA,MAC1D,YAAY,eAAe,OAAO;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,MAAM,gBACJ,IACA,QACuC;AACvC,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,YAAY,qBAAqB,EAAE,IAAI,MAAM;AAAA,IACpE,SAAS,KAAK;AAMZ,UAAI,eAAe,eAAe,IAAI,WAAW,IAAK,QAAO;AAC7D,YAAM;AAAA,IACR;AAGA,UAAM,SACJ,YAAY,QACZ,OAAO,YAAY,YACnB,kBAAmB,UACd,QAAsC,eACvC;AACN,WAAO,uBAAuB,MAAM,IAAK,SAAmC;AAAA,EAC9E;AAAA,EAEA,MAAM,UAAU,SAGZ,CAAC,GAA0B;AAC7B,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,OAAO,OAAO,cAAc;AAAA,MAC5B,WAAW;AAAA,IACb,CAAC;AACD,QAAI,OAAO,OAAO,WAAW,YAAY,OAAO,OAAO,SAAS,GAAG;AACjE,aAAO,IAAI,UAAU,OAAO,MAAM;AAAA,IACpC;AACA,UAAM,UAAU,MAAM,KAAK,YAAY,aAAa,OAAO,SAAS,CAAC,IAAI,OAAO,MAAM;AACtF,UAAM,QAAS,QAAgC;AAC/C,QAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,OAAO,MAAM;AAAA,QACX,CAAC,UACC,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAA2B,OAAO,YAC1C,OAAQ,MAA6B,SAAS;AAAA,MAClD;AAAA,MACA,YAAY,eAAe,OAAO;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,QAAiE;AAChF,QAAI;AACF,YAAM,KAAK,YAAY,UAAU,MAAM;AACvC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,KAAK,kBAAkB,wBAAwB;AAAA,MACzD;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,gBAAgB,IAAI,WAAW,OAAO,IAAI,WAAW,MAAM;AAC5E,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,QAAQ,KAAK,kBACT,0FACA;AAAA,QACN;AAAA,MACF;AAKA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,eAAe,cAAc,IAAI,UAAU,qBAAqB,GAAG;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,cAAsB,QAAwC;AACtF,QAAI;AACJ,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACvD,cAAQ,eAAe;AACvB,YAAM,gBAAgB,YAAY,QAAQ,KAAK,SAAS;AACxD,YAAM,WAAW,SAAS,YAAY,IAAI,CAAC,QAAQ,aAAa,CAAC,IAAI;AACrE,UAAI;AACJ,UAAI;AACF,mBAAW,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,YAAY,IAAI;AAAA,UAChE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,QAAQ;AAAA,YACR,GAAI,KAAK,UAAU,SACf,EAAE,eAAe,UAAU,KAAK,KAAK,GAAG,IACxC,CAAC;AAAA,UACP;AAAA,UACA,QAAQ;AAAA,QACV,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,YAAI,QAAQ,QAAS,OAAM;AAC3B,oBAAY;AACZ,YAAI,UAAU,aAAa;AACzB,gBAAM,KAAK,MAAM,UAAU,OAAO,CAAC;AACnC;AAAA,QACF;AACA,cAAM,IAAI;AAAA,UACR,gCAAgC,cAAc,CAAC,cAAc,qBAAqB,GAAG,CAAC,MACnF,KAAK,kBAAkB,oCAA+B;AAAA,QAC3D;AAAA,MACF;AAEA,UAAI,SAAS,WAAW,OAAO,SAAS,UAAU,KAAK;AACrD,oBAAY,IAAI;AAAA,UACd,qBAAqB,SAAS,MAAM;AAAA,UACpC,SAAS;AAAA,QACX;AACA,YAAI,UAAU,aAAa;AACzB,gBAAM,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;AAChD;AAAA,QACF;AACA,cAAM;AAAA,MACR;AACA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI;AAAA,UACR,qBAAqB,SAAS,MAAM,QAAQ,aAAa,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,UACtE,SAAS;AAAA,QACX;AAAA,MACF;AACA,UAAI;AACF,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B,QAAQ;AACN,cAAM,IAAI,YAAY,kCAAkC;AAAA,MAC1D;AAAA,IACF;AACA,UAAM,qBAAqB,QACvB,YACA,IAAI,YAAY,wBAAwB;AAAA,EAC9C;AACF;AAEA,SAAS,uBAAuB,OAAkD;AAChF,SACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAA2B,OAAO,YAC1C,OAAQ,MAAmC,eAAe;AAE9D;AAEA,SAAS,eAAe,SAAiC;AACvD,QAAM,SAAU,QAAsC;AACtD,MAAI,OAAO,WAAW,YAAY,OAAO,SAAS,EAAG,QAAO;AAC5D,MAAI,OAAO,WAAW,SAAU,QAAO,OAAO,MAAM;AACpD,SAAO;AACT;AAOO,SAAS,gBAAgB,KAAsB;AACpD,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,WACE,OAAO,aAAa,eACpB,OAAO,aAAa,eACpB,OAAO,aAAa,WACpB,OAAO,aAAa;AAAA,EAExB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,qBAAqB,KAAsB;AAClD,MAAI,EAAE,eAAe,OAAQ,QAAO;AACpC,QAAM,OAAQ,IAA8B;AAC5C,SAAO,OAAO,SAAS,YAAY,KAAK,SAAS,IAAI,GAAG,IAAI,IAAI,KAAK,IAAI,MAAM,IAAI;AACrF;AAGA,SAAS,qBAAqB,OAAuB;AACnD,MAAI,MAAM,MAAM;AAChB,SAAO,MAAM,KAAK,MAAM,WAAW,MAAM,CAAC,MAAM,GAAM;AACtD,SAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;AAEA,SAAS,UAAU,SAAyB;AAC1C,SAAO,KAAK,IAAI,oBAAoB,MAAQ,KAAK,OAAO;AAC1D;AAEA,SAAS,aAAa,UAAoB,SAAyB;AACjE,QAAM,cAAc,SAAS,QAAQ,IAAI,aAAa;AACtD,MAAI,gBAAgB,MAAM;AACxB,UAAM,SAAS,OAAO,WAAW;AACjC,QAAI,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AACzC,aAAO,KAAK,IAAI,oBAAoB,KAAK,KAAK,SAAS,GAAK,CAAC;AAAA,IAC/D;AAAA,EACF;AACA,SAAO,UAAU,OAAO;AAC1B;;;ACtVO,IAAM,gBAAgB;AAE7B,SAAS,QAAQ,IAAmD;AAClE,MAAI,OAAO,OAAO,YAAY,CAAC,OAAO,SAAS,EAAE,KAAK,MAAM,EAAG,QAAO;AACtE,SAAO,IAAI,KAAK,EAAE,EAAE,YAAY;AAClC;AAEO,SAAS,uBACd,QACsB;AACtB,QAAM,WAAwC,CAAC;AAC/C,aAAW,iBAAiB,OAAO,kBAAkB,CAAC,GAAG;AACvD,eAAW,aAAa,cAAc,cAAc,CAAC,GAAG;AACtD,YAAM,OAAO,OAAO,UAAU,SAAS,WAAW,UAAU,KAAK,KAAK,IAAI;AAC1E,UAAI,KAAK,WAAW,EAAG;AACvB,YAAM,UACJ,OAAO,UAAU,YAAY,YAAY,UAAU,QAAQ,KAAK,EAAE,SAAS,IACvE,UAAU,QAAQ,KAAK,IACvB;AACN,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,YAAY;AAAA,QACZ,GAAI,QAAQ,UAAU,SAAS,MAAM,SACjC,EAAE,UAAU,QAAQ,UAAU,SAAS,EAAE,IACzC,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAIA,WAAS,KAAK,CAAC,GAAG,MAAM;AACtB,UAAM,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,QAAQ,IAAI,OAAO;AACzD,UAAM,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,QAAQ,IAAI,OAAO;AACzD,QAAI,OAAO,MAAM,GAAG,KAAK,OAAO,MAAM,GAAG,EAAG,QAAO;AACnD,QAAI,MAAM,IAAK,QAAO;AACtB,QAAI,MAAM,IAAK,QAAO;AACtB,WAAO;AAAA,EACT,CAAC;AAED,QAAM,QACJ,OAAO,OAAO,kBAAkB,YAAY,OAAO,cAAc,KAAK,EAAE,SAAS,IAC7E,OAAO,cAAc,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,IACzC;AAEN,SAAO;AAAA,IACL,IAAI,OAAO,OAAO,EAAE;AAAA,IACpB,QAAQ;AAAA,IACR,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,IACvC,GAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,KAAK,EAAE,SAAS,IACrE,EAAE,SAAS,OAAO,QAAQ,KAAK,EAAE,IACjC,CAAC;AAAA,IACL,UAAU,QAAQ,OAAO,UAAU,KAAK;AAAA,IACxC,GAAI,QAAQ,OAAO,YAAY,MAAS,MAAM,SAC1C,EAAE,QAAQ,QAAQ,OAAO,YAAY,MAAS,EAAE,IAChD,CAAC;AAAA,IACL,GAAI,OAAO,OAAO,kBAAkB,YAAY,YAChD,OAAO,iBAAiB,QAAQ,SAAS,IACrC,EAAE,UAAU,OAAO,iBAAiB,QAAQ,IAC5C,CAAC;AAAA,IACL;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,MAAqC;AACtE,SAAO;AAAA,IACL,IAAI,OAAO,KAAK,EAAE;AAAA,IAClB,SAAS,KAAK;AAAA,IACd,GAAI,QAAQ,KAAK,UAAU,MAAM,SAC7B,EAAE,YAAY,QAAQ,KAAK,UAAU,EAAE,IACvC,CAAC;AAAA,IACL,GAAI,MAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,KAAK,SAAS,IAAI,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,EAChF;AACF;;;AFzCO,SAAS,gBACd,iBACA,MAAyB,QAAQ,KACb;AACpB,MAAI,OAAO,oBAAoB,YAAY,gBAAgB,KAAK,EAAE,SAAS,GAAG;AAC5E,WAAO,gBAAgB,KAAK;AAAA,EAC9B;AACA,aAAW,QAAQ,CAAC,wBAAwB,eAAe,GAAG;AAC5D,UAAM,QAAQ,IAAI,IAAI;AACtB,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,aAAO,MAAM,KAAK;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,gBAAgB,MAAwC;AAC/D,SAAO,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,YAAY,MAAM;AACxE;AAEO,SAAS,mBACd,SACyB;AACzB,MAAI,SAA2B;AAC/B,QAAM,YAAY,MAAiB;AACjC,QAAI,CAAC,QAAQ;AACX,YAAM,UAAU,QAAQ,SAAS,WAAW;AAK5C,YAAM,QAAQ,gBAAgB,OAAO,IACjC,SACA,gBAAgB,QAAQ,SAAS,MAAM;AAC3C,eAAS,IAAI,UAAU,EAAE,OAAO,QAAQ,CAAC;AAAA,IAC3C;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,MAAM,WAAW,QAAsB;AACrC,aAAO,UAAU,EAAE,WAAW,MAAM;AAAA,IACtC;AAAA,IACA,MAAM,mBAAmB,MAAwD;AAC/E,YAAM,OAAO,MAAM,UAAU,EAAE,kBAAkB;AAAA,QAC/C,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,MACf,CAAC;AAED,YAAM,WAAsC,CAAC;AAC7C,UAAI,eAAe,KAAK,cAAc,SAAS;AAC/C,iBAAW,QAAQ,KAAK,eAAe;AACrC,cAAM,YAAY,eAAe,IAAI,KAAK,KAAK,UAAU,GAAG,KAAK,QAAQ;AACzE,YAAI,cAAc,KAAK,QAAQ,gBAAgB,IAAI,GAAG;AACpD,mBAAS,KAAK,IAAI;AAAA,QACpB;AACA,YAAI,aAAa,KAAK,MAAM;AAC1B,yBAAe;AAAA,QACjB;AAAA,MACF;AAEA,YAAM,gBAAgB,CAAC;AACvB,iBAAW,QAAQ,UAAU;AAC3B,cAAM,SAAS,MAAM,UAAU,EAAE,gBAAgB,KAAK,IAAI,KAAK,MAAM;AACrE,YAAI,WAAW,KAAM;AACrB,sBAAc,KAAK,uBAAuB,MAAM,CAAC;AAAA,MACnD;AAIA,YAAM,aAAa,eAAe,OAAO,KAAK;AAC9C,aAAO,EAAE,eAAe,WAAW;AAAA,IACrC;AAAA,IACA,MAAM,oBAAoB,MAGY;AACpC,YAAM,OAAO,MAAM,UAAU,EAAE,UAAU;AAAA,QACvC,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,aAAO;AAAA,QACL,UAAU,KAAK,MAAM,IAAI,kBAAkB;AAAA,QAC3C,YAAY,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,gCAA+D;AAAA,EAC1E,IAAI;AAAA,EACJ,aAAa;AAAA,EACb,SAAS;AACX;AAOO,SAAS,+BAAwC;AACtD,MAAI,qBAAqB,aAAa,MAAM,QAAW;AACrD,WAAO;AAAA,EACT;AACA,4BAA0B,6BAA6B;AACvD,SAAO;AACT;AAEA,6BAA6B;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@remnic/connector-bee",
|
|
3
|
+
"version": "9.3.631",
|
|
4
|
+
"description": "Bee wearable connector for Remnic — pull, clean, and remember bracelet transcripts via the Bee developer API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"@remnic/core": "^9.3.631"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"tsup": "^8.0.0",
|
|
25
|
+
"typescript": "^5.7.0",
|
|
26
|
+
"tsx": "^4.0.0",
|
|
27
|
+
"@remnic/core": "9.3.631"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/joshuaswarren/remnic.git",
|
|
33
|
+
"directory": "packages/connector-bee"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"remnic",
|
|
37
|
+
"memory",
|
|
38
|
+
"bee",
|
|
39
|
+
"bee.computer",
|
|
40
|
+
"wearable",
|
|
41
|
+
"transcript"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
45
|
+
"precheck-types": "node ../../scripts/ensure-bench-build-deps.mjs",
|
|
46
|
+
"check-types": "tsc --noEmit",
|
|
47
|
+
"test": "NODE_OPTIONS=\"${NODE_OPTIONS:+$NODE_OPTIONS }--conditions=remnic-source\" tsx --test src/client.test.ts src/normalize.test.ts src/index.test.ts"
|
|
48
|
+
}
|
|
49
|
+
}
|