@m1a0rz/agent-identity 0.3.1 → 0.3.3
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-cn.md +19 -9
- package/README.md +22 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -22
- package/dist/src/actions/identity-actions.d.ts +7 -1
- package/dist/src/actions/identity-actions.d.ts.map +1 -1
- package/dist/src/actions/identity-actions.js +89 -36
- package/dist/src/commands/identity-commands.d.ts.map +1 -1
- package/dist/src/commands/identity-commands.js +11 -6
- package/dist/src/hooks/after-tool-call.d.ts +12 -0
- package/dist/src/hooks/after-tool-call.d.ts.map +1 -0
- package/dist/src/hooks/after-tool-call.js +31 -0
- package/dist/src/hooks/before-agent-start.d.ts +3 -3
- package/dist/src/hooks/before-agent-start.d.ts.map +1 -1
- package/dist/src/hooks/before-agent-start.js +7 -21
- package/dist/src/hooks/before-tool-call.d.ts +19 -14
- package/dist/src/hooks/before-tool-call.d.ts.map +1 -1
- package/dist/src/hooks/before-tool-call.js +194 -132
- package/dist/src/hooks/llm-input.js +3 -3
- package/dist/src/hooks/sessions-send-propagation.js +1 -1
- package/dist/src/hooks/sessions-spawn-propagation.js +1 -1
- package/dist/src/hooks/subagent-ended-cleanup.js +2 -2
- package/dist/src/risk/low-risk-tools.d.ts.map +1 -1
- package/dist/src/risk/low-risk-tools.js +0 -2
- package/dist/src/routes/oidc-login.d.ts.map +1 -1
- package/dist/src/routes/oidc-login.js +49 -7
- package/dist/src/services/identity-service.d.ts +3 -2
- package/dist/src/services/identity-service.d.ts.map +1 -1
- package/dist/src/services/identity-service.js +3 -2
- package/dist/src/services/oidc-client.d.ts +25 -8
- package/dist/src/services/oidc-client.d.ts.map +1 -1
- package/dist/src/services/oidc-client.js +55 -1
- package/dist/src/services/session-refresh.d.ts.map +1 -1
- package/dist/src/services/session-refresh.js +11 -2
- package/dist/src/services/tip-acquisition.d.ts +0 -1
- package/dist/src/services/tip-acquisition.d.ts.map +1 -1
- package/dist/src/services/tip-acquisition.js +2 -2
- package/dist/src/services/tip-propagation.d.ts.map +1 -1
- package/dist/src/services/tip-propagation.js +1 -2
- package/dist/src/services/tip-with-refresh.d.ts +1 -1
- package/dist/src/services/tip-with-refresh.d.ts.map +1 -1
- package/dist/src/services/tip-with-refresh.js +3 -5
- package/dist/src/store/credential-env-snapshot.d.ts +38 -0
- package/dist/src/store/credential-env-snapshot.d.ts.map +1 -0
- package/dist/src/store/credential-env-snapshot.js +101 -0
- package/dist/src/store/encryption.d.ts +30 -0
- package/dist/src/store/encryption.d.ts.map +1 -0
- package/dist/src/store/encryption.js +147 -0
- package/dist/src/store/oidc-state-store.d.ts +8 -1
- package/dist/src/store/oidc-state-store.d.ts.map +1 -1
- package/dist/src/store/oidc-state-store.js +3 -1
- package/dist/src/store/{group-sender-store.d.ts → sender-session-store.d.ts} +14 -7
- package/dist/src/store/sender-session-store.d.ts.map +1 -0
- package/dist/src/store/{group-sender-store.js → sender-session-store.js} +41 -12
- package/dist/src/store/session-store.d.ts.map +1 -1
- package/dist/src/store/session-store.js +91 -8
- package/dist/src/store/tip-store.d.ts +11 -6
- package/dist/src/store/tip-store.d.ts.map +1 -1
- package/dist/src/store/tip-store.js +23 -54
- package/dist/src/tools/identity-approve-tool.js +1 -1
- package/dist/src/tools/identity-fetch.d.ts.map +1 -1
- package/dist/src/tools/identity-fetch.js +2 -1
- package/dist/src/tools/identity-list-credentials.d.ts +2 -0
- package/dist/src/tools/identity-list-credentials.d.ts.map +1 -1
- package/dist/src/tools/identity-list-credentials.js +7 -4
- package/dist/src/tools/identity-login.js +1 -1
- package/dist/src/tools/identity-logout.js +1 -1
- package/dist/src/tools/identity-set-binding.js +1 -1
- package/dist/src/tools/identity-status.js +1 -1
- package/dist/src/tools/identity-unset-binding.js +1 -1
- package/dist/src/tools/identity-whoami.js +1 -1
- package/dist/src/utils/derive-session-key.d.ts +34 -4
- package/dist/src/utils/derive-session-key.d.ts.map +1 -1
- package/dist/src/utils/derive-session-key.js +50 -5
- package/package.json +4 -3
- package/skills/SKILL.md +75 -150
- package/dist/src/store/group-sender-store.d.ts.map +0 -1
package/README-cn.md
CHANGED
|
@@ -11,7 +11,9 @@ UserPool OIDC 登录、TIP (Trusted Identity Provider) 令牌(通过 Identity
|
|
|
11
11
|
- **OIDC 登录**:`/identity login` 返回 IdP 授权 URL。用户打开 URL 后,IdP 重定向到 `/identity/oauth/callback`。
|
|
12
12
|
- **TIP 令牌**:当会话中有已登录用户时,`before_agent_start` 钩子会获取 TIP 令牌。
|
|
13
13
|
- **凭据 3LO**:`/identity fetch <provider>` 返回授权 URL。IdP 重定向到 Identity 提供的回调地址(控制台配置)。
|
|
14
|
-
- **凭据绑定**:`/identity set <provider> <envVar>`
|
|
14
|
+
- **凭据绑定**:`/identity set <provider> <envVar>` 将存储的凭据绑定到环境变量。凭据按工具调用粒度安全注入,并发多用户会话之间互相隔离。
|
|
15
|
+
- **加密会话存储**:`sessions.json` 使用 AES-256-GCM 加密存储在磁盘上。旧版明文文件首次加载时自动迁移。
|
|
16
|
+
- **内存 TIP 缓存**:TIP 令牌仅存储在内存中(不持久化到磁盘)。TIP 是短效令牌,可随时从用户 session token 重新获取。
|
|
15
17
|
- **动态 UserPool**:通过 `userPoolName` + `clientName` 解析 OIDC 配置(无需手动配置 clientId)。
|
|
16
18
|
- **凭证加载**:从环境变量、文件或 STS AssumeRole 加载 AK/SK(veadk 风格)。
|
|
17
19
|
|
|
@@ -251,16 +253,24 @@ TIP token 通过 `GetWorkloadAccessTokenForJWT` 获取。工作负载行为:
|
|
|
251
253
|
|
|
252
254
|
## 钩子
|
|
253
255
|
|
|
254
|
-
- **before_agent_start** - 获取 TIP token
|
|
255
|
-
- **subagent_spawned** - 在子 agent 创建时将 TIP
|
|
256
|
-
- **before_tool_call** -
|
|
256
|
+
- **before_agent_start** - 仅为主 agent 获取 TIP token。
|
|
257
|
+
- **subagent_spawned** - 在子 agent 创建时将 TIP 传播到子会话。
|
|
258
|
+
- **before_tool_call** - 群组上下文注入、可选 AuthZ(TIP 检查、CheckPermission、风险审批)、工具调用级凭据注入。
|
|
259
|
+
- **after_tool_call** - 清理工具调用级凭据注入状态。
|
|
257
260
|
|
|
258
261
|
## 数据存储
|
|
259
262
|
|
|
260
263
|
插件数据位于 `~/.openclaw/plugins/identity/`:
|
|
261
264
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
| 文件 | 描述 |
|
|
266
|
+
|------|------|
|
|
267
|
+
| `sessions.json` | 加密存储的 sessionKey → userToken 映射。加载/保存时清理过期条目。 |
|
|
268
|
+
| `credential-env-bindings.json` | 按 session:`{ [sessionKey]: { [provider]: envVar } }` |
|
|
269
|
+
|
|
270
|
+
**仅内存存储(不持久化到磁盘):**
|
|
271
|
+
|
|
272
|
+
| 数据 | 描述 |
|
|
273
|
+
|------|------|
|
|
274
|
+
| TIP 令牌 | sessionKey → TIP token 缓存。短效,按需从 session token 重新获取。 |
|
|
275
|
+
| 凭据 | 按 session(api_key、oauth2)。gateway 重启后丢失;logout 时清除。 |
|
|
276
|
+
| OIDC state | 临时数据,5 分钟 TTL。 |
|
package/README.md
CHANGED
|
@@ -9,11 +9,13 @@ Integrates with [Volcengine Agent Identity and Permission Management](https://ww
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- **OIDC Login**: `/identity login` returns IdP auth URL (no HTTP start endpoint). User opens URL, IdP redirects to `/identity/oauth/callback`.
|
|
12
|
-
- **TIP Token**: `before_agent_start` hook fetches TIP token when session has a logged-in user
|
|
12
|
+
- **TIP Token**: `before_agent_start` hook fetches TIP token when session has a logged-in user.
|
|
13
13
|
- **Credential 3LO**: `/identity fetch <provider>` returns auth URL. IdP redirects to Identity-provided callback (control-plane config).
|
|
14
|
-
- **Credential Binding**: `/identity set <provider> <envVar>` binds stored credential to env var. Credentials are injected
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
14
|
+
- **Credential Binding**: `/identity set <provider> <envVar>` binds stored credential to env var. Credentials are securely injected per-tool-call, isolated between concurrent multi-user sessions.
|
|
15
|
+
- **Encrypted Session Storage**: `sessions.json` is encrypted at rest (AES-256-GCM). Plaintext sessions from older versions are auto-migrated on first load.
|
|
16
|
+
- **In-memory TIP Cache**: TIP tokens are stored only in memory (no disk persistence). They are short-lived and re-obtained from the user's session token on demand.
|
|
17
|
+
- **Dynamic UserPool**: Resolve OIDC config by `userPoolName` + `clientName` (no manual clientId).
|
|
18
|
+
- **Credentials**: Load AK/SK from env, file, or STS AssumeRole (veadk-style).
|
|
17
19
|
|
|
18
20
|
## HTTP Endpoints
|
|
19
21
|
|
|
@@ -251,16 +253,24 @@ Follow-up messages (login success, credential fetch done) are not delivered when
|
|
|
251
253
|
|
|
252
254
|
## Hooks
|
|
253
255
|
|
|
254
|
-
- **before_agent_start** - Fetch TIP token
|
|
255
|
-
- **subagent_spawned** - Propagate TIP to child session on subagent spawn
|
|
256
|
-
- **before_tool_call** -
|
|
256
|
+
- **before_agent_start** - Fetch TIP token for main agent only.
|
|
257
|
+
- **subagent_spawned** - Propagate TIP to child session on subagent spawn.
|
|
258
|
+
- **before_tool_call** - Group context injection, optional AuthZ (TIP check, CheckPermission, risk approval), and per-tool-call credential injection.
|
|
259
|
+
- **after_tool_call** - Clean up per-tool-call credential injection state.
|
|
257
260
|
|
|
258
261
|
## Data Storage
|
|
259
262
|
|
|
260
263
|
Plugin data at `~/.openclaw/plugins/identity/`:
|
|
261
264
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
| File | Description |
|
|
266
|
+
|------|-------------|
|
|
267
|
+
| `sessions.json` | Encrypted sessionKey → userToken mapping. Expired entries pruned on load/save. |
|
|
268
|
+
| `credential-env-bindings.json` | Per-session: `{ [sessionKey]: { [provider]: envVar } }` |
|
|
269
|
+
|
|
270
|
+
**In-memory only (not persisted to disk):**
|
|
271
|
+
|
|
272
|
+
| Data | Description |
|
|
273
|
+
|------|-------------|
|
|
274
|
+
| TIP tokens | sessionKey → TIP token cache. Short-lived, re-obtained from session token on demand. |
|
|
275
|
+
| Credentials | Per-session (api_key, oauth2). Lost on gateway restart; cleared on logout. |
|
|
276
|
+
| OIDC state | Ephemeral, 5 min TTL. |
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAqE7D,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,QA0YtD"}
|
package/dist/index.js
CHANGED
|
@@ -19,9 +19,10 @@ import { createLlmInputHandler } from "./src/hooks/llm-input.js";
|
|
|
19
19
|
import { createSessionsSendPropagationHandler } from "./src/hooks/sessions-send-propagation.js";
|
|
20
20
|
import { createSessionsSpawnPropagationHandler } from "./src/hooks/sessions-spawn-propagation.js";
|
|
21
21
|
import { createSubagentEndedCleanupHandler } from "./src/hooks/subagent-ended-cleanup.js";
|
|
22
|
-
import { setSender, clearSender } from "./src/store/
|
|
23
|
-
import { deriveSessionKey,
|
|
22
|
+
import { setSender, clearSender } from "./src/store/sender-session-store.js";
|
|
23
|
+
import { deriveSessionKey, needsSenderIsolation, } from "./src/utils/derive-session-key.js";
|
|
24
24
|
import { createBeforeToolCallHandler } from "./src/hooks/before-tool-call.js";
|
|
25
|
+
import { createAfterToolCallHandler } from "./src/hooks/after-tool-call.js";
|
|
25
26
|
import * as skillPathStore from "./src/store/skill-path-store.js";
|
|
26
27
|
import { createOIDCCallbackHandler, createOIDCCallbackHandlerLazy, } from "./src/routes/oidc-login.js";
|
|
27
28
|
import { IdentityClient, resolveOIDCConfig, } from "./src/services/identity-client.js";
|
|
@@ -42,7 +43,8 @@ import { createIdentityStatusTool } from "./src/tools/identity-status.js";
|
|
|
42
43
|
import { createIdentityUnsetBindingTool } from "./src/tools/identity-unset-binding.js";
|
|
43
44
|
import { createIdentityWhoamiTool } from "./src/tools/identity-whoami.js";
|
|
44
45
|
import { parseSessionKeyToDeliveryTarget, } from "./src/utils/derive-session-key.js";
|
|
45
|
-
import { logInfo, logWarn } from "./src/utils/logger.js";
|
|
46
|
+
import { logDebug, logInfo, logWarn } from "./src/utils/logger.js";
|
|
47
|
+
import { initEncryptionKey } from "./src/store/encryption.js";
|
|
46
48
|
const PLUGIN_STORE_DIR = "~/.openclaw/plugins/identity";
|
|
47
49
|
/**
|
|
48
50
|
* Whether Identity should be enabled.
|
|
@@ -64,6 +66,7 @@ function hasAnyIdentityConfig(identity) {
|
|
|
64
66
|
export default function register(api) {
|
|
65
67
|
const pluginConfig = (api.pluginConfig ?? {});
|
|
66
68
|
const storeDir = api.resolvePath(PLUGIN_STORE_DIR);
|
|
69
|
+
initEncryptionKey(storeDir);
|
|
67
70
|
const identityCfg = pluginConfig.identity;
|
|
68
71
|
const hasIdentity = hasAnyIdentityConfig(identityCfg);
|
|
69
72
|
const userpool = pluginConfig.userpool;
|
|
@@ -320,7 +323,7 @@ export default function register(api) {
|
|
|
320
323
|
accountId: ctx.accountId,
|
|
321
324
|
config: api.runtime.config.loadConfig(),
|
|
322
325
|
});
|
|
323
|
-
if (!sessionKey || !
|
|
326
|
+
if (!sessionKey || !needsSenderIsolation(sessionKey))
|
|
324
327
|
return;
|
|
325
328
|
setSender(sessionKey, {
|
|
326
329
|
senderId,
|
|
@@ -330,7 +333,7 @@ export default function register(api) {
|
|
|
330
333
|
messageId: metadata?.messageId,
|
|
331
334
|
capturedAt: Date.now(),
|
|
332
335
|
});
|
|
333
|
-
|
|
336
|
+
logDebug(api.logger, `sender captured session=${sessionKey} sender=${senderId}`);
|
|
334
337
|
}, { priority: 200 });
|
|
335
338
|
api.on("session_end", (_event, ctx) => {
|
|
336
339
|
if (ctx.sessionKey)
|
|
@@ -366,10 +369,7 @@ export default function register(api) {
|
|
|
366
369
|
logger: api.logger,
|
|
367
370
|
}));
|
|
368
371
|
}
|
|
369
|
-
const toolCheck = authz?.toolCheck ?? false;
|
|
370
372
|
const skillReadCheck = authz?.skillReadCheck ?? false;
|
|
371
|
-
const requireRiskApproval = authz?.requireRiskApproval ?? false;
|
|
372
|
-
const hasAuthz = toolCheck || skillReadCheck || requireRiskApproval;
|
|
373
373
|
api.on("llm_input", createLlmInputHandler({
|
|
374
374
|
enabled: skillReadCheck,
|
|
375
375
|
logger: api.logger,
|
|
@@ -380,18 +380,19 @@ export default function register(api) {
|
|
|
380
380
|
skillPathStore.clearSessionById(ctx.sessionId);
|
|
381
381
|
});
|
|
382
382
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
383
|
+
// before_tool_call: authz, credential injection, group sender context
|
|
384
|
+
api.on("before_tool_call", createBeforeToolCallHandler({
|
|
385
|
+
storeDir,
|
|
386
|
+
identityClient: hasIdentity ? identityClient : undefined,
|
|
387
|
+
namespaceName: authz?.namespaceName ?? "default",
|
|
388
|
+
logger: api.logger,
|
|
389
|
+
sendToSession,
|
|
390
|
+
authz,
|
|
391
|
+
approvalTtlMs,
|
|
392
|
+
identityService: hasIdentity ? identityService : undefined,
|
|
393
|
+
getOidcConfigForRefresh: getOidcConfigForRefresh ?? undefined,
|
|
394
|
+
configWorkloadName: identityCfg?.workloadName,
|
|
395
|
+
}));
|
|
396
|
+
// Companion after_tool_call: restore env snapshot set by credential injection
|
|
397
|
+
api.on("after_tool_call", createAfterToolCallHandler({ logger: api.logger }));
|
|
397
398
|
}
|
|
@@ -86,7 +86,11 @@ export type ListCredentialsResult = {
|
|
|
86
86
|
hasMore: boolean;
|
|
87
87
|
totalCount?: number;
|
|
88
88
|
};
|
|
89
|
-
export
|
|
89
|
+
export type ListCredentialsFilter = {
|
|
90
|
+
name?: string;
|
|
91
|
+
flow?: string;
|
|
92
|
+
};
|
|
93
|
+
export declare function runListCredentials(deps: IdentityActionsDeps, sessionKey: string, page?: number, filter?: ListCredentialsFilter): Promise<ListCredentialsResult>;
|
|
90
94
|
export type ListTipsResult = {
|
|
91
95
|
tips: Array<{
|
|
92
96
|
sessionKey: string;
|
|
@@ -124,6 +128,8 @@ export declare function runFetch(deps: IdentityActionsDeps, sessionKey: string,
|
|
|
124
128
|
scopes?: string[];
|
|
125
129
|
deliveryTarget?: SessionKeyDeliveryTarget | null;
|
|
126
130
|
config?: OpenClawConfig;
|
|
131
|
+
/** "tool" = agent tool call, "command" = slash command. Defaults to "command". */
|
|
132
|
+
source?: "tool" | "command";
|
|
127
133
|
}): Promise<FetchResult>;
|
|
128
134
|
export type SetBindingResult = {
|
|
129
135
|
ok: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-actions.d.ts","sourceRoot":"","sources":["../../../src/actions/identity-actions.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"identity-actions.d.ts","sourceRoot":"","sources":["../../../src/actions/identity-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { logDebug, logInfo, logWarn } from "../utils/logger.js";
|
|
17
17
|
import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
|
|
18
|
-
import { fetchOIDCDiscovery, buildAuthorizationUrl, generateState, } from "../services/oidc-client.js";
|
|
18
|
+
import { fetchOIDCDiscovery, buildAuthorizationUrl, generateState, generatePKCE, generateNonce, } from "../services/oidc-client.js";
|
|
19
19
|
import { loadCredentialEnvBindings, loadAllCredentialEnvBindings, setCredentialEnvBinding, deleteCredentialEnvBinding, } from "../store/credential-env-bindings.js";
|
|
20
20
|
import { loadCredentials, setCredential, getCredential, deleteCredentialsForSession, } from "../store/credential-store.js";
|
|
21
21
|
import { getSession, deleteSession } from "../store/session-store.js";
|
|
22
22
|
import { createState } from "../store/oidc-state-store.js";
|
|
23
|
-
import {
|
|
23
|
+
import { getAllTIPTokens, deleteTIPToken } from "../store/tip-store.js";
|
|
24
24
|
import { extractDelegationChainFromJwt } from "../utils/auth.js";
|
|
25
25
|
import { resolveAgentId, } from "../utils/derive-session-key.js";
|
|
26
26
|
function inferFlowFromProvider(info) {
|
|
@@ -32,6 +32,7 @@ function inferFlowFromProvider(info) {
|
|
|
32
32
|
}
|
|
33
33
|
const OAUTH_POLL_INTERVAL_MS = 2500;
|
|
34
34
|
const OAUTH_POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
35
|
+
const OAUTH_QUICK_RETRY_MS = 1000;
|
|
35
36
|
function sleep(ms) {
|
|
36
37
|
return new Promise((r) => setTimeout(r, ms));
|
|
37
38
|
}
|
|
@@ -133,13 +134,18 @@ export async function runLogin(deps, sessionKey, options) {
|
|
|
133
134
|
const oidcConfig = await getOidcConfig();
|
|
134
135
|
const discovery = await fetchOIDCDiscovery(oidcConfig.discoveryUrl);
|
|
135
136
|
const state = await generateState();
|
|
136
|
-
|
|
137
|
+
const { codeVerifier, codeChallenge } = await generatePKCE();
|
|
138
|
+
const nonce = await generateNonce();
|
|
139
|
+
await createState(storeDir, sessionKey, "", state, deliveryTarget, { codeVerifier, nonce });
|
|
137
140
|
const authUrl = buildAuthorizationUrl({
|
|
138
141
|
authorizationEndpoint: discovery.authorization_endpoint,
|
|
139
142
|
clientId: oidcConfig.clientId,
|
|
140
143
|
redirectUri: oidcConfig.callbackUrl,
|
|
141
144
|
scope: oidcConfig.scope ?? "openid profile email offline_access",
|
|
142
145
|
state,
|
|
146
|
+
codeChallenge,
|
|
147
|
+
codeChallengeMethod: "S256",
|
|
148
|
+
nonce,
|
|
143
149
|
});
|
|
144
150
|
logInfo(logger, `login returning IdP URL for sessionKey=${sessionKey.slice(0, 24)}...`);
|
|
145
151
|
return { kind: "auth_url", authUrl };
|
|
@@ -156,30 +162,40 @@ export async function runLogout(deps, sessionKey) {
|
|
|
156
162
|
const { storeDir, logger } = deps;
|
|
157
163
|
logDebug(logger, `logout sessionKey=${sessionKey.slice(0, 24)}...`);
|
|
158
164
|
await deleteSession(storeDir, sessionKey);
|
|
159
|
-
|
|
160
|
-
delete tokens[sessionKey];
|
|
161
|
-
await saveTIPTokens(storeDir, tokens);
|
|
165
|
+
deleteTIPToken(sessionKey);
|
|
162
166
|
deleteCredentialsForSession(storeDir, sessionKey);
|
|
163
167
|
return { ok: true };
|
|
164
168
|
}
|
|
165
169
|
const LIST_PAGE_SIZE = 10;
|
|
166
|
-
export async function runListCredentials(deps, sessionKey, page = 1) {
|
|
167
|
-
const { storeDir, identityClient, logger } = deps;
|
|
170
|
+
export async function runListCredentials(deps, sessionKey, page = 1, filter) {
|
|
171
|
+
const { storeDir, identityClient, identityService, logger } = deps;
|
|
168
172
|
const creds = await loadCredentials(storeDir, sessionKey);
|
|
169
173
|
const bindings = await loadCredentialEnvBindings(storeDir, sessionKey);
|
|
170
174
|
let providers = [];
|
|
171
175
|
let totalCount = 0;
|
|
172
176
|
if (identityClient) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
PageSize: LIST_PAGE_SIZE,
|
|
177
|
-
});
|
|
178
|
-
providers = result.CredentialProviders ?? result.Data ?? [];
|
|
179
|
-
totalCount = result.TotalCount ?? 0;
|
|
177
|
+
const session = await getSession(storeDir, sessionKey);
|
|
178
|
+
if (!session || !identityService.parseUserToken(session.userToken).valid) {
|
|
179
|
+
logWarn(logger, `list-credentials: no valid session for key=${sessionKey.slice(0, 24)}...`);
|
|
180
180
|
}
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
else {
|
|
182
|
+
try {
|
|
183
|
+
const apiFilter = {};
|
|
184
|
+
if (filter?.name)
|
|
185
|
+
apiFilter.Name = filter.name;
|
|
186
|
+
if (filter?.flow)
|
|
187
|
+
apiFilter.Flow = filter.flow;
|
|
188
|
+
const result = await identityClient.listCredentialProviders({
|
|
189
|
+
PageNumber: page,
|
|
190
|
+
PageSize: LIST_PAGE_SIZE,
|
|
191
|
+
...(Object.keys(apiFilter).length > 0 ? { Filter: apiFilter } : {}),
|
|
192
|
+
});
|
|
193
|
+
providers = result.CredentialProviders ?? result.Data ?? [];
|
|
194
|
+
totalCount = result.TotalCount ?? 0;
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
logWarn(logger, `list-credentials API error: ${String(e)}`);
|
|
198
|
+
}
|
|
183
199
|
}
|
|
184
200
|
}
|
|
185
201
|
const providerNames = new Set(providers.map((p) => p.Name));
|
|
@@ -232,7 +248,7 @@ export async function runListCredentials(deps, sessionKey, page = 1) {
|
|
|
232
248
|
}
|
|
233
249
|
export async function runListTips(deps) {
|
|
234
250
|
const { storeDir } = deps;
|
|
235
|
-
const tokens =
|
|
251
|
+
const tokens = getAllTIPTokens();
|
|
236
252
|
const bindingsBySession = await loadAllCredentialEnvBindings(storeDir);
|
|
237
253
|
const now = Date.now();
|
|
238
254
|
const tips = [];
|
|
@@ -292,27 +308,10 @@ export async function runConfig(deps) {
|
|
|
292
308
|
}
|
|
293
309
|
export async function runFetch(deps, sessionKey, params) {
|
|
294
310
|
const { identityClient, storeDir, sendCredentialMessage, logger } = deps;
|
|
295
|
-
const { provider, flow, flowExplicit, redirectUrl, scopes, deliveryTarget, config } = params;
|
|
311
|
+
const { provider, flow, flowExplicit, redirectUrl, scopes, deliveryTarget, config, source = "command" } = params;
|
|
296
312
|
if (!identityClient) {
|
|
297
313
|
return { kind: "error", message: "Identity service not configured. Cannot add credentials." };
|
|
298
314
|
}
|
|
299
|
-
let effectiveFlow = flow;
|
|
300
|
-
if (!flowExplicit) {
|
|
301
|
-
try {
|
|
302
|
-
const listResult = await identityClient.listCredentialProviders({
|
|
303
|
-
PageNumber: 1,
|
|
304
|
-
PageSize: 20,
|
|
305
|
-
Filter: { Name: provider },
|
|
306
|
-
});
|
|
307
|
-
const list = listResult.CredentialProviders ?? listResult.Data ?? [];
|
|
308
|
-
const info = list.find((p) => p.Name === provider);
|
|
309
|
-
if (info)
|
|
310
|
-
effectiveFlow = inferFlowFromProvider(info);
|
|
311
|
-
}
|
|
312
|
-
catch {
|
|
313
|
-
// keep default
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
315
|
const ctxAgentId = resolveAgentId({ sessionKey, config: config });
|
|
317
316
|
const tipRefreshOptions = deps.getOidcConfigForRefresh
|
|
318
317
|
? {
|
|
@@ -330,6 +329,23 @@ export async function runFetch(deps, sessionKey, params) {
|
|
|
330
329
|
message: "Login first with `/identity login` to establish identity before adding credentials.",
|
|
331
330
|
};
|
|
332
331
|
}
|
|
332
|
+
let effectiveFlow = flow;
|
|
333
|
+
if (!flowExplicit) {
|
|
334
|
+
try {
|
|
335
|
+
const listResult = await identityClient.listCredentialProviders({
|
|
336
|
+
PageNumber: 1,
|
|
337
|
+
PageSize: 20,
|
|
338
|
+
Filter: { Name: provider },
|
|
339
|
+
});
|
|
340
|
+
const list = listResult.CredentialProviders ?? listResult.Data ?? [];
|
|
341
|
+
const info = list.find((p) => p.Name === provider);
|
|
342
|
+
if (info)
|
|
343
|
+
effectiveFlow = inferFlowFromProvider(info);
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// keep default
|
|
347
|
+
}
|
|
348
|
+
}
|
|
333
349
|
try {
|
|
334
350
|
if (effectiveFlow === "apikey") {
|
|
335
351
|
const result = await identityClient.getResourceApiKey({
|
|
@@ -361,6 +377,43 @@ export async function runFetch(deps, sessionKey, params) {
|
|
|
361
377
|
return { kind: "success", message: `✓ Credential for \`${provider}\` added (direct token).` };
|
|
362
378
|
}
|
|
363
379
|
if (oauthResult.authorizationUrl) {
|
|
380
|
+
// Quick retry: the server may have a cached token that wasn't ready on
|
|
381
|
+
// the first call (e.g. user previously authorized). A short pause then
|
|
382
|
+
// re-check avoids unnecessary polling / returning authUrl to the user.
|
|
383
|
+
await sleep(OAUTH_QUICK_RETRY_MS);
|
|
384
|
+
try {
|
|
385
|
+
const retry = await identityClient.getResourceOauth2Token({
|
|
386
|
+
providerName: provider,
|
|
387
|
+
identityToken: tip.token,
|
|
388
|
+
flow: oauth2Flow,
|
|
389
|
+
redirectUrl,
|
|
390
|
+
scopes: scopes?.length ? scopes : undefined,
|
|
391
|
+
});
|
|
392
|
+
if (retry.accessToken) {
|
|
393
|
+
await setCredential(storeDir, sessionKey, provider, {
|
|
394
|
+
type: "oauth2",
|
|
395
|
+
status: "authenticated",
|
|
396
|
+
accessToken: retry.accessToken,
|
|
397
|
+
expiresAt: Date.now() + 3600 * 1000,
|
|
398
|
+
});
|
|
399
|
+
return { kind: "success", message: `✓ Credential for \`${provider}\` added (cached token).` };
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
// quick retry failed — proceed with authUrl flow
|
|
404
|
+
}
|
|
405
|
+
if (source === "tool") {
|
|
406
|
+
// Agent tool context: return authUrl for the agent to display.
|
|
407
|
+
// No background polling — the agent should re-call identity_fetch
|
|
408
|
+
// after the user completes authorization in the browser.
|
|
409
|
+
logInfo(logger, `fetch returning auth URL for provider=${provider} (tool, no poll)`);
|
|
410
|
+
return {
|
|
411
|
+
kind: "auth_url",
|
|
412
|
+
authUrl: oauthResult.authorizationUrl,
|
|
413
|
+
message: `Open this URL to authorize \`${provider}\`. After you complete authorization in the browser, tell me and I will fetch the credential.`,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
// Slash command context: start background polling and notify via channel message.
|
|
364
417
|
logInfo(logger, `fetch returning auth URL for provider=${provider}, starting poll`);
|
|
365
418
|
const target = deliveryTarget ?? sessionKey;
|
|
366
419
|
pollOAuthAndNotify({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-commands.d.ts","sourceRoot":"","sources":["../../../src/commands/identity-commands.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAUL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACf,MAAM,gCAAgC,CAAC;AAYxC,YAAY,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC;AAEhD,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"identity-commands.d.ts","sourceRoot":"","sources":["../../../src/commands/identity-commands.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAUL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACf,MAAM,gCAAgC,CAAC;AAYxC,YAAY,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC;AAEhD,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAyoBvD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,oBAAoB;;;;;mBAlf3C,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EA2fpE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB;;;;;mBA9frC,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EAugBpE"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { runStatus, runLogin, runLogout, runListCredentials, runListTips, runConfig, runFetch, runSetBinding, runUnsetBinding, } from "../actions/identity-actions.js";
|
|
17
17
|
import { deriveSessionKey, deriveDeliveryTargetFromContext, } from "../utils/derive-session-key.js";
|
|
18
|
-
import { buildEffectiveSessionKey } from "../store/
|
|
18
|
+
import { buildEffectiveSessionKey } from "../store/sender-session-store.js";
|
|
19
19
|
import { logDebug } from "../utils/logger.js";
|
|
20
20
|
import { diagnoseRisk } from "../risk/diagnose-risk.js";
|
|
21
21
|
import { getRiskPatterns } from "../risk/classify-risk.js";
|
|
@@ -88,7 +88,7 @@ function parseSubcommand(args) {
|
|
|
88
88
|
rest: raw.slice(space + 1).trim(),
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
-
/** Parse list args: optional page. E.g. "list", "list 2", "list --
|
|
91
|
+
/** Parse list args: optional page and filters. E.g. "list", "list 2", "list --name=github --flow=M2M". */
|
|
92
92
|
function parseListArgs(rest) {
|
|
93
93
|
const parts = rest.split(/\s+/).filter(Boolean);
|
|
94
94
|
const flags = {};
|
|
@@ -104,7 +104,11 @@ function parseListArgs(rest) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
const page = flags.page ? parseInt(flags.page, 10) : positional[0];
|
|
107
|
-
return {
|
|
107
|
+
return {
|
|
108
|
+
page: Number.isFinite(page) && page >= 1 ? page : 1,
|
|
109
|
+
name: flags.name || undefined,
|
|
110
|
+
flow: flags.flow || undefined,
|
|
111
|
+
};
|
|
108
112
|
}
|
|
109
113
|
/** Parse fetch args: provider and flags (--flow, --redirectUrl, --scopes). flow can be omitted to auto-infer from provider. */
|
|
110
114
|
function parseFetchArgs(rest) {
|
|
@@ -165,7 +169,7 @@ function createIdentityHandler(deps) {
|
|
|
165
169
|
config: ctx.config,
|
|
166
170
|
});
|
|
167
171
|
const sessionKey = baseSessionKey
|
|
168
|
-
? buildEffectiveSessionKey(baseSessionKey, ctx.senderId)
|
|
172
|
+
? buildEffectiveSessionKey(baseSessionKey, ctx.senderId, ctx.channel)
|
|
169
173
|
: null;
|
|
170
174
|
const needsSession = [
|
|
171
175
|
"login",
|
|
@@ -331,8 +335,9 @@ async function handleLogout(deps, sessionKey) {
|
|
|
331
335
|
return { text: "✓ Logged out." };
|
|
332
336
|
}
|
|
333
337
|
async function handleListCredentials(deps, sessionKey, rest) {
|
|
334
|
-
const { page } = parseListArgs(rest);
|
|
335
|
-
const
|
|
338
|
+
const { page, name, flow } = parseListArgs(rest);
|
|
339
|
+
const filter = name || flow ? { name, flow } : undefined;
|
|
340
|
+
const result = await runListCredentials(deps, sessionKey, page, filter);
|
|
336
341
|
const lines = [];
|
|
337
342
|
if (result.providers.length > 0) {
|
|
338
343
|
lines.push(`**Available (page ${result.page}):**`);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type PluginLogger } from "../utils/logger.js";
|
|
2
|
+
export type AfterToolCallDeps = {
|
|
3
|
+
logger?: PluginLogger;
|
|
4
|
+
};
|
|
5
|
+
export declare function createAfterToolCallHandler(deps: AfterToolCallDeps): (event: {
|
|
6
|
+
toolName: string;
|
|
7
|
+
runId?: string;
|
|
8
|
+
toolCallId?: string;
|
|
9
|
+
}, _ctx: {
|
|
10
|
+
sessionKey?: string;
|
|
11
|
+
}) => void;
|
|
12
|
+
//# sourceMappingURL=after-tool-call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"after-tool-call.d.ts","sourceRoot":"","sources":["../../../src/hooks/after-tool-call.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,iBAAiB,IAI9D,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EAChE,MAAM;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAC5B,IAAI,CASR"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* after_tool_call hook: restore process.env snapshot and release per-key locks
|
|
18
|
+
* set by before_tool_call credential injection.
|
|
19
|
+
*/
|
|
20
|
+
import { hasSnapshot, restoreEnvSnapshot } from "../store/credential-env-snapshot.js";
|
|
21
|
+
import { logDebug } from "../utils/logger.js";
|
|
22
|
+
export function createAfterToolCallHandler(deps) {
|
|
23
|
+
const { logger } = deps;
|
|
24
|
+
return (event, _ctx) => {
|
|
25
|
+
if (!hasSnapshot(event.runId, event.toolCallId))
|
|
26
|
+
return;
|
|
27
|
+
logDebug(logger, `restoring env snapshot for tool=${event.toolName} ` +
|
|
28
|
+
`run=${event.runId ?? "?"} toolCallId=${event.toolCallId ?? "?"}`);
|
|
29
|
+
restoreEnvSnapshot(event.runId, event.toolCallId);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* before_agent_start hook: fetch TIP token for main agent only.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* 3. Main: getOrRefreshTIPToken (fetches TIP, refreshes userToken if expired)
|
|
3
|
+
* Credential env injection is handled per-tool-call in before_tool_call
|
|
4
|
+
* to avoid process.env race conditions between concurrent runs.
|
|
6
5
|
*/
|
|
7
6
|
import type { IdentityService } from "../services/identity-service.js";
|
|
8
7
|
import type { OIDCConfigForRefresh } from "../services/session-refresh.js";
|
|
@@ -13,6 +12,7 @@ export type BeforeAgentStartDeps = {
|
|
|
13
12
|
getOidcConfigForRefresh?: () => Promise<OIDCConfigForRefresh>;
|
|
14
13
|
logger: {
|
|
15
14
|
info?: (msg: string) => void;
|
|
15
|
+
debug?: (msg: string) => void;
|
|
16
16
|
warn?: (msg: string) => void;
|
|
17
17
|
};
|
|
18
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"before-agent-start.d.ts","sourceRoot":"","sources":["../../../src/hooks/before-agent-start.ts"],"names":[],"mappings":"AAgBA
|
|
1
|
+
{"version":3,"file":"before-agent-start.d.ts","sourceRoot":"","sources":["../../../src/hooks/before-agent-start.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAM3E,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACvG,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,oBAAoB,IAWpE,QAAQ;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,EAChD,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAC7C,OAAO,CAAC;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuB/C"}
|
|
@@ -14,15 +14,11 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
|
|
17
|
-
import { logWarn } from "../utils/logger.js";
|
|
18
|
-
import { loadCredentialEnvBindings } from "../store/credential-env-bindings.js";
|
|
19
|
-
import { getCredential, resolveCredentialValue } from "../store/credential-store.js";
|
|
17
|
+
import { logDebug, logWarn } from "../utils/logger.js";
|
|
20
18
|
import { isSubagentSessionKey } from "../utils/derive-session-key.js";
|
|
21
|
-
import { resolveEffectiveSessionKey } from "../store/
|
|
19
|
+
import { resolveEffectiveSessionKey } from "../store/sender-session-store.js";
|
|
22
20
|
export function createBeforeAgentStartHandler(deps) {
|
|
23
21
|
const { storeDir, identityService, configWorkloadName, getOidcConfigForRefresh, logger } = deps;
|
|
24
|
-
// Always pass identityService so we can try fetch with session.userToken when TIP is missing/expired.
|
|
25
|
-
// getOidcConfigForRefresh is only needed for refresh when userToken itself has expired.
|
|
26
22
|
const tipRefreshOptions = {
|
|
27
23
|
identityService,
|
|
28
24
|
getOidcConfigForRefresh,
|
|
@@ -36,27 +32,17 @@ export function createBeforeAgentStartHandler(deps) {
|
|
|
36
32
|
if (isSubagentSessionKey(sessionKey))
|
|
37
33
|
return;
|
|
38
34
|
const effectiveKey = resolveEffectiveSessionKey(sessionKey);
|
|
39
|
-
|
|
40
|
-
const bindings = await loadCredentialEnvBindings(storeDir, effectiveKey);
|
|
41
|
-
for (const [provider, envVar] of Object.entries(bindings)) {
|
|
42
|
-
const cred = await getCredential(storeDir, effectiveKey, provider);
|
|
43
|
-
const value = cred ? resolveCredentialValue(cred) : undefined;
|
|
44
|
-
if (value)
|
|
45
|
-
process.env[envVar] = value;
|
|
46
|
-
else
|
|
47
|
-
delete process.env[envVar];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
/* best-effort */
|
|
52
|
-
}
|
|
35
|
+
logDebug(logger, `before_agent_start: fetching TIP for key=${effectiveKey}`);
|
|
53
36
|
try {
|
|
54
37
|
const tip = await getOrRefreshTIPToken(storeDir, effectiveKey, {
|
|
55
38
|
...tipRefreshOptions,
|
|
56
39
|
ctxAgentId: ctx.agentId,
|
|
57
40
|
});
|
|
58
|
-
if (!tip)
|
|
41
|
+
if (!tip) {
|
|
42
|
+
logDebug(logger, `before_agent_start: no TIP available for key=${effectiveKey}`);
|
|
59
43
|
return;
|
|
44
|
+
}
|
|
45
|
+
logDebug(logger, `before_agent_start: TIP ready for key=${effectiveKey} sub=${tip.sub}`);
|
|
60
46
|
}
|
|
61
47
|
catch (err) {
|
|
62
48
|
logWarn(logger, `failed to get TIP for ${effectiveKey}: ${String(err)}`);
|