@vellumai/cli 0.8.1 → 0.8.2
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/package.json +1 -1
- package/src/__tests__/backup.test.ts +13 -3
- package/src/__tests__/input-history.test.ts +102 -0
- package/src/__tests__/preload.ts +5 -1
- package/src/__tests__/provider-secrets.test.ts +290 -0
- package/src/__tests__/setup.test.ts +296 -0
- package/src/__tests__/teleport.test.ts +190 -163
- package/src/commands/client.ts +57 -1
- package/src/commands/setup.ts +101 -96
- package/src/components/DefaultMainScreen.tsx +72 -119
- package/src/lib/assistant-config.ts +4 -2
- package/src/lib/environments/paths.ts +21 -0
- package/src/lib/input-history.ts +5 -8
- package/src/lib/provider-secrets.ts +413 -0
- package/src/lib/sync-cloud-assistants.ts +23 -9
- package/src/shared/provider-env-vars.ts +3 -0
- package/src/lib/doctor-client.ts +0 -153
|
@@ -20,7 +20,19 @@ const testDir = mkdtempSync(join(tmpdir(), "cli-teleport-test-"));
|
|
|
20
20
|
process.env.VELLUM_LOCKFILE_DIR = testDir;
|
|
21
21
|
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
23
|
-
// Mocks — must be set up before importing the module under test
|
|
23
|
+
// Mocks — must be set up before importing the module under test.
|
|
24
|
+
//
|
|
25
|
+
// We use Bun's `mock.module()` API (rather than `spyOn` on the imported
|
|
26
|
+
// module namespace). `spyOn` mutates the shared module namespace, and
|
|
27
|
+
// `mockRestore()` in `afterAll` was not reliably restoring on CI — the
|
|
28
|
+
// mutations leaked into sibling test files (notably `guardian-token.test.ts`)
|
|
29
|
+
// and caused flaky failures when the runtime function for that file returned
|
|
30
|
+
// teleport's mock value instead of the real implementation.
|
|
31
|
+
//
|
|
32
|
+
// `mock.module()` registers a per-module factory in Bun's loader. To prevent
|
|
33
|
+
// the mock from leaking across files in the same `bun test` run, we capture
|
|
34
|
+
// the real exports up front and re-register them in `afterAll` to restore
|
|
35
|
+
// the original module bindings.
|
|
24
36
|
// ---------------------------------------------------------------------------
|
|
25
37
|
|
|
26
38
|
import * as assistantConfig from "../lib/assistant-config.js";
|
|
@@ -28,83 +40,95 @@ import * as guardianToken from "../lib/guardian-token.js";
|
|
|
28
40
|
import * as platformClient from "../lib/platform-client.js";
|
|
29
41
|
import * as localRuntimeClient from "../lib/local-runtime-client.js";
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
} as unknown as
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
"
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
43
|
+
// Snapshot the real exports before any `mock.module()` call so we can
|
|
44
|
+
// reliably restore them after this file's tests complete, regardless of
|
|
45
|
+
// how Bun's loader treats namespace bindings post-mock.
|
|
46
|
+
const realAssistantConfig = { ...assistantConfig };
|
|
47
|
+
const realGuardianToken = { ...guardianToken };
|
|
48
|
+
const realPlatformClient = { ...platformClient };
|
|
49
|
+
const realLocalRuntimeClient = { ...localRuntimeClient };
|
|
50
|
+
|
|
51
|
+
const findAssistantByNameMock = mock<
|
|
52
|
+
typeof assistantConfig.findAssistantByName
|
|
53
|
+
>(() => null);
|
|
54
|
+
const saveAssistantEntryMock = mock<typeof assistantConfig.saveAssistantEntry>(
|
|
55
|
+
() => {},
|
|
56
|
+
);
|
|
57
|
+
const loadAllAssistantsMock = mock<typeof assistantConfig.loadAllAssistants>(
|
|
58
|
+
() => [],
|
|
59
|
+
);
|
|
60
|
+
const removeAssistantEntryMock = mock<
|
|
61
|
+
typeof assistantConfig.removeAssistantEntry
|
|
62
|
+
>(() => {});
|
|
63
|
+
|
|
64
|
+
mock.module("../lib/assistant-config.js", () => ({
|
|
65
|
+
...realAssistantConfig,
|
|
66
|
+
findAssistantByName: findAssistantByNameMock,
|
|
67
|
+
saveAssistantEntry: saveAssistantEntryMock,
|
|
68
|
+
loadAllAssistants: loadAllAssistantsMock,
|
|
69
|
+
removeAssistantEntry: removeAssistantEntryMock,
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
const loadGuardianTokenMock = mock<typeof guardianToken.loadGuardianToken>(
|
|
73
|
+
() =>
|
|
74
|
+
({
|
|
75
|
+
accessToken: "local-token",
|
|
76
|
+
accessTokenExpiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
77
|
+
}) as unknown as ReturnType<typeof guardianToken.loadGuardianToken>,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const leaseGuardianTokenMock = mock<typeof guardianToken.leaseGuardianToken>(
|
|
81
|
+
async () =>
|
|
82
|
+
({
|
|
83
|
+
accessToken: "leased-token",
|
|
84
|
+
accessTokenExpiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
85
|
+
}) as unknown as Awaited<
|
|
86
|
+
ReturnType<typeof guardianToken.leaseGuardianToken>
|
|
87
|
+
>,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const computeDeviceIdMock = mock<typeof guardianToken.computeDeviceId>(
|
|
91
|
+
() => "device-id-123",
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
mock.module("../lib/guardian-token.js", () => ({
|
|
95
|
+
...realGuardianToken,
|
|
96
|
+
loadGuardianToken: loadGuardianTokenMock,
|
|
97
|
+
leaseGuardianToken: leaseGuardianTokenMock,
|
|
98
|
+
computeDeviceId: computeDeviceIdMock,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
const readPlatformTokenMock = mock<typeof platformClient.readPlatformToken>(
|
|
102
|
+
() => "platform-token",
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const getPlatformUrlMock = mock<typeof platformClient.getPlatformUrl>(
|
|
106
|
+
() => "https://platform.vellum.ai",
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const hatchAssistantMock = mock<typeof platformClient.hatchAssistant>(
|
|
110
|
+
async () => ({
|
|
111
|
+
assistant: {
|
|
112
|
+
id: "platform-new-id",
|
|
113
|
+
name: "platform-new",
|
|
114
|
+
status: "active",
|
|
115
|
+
},
|
|
116
|
+
reusedExisting: false,
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
93
119
|
|
|
94
|
-
const platformPollJobStatusMock =
|
|
95
|
-
platformClient
|
|
96
|
-
|
|
97
|
-
).mockResolvedValue({
|
|
120
|
+
const platformPollJobStatusMock = mock<
|
|
121
|
+
typeof platformClient.platformPollJobStatus
|
|
122
|
+
>(async () => ({
|
|
98
123
|
jobId: "platform-job-1",
|
|
99
124
|
type: "export",
|
|
100
125
|
status: "complete",
|
|
101
126
|
bundleKey: "platform-bundle-key-abc",
|
|
102
|
-
});
|
|
127
|
+
}));
|
|
103
128
|
|
|
104
|
-
const platformRequestSignedUrlMock =
|
|
105
|
-
platformClient
|
|
106
|
-
|
|
107
|
-
).mockImplementation(async (params) => ({
|
|
129
|
+
const platformRequestSignedUrlMock = mock<
|
|
130
|
+
typeof platformClient.platformRequestSignedUrl
|
|
131
|
+
>(async (params) => ({
|
|
108
132
|
url:
|
|
109
133
|
params.operation === "upload"
|
|
110
134
|
? "https://storage.googleapis.com/bucket/signed-upload"
|
|
@@ -113,10 +137,9 @@ const platformRequestSignedUrlMock = spyOn(
|
|
|
113
137
|
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
114
138
|
}));
|
|
115
139
|
|
|
116
|
-
const platformImportBundleFromGcsMock =
|
|
117
|
-
platformClient
|
|
118
|
-
|
|
119
|
-
).mockResolvedValue({
|
|
140
|
+
const platformImportBundleFromGcsMock = mock<
|
|
141
|
+
typeof platformClient.platformImportBundleFromGcs
|
|
142
|
+
>(async () => ({
|
|
120
143
|
statusCode: 200,
|
|
121
144
|
body: {
|
|
122
145
|
success: true,
|
|
@@ -128,12 +151,11 @@ const platformImportBundleFromGcsMock = spyOn(
|
|
|
128
151
|
backups_created: 1,
|
|
129
152
|
},
|
|
130
153
|
} as Record<string, unknown>,
|
|
131
|
-
});
|
|
154
|
+
}));
|
|
132
155
|
|
|
133
|
-
const platformImportPreflightFromGcsMock =
|
|
134
|
-
platformClient
|
|
135
|
-
|
|
136
|
-
).mockResolvedValue({
|
|
156
|
+
const platformImportPreflightFromGcsMock = mock<
|
|
157
|
+
typeof platformClient.platformImportPreflightFromGcs
|
|
158
|
+
>(async () => ({
|
|
137
159
|
statusCode: 200,
|
|
138
160
|
body: {
|
|
139
161
|
can_import: true,
|
|
@@ -144,71 +166,84 @@ const platformImportPreflightFromGcsMock = spyOn(
|
|
|
144
166
|
total_files: 3,
|
|
145
167
|
},
|
|
146
168
|
} as Record<string, unknown>,
|
|
147
|
-
});
|
|
169
|
+
}));
|
|
148
170
|
|
|
149
|
-
const checkExistingPlatformAssistantMock =
|
|
150
|
-
platformClient
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
} as unknown as Awaited<
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
const fetchCurrentUserMock =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
171
|
+
const checkExistingPlatformAssistantMock = mock<
|
|
172
|
+
typeof platformClient.checkExistingPlatformAssistant
|
|
173
|
+
>(async () => null);
|
|
174
|
+
|
|
175
|
+
const ensureSelfHostedLocalRegistrationMock = mock<
|
|
176
|
+
typeof platformClient.ensureSelfHostedLocalRegistration
|
|
177
|
+
>(
|
|
178
|
+
async () =>
|
|
179
|
+
({
|
|
180
|
+
assistant: { id: "platform-assistant-1", name: "my-assistant" },
|
|
181
|
+
registration: {
|
|
182
|
+
client_installation_id: "device-id-123",
|
|
183
|
+
runtime_assistant_id: "target-local",
|
|
184
|
+
client_platform: "cli",
|
|
185
|
+
},
|
|
186
|
+
assistant_api_key: "api-key-123",
|
|
187
|
+
webhook_secret: "webhook-secret-123",
|
|
188
|
+
}) as unknown as Awaited<
|
|
189
|
+
ReturnType<typeof platformClient.ensureSelfHostedLocalRegistration>
|
|
190
|
+
>,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const injectCredentialsIntoAssistantMock = mock<
|
|
194
|
+
typeof platformClient.injectCredentialsIntoAssistant
|
|
195
|
+
>(async () => true);
|
|
196
|
+
|
|
197
|
+
const fetchCurrentUserMock = mock<typeof platformClient.fetchCurrentUser>(
|
|
198
|
+
async () =>
|
|
199
|
+
({
|
|
200
|
+
id: "user-1",
|
|
201
|
+
email: "test@example.com",
|
|
202
|
+
display: "Test",
|
|
203
|
+
}) as unknown as Awaited<
|
|
204
|
+
ReturnType<typeof platformClient.fetchCurrentUser>
|
|
205
|
+
>,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const fetchOrganizationIdMock = mock<typeof platformClient.fetchOrganizationId>(
|
|
209
|
+
async () => "org-1",
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
mock.module("../lib/platform-client.js", () => ({
|
|
213
|
+
...realPlatformClient,
|
|
214
|
+
readPlatformToken: readPlatformTokenMock,
|
|
215
|
+
getPlatformUrl: getPlatformUrlMock,
|
|
216
|
+
hatchAssistant: hatchAssistantMock,
|
|
217
|
+
platformPollJobStatus: platformPollJobStatusMock,
|
|
218
|
+
platformRequestSignedUrl: platformRequestSignedUrlMock,
|
|
219
|
+
platformImportBundleFromGcs: platformImportBundleFromGcsMock,
|
|
220
|
+
platformImportPreflightFromGcs: platformImportPreflightFromGcsMock,
|
|
221
|
+
checkExistingPlatformAssistant: checkExistingPlatformAssistantMock,
|
|
222
|
+
ensureSelfHostedLocalRegistration: ensureSelfHostedLocalRegistrationMock,
|
|
223
|
+
injectCredentialsIntoAssistant: injectCredentialsIntoAssistantMock,
|
|
224
|
+
fetchCurrentUser: fetchCurrentUserMock,
|
|
225
|
+
fetchOrganizationId: fetchOrganizationIdMock,
|
|
226
|
+
}));
|
|
227
|
+
|
|
228
|
+
const localRuntimeExportToGcsMock = mock<
|
|
229
|
+
typeof localRuntimeClient.localRuntimeExportToGcs
|
|
230
|
+
>(async () => ({ jobId: "local-export-job-1" }));
|
|
231
|
+
|
|
232
|
+
const localRuntimeImportFromGcsMock = mock<
|
|
233
|
+
typeof localRuntimeClient.localRuntimeImportFromGcs
|
|
234
|
+
>(async () => ({ jobId: "local-import-job-1" }));
|
|
198
235
|
|
|
199
236
|
// Default to a fixed version string. Tests that exercise the version-gate
|
|
200
237
|
// surface override this mock per-case to assert the value flows from the
|
|
201
238
|
// target runtime's `/v1/identity` (NOT from `cliPkg.version`) into the
|
|
202
239
|
// download signed-URL request.
|
|
203
|
-
const localRuntimeIdentityMock =
|
|
204
|
-
localRuntimeClient
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
"localRuntimePollJobStatus",
|
|
211
|
-
).mockImplementation(async (_runtimeUrl, _token, jobId) => ({
|
|
240
|
+
const localRuntimeIdentityMock = mock<
|
|
241
|
+
typeof localRuntimeClient.localRuntimeIdentity
|
|
242
|
+
>(async () => ({ version: "0.6.5" }));
|
|
243
|
+
|
|
244
|
+
const localRuntimePollJobStatusMock = mock<
|
|
245
|
+
typeof localRuntimeClient.localRuntimePollJobStatus
|
|
246
|
+
>(async (_runtimeUrl, _token, jobId) => ({
|
|
212
247
|
jobId,
|
|
213
248
|
type: jobId.includes("import") ? "import" : "export",
|
|
214
249
|
status: "complete",
|
|
@@ -224,6 +259,14 @@ const localRuntimePollJobStatusMock = spyOn(
|
|
|
224
259
|
},
|
|
225
260
|
}));
|
|
226
261
|
|
|
262
|
+
mock.module("../lib/local-runtime-client.js", () => ({
|
|
263
|
+
...realLocalRuntimeClient,
|
|
264
|
+
localRuntimeExportToGcs: localRuntimeExportToGcsMock,
|
|
265
|
+
localRuntimeImportFromGcs: localRuntimeImportFromGcsMock,
|
|
266
|
+
localRuntimeIdentity: localRuntimeIdentityMock,
|
|
267
|
+
localRuntimePollJobStatus: localRuntimePollJobStatusMock,
|
|
268
|
+
}));
|
|
269
|
+
|
|
227
270
|
const hatchLocalMock = mock(async () => {});
|
|
228
271
|
|
|
229
272
|
mock.module("../lib/hatch-local.js", () => ({
|
|
@@ -285,29 +328,13 @@ import type { AssistantEntry } from "../lib/assistant-config.js";
|
|
|
285
328
|
// ---------------------------------------------------------------------------
|
|
286
329
|
|
|
287
330
|
afterAll(() => {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
getPlatformUrlMock.mockRestore();
|
|
296
|
-
hatchAssistantMock.mockRestore();
|
|
297
|
-
checkExistingPlatformAssistantMock.mockRestore();
|
|
298
|
-
platformPollJobStatusMock.mockRestore();
|
|
299
|
-
platformRequestSignedUrlMock.mockRestore();
|
|
300
|
-
platformImportBundleFromGcsMock.mockRestore();
|
|
301
|
-
platformImportPreflightFromGcsMock.mockRestore();
|
|
302
|
-
ensureSelfHostedLocalRegistrationMock.mockRestore();
|
|
303
|
-
injectCredentialsIntoAssistantMock.mockRestore();
|
|
304
|
-
fetchCurrentUserMock.mockRestore();
|
|
305
|
-
fetchOrganizationIdMock.mockRestore();
|
|
306
|
-
computeDeviceIdMock.mockRestore();
|
|
307
|
-
localRuntimeExportToGcsMock.mockRestore();
|
|
308
|
-
localRuntimeImportFromGcsMock.mockRestore();
|
|
309
|
-
localRuntimeIdentityMock.mockRestore();
|
|
310
|
-
localRuntimePollJobStatusMock.mockRestore();
|
|
331
|
+
// Restore the real module exports so the mocks do not leak into sibling
|
|
332
|
+
// test files (e.g. guardian-token.test.ts, platform-client tests) that
|
|
333
|
+
// run in the same `bun test` invocation.
|
|
334
|
+
mock.module("../lib/assistant-config.js", () => realAssistantConfig);
|
|
335
|
+
mock.module("../lib/guardian-token.js", () => realGuardianToken);
|
|
336
|
+
mock.module("../lib/platform-client.js", () => realPlatformClient);
|
|
337
|
+
mock.module("../lib/local-runtime-client.js", () => realLocalRuntimeClient);
|
|
311
338
|
rmSync(testDir, { recursive: true, force: true });
|
|
312
339
|
delete process.env.VELLUM_LOCKFILE_DIR;
|
|
313
340
|
});
|
package/src/commands/client.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
findAssistantByName,
|
|
7
7
|
getActiveAssistant,
|
|
8
8
|
resolveAssistant,
|
|
9
|
+
saveAssistantEntry,
|
|
9
10
|
} from "../lib/assistant-config";
|
|
10
11
|
import {
|
|
11
12
|
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
} from "../lib/client-identity";
|
|
22
23
|
import {
|
|
23
24
|
fetchOrganizationId,
|
|
25
|
+
fetchPlatformAssistants,
|
|
24
26
|
readPlatformToken,
|
|
25
27
|
} from "../lib/platform-client";
|
|
26
28
|
import { tuiLog } from "../lib/tui-log";
|
|
@@ -39,6 +41,7 @@ const FALLBACK_RUNTIME_URL = `http://127.0.0.1:${GATEWAY_PORT}`;
|
|
|
39
41
|
interface ParsedArgs {
|
|
40
42
|
runtimeUrl: string;
|
|
41
43
|
assistantId: string;
|
|
44
|
+
assistantName?: string;
|
|
42
45
|
species: Species;
|
|
43
46
|
/** "vellum" for platform-hosted assistants, undefined for local. */
|
|
44
47
|
cloud?: string;
|
|
@@ -52,6 +55,15 @@ interface ParsedArgs {
|
|
|
52
55
|
zone?: string;
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
function readAssistantName(
|
|
59
|
+
entry: ReturnType<typeof findAssistantByName>,
|
|
60
|
+
): string | undefined {
|
|
61
|
+
const rawName = entry?.name ?? entry?.assistantName;
|
|
62
|
+
return typeof rawName === "string" && rawName.trim()
|
|
63
|
+
? rawName.trim()
|
|
64
|
+
: undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
55
67
|
function parseArgs(): ParsedArgs {
|
|
56
68
|
const args = process.argv.slice(3);
|
|
57
69
|
|
|
@@ -112,6 +124,7 @@ function parseArgs(): ParsedArgs {
|
|
|
112
124
|
|
|
113
125
|
let runtimeUrl = entry?.localUrl || entry?.runtimeUrl || FALLBACK_RUNTIME_URL;
|
|
114
126
|
let assistantId = entry?.assistantId || DAEMON_INTERNAL_ASSISTANT_ID;
|
|
127
|
+
let assistantName = readAssistantName(entry);
|
|
115
128
|
const cloud = entry?.cloud;
|
|
116
129
|
const species: Species = (entry?.species as Species) ?? "vellum";
|
|
117
130
|
|
|
@@ -134,6 +147,7 @@ function parseArgs(): ParsedArgs {
|
|
|
134
147
|
flagArgs[i + 1]
|
|
135
148
|
) {
|
|
136
149
|
assistantId = flagArgs[++i];
|
|
150
|
+
assistantName = undefined;
|
|
137
151
|
} else if ((flag === "--interface" || flag === "-i") && flagArgs[i + 1]) {
|
|
138
152
|
const value = flagArgs[++i];
|
|
139
153
|
if (!(SUPPORTED_INTERFACES as readonly string[]).includes(value)) {
|
|
@@ -149,6 +163,7 @@ function parseArgs(): ParsedArgs {
|
|
|
149
163
|
return {
|
|
150
164
|
runtimeUrl: maybeSwapToLocalhost(runtimeUrl.replace(/\/+$/, "")),
|
|
151
165
|
assistantId,
|
|
166
|
+
assistantName,
|
|
152
167
|
species,
|
|
153
168
|
cloud,
|
|
154
169
|
platformToken,
|
|
@@ -225,6 +240,39 @@ ${ANSI.bold}EXAMPLES:${ANSI.reset}
|
|
|
225
240
|
`);
|
|
226
241
|
}
|
|
227
242
|
|
|
243
|
+
async function maybeHydratePlatformAssistantName(
|
|
244
|
+
assistantId: string,
|
|
245
|
+
assistantName: string | undefined,
|
|
246
|
+
cloud: string | undefined,
|
|
247
|
+
platformToken: string | undefined,
|
|
248
|
+
): Promise<string | undefined> {
|
|
249
|
+
if (cloud !== "vellum" || assistantName || !platformToken) {
|
|
250
|
+
return assistantName;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
const matchedAssistant = (
|
|
255
|
+
await fetchPlatformAssistants(platformToken)
|
|
256
|
+
).find((assistant) => assistant.id === assistantId);
|
|
257
|
+
const hydratedName = matchedAssistant?.name.trim();
|
|
258
|
+
if (!hydratedName) {
|
|
259
|
+
return assistantName;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const entry = findAssistantByName(assistantId);
|
|
263
|
+
if (entry && entry.name !== hydratedName) {
|
|
264
|
+
saveAssistantEntry({
|
|
265
|
+
...entry,
|
|
266
|
+
name: hydratedName,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return hydratedName;
|
|
271
|
+
} catch {
|
|
272
|
+
return assistantName;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
228
276
|
/**
|
|
229
277
|
* Walk up from this file's location to find a sibling `clients/web` package.
|
|
230
278
|
*
|
|
@@ -289,6 +337,7 @@ export async function client(): Promise<void> {
|
|
|
289
337
|
const {
|
|
290
338
|
runtimeUrl,
|
|
291
339
|
assistantId,
|
|
340
|
+
assistantName: parsedAssistantName,
|
|
292
341
|
species,
|
|
293
342
|
cloud,
|
|
294
343
|
platformToken,
|
|
@@ -312,6 +361,13 @@ export async function client(): Promise<void> {
|
|
|
312
361
|
interfaceId,
|
|
313
362
|
});
|
|
314
363
|
|
|
364
|
+
const assistantName = await maybeHydratePlatformAssistantName(
|
|
365
|
+
assistantId,
|
|
366
|
+
parsedAssistantName,
|
|
367
|
+
cloud,
|
|
368
|
+
platformToken,
|
|
369
|
+
);
|
|
370
|
+
|
|
315
371
|
// Build pre-constructed request headers merged from auth + client registration.
|
|
316
372
|
// Spreading into every fetch site ensures consistency across REST and SSE endpoints.
|
|
317
373
|
let auth: Record<string, string> | undefined;
|
|
@@ -348,6 +404,6 @@ export async function client(): Promise<void> {
|
|
|
348
404
|
console.log(`${ANSI.dim}Disconnected.${ANSI.reset}`);
|
|
349
405
|
process.exit(0);
|
|
350
406
|
},
|
|
351
|
-
{ auth, project, zone },
|
|
407
|
+
{ auth, project, zone, assistantName },
|
|
352
408
|
);
|
|
353
409
|
}
|