@clubnet/seedclub 0.2.17 → 0.2.19
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 +88 -27
- package/assets/extensions/seedclub/api-client.ts +24 -6
- package/assets/extensions/seedclub/auth.ts +181 -10
- package/assets/extensions/seedclub/branding.ts +13 -0
- package/assets/extensions/seedclub/commands/clip-status.ts +187 -0
- package/assets/extensions/seedclub/commands/seedclub.ts +169 -25
- package/assets/extensions/seedclub/commands/transcript-intent.ts +998 -0
- package/assets/extensions/seedclub/commands/transcripts.ts +383 -0
- package/assets/extensions/seedclub/index.ts +218 -27
- package/assets/extensions/seedclub/package-lock.json +8 -1
- package/assets/extensions/seedclub/package.json +5 -2
- package/assets/extensions/seedclub/tool-utils.ts +45 -3
- package/assets/extensions/seedclub/tools/crm.ts +183 -0
- package/assets/extensions/seedclub/tools/media.ts +217 -0
- package/assets/extensions/seedclub/tools/meetings.ts +1053 -0
- package/assets/extensions/seedclub/tools/utility.ts +106 -9
- package/assets/extensions/seedclub-ui/editor.ts +16 -29
- package/assets/extensions/seedclub-ui/footer.ts +1 -3
- package/assets/extensions/seedclub-ui/state.ts +10 -1
- package/assets/extensions/seedclub-ui/welcome.ts +274 -85
- package/assets/theme/dark.json +47 -59
- package/assets/theme/light.json +49 -61
- package/bin/cli.js +380 -23
- package/bin/pi-main-launcher.js +29 -0
- package/package.json +8 -4
- package/postinstall.js +1 -1
- package/assets/extensions/seedclub/commands/add.ts +0 -601
- package/assets/extensions/seedclub/commands/extract.ts +0 -123
- package/assets/extensions/seedclub/commands/signals.ts +0 -86
- package/assets/extensions/seedclub/commands/sort.ts +0 -91
- package/assets/extensions/seedclub/dia-cookies.ts +0 -126
- package/assets/extensions/seedclub/extraction/capture.ts +0 -47
- package/assets/extensions/seedclub/extraction/schema.ts +0 -117
- package/assets/extensions/seedclub/tools/extractions.ts +0 -517
- package/assets/extensions/seedclub/tools/signals.ts +0 -275
- package/assets/extensions/seedclub/twitter-client.ts +0 -277
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# seedclub
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Seed Club's pi-based agent for authenticated access to Seed Club programs, CRM, meetings, media, and headlines.
|
|
4
4
|
|
|
5
5
|
Requirements: Node.js 22+
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## What it is
|
|
8
8
|
|
|
9
|
-
This repo
|
|
9
|
+
`seedclub` is the Seed Club distribution of pi. This repo provides the `seedclub` launcher, installs the Seed Club theme and extensions, and adds Seed Club-specific workflows for connecting your account, using CRM/meeting/media tools, retrieving transcripts, and working with Seed Club-backed app surfaces.
|
|
10
|
+
|
|
11
|
+
This repo is the source of truth for the published package:
|
|
10
12
|
|
|
11
13
|
- GitHub repo: `seedclub/seedclub-agent`
|
|
12
14
|
- npm package: `@clubnet/seedclub`
|
|
13
15
|
- user-facing command: `seedclub`
|
|
14
16
|
|
|
15
|
-
This is the Pi wrapper repo, not the older extracted full-agent monorepo.
|
|
16
|
-
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
19
|
```bash
|
|
@@ -26,7 +26,15 @@ Then:
|
|
|
26
26
|
seedclub
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
### First run
|
|
30
|
+
|
|
31
|
+
On a fresh install, the normal setup flow is:
|
|
32
|
+
|
|
33
|
+
1. `/login` to sign in to a model provider such as Anthropic, OpenAI, or Gemini
|
|
34
|
+
2. `/model` to choose the model you want to use
|
|
35
|
+
3. `/connect` to connect your Seed Club account
|
|
36
|
+
|
|
37
|
+
After that, run `/seedclub` to open the main Seed Club menu.
|
|
30
38
|
|
|
31
39
|
### Alternative: curl | bash
|
|
32
40
|
|
|
@@ -34,9 +42,9 @@ Use `/login` to set up your API keys.
|
|
|
34
42
|
curl -fsSL https://raw.githubusercontent.com/seedclub/seedclub-agent/main/install.sh | bash
|
|
35
43
|
```
|
|
36
44
|
|
|
37
|
-
###
|
|
45
|
+
### Internal install auth
|
|
38
46
|
|
|
39
|
-
seedclub is
|
|
47
|
+
`@clubnet/seedclub` is currently a private npm package. This auth is only for installing or updating the package from npm. It is separate from `/login` and `/connect` inside the app.
|
|
40
48
|
|
|
41
49
|
Fast path:
|
|
42
50
|
|
|
@@ -54,15 +62,56 @@ echo "//registry.npmjs.org/:_authToken=YOUR_NPM_TOKEN" >> ~/.npmrc
|
|
|
54
62
|
|
|
55
63
|
Then `npm install -g @clubnet/seedclub` works.
|
|
56
64
|
|
|
65
|
+
## Core workflow
|
|
66
|
+
|
|
67
|
+
The normal interactive flow is:
|
|
68
|
+
|
|
69
|
+
1. Start the app with `seedclub`
|
|
70
|
+
2. Complete `/login`, `/model`, and `/connect` if this is your first run
|
|
71
|
+
3. Open `/seedclub`
|
|
72
|
+
4. Choose the CRM, meetings, media, recordings, or transcript workflow you need
|
|
73
|
+
|
|
57
74
|
## Commands
|
|
58
75
|
|
|
59
76
|
| Command | What it does |
|
|
60
77
|
|---|---|
|
|
61
|
-
| `/
|
|
62
|
-
| `/
|
|
63
|
-
| `/
|
|
78
|
+
| `/login` | Sign in to a model provider for the underlying agent |
|
|
79
|
+
| `/model` | Choose which model to use |
|
|
80
|
+
| `/connect` | Connect your Seed Club account |
|
|
81
|
+
| `/seedclub` | Main menu — connect, inspect access, and jump into CRM/meetings/media/headlines workflows |
|
|
82
|
+
| `/transcripts` | Export transcript VTT files with filters (date, person, time, output dir) |
|
|
64
83
|
| `seedclub setup-auth` | Configure npm auth for npmjs private package access in `~/.npmrc` |
|
|
65
84
|
|
|
85
|
+
Natural-language transcript retrieval is also supported (no slash command required). Examples: `download vibhu transcripts from 11am`, `i need transcripts for all guests on 11am last week`. Seed Club will run metadata-first export confirmation and then write VTT files.
|
|
86
|
+
|
|
87
|
+
## Auth
|
|
88
|
+
|
|
89
|
+
There are two separate auth layers in the product:
|
|
90
|
+
|
|
91
|
+
1. Model auth: `/login`
|
|
92
|
+
This signs you into the LLM provider you want the agent to use.
|
|
93
|
+
2. Seed Club auth: `/connect`
|
|
94
|
+
This connects the CLI to your Seed Club account so Seed Club tools and commands can read and write account data.
|
|
95
|
+
|
|
96
|
+
`/seedclub` is the main entry point for Seed Club actions. If you are not connected yet, it will start the Seed Club connect flow automatically.
|
|
97
|
+
|
|
98
|
+
Power-user env overrides:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
export SEEDCLUB_API_URL=http://localhost:3001
|
|
102
|
+
export SEEDCLUB_AUTH_URL=http://localhost:3000
|
|
103
|
+
export SEEDCLUB_ACCESS_TOKEN=<bearer-token>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Production defaults are already built into the auth extension:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
SEEDCLUB_API_URL=https://api.seedclub.com
|
|
110
|
+
SEEDCLUB_AUTH_URL=https://auth.seedclub.com
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
You do not need to export those for normal use. Only set env vars when you want to override them for local/dev.
|
|
114
|
+
|
|
66
115
|
## How it works
|
|
67
116
|
|
|
68
117
|
seedclub is an npm package (`@clubnet/seedclub`) that wraps [pi](https://github.com/badlogic/pi-mono) as a dependency. Installing the package globally gives you the `seedclub` command.
|
|
@@ -98,9 +147,9 @@ seedclub pins versions in `package.json`:
|
|
|
98
147
|
|
|
99
148
|
```json
|
|
100
149
|
{
|
|
101
|
-
"version": "0.2.
|
|
150
|
+
"version": "0.2.18",
|
|
102
151
|
"dependencies": {
|
|
103
|
-
"@mariozechner/pi-coding-agent": "0.
|
|
152
|
+
"@mariozechner/pi-coding-agent": "0.65.2"
|
|
104
153
|
}
|
|
105
154
|
}
|
|
106
155
|
```
|
|
@@ -124,6 +173,8 @@ It's a standard pi theme with 51 color tokens. Edit it to change any visual aspe
|
|
|
124
173
|
|
|
125
174
|
The `vars` block at the top defines reusable colors (e.g. `brand: "#00C853"`) that are referenced throughout `colors`. To change the brand color, just update `vars.brand`.
|
|
126
175
|
|
|
176
|
+
Seed Club keeps product-level names in `vars` (`editorBg`, `messageBg`, `successBg`, `errorBg`) and maps pi's required tokens to those names (`selectedBg`, `userMessageBg`, `toolPendingBg`, etc.). The CLI probes the terminal background with OSC 11 and sets `COLORFGBG` before pi starts when no explicit theme is configured, so light/dark defaults track the actual terminal window instead of pi's fallback.
|
|
177
|
+
|
|
127
178
|
**Hot reload:** Edit the theme file while seedclub is running and it reloads instantly.
|
|
128
179
|
|
|
129
180
|
Colors can be hex (`"#00C853"`), 256-color palette index (`242`), a reference to a `vars` entry (`"brand"`), or empty string (`""`) for the terminal default.
|
|
@@ -165,36 +216,46 @@ The `assets/` directory contains the canonical source for extensions and themes.
|
|
|
165
216
|
|
|
166
217
|
### Updating pi
|
|
167
218
|
|
|
168
|
-
When a new pi version comes out
|
|
219
|
+
When a new pi version comes out, upgrading it for Seed Club users means shipping a new `@clubnet/seedclub` package that depends on the newer pi release.
|
|
169
220
|
|
|
170
|
-
|
|
221
|
+
Recommended flow:
|
|
222
|
+
|
|
223
|
+
1. **Update and test locally:**
|
|
171
224
|
```bash
|
|
172
|
-
# Update the dependency
|
|
173
225
|
npm install @mariozechner/pi-coding-agent@NEW_VERSION
|
|
174
226
|
npm install -g ./
|
|
175
|
-
seedclub
|
|
227
|
+
seedclub version
|
|
228
|
+
seedclub
|
|
176
229
|
```
|
|
177
230
|
|
|
178
|
-
|
|
179
|
-
|
|
231
|
+
Verify the wrapper still launches, the Seed Club UI loads, and the core flows you care about still work.
|
|
232
|
+
|
|
233
|
+
2. **Commit the dependency change and bump the package version:**
|
|
234
|
+
The pi version is carried by this package, so users only receive it once the Seed Club package version is bumped and released.
|
|
180
235
|
```json
|
|
181
236
|
{
|
|
182
237
|
"version": "0.3.0",
|
|
183
238
|
"dependencies": {
|
|
184
|
-
"@mariozechner/pi-coding-agent": "
|
|
239
|
+
"@mariozechner/pi-coding-agent": "NEW_VERSION"
|
|
185
240
|
}
|
|
186
241
|
}
|
|
187
242
|
```
|
|
188
243
|
|
|
189
|
-
3. **
|
|
244
|
+
3. **Push the release:**
|
|
190
245
|
```bash
|
|
191
246
|
git add -A
|
|
192
|
-
git commit -m "bump pi to
|
|
193
|
-
|
|
194
|
-
|
|
247
|
+
git commit -m "bump pi to NEW_VERSION"
|
|
248
|
+
npm version patch|minor|major
|
|
249
|
+
git push --follow-tags
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
4. **Users pull the update:**
|
|
253
|
+
Users get the newer pi runtime when they run:
|
|
254
|
+
```bash
|
|
255
|
+
seedclub update
|
|
195
256
|
```
|
|
196
257
|
|
|
197
|
-
|
|
258
|
+
A fresh `npm install -g @clubnet/seedclub` also installs the new packaged pi version.
|
|
198
259
|
|
|
199
260
|
### Updating extensions
|
|
200
261
|
|
|
@@ -208,8 +269,8 @@ seedclub # test
|
|
|
208
269
|
# When ready, bump package version and publish
|
|
209
270
|
git add -A
|
|
210
271
|
git commit -m "update extensions"
|
|
211
|
-
|
|
212
|
-
|
|
272
|
+
npm version patch|minor|major
|
|
273
|
+
git push --follow-tags
|
|
213
274
|
```
|
|
214
275
|
|
|
215
276
|
For reproducible extension dependency installs, commit `assets/extensions/seedclub/package-lock.json` and keep it in sync when changing extension deps.
|
|
@@ -52,12 +52,26 @@ interface RequestOptions {
|
|
|
52
52
|
params?: Record<string, string | number | undefined>;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
async function shouldClearCredentialsAfterUnauthorized(apiBase: string, token: string): Promise<boolean> {
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL("/session/context", apiBase.endsWith("/") ? apiBase : `${apiBase}/`);
|
|
58
|
+
const response = await fetch(url.toString(), {
|
|
59
|
+
method: "GET",
|
|
60
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
61
|
+
});
|
|
62
|
+
return response.status === 401;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
55
68
|
async function apiRequest<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
|
|
56
69
|
const { method = "GET", body, params } = options;
|
|
57
70
|
const token = await getAuthToken();
|
|
58
71
|
const apiBase = cachedApiBase || getApiBase();
|
|
59
72
|
|
|
60
|
-
const
|
|
73
|
+
const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
74
|
+
const url = new URL(path, apiBase.endsWith("/") ? apiBase : `${apiBase}/`);
|
|
61
75
|
if (params) {
|
|
62
76
|
for (const [key, value] of Object.entries(params)) {
|
|
63
77
|
if (value !== undefined) url.searchParams.set(key, String(value));
|
|
@@ -70,11 +84,6 @@ async function apiRequest<T>(endpoint: string, options: RequestOptions = {}): Pr
|
|
|
70
84
|
body: body ? JSON.stringify(body) : undefined,
|
|
71
85
|
});
|
|
72
86
|
|
|
73
|
-
if (response.status === 401) {
|
|
74
|
-
await clearCredentials();
|
|
75
|
-
throw new ApiError(401, "Token expired or revoked. Run /seedclub to reconnect.");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
87
|
const text = await response.text();
|
|
79
88
|
let data: any;
|
|
80
89
|
try {
|
|
@@ -86,6 +95,15 @@ async function apiRequest<T>(endpoint: string, options: RequestOptions = {}): Pr
|
|
|
86
95
|
throw new ApiError(response.status, `Invalid JSON response from server: ${text.slice(0, 200)}`);
|
|
87
96
|
}
|
|
88
97
|
|
|
98
|
+
if (response.status === 401) {
|
|
99
|
+
const shouldClear = await shouldClearCredentialsAfterUnauthorized(apiBase, token);
|
|
100
|
+
if (shouldClear) {
|
|
101
|
+
await clearCredentials();
|
|
102
|
+
throw new ApiError(401, "Token expired or revoked. Run /seedclub to reconnect.");
|
|
103
|
+
}
|
|
104
|
+
throw new ApiError(401, data?.error || "Request failed (401)", data?.details);
|
|
105
|
+
}
|
|
106
|
+
|
|
89
107
|
if (!response.ok) {
|
|
90
108
|
throw new ApiError(response.status, data.error || `Request failed (${response.status})`, data.details);
|
|
91
109
|
}
|
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Token storage for Seed Club.
|
|
3
3
|
*
|
|
4
|
-
* Priority: SEEDCLUB_TOKEN env var > stored token file.
|
|
4
|
+
* Priority: SEEDCLUB_ACCESS_TOKEN / SEEDCLUB_TOKEN env var > stored token file.
|
|
5
5
|
* Use /seedclub to connect.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
9
10
|
import { homedir } from "node:os";
|
|
10
11
|
import { join } from "node:path";
|
|
11
12
|
|
|
12
13
|
const CONFIG_DIR = join(homedir(), ".config", "seedclub");
|
|
13
14
|
const TOKEN_FILE = join(CONFIG_DIR, "token");
|
|
15
|
+
const BASES_FILE = join(CONFIG_DIR, "bases.json");
|
|
14
16
|
const LEGACY_CONFIG_DIR = join(homedir(), ".config", "looseleaf");
|
|
15
17
|
const LEGACY_TOKEN_FILE = join(LEGACY_CONFIG_DIR, "token");
|
|
16
|
-
const DEFAULT_API_BASE = "https://
|
|
18
|
+
const DEFAULT_API_BASE = "https://api.seedclub.com";
|
|
19
|
+
const DEFAULT_AUTH_BASE = "https://auth.seedclub.com";
|
|
20
|
+
const LOCAL_API_BASE = "http://localhost:3001";
|
|
21
|
+
const LOCAL_AUTH_BASE = "http://localhost:3000";
|
|
22
|
+
|
|
23
|
+
export type SeedclubEnvironmentMode = "local" | "prod" | "custom";
|
|
17
24
|
|
|
18
25
|
export interface StoredToken {
|
|
19
26
|
token: string;
|
|
@@ -21,33 +28,134 @@ export interface StoredToken {
|
|
|
21
28
|
name?: string;
|
|
22
29
|
createdAt: string;
|
|
23
30
|
apiBase: string;
|
|
31
|
+
authBase?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface StoredBases {
|
|
35
|
+
apiBase: string;
|
|
36
|
+
authBase: string;
|
|
37
|
+
mode: SeedclubEnvironmentMode;
|
|
38
|
+
updatedAt: string;
|
|
24
39
|
}
|
|
25
40
|
|
|
26
41
|
let _cachedApiBase: string | null = null;
|
|
42
|
+
let _cachedAuthBase: string | null = null;
|
|
43
|
+
let _cachedBases: StoredBases | null | undefined = undefined;
|
|
44
|
+
|
|
45
|
+
function shouldPreferLocalBases(): boolean {
|
|
46
|
+
return (
|
|
47
|
+
process.env.SEEDCLUB_LOCAL === "1" ||
|
|
48
|
+
process.env.SEEDCLUB_LOCAL === "true" ||
|
|
49
|
+
process.env.SEEDCLUB_ENV === "local"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function tryReadStoredBasesSync(): StoredBases | null {
|
|
54
|
+
if (_cachedBases !== undefined) {
|
|
55
|
+
return _cachedBases;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const content = readFileSync(BASES_FILE, "utf-8");
|
|
60
|
+
const stored = JSON.parse(content) as StoredBases;
|
|
61
|
+
if (
|
|
62
|
+
!stored ||
|
|
63
|
+
typeof stored.apiBase !== "string" ||
|
|
64
|
+
typeof stored.authBase !== "string" ||
|
|
65
|
+
typeof stored.mode !== "string"
|
|
66
|
+
) {
|
|
67
|
+
_cachedBases = null;
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
_cachedBases = stored;
|
|
71
|
+
return stored;
|
|
72
|
+
} catch {
|
|
73
|
+
_cachedBases = null;
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
27
77
|
|
|
28
78
|
export function getApiBase(): string {
|
|
29
|
-
if (
|
|
30
|
-
|
|
79
|
+
if (
|
|
80
|
+
process.env.SEEDCLUB_API_URL ||
|
|
81
|
+
process.env.SEEDCLUB_API ||
|
|
82
|
+
process.env.SEED_NETWORK_API
|
|
83
|
+
)
|
|
84
|
+
return process.env.SEEDCLUB_API_URL || process.env.SEEDCLUB_API || process.env.SEED_NETWORK_API!;
|
|
85
|
+
if (shouldPreferLocalBases()) return LOCAL_API_BASE;
|
|
86
|
+
const storedBases = tryReadStoredBasesSync();
|
|
87
|
+
if (storedBases?.apiBase) return storedBases.apiBase;
|
|
31
88
|
if (_cachedApiBase) return _cachedApiBase;
|
|
32
89
|
return DEFAULT_API_BASE;
|
|
33
90
|
}
|
|
34
91
|
|
|
92
|
+
export function getAuthBase(): string {
|
|
93
|
+
if (
|
|
94
|
+
process.env.SEEDCLUB_AUTH_URL ||
|
|
95
|
+
process.env.SEEDCLUB_AUTH ||
|
|
96
|
+
process.env.SEED_NETWORK_AUTH
|
|
97
|
+
)
|
|
98
|
+
return process.env.SEEDCLUB_AUTH_URL || process.env.SEEDCLUB_AUTH || process.env.SEED_NETWORK_AUTH!;
|
|
99
|
+
if (shouldPreferLocalBases()) return LOCAL_AUTH_BASE;
|
|
100
|
+
const storedBases = tryReadStoredBasesSync();
|
|
101
|
+
if (storedBases?.authBase) return storedBases.authBase;
|
|
102
|
+
if (_cachedAuthBase) return _cachedAuthBase;
|
|
103
|
+
return DEFAULT_AUTH_BASE;
|
|
104
|
+
}
|
|
105
|
+
|
|
35
106
|
export function setCachedApiBase(apiBase: string): void {
|
|
36
107
|
_cachedApiBase = apiBase;
|
|
37
108
|
}
|
|
38
109
|
|
|
110
|
+
export function setCachedAuthBase(authBase: string): void {
|
|
111
|
+
_cachedAuthBase = authBase;
|
|
112
|
+
}
|
|
113
|
+
|
|
39
114
|
export function clearCachedApiBase(): void {
|
|
40
115
|
_cachedApiBase = null;
|
|
41
116
|
}
|
|
42
117
|
|
|
118
|
+
export function clearCachedAuthBase(): void {
|
|
119
|
+
_cachedAuthBase = null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function getDefaultBases(mode: "local" | "prod"): { apiBase: string; authBase: string } {
|
|
123
|
+
if (mode === "local") {
|
|
124
|
+
return {
|
|
125
|
+
apiBase: LOCAL_API_BASE,
|
|
126
|
+
authBase: LOCAL_AUTH_BASE,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
apiBase: DEFAULT_API_BASE,
|
|
132
|
+
authBase: DEFAULT_AUTH_BASE,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
43
136
|
async function tryReadTokenFile(path: string): Promise<StoredToken | null> {
|
|
44
137
|
try {
|
|
45
138
|
const content = await readFile(path, "utf-8");
|
|
46
139
|
const stored = JSON.parse(content) as StoredToken;
|
|
47
|
-
if (!stored.token || !stored.token.
|
|
48
|
-
if (
|
|
140
|
+
if (!stored.token || typeof stored.token !== "string" || !stored.token.trim()) return null;
|
|
141
|
+
if (
|
|
142
|
+
stored.apiBase &&
|
|
143
|
+
!shouldPreferLocalBases() &&
|
|
144
|
+
!process.env.SEEDCLUB_API_URL &&
|
|
145
|
+
!process.env.SEEDCLUB_API &&
|
|
146
|
+
!process.env.SEED_NETWORK_API
|
|
147
|
+
) {
|
|
49
148
|
_cachedApiBase = stored.apiBase;
|
|
50
149
|
}
|
|
150
|
+
if (
|
|
151
|
+
stored.authBase &&
|
|
152
|
+
!shouldPreferLocalBases() &&
|
|
153
|
+
!process.env.SEEDCLUB_AUTH_URL &&
|
|
154
|
+
!process.env.SEEDCLUB_AUTH &&
|
|
155
|
+
!process.env.SEED_NETWORK_AUTH
|
|
156
|
+
) {
|
|
157
|
+
_cachedAuthBase = stored.authBase;
|
|
158
|
+
}
|
|
51
159
|
return stored;
|
|
52
160
|
} catch {
|
|
53
161
|
return null;
|
|
@@ -61,24 +169,85 @@ export async function getStoredToken(): Promise<StoredToken | null> {
|
|
|
61
169
|
return await tryReadTokenFile(LEGACY_TOKEN_FILE);
|
|
62
170
|
}
|
|
63
171
|
|
|
172
|
+
export async function getStoredBases(): Promise<StoredBases | null> {
|
|
173
|
+
try {
|
|
174
|
+
const content = await readFile(BASES_FILE, "utf-8");
|
|
175
|
+
const stored = JSON.parse(content) as StoredBases;
|
|
176
|
+
if (
|
|
177
|
+
!stored ||
|
|
178
|
+
typeof stored.apiBase !== "string" ||
|
|
179
|
+
typeof stored.authBase !== "string" ||
|
|
180
|
+
typeof stored.mode !== "string"
|
|
181
|
+
) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
_cachedBases = stored;
|
|
185
|
+
return stored;
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
64
191
|
export async function getToken(): Promise<string | null> {
|
|
65
|
-
if (process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)
|
|
66
|
-
return (process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)!;
|
|
192
|
+
if (process.env.SEEDCLUB_ACCESS_TOKEN || process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)
|
|
193
|
+
return (process.env.SEEDCLUB_ACCESS_TOKEN || process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)!;
|
|
67
194
|
const stored = await getStoredToken();
|
|
68
195
|
return stored?.token ?? null;
|
|
69
196
|
}
|
|
70
197
|
|
|
198
|
+
export async function storeBases(
|
|
199
|
+
apiBase: string,
|
|
200
|
+
authBase: string,
|
|
201
|
+
mode: SeedclubEnvironmentMode = "custom",
|
|
202
|
+
): Promise<void> {
|
|
203
|
+
const stored: StoredBases = {
|
|
204
|
+
apiBase,
|
|
205
|
+
authBase,
|
|
206
|
+
mode,
|
|
207
|
+
updatedAt: new Date().toISOString(),
|
|
208
|
+
};
|
|
209
|
+
_cachedApiBase = apiBase;
|
|
210
|
+
_cachedAuthBase = authBase;
|
|
211
|
+
_cachedBases = stored;
|
|
212
|
+
await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
213
|
+
await writeFile(BASES_FILE, JSON.stringify(stored, null, 2), { mode: 0o600 });
|
|
214
|
+
try {
|
|
215
|
+
await chmod(BASES_FILE, 0o600);
|
|
216
|
+
} catch {}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export async function clearStoredBases(): Promise<boolean> {
|
|
220
|
+
_cachedBases = null;
|
|
221
|
+
try {
|
|
222
|
+
await unlink(BASES_FILE);
|
|
223
|
+
return true;
|
|
224
|
+
} catch {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
71
229
|
export async function storeToken(
|
|
72
230
|
token: string,
|
|
73
231
|
email: string,
|
|
74
232
|
apiBase: string,
|
|
75
|
-
options?: { name?: string },
|
|
233
|
+
options?: { authBase?: string; name?: string },
|
|
76
234
|
): Promise<void> {
|
|
235
|
+
_cachedApiBase = apiBase;
|
|
236
|
+
if (options?.authBase) {
|
|
237
|
+
_cachedAuthBase = options.authBase;
|
|
238
|
+
}
|
|
77
239
|
await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
78
240
|
await writeFile(
|
|
79
241
|
TOKEN_FILE,
|
|
80
242
|
JSON.stringify(
|
|
81
|
-
{
|
|
243
|
+
{
|
|
244
|
+
token,
|
|
245
|
+
email,
|
|
246
|
+
name: options?.name,
|
|
247
|
+
createdAt: new Date().toISOString(),
|
|
248
|
+
apiBase,
|
|
249
|
+
authBase: options?.authBase,
|
|
250
|
+
},
|
|
82
251
|
null,
|
|
83
252
|
2,
|
|
84
253
|
),
|
|
@@ -90,6 +259,8 @@ export async function storeToken(
|
|
|
90
259
|
}
|
|
91
260
|
|
|
92
261
|
export async function clearStoredToken(): Promise<boolean> {
|
|
262
|
+
_cachedApiBase = null;
|
|
263
|
+
_cachedAuthBase = null;
|
|
93
264
|
try {
|
|
94
265
|
await unlink(TOKEN_FILE);
|
|
95
266
|
return true;
|
|
@@ -6,6 +6,19 @@ IMPORTANT BRANDING RULES:
|
|
|
6
6
|
- Never mention the name \"pi\" in any response.
|
|
7
7
|
- If the user mentions \"pi\", interpret it as Seed Club and respond using \"Seed Club\".
|
|
8
8
|
- When summarizing intent or describing the system, use Seed Club wording only.
|
|
9
|
+
|
|
10
|
+
IMPORTANT SEED CLUB API ROUTING:
|
|
11
|
+
- For transcript-first discovery/review asks (not file retrieval), call seedclub_list_meeting_transcripts first with the program slug and limit 20.
|
|
12
|
+
- For transcript inventory questions like "what was the last day we have transcripts for 11am?", keep the user prompt in the normal agent flow and answer with the date plus the full transcript_for names from returned rows.
|
|
13
|
+
- Treat conversation-level transcripts as valid 11am interview/show transcripts. For "latest/last transcript" questions, use seedclub_list_meeting_transcripts.latest.overall first; only use latest.meeting when the user explicitly asks for meeting-level records.
|
|
14
|
+
- Treat "on 11am" or "for 11am" as the 11am program slug when that program is accessible, not as an 11am time filter.
|
|
15
|
+
- If the user asks for "all transcripts" and the tool returns a limited page, summarize the returned transcript rows and say you can fetch full text for selected rows. Do not claim exhaustive coverage unless the API result proves it.
|
|
16
|
+
- Use includeFullText only after narrowing to rows the user wants to inspect.
|
|
17
|
+
- Use seedclub_list_program_media_assets(assetKind=full_conversation) only when the user asks for full conversations or media assets. Do not use it as the default source for transcript inventory.
|
|
18
|
+
- For transcript retrieval asks (e.g., "I need transcripts...", "get transcripts...", guest/date transcript pulls), first use chat and metadata tools to establish exact constraints when needed, then call seedclub_export_transcripts with the user's original request. This preserves the user prompt in context, confirms destination, then writes VTT files.
|
|
19
|
+
- Treat short transcript artifact prompts like "today's transcripts", "2pm transcripts", "Tuesday VTTs", or "recent captions" as local transcript export tool requests, not inline transcript display requests.
|
|
20
|
+
- Do not paste full transcript JSON or raw session context into the reply unless the user explicitly asks for raw data.
|
|
21
|
+
- Use seedclub_list_show_recordings for show recording URLs, then follow transcript event_date only when needed.
|
|
9
22
|
`;
|
|
10
23
|
|
|
11
24
|
const BRAND_REGEX = /\bpi\b/gi;
|