@vellumai/assistant 0.4.37 → 0.4.41
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/ARCHITECTURE.md +3 -3
- package/README.md +13 -13
- package/bun.lock +80 -24
- package/docs/architecture/integrations.md +126 -128
- package/docs/runbook-trusted-contacts.md +1 -1
- package/docs/trusted-contact-access.md +12 -12
- package/package.json +3 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -14
- package/src/__tests__/app-bundler.test.ts +209 -0
- package/src/__tests__/app-compiler.test.ts +279 -0
- package/src/__tests__/app-executors.test.ts +293 -483
- package/src/__tests__/app-migration.test.ts +148 -0
- package/src/__tests__/app-routes-csp.test.ts +202 -0
- package/src/__tests__/avatar-e2e.test.ts +452 -0
- package/src/__tests__/avatar-generator.test.ts +193 -0
- package/src/__tests__/avatar-router.test.ts +186 -0
- package/src/__tests__/browser-download-timeout.test.ts +28 -0
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +9 -9
- package/src/__tests__/call-domain.test.ts +3 -7
- package/src/__tests__/credential-security-e2e.test.ts +19 -12
- package/src/__tests__/credentials-cli.test.ts +30 -4
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/handlers-slack-config.test.ts +0 -72
- package/src/__tests__/handlers-telegram-config.test.ts +19 -12
- package/src/__tests__/handlers-twitter-config.test.ts +105 -48
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/integration-status.test.ts +15 -5
- package/src/__tests__/integrations-cli.test.ts +1 -1
- package/src/__tests__/invite-redemption-service.test.ts +62 -7
- package/src/__tests__/ipc-snapshot.test.ts +0 -8
- package/src/__tests__/managed-avatar-client.test.ts +280 -0
- package/src/__tests__/mcp-cli.test.ts +3 -3
- package/src/__tests__/oauth-cli.test.ts +203 -0
- package/src/__tests__/relay-server.test.ts +3 -3
- package/src/__tests__/secret-onetime-send.test.ts +19 -12
- package/src/__tests__/secure-keys.test.ts +78 -0
- package/src/__tests__/session-messaging-secret-redirect.test.ts +3 -0
- package/src/__tests__/slack-channel-config.test.ts +23 -16
- package/src/__tests__/slack-share-routes.test.ts +263 -0
- package/src/__tests__/sms-messaging-provider.test.ts +3 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +7 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +10 -10
- package/src/__tests__/twilio-config.test.ts +15 -36
- package/src/__tests__/twilio-provider.test.ts +4 -0
- package/src/__tests__/twitter-auth-handler.test.ts +27 -14
- package/src/__tests__/twitter-cli-error-shaping.test.ts +1 -1
- package/src/__tests__/twitter-cli-routing.test.ts +38 -53
- package/src/__tests__/twitter-oauth-client.test.ts +18 -47
- package/src/__tests__/voice-invite-redemption.test.ts +27 -3
- package/src/amazon/cart.ts +1 -1
- package/src/amazon/client.ts +89 -7
- package/src/approvals/guardian-request-resolvers.ts +2 -2
- package/src/bundler/app-bundler.ts +77 -32
- package/src/bundler/app-compiler.ts +195 -0
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/package-resolver.ts +185 -0
- package/src/calls/call-domain.ts +4 -14
- package/src/calls/relay-server.ts +2 -2
- package/src/calls/twilio-config.ts +5 -24
- package/src/calls/twilio-rest.ts +19 -5
- package/src/cli/amazon.ts +74 -249
- package/src/cli/audit.ts +2 -2
- package/src/cli/autonomy.ts +9 -9
- package/src/cli/channels.ts +5 -5
- package/src/cli/completions.ts +27 -27
- package/src/cli/config.ts +14 -14
- package/src/cli/contacts.ts +27 -27
- package/src/cli/credentials.ts +28 -28
- package/src/cli/dev.ts +2 -2
- package/src/cli/doctor.ts +2 -2
- package/src/cli/email.ts +82 -82
- package/src/cli/influencer.ts +13 -13
- package/src/cli/integrations.ts +19 -144
- package/src/cli/keys.ts +10 -10
- package/src/cli/map.ts +4 -4
- package/src/cli/mcp.ts +17 -17
- package/src/cli/memory.ts +18 -18
- package/src/cli/notifications.ts +13 -13
- package/src/cli/oauth.ts +77 -0
- package/src/cli/program.ts +2 -0
- package/src/cli/sequence.ts +27 -27
- package/src/cli/sessions.ts +12 -12
- package/src/cli/trust.ts +8 -8
- package/src/cli/twitter.ts +124 -70
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/agentmail/SKILL.md +34 -34
- package/src/config/bundled-skills/amazon/SKILL.md +54 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +137 -3
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +10 -4
- package/src/config/bundled-skills/configure-settings/SKILL.md +18 -18
- package/src/config/bundled-skills/contacts/SKILL.md +12 -12
- package/src/config/bundled-skills/doordash/lib/client.ts +7 -9
- package/src/config/bundled-skills/email-setup/SKILL.md +4 -4
- package/src/config/bundled-skills/frontend-design/icon.svg +16 -0
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +143 -162
- package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +4 -4
- package/src/config/bundled-skills/influencer/SKILL.md +13 -13
- package/src/config/bundled-skills/mcp-setup/SKILL.md +11 -11
- package/src/config/bundled-skills/phone-calls/SKILL.md +48 -54
- package/src/config/bundled-skills/public-ingress/SKILL.md +6 -6
- package/src/config/bundled-skills/slack-app-setup/SKILL.md +1 -1
- package/src/config/bundled-skills/sms-setup/SKILL.md +3 -3
- package/src/config/bundled-skills/telegram-setup/SKILL.md +2 -2
- package/src/config/bundled-skills/twilio-setup/SKILL.md +136 -225
- package/src/config/bundled-skills/twitter/SKILL.md +68 -44
- package/src/config/bundled-skills/voice-setup/SKILL.md +2 -2
- package/src/config/core-schema.ts +26 -0
- package/src/config/env.ts +4 -0
- package/src/config/feature-flag-registry.json +9 -1
- package/src/config/schema.ts +8 -0
- package/src/config/system-prompt.ts +6 -3
- package/src/config/templates/BOOTSTRAP.md +7 -5
- package/src/contacts/contacts-write.ts +5 -1
- package/src/daemon/handlers/apps.ts +31 -4
- package/src/daemon/handlers/config-ingress.ts +3 -3
- package/src/daemon/handlers/config-integrations.ts +120 -49
- package/src/daemon/handlers/config-slack-channel.ts +26 -7
- package/src/daemon/handlers/config-slack.ts +1 -54
- package/src/daemon/handlers/config-telegram.ts +28 -10
- package/src/daemon/handlers/config.ts +1 -4
- package/src/daemon/handlers/twitter-auth.ts +11 -4
- package/src/daemon/ipc-contract/apps.ts +0 -13
- package/src/daemon/ipc-contract-inventory.json +0 -2
- package/src/daemon/lifecycle.ts +8 -1
- package/src/daemon/session-messaging.ts +2 -2
- package/src/daemon/tool-side-effects.ts +30 -0
- package/src/email/providers/agentmail.ts +1 -1
- package/src/email/providers/index.ts +1 -1
- package/src/email/service.ts +1 -1
- package/src/gallery/default-gallery.ts +538 -0
- package/src/gallery/gallery-manifest.ts +5 -1
- package/src/influencer/client.ts +8 -6
- package/src/mcp/client.ts +1 -1
- package/src/media/avatar-router.ts +99 -0
- package/src/media/avatar-types.ts +60 -0
- package/src/media/managed-avatar-client.ts +189 -0
- package/src/memory/app-migration.ts +114 -0
- package/src/memory/app-store.ts +11 -0
- package/src/memory/qdrant-client.ts +1 -1
- package/src/messaging/providers/slack/client.ts +12 -2
- package/src/messaging/providers/sms/adapter.ts +6 -10
- package/src/migrations/data-layout.ts +8 -1
- package/src/oauth/token-persistence.ts +9 -6
- package/src/runtime/assistant-scope.ts +5 -0
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/channel-readiness-service.ts +9 -4
- package/src/runtime/gateway-internal-client.ts +11 -3
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +23 -13
- package/src/runtime/middleware/twilio-validation.ts +2 -2
- package/src/runtime/routes/app-routes.ts +131 -3
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +3 -3
- package/src/runtime/routes/integration-routes.ts +2 -2
- package/src/runtime/routes/slack-share-routes.ts +235 -0
- package/src/runtime/routes/twilio-routes.ts +47 -34
- package/src/schedule/integration-status.ts +2 -3
- package/src/security/token-manager.ts +11 -3
- package/src/tools/apps/executors.ts +116 -8
- package/src/tools/browser/browser-manager.ts +30 -2
- package/src/tools/browser/chrome-cdp.ts +31 -3
- package/src/tools/credentials/vault.ts +9 -7
- package/src/tools/executor.ts +4 -0
- package/src/tools/system/avatar-generator.ts +55 -34
- package/src/twitter/client.ts +1 -1
- package/src/twitter/oauth-client.ts +31 -43
- package/src/twitter/router.ts +25 -23
- package/src/util/platform.ts +5 -0
- package/src/slack/slack-webhook.ts +0 -66
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import * as net from "node:net";
|
|
2
2
|
|
|
3
|
-
import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
4
3
|
import {
|
|
5
|
-
|
|
4
|
+
getNestedValue,
|
|
5
|
+
loadRawConfig,
|
|
6
|
+
saveRawConfig,
|
|
7
|
+
setNestedValue,
|
|
8
|
+
} from "../../config/loader.js";
|
|
9
|
+
import {
|
|
10
|
+
deleteSecureKeyAsync,
|
|
6
11
|
getSecureKey,
|
|
7
|
-
|
|
12
|
+
setSecureKeyAsync,
|
|
8
13
|
} from "../../security/secure-keys.js";
|
|
9
14
|
import {
|
|
10
15
|
deleteCredentialMetadata,
|
|
@@ -17,11 +22,11 @@ import type {
|
|
|
17
22
|
} from "../ipc-protocol.js";
|
|
18
23
|
import { defineHandlers, type HandlerContext, log } from "./shared.js";
|
|
19
24
|
|
|
20
|
-
export function handleVercelApiConfig(
|
|
25
|
+
export async function handleVercelApiConfig(
|
|
21
26
|
msg: VercelApiConfigRequest,
|
|
22
27
|
socket: net.Socket,
|
|
23
28
|
ctx: HandlerContext,
|
|
24
|
-
): void {
|
|
29
|
+
): Promise<void> {
|
|
25
30
|
try {
|
|
26
31
|
if (msg.action === "get") {
|
|
27
32
|
const existing = getSecureKey("credential:vercel:api_token");
|
|
@@ -40,7 +45,10 @@ export function handleVercelApiConfig(
|
|
|
40
45
|
});
|
|
41
46
|
return;
|
|
42
47
|
}
|
|
43
|
-
const stored =
|
|
48
|
+
const stored = await setSecureKeyAsync(
|
|
49
|
+
"credential:vercel:api_token",
|
|
50
|
+
msg.apiToken,
|
|
51
|
+
);
|
|
44
52
|
if (!stored) {
|
|
45
53
|
ctx.send(socket, {
|
|
46
54
|
type: "vercel_api_config_response",
|
|
@@ -59,7 +67,16 @@ export function handleVercelApiConfig(
|
|
|
59
67
|
success: true,
|
|
60
68
|
});
|
|
61
69
|
} else {
|
|
62
|
-
|
|
70
|
+
const r = await deleteSecureKeyAsync("credential:vercel:api_token");
|
|
71
|
+
if (r === "error") {
|
|
72
|
+
ctx.send(socket, {
|
|
73
|
+
type: "vercel_api_config_response",
|
|
74
|
+
hasToken: !!getSecureKey("credential:vercel:api_token"),
|
|
75
|
+
success: false,
|
|
76
|
+
error: "Failed to delete Vercel API token from secure storage",
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
63
80
|
deleteCredentialMetadata("vercel", "api_token");
|
|
64
81
|
ctx.send(socket, {
|
|
65
82
|
type: "vercel_api_config_response",
|
|
@@ -87,27 +104,27 @@ function hasTwitterClientId(): boolean {
|
|
|
87
104
|
);
|
|
88
105
|
}
|
|
89
106
|
|
|
90
|
-
export function handleTwitterIntegrationConfig(
|
|
107
|
+
export async function handleTwitterIntegrationConfig(
|
|
91
108
|
msg: TwitterIntegrationConfigRequest,
|
|
92
109
|
socket: net.Socket,
|
|
93
110
|
ctx: HandlerContext,
|
|
94
|
-
): void {
|
|
111
|
+
): Promise<void> {
|
|
95
112
|
try {
|
|
96
113
|
if (msg.action === "get") {
|
|
97
114
|
const raw = loadRawConfig();
|
|
98
115
|
const mode =
|
|
99
|
-
(raw
|
|
100
|
-
|
|
116
|
+
(getNestedValue(raw, "twitter.integrationMode") as
|
|
117
|
+
| "local_byo"
|
|
118
|
+
| "managed"
|
|
119
|
+
| undefined) ?? "local_byo";
|
|
101
120
|
const strategy =
|
|
102
|
-
(raw.
|
|
121
|
+
(getNestedValue(raw, "twitter.operationStrategy") as
|
|
103
122
|
| "oauth"
|
|
104
123
|
| "browser"
|
|
105
124
|
| "auto"
|
|
106
125
|
| undefined) ?? "auto";
|
|
107
|
-
const strategyConfigured =
|
|
108
|
-
raw,
|
|
109
|
-
"twitterOperationStrategy",
|
|
110
|
-
);
|
|
126
|
+
const strategyConfigured =
|
|
127
|
+
getNestedValue(raw, "twitter.operationStrategy") !== undefined;
|
|
111
128
|
const localClientConfigured = hasTwitterClientId();
|
|
112
129
|
const connected = !!getSecureKey(
|
|
113
130
|
"credential:integration:twitter:access_token",
|
|
@@ -127,15 +144,13 @@ export function handleTwitterIntegrationConfig(
|
|
|
127
144
|
} else if (msg.action === "get_strategy") {
|
|
128
145
|
const raw = loadRawConfig();
|
|
129
146
|
const strategy =
|
|
130
|
-
(raw.
|
|
147
|
+
(getNestedValue(raw, "twitter.operationStrategy") as
|
|
131
148
|
| "oauth"
|
|
132
149
|
| "browser"
|
|
133
150
|
| "auto"
|
|
134
151
|
| undefined) ?? "auto";
|
|
135
|
-
const strategyConfigured =
|
|
136
|
-
raw,
|
|
137
|
-
"twitterOperationStrategy",
|
|
138
|
-
);
|
|
152
|
+
const strategyConfigured =
|
|
153
|
+
getNestedValue(raw, "twitter.operationStrategy") !== undefined;
|
|
139
154
|
ctx.send(socket, {
|
|
140
155
|
type: "twitter_integration_config_response",
|
|
141
156
|
success: true,
|
|
@@ -162,7 +177,7 @@ export function handleTwitterIntegrationConfig(
|
|
|
162
177
|
return;
|
|
163
178
|
}
|
|
164
179
|
const raw = loadRawConfig();
|
|
165
|
-
raw.
|
|
180
|
+
setNestedValue(raw, "twitter.operationStrategy", value);
|
|
166
181
|
saveRawConfig(raw);
|
|
167
182
|
ctx.send(socket, {
|
|
168
183
|
type: "twitter_integration_config_response",
|
|
@@ -177,7 +192,7 @@ export function handleTwitterIntegrationConfig(
|
|
|
177
192
|
});
|
|
178
193
|
} else if (msg.action === "set_mode") {
|
|
179
194
|
const raw = loadRawConfig();
|
|
180
|
-
raw.
|
|
195
|
+
setNestedValue(raw, "twitter.integrationMode", msg.mode ?? "local_byo");
|
|
181
196
|
saveRawConfig(raw);
|
|
182
197
|
ctx.send(socket, {
|
|
183
198
|
type: "twitter_integration_config_response",
|
|
@@ -205,8 +220,8 @@ export function handleTwitterIntegrationConfig(
|
|
|
205
220
|
const previousClientId =
|
|
206
221
|
getSecureKey("credential:integration:twitter:client_id") ??
|
|
207
222
|
getSecureKey("credential:integration:twitter:oauth_client_id");
|
|
208
|
-
// Write canonical key
|
|
209
|
-
const storedId =
|
|
223
|
+
// Write canonical key (async — writes broker + encrypted store)
|
|
224
|
+
const storedId = await setSecureKeyAsync(
|
|
210
225
|
"credential:integration:twitter:client_id",
|
|
211
226
|
msg.clientId,
|
|
212
227
|
);
|
|
@@ -222,30 +237,34 @@ export function handleTwitterIntegrationConfig(
|
|
|
222
237
|
return;
|
|
223
238
|
}
|
|
224
239
|
// Also write legacy key for backward compatibility
|
|
225
|
-
|
|
240
|
+
await setSecureKeyAsync(
|
|
226
241
|
"credential:integration:twitter:oauth_client_id",
|
|
227
242
|
msg.clientId,
|
|
228
243
|
);
|
|
229
244
|
if (msg.clientSecret) {
|
|
230
245
|
// Write canonical key
|
|
231
|
-
const storedSecret =
|
|
246
|
+
const storedSecret = await setSecureKeyAsync(
|
|
232
247
|
"credential:integration:twitter:client_secret",
|
|
233
248
|
msg.clientSecret,
|
|
234
249
|
);
|
|
235
250
|
if (!storedSecret) {
|
|
236
251
|
// Roll back the client ID to its previous value to avoid inconsistent OAuth state
|
|
237
252
|
if (previousClientId) {
|
|
238
|
-
|
|
253
|
+
await setSecureKeyAsync(
|
|
239
254
|
"credential:integration:twitter:client_id",
|
|
240
255
|
previousClientId,
|
|
241
256
|
);
|
|
242
|
-
|
|
257
|
+
await setSecureKeyAsync(
|
|
243
258
|
"credential:integration:twitter:oauth_client_id",
|
|
244
259
|
previousClientId,
|
|
245
260
|
);
|
|
246
261
|
} else {
|
|
247
|
-
|
|
248
|
-
|
|
262
|
+
await deleteSecureKeyAsync(
|
|
263
|
+
"credential:integration:twitter:client_id",
|
|
264
|
+
);
|
|
265
|
+
await deleteSecureKeyAsync(
|
|
266
|
+
"credential:integration:twitter:oauth_client_id",
|
|
267
|
+
);
|
|
249
268
|
}
|
|
250
269
|
ctx.send(socket, {
|
|
251
270
|
type: "twitter_integration_config_response",
|
|
@@ -258,14 +277,18 @@ export function handleTwitterIntegrationConfig(
|
|
|
258
277
|
return;
|
|
259
278
|
}
|
|
260
279
|
// Also write legacy key for backward compatibility
|
|
261
|
-
|
|
280
|
+
await setSecureKeyAsync(
|
|
262
281
|
"credential:integration:twitter:oauth_client_secret",
|
|
263
282
|
msg.clientSecret,
|
|
264
283
|
);
|
|
265
284
|
} else {
|
|
266
285
|
// Clear any stale secret when updating client without a secret (e.g. switching to PKCE)
|
|
267
|
-
|
|
268
|
-
|
|
286
|
+
await deleteSecureKeyAsync(
|
|
287
|
+
"credential:integration:twitter:client_secret",
|
|
288
|
+
);
|
|
289
|
+
await deleteSecureKeyAsync(
|
|
290
|
+
"credential:integration:twitter:oauth_client_secret",
|
|
291
|
+
);
|
|
269
292
|
}
|
|
270
293
|
ctx.send(socket, {
|
|
271
294
|
type: "twitter_integration_config_response",
|
|
@@ -278,35 +301,83 @@ export function handleTwitterIntegrationConfig(
|
|
|
278
301
|
});
|
|
279
302
|
} else if (msg.action === "clear_local_client") {
|
|
280
303
|
// If connected, disconnect first
|
|
304
|
+
const deleteResults: Array<"deleted" | "not-found" | "error"> = [];
|
|
281
305
|
if (getSecureKey("credential:integration:twitter:access_token")) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
306
|
+
deleteResults.push(
|
|
307
|
+
await deleteSecureKeyAsync(
|
|
308
|
+
"credential:integration:twitter:access_token",
|
|
309
|
+
),
|
|
310
|
+
);
|
|
311
|
+
deleteResults.push(
|
|
312
|
+
await deleteSecureKeyAsync(
|
|
313
|
+
"credential:integration:twitter:refresh_token",
|
|
314
|
+
),
|
|
315
|
+
);
|
|
285
316
|
}
|
|
286
317
|
// Remove both canonical and legacy client credential keys
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
318
|
+
deleteResults.push(
|
|
319
|
+
await deleteSecureKeyAsync("credential:integration:twitter:client_id"),
|
|
320
|
+
);
|
|
321
|
+
deleteResults.push(
|
|
322
|
+
await deleteSecureKeyAsync(
|
|
323
|
+
"credential:integration:twitter:client_secret",
|
|
324
|
+
),
|
|
325
|
+
);
|
|
326
|
+
deleteResults.push(
|
|
327
|
+
await deleteSecureKeyAsync(
|
|
328
|
+
"credential:integration:twitter:oauth_client_id",
|
|
329
|
+
),
|
|
330
|
+
);
|
|
331
|
+
deleteResults.push(
|
|
332
|
+
await deleteSecureKeyAsync(
|
|
333
|
+
"credential:integration:twitter:oauth_client_secret",
|
|
334
|
+
),
|
|
335
|
+
);
|
|
336
|
+
const hasDeleteError = deleteResults.some((r) => r === "error");
|
|
337
|
+
if (!hasDeleteError) {
|
|
338
|
+
deleteCredentialMetadata("integration:twitter", "access_token");
|
|
339
|
+
}
|
|
291
340
|
ctx.send(socket, {
|
|
292
341
|
type: "twitter_integration_config_response",
|
|
293
|
-
success:
|
|
342
|
+
success: !hasDeleteError,
|
|
294
343
|
managedAvailable: false,
|
|
295
|
-
localClientConfigured: false,
|
|
296
|
-
connected:
|
|
344
|
+
localClientConfigured: hasDeleteError ? hasTwitterClientId() : false,
|
|
345
|
+
connected: hasDeleteError
|
|
346
|
+
? !!getSecureKey("credential:integration:twitter:access_token")
|
|
347
|
+
: false,
|
|
348
|
+
...(hasDeleteError
|
|
349
|
+
? {
|
|
350
|
+
error:
|
|
351
|
+
"Failed to delete some Twitter credentials from secure storage",
|
|
352
|
+
}
|
|
353
|
+
: {}),
|
|
297
354
|
});
|
|
298
355
|
} else if (msg.action === "disconnect") {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
356
|
+
const dr1 = await deleteSecureKeyAsync(
|
|
357
|
+
"credential:integration:twitter:access_token",
|
|
358
|
+
);
|
|
359
|
+
const dr2 = await deleteSecureKeyAsync(
|
|
360
|
+
"credential:integration:twitter:refresh_token",
|
|
361
|
+
);
|
|
302
362
|
// Client credentials (client_id, oauth_client_id, etc.) are intentionally
|
|
303
363
|
// preserved so the user can re-connect without reconfiguring.
|
|
364
|
+
const disconnectFailed = dr1 === "error" || dr2 === "error";
|
|
365
|
+
if (!disconnectFailed) {
|
|
366
|
+
deleteCredentialMetadata("integration:twitter", "access_token");
|
|
367
|
+
}
|
|
304
368
|
ctx.send(socket, {
|
|
305
369
|
type: "twitter_integration_config_response",
|
|
306
|
-
success:
|
|
370
|
+
success: !disconnectFailed,
|
|
307
371
|
managedAvailable: false,
|
|
308
372
|
localClientConfigured: hasTwitterClientId(),
|
|
309
|
-
connected:
|
|
373
|
+
connected: disconnectFailed
|
|
374
|
+
? !!getSecureKey("credential:integration:twitter:access_token")
|
|
375
|
+
: false,
|
|
376
|
+
...(disconnectFailed
|
|
377
|
+
? {
|
|
378
|
+
error: "Failed to delete Twitter tokens from secure storage",
|
|
379
|
+
}
|
|
380
|
+
: {}),
|
|
310
381
|
});
|
|
311
382
|
} else {
|
|
312
383
|
ctx.send(socket, {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
deleteSecureKeyAsync,
|
|
3
3
|
getSecureKey,
|
|
4
|
-
|
|
4
|
+
setSecureKeyAsync,
|
|
5
5
|
} from "../../security/secure-keys.js";
|
|
6
6
|
import {
|
|
7
7
|
deleteCredentialMetadata,
|
|
@@ -122,7 +122,10 @@ export async function setSlackChannelConfig(
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
const stored =
|
|
125
|
+
const stored = await setSecureKeyAsync(
|
|
126
|
+
"credential:slack_channel:bot_token",
|
|
127
|
+
botToken,
|
|
128
|
+
);
|
|
126
129
|
if (!stored) {
|
|
127
130
|
const storedBotToken = !!getSecureKey(
|
|
128
131
|
"credential:slack_channel:bot_token",
|
|
@@ -166,7 +169,10 @@ export async function setSlackChannelConfig(
|
|
|
166
169
|
};
|
|
167
170
|
}
|
|
168
171
|
|
|
169
|
-
const stored =
|
|
172
|
+
const stored = await setSecureKeyAsync(
|
|
173
|
+
"credential:slack_channel:app_token",
|
|
174
|
+
appToken,
|
|
175
|
+
);
|
|
170
176
|
if (!stored) {
|
|
171
177
|
const storedBotToken = !!getSecureKey(
|
|
172
178
|
"credential:slack_channel:bot_token",
|
|
@@ -207,10 +213,23 @@ export async function setSlackChannelConfig(
|
|
|
207
213
|
};
|
|
208
214
|
}
|
|
209
215
|
|
|
210
|
-
export function clearSlackChannelConfig(): SlackChannelConfigResult {
|
|
211
|
-
|
|
216
|
+
export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResult> {
|
|
217
|
+
const r1 = await deleteSecureKeyAsync("credential:slack_channel:bot_token");
|
|
218
|
+
const r2 = await deleteSecureKeyAsync("credential:slack_channel:app_token");
|
|
219
|
+
|
|
220
|
+
if (r1 === "error" || r2 === "error") {
|
|
221
|
+
const hasBotToken = !!getSecureKey("credential:slack_channel:bot_token");
|
|
222
|
+
const hasAppToken = !!getSecureKey("credential:slack_channel:app_token");
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
hasBotToken,
|
|
226
|
+
hasAppToken,
|
|
227
|
+
connected: hasBotToken && hasAppToken,
|
|
228
|
+
error: "Failed to delete Slack channel credentials from secure storage",
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
212
232
|
deleteCredentialMetadata("slack_channel", "bot_token");
|
|
213
|
-
deleteSecureKey("credential:slack_channel:app_token");
|
|
214
233
|
deleteCredentialMetadata("slack_channel", "app_token");
|
|
215
234
|
|
|
216
235
|
return {
|
|
@@ -1,61 +1,9 @@
|
|
|
1
1
|
import * as net from "node:net";
|
|
2
2
|
|
|
3
3
|
import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
4
|
-
import {
|
|
5
|
-
import { postToSlackWebhook } from "../../slack/slack-webhook.js";
|
|
6
|
-
import type {
|
|
7
|
-
ShareToSlackRequest,
|
|
8
|
-
SlackWebhookConfigRequest,
|
|
9
|
-
} from "../ipc-protocol.js";
|
|
4
|
+
import type { SlackWebhookConfigRequest } from "../ipc-protocol.js";
|
|
10
5
|
import { defineHandlers, type HandlerContext, log } from "./shared.js";
|
|
11
6
|
|
|
12
|
-
export async function handleShareToSlack(
|
|
13
|
-
msg: ShareToSlackRequest,
|
|
14
|
-
socket: net.Socket,
|
|
15
|
-
ctx: HandlerContext,
|
|
16
|
-
): Promise<void> {
|
|
17
|
-
try {
|
|
18
|
-
const config = loadRawConfig();
|
|
19
|
-
const webhookUrl = config.slackWebhookUrl as string | undefined;
|
|
20
|
-
if (!webhookUrl) {
|
|
21
|
-
ctx.send(socket, {
|
|
22
|
-
type: "share_to_slack_response",
|
|
23
|
-
success: false,
|
|
24
|
-
error:
|
|
25
|
-
"No Slack webhook URL configured. Provide one here in the chat, or set it from the Settings page.",
|
|
26
|
-
});
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const app = getApp(msg.appId);
|
|
31
|
-
if (!app) {
|
|
32
|
-
ctx.send(socket, {
|
|
33
|
-
type: "share_to_slack_response",
|
|
34
|
-
success: false,
|
|
35
|
-
error: `App not found: ${msg.appId}`,
|
|
36
|
-
});
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
await postToSlackWebhook(
|
|
41
|
-
webhookUrl,
|
|
42
|
-
app.name,
|
|
43
|
-
app.description ?? "",
|
|
44
|
-
"\u{1F4F1}",
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
ctx.send(socket, { type: "share_to_slack_response", success: true });
|
|
48
|
-
} catch (err) {
|
|
49
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
50
|
-
log.error({ err, appId: msg.appId }, "Failed to share app to Slack");
|
|
51
|
-
ctx.send(socket, {
|
|
52
|
-
type: "share_to_slack_response",
|
|
53
|
-
success: false,
|
|
54
|
-
error: message,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
7
|
export function handleSlackWebhookConfig(
|
|
60
8
|
msg: SlackWebhookConfigRequest,
|
|
61
9
|
socket: net.Socket,
|
|
@@ -89,6 +37,5 @@ export function handleSlackWebhookConfig(
|
|
|
89
37
|
}
|
|
90
38
|
|
|
91
39
|
export const slackHandlers = defineHandlers({
|
|
92
|
-
share_to_slack: handleShareToSlack,
|
|
93
40
|
slack_webhook_config: handleSlackWebhookConfig,
|
|
94
41
|
});
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
shouldUsePlatformCallbacks,
|
|
7
7
|
} from "../../inbound/platform-callback-registration.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
deleteSecureKeyAsync,
|
|
10
10
|
getSecureKey,
|
|
11
|
-
|
|
11
|
+
setSecureKeyAsync,
|
|
12
12
|
} from "../../security/secure-keys.js";
|
|
13
13
|
import {
|
|
14
14
|
deleteCredentialMetadata,
|
|
@@ -130,8 +130,11 @@ export async function setTelegramConfig(
|
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
// Store bot token securely
|
|
134
|
-
const stored =
|
|
133
|
+
// Store bot token securely (async — writes broker + encrypted store)
|
|
134
|
+
const stored = await setSecureKeyAsync(
|
|
135
|
+
"credential:telegram:bot_token",
|
|
136
|
+
resolvedToken,
|
|
137
|
+
);
|
|
135
138
|
if (!stored) {
|
|
136
139
|
return {
|
|
137
140
|
success: false,
|
|
@@ -152,7 +155,7 @@ export async function setTelegramConfig(
|
|
|
152
155
|
if (!hasWebhookSecret) {
|
|
153
156
|
const { randomUUID } = await import("node:crypto");
|
|
154
157
|
const webhookSecret = randomUUID();
|
|
155
|
-
const secretStored =
|
|
158
|
+
const secretStored = await setSecureKeyAsync(
|
|
156
159
|
"credential:telegram:webhook_secret",
|
|
157
160
|
webhookSecret,
|
|
158
161
|
);
|
|
@@ -164,7 +167,7 @@ export async function setTelegramConfig(
|
|
|
164
167
|
// When the token came from secure storage it was already valid
|
|
165
168
|
// configuration; deleting it would destroy working state.
|
|
166
169
|
if (isNewToken) {
|
|
167
|
-
|
|
170
|
+
await deleteSecureKeyAsync("credential:telegram:bot_token");
|
|
168
171
|
deleteCredentialMetadata("telegram", "bot_token");
|
|
169
172
|
}
|
|
170
173
|
return {
|
|
@@ -226,10 +229,8 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
|
|
|
226
229
|
}
|
|
227
230
|
}
|
|
228
231
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
deleteSecureKey("credential:telegram:webhook_secret");
|
|
232
|
-
deleteCredentialMetadata("telegram", "webhook_secret");
|
|
232
|
+
const r1 = await deleteSecureKeyAsync("credential:telegram:bot_token");
|
|
233
|
+
const r2 = await deleteSecureKeyAsync("credential:telegram:webhook_secret");
|
|
233
234
|
|
|
234
235
|
// Trigger reconcile to deregister webhook
|
|
235
236
|
const effectiveUrl = getIngressPublicBaseUrl();
|
|
@@ -237,6 +238,23 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
|
|
|
237
238
|
triggerGatewayReconcile(effectiveUrl);
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
if (r1 === "error" || r2 === "error") {
|
|
242
|
+
const hasBotToken = !!getSecureKey("credential:telegram:bot_token");
|
|
243
|
+
const hasWebhookSecret = !!getSecureKey(
|
|
244
|
+
"credential:telegram:webhook_secret",
|
|
245
|
+
);
|
|
246
|
+
return {
|
|
247
|
+
success: false,
|
|
248
|
+
hasBotToken,
|
|
249
|
+
connected: hasBotToken && hasWebhookSecret,
|
|
250
|
+
hasWebhookSecret,
|
|
251
|
+
error: "Failed to delete Telegram credentials from secure storage",
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
deleteCredentialMetadata("telegram", "bot_token");
|
|
256
|
+
deleteCredentialMetadata("telegram", "webhook_secret");
|
|
257
|
+
|
|
240
258
|
return {
|
|
241
259
|
success: true,
|
|
242
260
|
hasBotToken: false,
|
|
@@ -40,10 +40,7 @@ export {
|
|
|
40
40
|
handleSchedulesList,
|
|
41
41
|
handleScheduleToggle,
|
|
42
42
|
} from "./config-scheduling.js";
|
|
43
|
-
export {
|
|
44
|
-
handleShareToSlack,
|
|
45
|
-
handleSlackWebhookConfig,
|
|
46
|
-
} from "./config-slack.js";
|
|
43
|
+
export { handleSlackWebhookConfig } from "./config-slack.js";
|
|
47
44
|
export {
|
|
48
45
|
handleTelegramConfig,
|
|
49
46
|
summarizeTelegramError,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as net from "node:net";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getNestedValue,
|
|
5
|
+
loadConfig,
|
|
6
|
+
loadRawConfig,
|
|
7
|
+
} from "../../config/loader.js";
|
|
4
8
|
import { getPublicBaseUrl } from "../../inbound/public-ingress-urls.js";
|
|
5
9
|
import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
|
|
6
10
|
import { getSecureKey } from "../../security/secure-keys.js";
|
|
@@ -51,7 +55,8 @@ export async function handleTwitterAuthStart(
|
|
|
51
55
|
try {
|
|
52
56
|
const raw = loadRawConfig();
|
|
53
57
|
const mode =
|
|
54
|
-
(raw.
|
|
58
|
+
(getNestedValue(raw, "twitter.integrationMode") as string | undefined) ??
|
|
59
|
+
"local_byo";
|
|
55
60
|
if (mode !== "local_byo") {
|
|
56
61
|
ctx.send(socket, {
|
|
57
62
|
type: "twitter_auth_result",
|
|
@@ -186,8 +191,10 @@ export function handleTwitterAuthStatus(
|
|
|
186
191
|
);
|
|
187
192
|
const raw = loadRawConfig();
|
|
188
193
|
const mode =
|
|
189
|
-
(raw
|
|
190
|
-
|
|
194
|
+
(getNestedValue(raw, "twitter.integrationMode") as
|
|
195
|
+
| "local_byo"
|
|
196
|
+
| "managed"
|
|
197
|
+
| undefined) ?? "local_byo";
|
|
191
198
|
const meta = getCredentialMetadata("integration:twitter", "access_token");
|
|
192
199
|
|
|
193
200
|
ctx.send(socket, {
|
|
@@ -127,11 +127,6 @@ export interface ShareAppCloudRequest {
|
|
|
127
127
|
appId: string;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
export interface ShareToSlackRequest {
|
|
131
|
-
type: "share_to_slack";
|
|
132
|
-
appId: string;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
130
|
export interface PublishPageRequest {
|
|
136
131
|
type: "publish_page";
|
|
137
132
|
html: string;
|
|
@@ -341,12 +336,6 @@ export interface AppRestoreResponse {
|
|
|
341
336
|
error?: string;
|
|
342
337
|
}
|
|
343
338
|
|
|
344
|
-
export interface ShareToSlackResponse {
|
|
345
|
-
type: "share_to_slack_response";
|
|
346
|
-
success: boolean;
|
|
347
|
-
error?: string;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
339
|
export interface PublishPageResponse {
|
|
351
340
|
type: "publish_page_response";
|
|
352
341
|
success: boolean;
|
|
@@ -389,7 +378,6 @@ export type _AppsClientMessages =
|
|
|
389
378
|
| AppFileAtVersionRequest
|
|
390
379
|
| AppRestoreRequest
|
|
391
380
|
| ShareAppCloudRequest
|
|
392
|
-
| ShareToSlackRequest
|
|
393
381
|
| AppUpdatePreviewRequest
|
|
394
382
|
| AppPreviewRequest
|
|
395
383
|
| PublishPageRequest
|
|
@@ -414,7 +402,6 @@ export type _AppsServerMessages =
|
|
|
414
402
|
| AppDiffResponse
|
|
415
403
|
| AppFileAtVersionResponse
|
|
416
404
|
| AppRestoreResponse
|
|
417
|
-
| ShareToSlackResponse
|
|
418
405
|
| AppUpdatePreviewResponse
|
|
419
406
|
| AppPreviewResponse
|
|
420
407
|
| PublishPageResponse
|
|
@@ -134,7 +134,6 @@
|
|
|
134
134
|
"session_switch",
|
|
135
135
|
"sessions_clear",
|
|
136
136
|
"share_app_cloud",
|
|
137
|
-
"share_to_slack",
|
|
138
137
|
"shared_app_delete",
|
|
139
138
|
"shared_apps_list",
|
|
140
139
|
"sign_bundle_payload_response",
|
|
@@ -291,7 +290,6 @@
|
|
|
291
290
|
"session_title_updated",
|
|
292
291
|
"sessions_clear_response",
|
|
293
292
|
"share_app_cloud_response",
|
|
294
|
-
"share_to_slack_response",
|
|
295
293
|
"shared_app_delete_response",
|
|
296
294
|
"shared_apps_list_response",
|
|
297
295
|
"sign_bundle_payload",
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { TwilioConversationRelayProvider } from "../calls/twilio-provider.js";
|
|
|
10
10
|
import { setVoiceBridgeDeps } from "../calls/voice-session-bridge.js";
|
|
11
11
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
12
12
|
import {
|
|
13
|
+
getQdrantHttpPortEnv,
|
|
13
14
|
getQdrantUrlEnv,
|
|
14
15
|
getRuntimeHttpHost,
|
|
15
16
|
getRuntimeHttpPort,
|
|
@@ -250,7 +251,13 @@ export async function runDaemon(): Promise<void> {
|
|
|
250
251
|
log.info("Daemon startup: DaemonServer started");
|
|
251
252
|
|
|
252
253
|
// Initialize Qdrant vector store — non-fatal so the daemon stays up without it
|
|
253
|
-
|
|
254
|
+
// Prefer QDRANT_HTTP_PORT (locally-spawned Qdrant on a specific port) over
|
|
255
|
+
// QDRANT_URL (external Qdrant instance) so the CLI can set the port without
|
|
256
|
+
// triggering QdrantManager's external mode which skips local process spawn.
|
|
257
|
+
const qdrantHttpPort = getQdrantHttpPortEnv();
|
|
258
|
+
const qdrantUrl = qdrantHttpPort
|
|
259
|
+
? `http://127.0.0.1:${qdrantHttpPort}`
|
|
260
|
+
: getQdrantUrlEnv() || config.memory.qdrant.url;
|
|
254
261
|
log.info({ qdrantUrl }, "Daemon startup: initializing Qdrant");
|
|
255
262
|
const qdrantManager = new QdrantManager({ url: qdrantUrl });
|
|
256
263
|
try {
|
|
@@ -403,7 +403,7 @@ export function redirectToSecurePrompt(
|
|
|
403
403
|
.then(async (result): Promise<void> => {
|
|
404
404
|
if (!result.value) return;
|
|
405
405
|
|
|
406
|
-
const {
|
|
406
|
+
const { setSecureKeyAsync } = await import("../security/secure-keys.js");
|
|
407
407
|
const { upsertCredentialMetadata } =
|
|
408
408
|
await import("../tools/credentials/metadata-store.js");
|
|
409
409
|
|
|
@@ -435,7 +435,7 @@ export function redirectToSecurePrompt(
|
|
|
435
435
|
);
|
|
436
436
|
} else {
|
|
437
437
|
const key = `credential:${target.service}:${target.field}`;
|
|
438
|
-
const stored =
|
|
438
|
+
const stored = await setSecureKeyAsync(key, result.value);
|
|
439
439
|
if (stored) {
|
|
440
440
|
try {
|
|
441
441
|
upsertCredentialMetadata(target.service, target.field, {});
|