@team-ai/teamai-cli 0.30.3 → 0.30.5

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/dist/cli.js CHANGED
@@ -61601,6 +61601,7 @@ __export(local_agent_exports, {
61601
61601
  import fs35 from "fs";
61602
61602
  import os9 from "os";
61603
61603
  import path76 from "path";
61604
+ import { createHash as createHash10 } from "crypto";
61604
61605
  import { execFile as execFile4 } from "child_process";
61605
61606
  import { promisify as promisify3 } from "util";
61606
61607
  function modelAgentKind(tool) {
@@ -61640,6 +61641,65 @@ async function acquireLocalAgentLock(waitMs = 0) {
61640
61641
  function getConfigPath() {
61641
61642
  return path76.join(getLocalAgentHome(), CONFIG_FILE);
61642
61643
  }
61644
+ function credentialKey(context) {
61645
+ const agentType = normalizeAgentType(context.tool ?? "workbuddy");
61646
+ return `${agentType}:${resolveLocalAgentId(context)}`;
61647
+ }
61648
+ function credentialEndpointDir(endpoint) {
61649
+ const endpointDigest = createHash10("sha256").update(normalizeEndpoint(endpoint)).digest("hex").slice(0, 16);
61650
+ return path76.join("credentials", endpointDigest);
61651
+ }
61652
+ function credentialRelativePath(endpoint, key) {
61653
+ const digest = createHash10("sha256").update(key).digest("hex");
61654
+ return path76.join(credentialEndpointDir(endpoint), `${digest}.token`);
61655
+ }
61656
+ async function readAgentCredential(config, context) {
61657
+ const entry = context ? config.agentCredentials?.[credentialKey(context)] : void 0;
61658
+ const candidate = entry ?? (!context ? Object.values(config.agentCredentials ?? {})[0] : void 0);
61659
+ if (candidate) {
61660
+ const credentialRoot = path76.join(getLocalAgentHome(), "credentials");
61661
+ const credentialPath = path76.resolve(getLocalAgentHome(), candidate.file);
61662
+ if (!credentialPath.startsWith(credentialRoot + path76.sep)) {
61663
+ throw new Error("HTTP local agent credential path is outside its credential directory.");
61664
+ }
61665
+ const value = (await readFileSafe(credentialPath))?.trim();
61666
+ if (value) return value;
61667
+ throw new Error(`HTTP local agent credential is missing for ${candidate.agentType}. Re-run HTTP setup for that agent.`);
61668
+ }
61669
+ const legacy = config.token?.trim();
61670
+ if (legacy) return legacy;
61671
+ if (context && Object.keys(config.agentCredentials ?? {}).length > 0) {
61672
+ throw new Error(
61673
+ `No HTTP credential is configured for ${normalizeAgentType(context.tool ?? "workbuddy")}. Re-run HTTP setup with this agent and its token.`
61674
+ );
61675
+ }
61676
+ return void 0;
61677
+ }
61678
+ async function writeAgentCredential(config, context, token) {
61679
+ const key = credentialKey(context);
61680
+ const tool = context.tool ?? "workbuddy";
61681
+ const agentType = normalizeAgentType(tool);
61682
+ const localAgentId = resolveLocalAgentId(context);
61683
+ const file = credentialRelativePath(config.endpoint, key);
61684
+ await ensureDir(path76.join(getLocalAgentHome(), path76.dirname(file)));
61685
+ await writeTokenFile(path76.join(getLocalAgentHome(), file), token);
61686
+ config.agentCredentials = {
61687
+ ...config.agentCredentials,
61688
+ [key]: {
61689
+ tool,
61690
+ agentType,
61691
+ localAgentId,
61692
+ file,
61693
+ createdAt: config.agentCredentials?.[key]?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
61694
+ }
61695
+ };
61696
+ return key;
61697
+ }
61698
+ async function promoteLegacyCredential(config, context) {
61699
+ if (!config.token || config.agentCredentials?.[credentialKey(context)]) return;
61700
+ await writeAgentCredential(config, context, config.token);
61701
+ await saveLocalAgentConfig(config);
61702
+ }
61643
61703
  function getManifestPath() {
61644
61704
  return path76.join(getLocalAgentHome(), MANIFEST_FILE);
61645
61705
  }
@@ -61809,7 +61869,7 @@ async function canonicalizeWorkspacePath(value) {
61809
61869
  }
61810
61870
  function mergeWorkspaceBindings(existing2, incoming, canonicalKey) {
61811
61871
  if (!existing2) return incoming;
61812
- const rank2 = (binding) => (binding.projectId ?? 0) > 0 ? 2 : binding.projectId === 0 ? 1 : 0;
61872
+ const rank2 = (binding) => binding.projectId > 0 ? 2 : binding.projectName === "__skipped__" ? 1 : 0;
61813
61873
  const existingRank = rank2(existing2);
61814
61874
  const incomingRank = rank2(incoming);
61815
61875
  const existingReal = existingRank === 2;
@@ -61821,6 +61881,8 @@ function mergeWorkspaceBindings(existing2, incoming, canonicalKey) {
61821
61881
  }
61822
61882
  const base = existingRank >= incomingRank ? { ...existing2 } : { ...incoming };
61823
61883
  base.enrolledAt ??= existing2.enrolledAt ?? incoming.enrolledAt;
61884
+ const agentKeys = [.../* @__PURE__ */ new Set([...existing2.agentKeys ?? [], ...incoming.agentKeys ?? []])];
61885
+ if (agentKeys.length > 0) base.agentKeys = agentKeys;
61824
61886
  if (!base.ideType) base.ideType = existing2.ideType ?? incoming.ideType;
61825
61887
  return base;
61826
61888
  }
@@ -61828,80 +61890,14 @@ function isWorkspaceEnrolled(config, workspacePath) {
61828
61890
  return config.workspaceBindings[workspacePath]?.enrolledAt !== void 0;
61829
61891
  }
61830
61892
  function hasWorkspaceBindingDecision(binding) {
61831
- return binding?.projectId !== void 0;
61832
- }
61833
- async function inferWorkspaceEnrollments(config) {
61834
- const inferred = {};
61835
- const add = async (workspacePath, enrolledAt = config.createdAt, ideType) => {
61836
- const canonical2 = await canonicalizeWorkspacePath(workspacePath);
61837
- inferred[canonical2] ??= { enrolledAt, ...ideType ? { ideType } : {} };
61838
- };
61839
- const inheritedAnchors = /* @__PURE__ */ new Set();
61840
- const missingBindings = /* @__PURE__ */ new Set();
61841
- const anchorsByPath = /* @__PURE__ */ new Map();
61842
- const positiveBindings = Object.entries(config.workspaceBindings).filter(([, binding]) => (binding.projectId ?? 0) > 0);
61843
- for (const [workspacePath] of positiveBindings) {
61844
- try {
61845
- await fs35.promises.stat(workspacePath);
61846
- } catch (error2) {
61847
- if (error2.code === "ENOENT") missingBindings.add(workspacePath);
61848
- continue;
61849
- }
61850
- const anchors = await resolveAnchors(workspacePath);
61851
- anchorsByPath.set(workspacePath, anchors);
61852
- if (anchors && anchors.projectAnchor !== anchors.workspaceRoot) {
61853
- inheritedAnchors.add(await canonicalizeWorkspacePath(anchors.projectAnchor));
61854
- }
61855
- }
61856
- const bindingTransactions = /* @__PURE__ */ new Map();
61857
- for (const [workspacePath, binding] of positiveBindings) {
61858
- if (!binding.boundAt) continue;
61859
- const key = `${binding.projectId}\0${binding.boundAt}`;
61860
- const paths = bindingTransactions.get(key) ?? [];
61861
- paths.push(workspacePath);
61862
- bindingTransactions.set(key, paths);
61863
- }
61864
- for (const paths of bindingTransactions.values()) {
61865
- if (!paths.some((workspacePath) => missingBindings.has(workspacePath))) continue;
61866
- for (const workspacePath of paths) {
61867
- if (missingBindings.has(workspacePath)) continue;
61868
- const anchors = anchorsByPath.get(workspacePath);
61869
- if (anchors && anchors.projectAnchor === anchors.workspaceRoot) inheritedAnchors.add(workspacePath);
61870
- }
61871
- }
61872
- for (const [workspacePath, binding] of Object.entries(config.workspaceBindings)) {
61873
- if ((binding.projectId ?? 0) > 0 && !missingBindings.has(workspacePath) && !inheritedAnchors.has(workspacePath)) {
61874
- await add(workspacePath, binding.boundAt ?? config.createdAt, binding.ideType);
61875
- }
61876
- }
61877
- const manifest = await readJson(getManifestPath());
61878
- for (const key of Object.keys(manifest?.scopes ?? {})) {
61879
- const parsed = parseScopeKey(key);
61880
- if (parsed.scope === "project" && parsed.workspacePath) await add(parsed.workspacePath);
61881
- }
61882
- const models = await readJson(getModelManifestPath());
61883
- for (const workspacePath of Object.keys(models?.workspaceModels ?? {})) await add(workspacePath);
61884
- const projectHomes = await listDirs(path76.join(getTeamaiHomePath(), "projects")).catch(() => []);
61885
- const mcpOwnershipFiles = [
61886
- path76.join(getTeamaiHomePath(), "managed-local-mcp.json"),
61887
- ...projectHomes.map((dir) => path76.join(getTeamaiHomePath(), "projects", dir, "managed-local-mcp.json"))
61888
- ];
61889
- for (const file of mcpOwnershipFiles) {
61890
- const records2 = await readJson(file);
61891
- for (const [key, owned] of Object.entries(records2 ?? {})) {
61892
- const match = /^[^:]+:local:(.+)$/.exec(key);
61893
- if (match?.[1] && owned.length > 0) await add(match[1]);
61894
- }
61895
- }
61896
- return inferred;
61893
+ return Boolean(binding && (binding.projectId > 0 || binding.projectName === "__skipped__"));
61897
61894
  }
61898
61895
  async function loadLocalAgentConfig(options2 = {}) {
61899
61896
  const fileConfig = await readJson(getConfigPath());
61900
61897
  if (fileConfig && "disabled" in fileConfig && fileConfig.disabled === true) return null;
61901
61898
  if (fileConfig && "endpoint" in fileConfig && fileConfig.endpoint) {
61902
- const { workspaceEnrollments: legacyEnrollments, ...storedConfig } = fileConfig;
61903
61899
  const config = {
61904
- ...storedConfig,
61900
+ ...fileConfig,
61905
61901
  endpoint: normalizeEndpoint(fileConfig.endpoint),
61906
61902
  workspaceBindings: fileConfig.workspaceBindings ?? {}
61907
61903
  };
@@ -61929,15 +61925,8 @@ async function loadLocalAgentConfig(options2 = {}) {
61929
61925
  }
61930
61926
  config.workspaceBindings = migrated;
61931
61927
  if (fileConfig.workspaceStateVersion !== 1) {
61932
- const enrollments = legacyEnrollments ?? await inferWorkspaceEnrollments(config);
61933
- for (const [wsPath, enrollment] of Object.entries(enrollments)) {
61934
- const canonicalKey = await canonicalizeWorkspacePath(wsPath);
61935
- const binding = config.workspaceBindings[canonicalKey] ?? {};
61936
- config.workspaceBindings[canonicalKey] = {
61937
- ...binding,
61938
- enrolledAt: binding.enrolledAt ?? enrollment.enrolledAt,
61939
- ideType: enrollment.ideType ?? binding.ideType
61940
- };
61928
+ for (const binding of Object.values(config.workspaceBindings)) {
61929
+ if (binding.projectId > 0) binding.enrolledAt ??= binding.boundAt;
61941
61930
  }
61942
61931
  config.workspaceStateVersion = 1;
61943
61932
  migrationChanged = true;
@@ -62158,12 +62147,13 @@ async function workspaceCacheGitignore(workspacePath, repoPath) {
62158
62147
  const file = path76.join(teamaiDir, ".gitignore");
62159
62148
  return await readFileSafe(file) === WORKSPACE_CACHE_GITIGNORE ? file : null;
62160
62149
  }
62161
- function authHeaders5(config, json = true) {
62150
+ async function authHeaders5(config, json = true, context) {
62162
62151
  const headers = {};
62163
62152
  if (json) headers["Content-Type"] = "application/json";
62164
- if (config.token) {
62165
- headers.Authorization = `Bearer ${config.token}`;
62166
- headers["X-API-Token"] = config.token;
62153
+ const token = await readAgentCredential(config, context);
62154
+ if (token) {
62155
+ headers.Authorization = `Bearer ${token}`;
62156
+ headers["X-API-Token"] = token;
62167
62157
  }
62168
62158
  return headers;
62169
62159
  }
@@ -62171,7 +62161,7 @@ async function localAgentFetch(config, tag, route, init3, opts) {
62171
62161
  const method = init3?.method ?? "GET";
62172
62162
  const url = `${config.endpoint}${resolveRoute(config, route)}`;
62173
62163
  const headers = {
62174
- ...authHeaders5(config, init3?.body !== void 0),
62164
+ ...await authHeaders5(config, init3?.body !== void 0, opts?.context),
62175
62165
  ...init3?.headers
62176
62166
  };
62177
62167
  logHttpRequest(tag, method, url, headers, init3?.body);
@@ -62207,12 +62197,13 @@ async function appendErrorLog(entry) {
62207
62197
  } catch {
62208
62198
  }
62209
62199
  }
62210
- async function fetchUserProjects(config) {
62200
+ async function fetchUserProjects(config, context) {
62211
62201
  const response = await localAgentFetch(
62212
62202
  config,
62213
62203
  localAgentTag({}),
62214
62204
  "projects",
62215
- { method: "GET" }
62205
+ { method: "GET" },
62206
+ { context }
62216
62207
  );
62217
62208
  return response.projects ?? [];
62218
62209
  }
@@ -62254,8 +62245,14 @@ async function execPluginCommand(cmd, timeoutMs) {
62254
62245
  );
62255
62246
  });
62256
62247
  }
62257
- async function fetchPluginConfig(config, tag) {
62258
- return localAgentFetch(config, tag, "getConfig", { method: "GET" }, { redactResponseLog: true });
62248
+ async function fetchPluginConfig(config, tag, context) {
62249
+ return localAgentFetch(
62250
+ config,
62251
+ tag,
62252
+ "getConfig",
62253
+ { method: "GET" },
62254
+ { redactResponseLog: true, context }
62255
+ );
62259
62256
  }
62260
62257
  function getPluginPullStatePath() {
62261
62258
  return path76.join(getLocalAgentHome(), "plugin-pull.json");
@@ -62293,7 +62290,16 @@ async function maybeReconcilePlugins(context) {
62293
62290
  const child = spawn13(
62294
62291
  process.execPath,
62295
62292
  [process.argv[1], "source", "reconcile-plugins"],
62296
- { detached: true, windowsHide: true, stdio: "ignore", env: { ...process.env, TEAMAI_PLUGIN_LOCAL_AGENT_ID: localAgentId } }
62293
+ {
62294
+ detached: true,
62295
+ windowsHide: true,
62296
+ stdio: "ignore",
62297
+ env: {
62298
+ ...process.env,
62299
+ TEAMAI_PLUGIN_LOCAL_AGENT_ID: localAgentId,
62300
+ TEAMAI_PLUGIN_TOOL: tool
62301
+ }
62302
+ }
62297
62303
  );
62298
62304
  child.unref();
62299
62305
  } catch (e) {
@@ -62309,7 +62315,8 @@ async function runPluginReconcileWorker() {
62309
62315
  const tag = "[local-agent] [plugin-reconcile]";
62310
62316
  const statePath = getPluginPullStatePath();
62311
62317
  try {
62312
- const resp = await fetchPluginConfig(config, tag);
62318
+ const context = { tool: process.env.TEAMAI_PLUGIN_TOOL ?? "workbuddy" };
62319
+ const resp = await fetchPluginConfig(config, tag, context);
62313
62320
  const { vars, plugins } = parseGetConfig(resp);
62314
62321
  const declaredSlugs = plugins.length ? ` [${plugins.map((p2) => p2.slug).join(", ")}]` : "";
62315
62322
  log.debug(`${tag} get-config: ${plugins.length} plugin(s) declared${declaredSlugs}`);
@@ -62390,11 +62397,19 @@ async function persistWorkspaceBinding(config, cwd, resolvedPath, projectId, pro
62390
62397
  }
62391
62398
  await saveLocalAgentConfig(config);
62392
62399
  }
62393
- async function enrollWorkspace(config, resolvedPath) {
62394
- const binding = config.workspaceBindings[resolvedPath] ?? {};
62395
- config.workspaceBindings[resolvedPath] = {
62400
+ async function enrollWorkspace(config, resolvedPath, agentKeys = []) {
62401
+ const enrolledAt = (/* @__PURE__ */ new Date()).toISOString();
62402
+ const binding = config.workspaceBindings[resolvedPath];
62403
+ const mergedAgentKeys = [.../* @__PURE__ */ new Set([...binding?.agentKeys ?? [], ...agentKeys])];
62404
+ config.workspaceBindings[resolvedPath] = binding ? {
62396
62405
  ...binding,
62397
- enrolledAt: binding.enrolledAt ?? (/* @__PURE__ */ new Date()).toISOString()
62406
+ enrolledAt: binding.enrolledAt ?? enrolledAt,
62407
+ ...mergedAgentKeys.length > 0 ? { agentKeys: mergedAgentKeys } : {}
62408
+ } : {
62409
+ projectId: 0,
62410
+ boundAt: enrolledAt,
62411
+ enrolledAt,
62412
+ ...mergedAgentKeys.length > 0 ? { agentKeys: mergedAgentKeys } : {}
62398
62413
  };
62399
62414
  }
62400
62415
  async function inheritWorktreeBinding(config, cwd, resolvedPath) {
@@ -62433,7 +62448,7 @@ async function bindWorkspaceToProject(workspacePath, projectId) {
62433
62448
  log.success(`\u5DF2\u5C06\u5DE5\u4F5C\u533A\u7ED1\u5B9A\u5230\u9879\u76EE\uFF1A${project.name} [id=${project.id}]`);
62434
62449
  return binding;
62435
62450
  }
62436
- async function ensureWorkspaceBinding(config, workspacePath, sessionId, cwd) {
62451
+ async function ensureWorkspaceBinding(config, workspacePath, agentContext, sessionId, cwd) {
62437
62452
  if (!isWorkspaceEnrolled(config, workspacePath)) return;
62438
62453
  if (hasWorkspaceBindingDecision(config.workspaceBindings[workspacePath])) return;
62439
62454
  if (await inheritWorktreeBinding(config, cwd, workspacePath)) return;
@@ -62446,7 +62461,7 @@ async function ensureWorkspaceBinding(config, workspacePath, sessionId, cwd) {
62446
62461
  }
62447
62462
  let projects;
62448
62463
  try {
62449
- projects = await fetchUserProjects(config);
62464
+ projects = await fetchUserProjects(config, agentContext);
62450
62465
  } catch (e) {
62451
62466
  log.debug(`local-agent: failed to load user projects: ${e.message}`);
62452
62467
  return;
@@ -62482,7 +62497,7 @@ function isBindPromptTool(tool) {
62482
62497
  const kind = modelAgentKind(tool);
62483
62498
  return kind === "codebuddy" || kind === "workbuddy";
62484
62499
  }
62485
- async function emitBindingHint(config, workspacePath, sessionId, cwd) {
62500
+ async function emitBindingHint(config, workspacePath, agentContext, sessionId, cwd) {
62486
62501
  if (!isWorkspaceEnrolled(config, workspacePath)) return;
62487
62502
  if (hasWorkspaceBindingDecision(config.workspaceBindings[workspacePath])) return;
62488
62503
  if (await inheritWorktreeBinding(config, cwd, workspacePath)) return;
@@ -62495,7 +62510,7 @@ async function emitBindingHint(config, workspacePath, sessionId, cwd) {
62495
62510
  }
62496
62511
  let projects;
62497
62512
  try {
62498
- projects = await fetchUserProjects(config);
62513
+ projects = await fetchUserProjects(config, agentContext);
62499
62514
  } catch {
62500
62515
  return;
62501
62516
  }
@@ -62709,16 +62724,19 @@ function stampWorkspaceTool(config, currentPath, tool) {
62709
62724
  const binding = config.workspaceBindings[currentPath];
62710
62725
  if (!binding?.enrolledAt) return false;
62711
62726
  const normalized = normalizeAgentType(tool);
62712
- if (binding.ideType !== normalized) {
62713
- binding.ideType = normalized;
62714
- return true;
62715
- }
62716
- return false;
62727
+ const key = credentialKey({ tool });
62728
+ const agentKeys = binding.agentKeys ?? [];
62729
+ if (binding.ideType === normalized && agentKeys.includes(key)) return false;
62730
+ binding.ideType = normalized;
62731
+ binding.agentKeys = [.../* @__PURE__ */ new Set([...agentKeys, key])];
62732
+ return true;
62717
62733
  }
62718
62734
  function selectToolWorkspaces(config, currentPath, currentTool) {
62719
62735
  return Object.keys(config.workspaceBindings).filter((wsPath) => {
62720
62736
  const b2 = config.workspaceBindings[wsPath];
62721
62737
  if (!b2.enrolledAt) return false;
62738
+ const key = credentialKey({ tool: currentTool });
62739
+ if (b2.agentKeys?.includes(key)) return true;
62722
62740
  const wsTool = b2.ideType ?? (wsPath === currentPath ? currentTool : void 0);
62723
62741
  return wsTool === currentTool;
62724
62742
  });
@@ -62781,7 +62799,7 @@ async function buildReportPayload(config, context) {
62781
62799
  name: path76.basename(wsPath),
62782
62800
  ide_type: currentTool
62783
62801
  };
62784
- if ((wsBinding?.projectId ?? 0) > 0) workspace.project_id = wsBinding.projectId;
62802
+ if (wsBinding.projectId > 0) workspace.project_id = wsBinding.projectId;
62785
62803
  if (wsScope.skills.length > 0) workspace.skills = wsScope.skills;
62786
62804
  if (wsScope.rules.length > 0) workspace.rules = wsScope.rules;
62787
62805
  const wsMcps = await scanMcpFromManifest("project", tool, wsPath);
@@ -62810,7 +62828,7 @@ async function buildSyncPayload(config, context) {
62810
62828
  path: wsPath,
62811
62829
  name: path76.basename(wsPath),
62812
62830
  ide_type: currentTool,
62813
- ...(wsBinding?.projectId ?? 0) > 0 ? { project_id: wsBinding.projectId } : {}
62831
+ ...wsBinding.projectId > 0 ? { project_id: wsBinding.projectId } : {}
62814
62832
  };
62815
62833
  });
62816
62834
  }
@@ -63476,7 +63494,7 @@ async function hookDeliveryProblem(teamConfig, localConfig2, tool, block) {
63476
63494
  const length = parts.filter(Boolean).join("\n\n").length;
63477
63495
  return length > hook.limit ? `${tool} cannot load this project's instructions: with the HTTP prompts they are ${length} characters, over the ${hook.limit}-character limit of its prompt section, so ${tool} skips them. Shorten the prompts for this project.` : null;
63478
63496
  }
63479
- async function ackCommand(config, tag, command, status2, version2, error2) {
63497
+ async function ackCommand(config, tag, context, command, status2, version2, error2) {
63480
63498
  await localAgentFetch(config, tag, "ack", {
63481
63499
  method: "POST",
63482
63500
  body: JSON.stringify({
@@ -63486,7 +63504,7 @@ async function ackCommand(config, tag, command, status2, version2, error2) {
63486
63504
  error: error2 ?? "",
63487
63505
  version: version2
63488
63506
  })
63489
- });
63507
+ }, { context });
63490
63508
  }
63491
63509
  function requireModelString(value, field) {
63492
63510
  if (typeof value !== "string" || !value.trim()) {
@@ -64463,7 +64481,7 @@ async function processCommands(config, commands, context) {
64463
64481
  }
64464
64482
  try {
64465
64483
  const version2 = await executeCommand(config, command, context);
64466
- await ackCommand(config, tag, command, "success", version2);
64484
+ await ackCommand(config, tag, context, command, "success", version2);
64467
64485
  if (command.type === "apply_model_config") modelConfigApplied = true;
64468
64486
  log.debug(`${tag} command ${command.id} (${command.type ?? ""}) succeeded`);
64469
64487
  if (command.type === "uninstall_teamai") {
@@ -64474,7 +64492,7 @@ async function processCommands(config, commands, context) {
64474
64492
  const error2 = e.message;
64475
64493
  log.error(`${tag} command ${command.id} failed: ${error2}`);
64476
64494
  try {
64477
- await ackCommand(config, tag, command, "failed", void 0, error2);
64495
+ await ackCommand(config, tag, context, command, "failed", void 0, error2);
64478
64496
  } catch (ackError) {
64479
64497
  log.debug(`${tag} failed to ack command ${command.id}: ${ackError.message}`);
64480
64498
  }
@@ -64505,10 +64523,10 @@ async function syncLocalAgent(context) {
64505
64523
  if (workspacePath && isWorkspaceEnrolled(config, workspacePath)) {
64506
64524
  const sid = context.event?.sessionId;
64507
64525
  if (context.event?.type === "session_start") {
64508
- await ensureWorkspaceBinding(config, workspacePath, sid, context.cwd);
64526
+ await ensureWorkspaceBinding(config, workspacePath, context, sid, context.cwd);
64509
64527
  }
64510
64528
  if (context.event?.type === "prompt_submit") {
64511
- await emitBindingHint(config, workspacePath, sid, context.cwd);
64529
+ await emitBindingHint(config, workspacePath, context, sid, context.cwd);
64512
64530
  }
64513
64531
  }
64514
64532
  }
@@ -64537,7 +64555,8 @@ async function syncLocalAgent(context) {
64537
64555
  await localAgentFetch(config, tag, "report", {
64538
64556
  method: "POST",
64539
64557
  body: JSON.stringify(reportPayload)
64540
- });
64558
+ }, { context });
64559
+ await promoteLegacyCredential(config, context);
64541
64560
  log.debug(`${tag} report OK`);
64542
64561
  }
64543
64562
  const syncPayload = await buildSyncPayload(config, context);
@@ -64546,7 +64565,7 @@ async function syncLocalAgent(context) {
64546
64565
  tag,
64547
64566
  "sync",
64548
64567
  { method: "POST", body: JSON.stringify(syncPayload) },
64549
- { redactResponseLog: true }
64568
+ { redactResponseLog: true, context }
64550
64569
  );
64551
64570
  const cmds = syncResponse.cmds;
64552
64571
  const commands = cmds && cmds.length > 0 ? cmds : syncResponse.commands ?? [];
@@ -64559,7 +64578,7 @@ async function syncLocalAgent(context) {
64559
64578
  await localAgentFetch(config, tag, "report", {
64560
64579
  method: "POST",
64561
64580
  body: JSON.stringify(reportPayload)
64562
- });
64581
+ }, { context });
64563
64582
  log.debug(`${tag} model config report OK`);
64564
64583
  }
64565
64584
  }
@@ -64688,8 +64707,16 @@ async function reportAndSyncFromHook(stdin, tool) {
64688
64707
  }
64689
64708
  }
64690
64709
  async function writeTokenFile(tokenPath, token) {
64691
- await fs35.promises.writeFile(tokenPath, token + "\n", { mode: 384 });
64692
- await fs35.promises.chmod(tokenPath, 384);
64710
+ await ensureDir(path76.dirname(tokenPath));
64711
+ const tmp = `${tokenPath}.${process.pid}.${createHash10("sha256").update(`${tokenPath}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 12)}.tmp`;
64712
+ try {
64713
+ await fs35.promises.writeFile(tmp, token + "\n", { mode: 384, flag: "wx" });
64714
+ await fs35.promises.chmod(tmp, 384);
64715
+ await fs35.promises.rename(tmp, tokenPath);
64716
+ } catch (error2) {
64717
+ await fs35.promises.rm(tmp, { force: true }).catch(() => void 0);
64718
+ throw error2;
64719
+ }
64693
64720
  }
64694
64721
  async function initLocalAgentHttp(options2) {
64695
64722
  const endpoint = normalizeEndpoint(options2.endpoint);
@@ -64700,20 +64727,45 @@ async function initLocalAgentHttp(options2) {
64700
64727
  if (existing2 && !options2.force && normalizeEndpoint(existing2.endpoint) !== endpoint) {
64701
64728
  throw new Error("HTTP local agent is initialized with another endpoint. Re-run with --force to overwrite.");
64702
64729
  }
64730
+ const requestedAgents = [...new Set((options2.filterAgents ?? []).map((agent) => agent.trim()).filter(Boolean))];
64731
+ if (options2.token?.startsWith("sk-") && requestedAgents.length !== 1) {
64732
+ throw new Error("A ClawPro instance token requires exactly one --agent.");
64733
+ }
64734
+ const sameEndpoint = Boolean(existing2 && normalizeEndpoint(existing2.endpoint) === endpoint);
64703
64735
  const config = {
64704
64736
  endpoint,
64705
- token: options2.token ?? existing2?.token,
64706
- createdAt: existing2?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
64737
+ // A token without --agent keeps the legacy machine-wide behavior. An
64738
+ // agent-scoped setup writes a credential file below and must not replace it.
64739
+ token: requestedAgents.length !== 1 ? options2.token ?? (sameEndpoint ? existing2?.token : void 0) : sameEndpoint ? existing2?.token : void 0,
64740
+ agentCredentials: sameEndpoint ? existing2?.agentCredentials : void 0,
64741
+ createdAt: sameEndpoint ? existing2?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
64707
64742
  workspaceStateVersion: 1,
64708
- workspaceBindings: existing2?.workspaceBindings ?? {},
64709
- userGroupId: existing2?.userGroupId,
64710
- userGroupName: existing2?.userGroupName
64711
- };
64743
+ workspaceBindings: sameEndpoint ? existing2?.workspaceBindings ?? {} : {},
64744
+ userGroupId: sameEndpoint ? existing2?.userGroupId : void 0,
64745
+ userGroupName: sameEndpoint ? existing2?.userGroupName : void 0,
64746
+ routes: sameEndpoint ? existing2?.routes : void 0
64747
+ };
64748
+ const configuredKeys = [];
64749
+ if (requestedAgents.length === 1 && options2.token) {
64750
+ configuredKeys.push(await writeAgentCredential(
64751
+ config,
64752
+ { tool: requestedAgents[0] },
64753
+ options2.token
64754
+ ));
64755
+ } else if (requestedAgents.length === 1) {
64756
+ const key = credentialKey({ tool: requestedAgents[0] });
64757
+ if (config.agentCredentials?.[key]) configuredKeys.push(key);
64758
+ }
64712
64759
  const workspacePath = await resolveWorkspacePath(options2.workspacePath ?? process.cwd());
64713
- if (workspacePath) await enrollWorkspace(config, workspacePath);
64760
+ if (workspacePath) await enrollWorkspace(config, workspacePath, configuredKeys);
64714
64761
  await ensureDir(getLocalAgentHome());
64715
64762
  await saveLocalAgentConfig(config);
64716
- if (options2.token) {
64763
+ if (existing2 && !sameEndpoint) {
64764
+ await remove(path76.join(getLocalAgentHome(), credentialEndpointDir(existing2.endpoint))).catch((error2) => {
64765
+ log.warn(`Could not remove credentials for the previous HTTP endpoint: ${error2.message}`);
64766
+ });
64767
+ }
64768
+ if (options2.token && requestedAgents.length !== 1) {
64717
64769
  await writeTokenFile(getTokenPath(), options2.token);
64718
64770
  }
64719
64771
  const teamConfig = createLocalAgentTeamConfig(endpoint);
@@ -64738,7 +64790,7 @@ async function pullLocalAgentForCwd(context) {
64738
64790
  async function describeLocalAgent(options2 = {}) {
64739
64791
  const config = await loadLocalAgentConfig(options2);
64740
64792
  if (!config) return null;
64741
- const boundProjects = Object.entries(config.workspaceBindings).filter(([, b2]) => (b2.projectId ?? 0) > 0).map(([workspacePath, b2]) => ({ path: workspacePath, projectName: b2.projectName, projectId: b2.projectId }));
64793
+ const boundProjects = Object.entries(config.workspaceBindings).filter(([, b2]) => b2.projectId !== 0).map(([workspacePath, b2]) => ({ path: workspacePath, projectName: b2.projectName, projectId: b2.projectId }));
64742
64794
  const manifest = await loadManifest2();
64743
64795
  const counts = { skills: 0, rules: 0, claudemd: 0 };
64744
64796
  for (const scope of Object.values(manifest.scopes)) {
@@ -64957,13 +65009,7 @@ async function bindCurrentProject(options2) {
64957
65009
  "Current workspace is not enrolled. Run `teamai init --http <ENDPOINT> --token <API_TOKEN>` here first."
64958
65010
  );
64959
65011
  }
64960
- await persistWorkspaceBinding(
64961
- config,
64962
- options2?.cwd ?? process.cwd(),
64963
- workspacePath,
64964
- 0,
64965
- "__skipped__"
64966
- );
65012
+ await persistWorkspaceBinding(config, options2?.cwd ?? process.cwd(), workspacePath, 0, "__skipped__");
64967
65013
  log.info(`\u5DF2\u8DF3\u8FC7\u7ED1\u5B9A\uFF0C\u4EE5\u540E\u4E0D\u518D\u63D0\u793A\u6B64\u5DE5\u4F5C\u533A\u3002`);
64968
65014
  return;
64969
65015
  }
@@ -65325,7 +65371,7 @@ __export(git_exclude_delivered_exports, {
65325
65371
  describeUnreadableGitExcludeSetting: () => describeUnreadableGitExcludeSetting,
65326
65372
  reportDeliveredGitExclude: () => reportDeliveredGitExclude
65327
65373
  });
65328
- import { createHash as createHash10 } from "crypto";
65374
+ import { createHash as createHash11 } from "crypto";
65329
65375
  import path79 from "path";
65330
65376
  function createDeliveryRecorder() {
65331
65377
  const reports = /* @__PURE__ */ new Map();
@@ -65376,7 +65422,7 @@ function deliveredOwnerElsewhere(localConfig2) {
65376
65422
  }
65377
65423
  function partitionId(localConfig2) {
65378
65424
  const dataHome = getDataHome(localConfig2);
65379
- return /-([0-9a-f]{16})$/.exec(path79.basename(dataHome))?.[1] ?? createHash10("sha256").update(path79.resolve(dataHome)).digest("hex").slice(0, 16);
65425
+ return /-([0-9a-f]{16})$/.exec(path79.basename(dataHome))?.[1] ?? createHash11("sha256").update(path79.resolve(dataHome)).digest("hex").slice(0, 16);
65380
65426
  }
65381
65427
  function partitionOwner(localConfig2, name) {
65382
65428
  return {
@@ -66129,15 +66175,19 @@ async function initHttp(url, options2) {
66129
66175
  await reconcileHooksForInit(teamConfig, localConfig2, filterAgents2);
66130
66176
  const { initLocalAgentHttp: initLocalAgentHttp2 } = await Promise.resolve().then(() => (init_local_agent(), local_agent_exports));
66131
66177
  try {
66178
+ const explicitLocalAgentToken = options2.token?.trim() || process.env.TEAMAI_API_TOKEN?.trim() || process.env.TEAMAI_API_KEY?.trim();
66132
66179
  await initLocalAgentHttp2({
66133
66180
  endpoint: url,
66134
- token: options2.token,
66181
+ token: explicitLocalAgentToken || (filterAgents2 ? void 0 : apiKey),
66135
66182
  force: options2.force,
66136
66183
  filterAgents: filterAgents2,
66137
66184
  workspacePath: projectRoot ?? process.cwd()
66138
66185
  });
66139
66186
  } catch (e) {
66140
- log.debug(`Local agent init: ${e.message}`);
66187
+ log.error(`Local agent init failed: ${e.message}`);
66188
+ process.exitCode = 1;
66189
+ closePrompt();
66190
+ return;
66141
66191
  }
66142
66192
  await runInitScript(localConfig2, "postInit", options2.dryRun, true);
66143
66193
  log.success("teamai initialized (HTTP read-only)!");
@@ -67478,7 +67528,7 @@ __export(learnings_publish_exports, {
67478
67528
  publishLearningsMaintenance: () => publishLearningsMaintenance,
67479
67529
  publishQueuedLearnings: () => publishQueuedLearnings
67480
67530
  });
67481
- import { createHash as createHash11 } from "crypto";
67531
+ import { createHash as createHash12 } from "crypto";
67482
67532
  import path83 from "path";
67483
67533
  import fs40 from "fs";
67484
67534
  function commitMessageFor(username) {
@@ -67679,7 +67729,7 @@ async function hasUncommittedChange(checkout, rel) {
67679
67729
  }
67680
67730
  async function contentDigest(file) {
67681
67731
  if (!fs40.existsSync(file)) return null;
67682
- return createHash11("sha256").update(await fs40.promises.readFile(file)).digest("hex");
67732
+ return createHash12("sha256").update(await fs40.promises.readFile(file)).digest("hex");
67683
67733
  }
67684
67734
  async function maintenanceRecordsDir(checkout) {
67685
67735
  const gitPath2 = (await createGitWithBlockTimeout(checkout).raw(["rev-parse", "--git-path", "teamai-maintenance"])).trim();
@@ -71133,7 +71183,7 @@ var init_definition = __esm({
71133
71183
  });
71134
71184
 
71135
71185
  // src/scorers/cache.ts
71136
- import { createHash as createHash12 } from "crypto";
71186
+ import { createHash as createHash13 } from "crypto";
71137
71187
  import fs48 from "fs/promises";
71138
71188
  import path95 from "path";
71139
71189
  function cacheDir(config) {
@@ -71276,7 +71326,7 @@ async function syncScorerDefinitions(config, options2 = {}) {
71276
71326
  }
71277
71327
  const { name } = parsed.definition;
71278
71328
  if (parsed.definition.isolation === "verified") result.verified.push(relativePath);
71279
- const sourceHash = createHash12("sha256").update(content).digest("hex");
71329
+ const sourceHash = createHash13("sha256").update(content).digest("hex");
71280
71330
  const revision = await (reachable ??= remotelyReachableRevision(knowledgeRoot));
71281
71331
  const holds = await revisionHoldsSources(knowledgeRoot, revision, [{ path: relativePath, content }]);
71282
71332
  const previous = await withoutStaleTeamRevision(knowledgeRoot, await readCache2(config, name));
@@ -74441,7 +74491,7 @@ __export(cache_exports, {
74441
74491
  syncWorkflowDefinitions: () => syncWorkflowDefinitions,
74442
74492
  workflowsRunBy: () => workflowsRunBy
74443
74493
  });
74444
- import { createHash as createHash13 } from "crypto";
74494
+ import { createHash as createHash14 } from "crypto";
74445
74495
  import fs49 from "fs/promises";
74446
74496
  import os11 from "os";
74447
74497
  import path97 from "path";
@@ -74646,7 +74696,7 @@ async function syncWorkflowDefinitions(config, options2 = {}) {
74646
74696
  checked.push({ stem, relativePath, diagnostics: resolved2.diagnostics });
74647
74697
  continue;
74648
74698
  }
74649
- const hash2 = createHash13("sha256").update(content);
74699
+ const hash2 = createHash14("sha256").update(content);
74650
74700
  for (const source of resolved2.sources) {
74651
74701
  hash2.update("\0").update(source.path).update("\0").update(source.content);
74652
74702
  }
@@ -74783,7 +74833,7 @@ async function loadWorkflowTree(config, cached) {
74783
74833
  if (failed4) return { failed: failed4 };
74784
74834
  const teamRevision = cached.teamRevision !== void 0 && revisions.every((revision) => revision === cached.teamRevision) ? cached.teamRevision : void 0;
74785
74835
  if (hashes.length === 0) return { workflows, sourceHash: cached.sourceHash, ...teamRevision ? { teamRevision } : {} };
74786
- const hash2 = createHash13("sha256").update(cached.sourceHash);
74836
+ const hash2 = createHash14("sha256").update(cached.sourceHash);
74787
74837
  for (const [name, sourceHash] of hashes.sort(([left], [right]) => left.localeCompare(right))) {
74788
74838
  hash2.update("\0").update(name).update("\0").update(sourceHash);
74789
74839
  }
@@ -77278,7 +77328,7 @@ __export(team_push_exports, {
77278
77328
  withDroppedRollouts: () => withDroppedRollouts
77279
77329
  });
77280
77330
  import fs55 from "fs";
77281
- import { createHash as createHash14 } from "crypto";
77331
+ import { createHash as createHash15 } from "crypto";
77282
77332
  import path105 from "path";
77283
77333
  async function readExistingStats(statsPath) {
77284
77334
  try {
@@ -77447,7 +77497,7 @@ function tokenDelta(cur, prev) {
77447
77497
  };
77448
77498
  }
77449
77499
  function segmentKey(transcript2) {
77450
- return createHash14("sha256").update(transcript2).digest("hex").slice(0, 16);
77500
+ return createHash15("sha256").update(transcript2).digest("hex").slice(0, 16);
77451
77501
  }
77452
77502
  function reportedSegments(entry) {
77453
77503
  const segments2 = {};
@@ -78319,7 +78369,7 @@ var init_delivery_schema = __esm({
78319
78369
  });
78320
78370
 
78321
78371
  // src/workflow/snapshot.ts
78322
- import { createHash as createHash15 } from "crypto";
78372
+ import { createHash as createHash16 } from "crypto";
78323
78373
  import fs56 from "fs";
78324
78374
  import path106 from "path";
78325
78375
  function withCommitIds(schema) {
@@ -78336,7 +78386,7 @@ function snapshotText(snapshot) {
78336
78386
  `;
78337
78387
  }
78338
78388
  function definitionHash(definition, workflows = {}) {
78339
- return createHash15("sha256").update(canonicalJson2({ definition, workflows })).digest("hex");
78389
+ return createHash16("sha256").update(canonicalJson2({ definition, workflows })).digest("hex");
78340
78390
  }
78341
78391
  function snapshotBody(snapshot) {
78342
78392
  const { snapshot: _format, run: _run, owner: _owner, ownedAt: _ownedAt, stateRevision: _revision, write: _write, ...body } = snapshot;
@@ -79299,7 +79349,7 @@ var init_records = __esm({
79299
79349
  });
79300
79350
 
79301
79351
  // src/scorers/label.ts
79302
- import { createHash as createHash16 } from "crypto";
79352
+ import { createHash as createHash17 } from "crypto";
79303
79353
  function sumUsage(first2, second) {
79304
79354
  if (!first2 || !second) return first2 ?? second;
79305
79355
  const tokens = (left, right) => left === void 0 && right === void 0 ? void 0 : (left ?? 0) + (right ?? 0);
@@ -79327,10 +79377,10 @@ function labelKey(label) {
79327
79377
  return canonicalJson2({ scorer: label.scorer, sourceHash: label.rubric.sourceHash, subject: label.subject, judge: label.judge.kind });
79328
79378
  }
79329
79379
  function labelId(label) {
79330
- return createHash16("sha256").update(labelKey(label)).digest("hex");
79380
+ return createHash17("sha256").update(labelKey(label)).digest("hex");
79331
79381
  }
79332
79382
  function labelVersion(label) {
79333
- return createHash16("sha256").update(canonicalJson2(label)).digest("hex");
79383
+ return createHash17("sha256").update(canonicalJson2(label)).digest("hex");
79334
79384
  }
79335
79385
  function newestLabels(labels) {
79336
79386
  const newest = /* @__PURE__ */ new Map();
@@ -89062,7 +89112,7 @@ var init_scrub = __esm({
89062
89112
  });
89063
89113
 
89064
89114
  // src/session-flow/agent-session.ts
89065
- import { createHash as createHash19 } from "crypto";
89115
+ import { createHash as createHash20 } from "crypto";
89066
89116
  import fs77 from "fs";
89067
89117
  import path129 from "path";
89068
89118
  function noAdapter(platform3, use, os25 = process.platform) {
@@ -89144,7 +89194,7 @@ async function writeCopyOnce(target, { session, sourcePlatform, cwd }) {
89144
89194
  } catch (e) {
89145
89195
  return { status: "failed", failure: { code: "invalid_workspace", cwd, message: messageOf(e) } };
89146
89196
  }
89147
- const key = createHash19("sha256").update(`${targetPlatform}\0${targetId}`).digest("hex").slice(0, 16);
89197
+ const key = createHash20("sha256").update(`${targetPlatform}\0${targetId}`).digest("hex").slice(0, 16);
89148
89198
  const lockPath = path129.join(getTeamaiHomeDir(), "locks", `session-copy-${key}.lock`);
89149
89199
  for (let attempt = 1; !await acquireLock(lockPath); attempt++) {
89150
89200
  if (attempt === COPY_LOCK_ATTEMPTS) {
@@ -90352,12 +90402,12 @@ var init_settings = __esm({
90352
90402
  });
90353
90403
 
90354
90404
  // src/scorers/entry.ts
90355
- import { createHash as createHash20 } from "crypto";
90405
+ import { createHash as createHash21 } from "crypto";
90356
90406
  function entryIdOf(scorer, subject) {
90357
- return `s${createHash20("sha256").update(canonicalJson2({ scorer, subject })).digest("hex").slice(0, 13)}`;
90407
+ return `s${createHash21("sha256").update(canonicalJson2({ scorer, subject })).digest("hex").slice(0, 13)}`;
90358
90408
  }
90359
90409
  function sessionEntryIdOf(scorer, sourceHash, subject) {
90360
- return `s${createHash20("sha256").update(canonicalJson2({ scorer, sourceHash, subject })).digest("hex").slice(0, 13)}`;
90410
+ return `s${createHash21("sha256").update(canonicalJson2({ scorer, sourceHash, subject })).digest("hex").slice(0, 13)}`;
90361
90411
  }
90362
90412
  function isLeaseRef(value) {
90363
90413
  return LEASE_REF.test(value);
@@ -90509,11 +90559,11 @@ var init_evidence = __esm({
90509
90559
  });
90510
90560
 
90511
90561
  // src/scorers/sampling.ts
90512
- import { createHash as createHash21 } from "crypto";
90562
+ import { createHash as createHash22 } from "crypto";
90513
90563
  function isSampled(scorer, subject, samplingRate) {
90514
90564
  if (samplingRate <= 0) return false;
90515
90565
  if (samplingRate >= 100) return true;
90516
- const digest = createHash21("sha256").update(canonicalJson2({ scorer, ...subject })).digest();
90566
+ const digest = createHash22("sha256").update(canonicalJson2({ scorer, ...subject })).digest();
90517
90567
  return digest.readUInt32BE(0) % 1e4 < samplingRate * 100;
90518
90568
  }
90519
90569
  var init_sampling = __esm({
@@ -91177,7 +91227,7 @@ var init_workspace = __esm({
91177
91227
  });
91178
91228
 
91179
91229
  // src/workflow/worktree.ts
91180
- import { createHash as createHash22 } from "crypto";
91230
+ import { createHash as createHash23 } from "crypto";
91181
91231
  import { execFile as execFile6, spawn as spawn6 } from "child_process";
91182
91232
  import fs83 from "fs/promises";
91183
91233
  import path137 from "path";
@@ -91238,7 +91288,7 @@ function git(cwd, args, options2) {
91238
91288
  }
91239
91289
  function worktreePath2(main2, branch) {
91240
91290
  const safe = branch.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "step";
91241
- const hash2 = createHash22("sha256").update(branch).digest("hex").slice(0, 12);
91291
+ const hash2 = createHash23("sha256").update(branch).digest("hex").slice(0, 12);
91242
91292
  return path137.join(main2, ".teamai", "workflow-worktrees", `${safe}-${hash2}`);
91243
91293
  }
91244
91294
  function stepCheckout(active, branch) {
@@ -92103,7 +92153,7 @@ var init_scoring_evidence = __esm({
92103
92153
  import * as fs85 from "fs";
92104
92154
  import * as path139 from "path";
92105
92155
  import { execFileSync as execFileSync7 } from "child_process";
92106
- import { createHash as createHash23, randomUUID as randomUUID13 } from "crypto";
92156
+ import { createHash as createHash24, randomUUID as randomUUID13 } from "crypto";
92107
92157
  function utcNow() {
92108
92158
  return (/* @__PURE__ */ new Date()).toISOString();
92109
92159
  }
@@ -92235,7 +92285,7 @@ function assertNoSymlinkedAncestors(target, root) {
92235
92285
  }
92236
92286
  async function withSessionPublicationLock(repoRoot, fn) {
92237
92287
  const real = await fs85.promises.realpath(repoRoot).catch(() => path139.resolve(repoRoot));
92238
- const key = createHash23("sha256").update(real).digest("hex").slice(0, 16);
92288
+ const key = createHash24("sha256").update(real).digest("hex").slice(0, 16);
92239
92289
  const lockPath = path139.join(getTeamaiHomeDir(), "locks", `session-publish-${key}.lock`);
92240
92290
  for (let attempt = 1; !await acquireLock(lockPath); attempt++) {
92241
92291
  if (attempt === INDEX_LOCK_ATTEMPTS) return { status: "busy", lockPath };
@@ -92249,7 +92299,7 @@ async function withSessionPublicationLock(repoRoot, fn) {
92249
92299
  }
92250
92300
  async function withSessionIndexLock(repoRoot, fn) {
92251
92301
  const real = await fs85.promises.realpath(repoRoot).catch(() => path139.resolve(repoRoot));
92252
- const key = createHash23("sha256").update(path139.join(real, "sessions")).digest("hex").slice(0, 16);
92302
+ const key = createHash24("sha256").update(path139.join(real, "sessions")).digest("hex").slice(0, 16);
92253
92303
  const lockPath = path139.join(getTeamaiHomeDir(), "locks", `sessions-${key}.lock`);
92254
92304
  for (let attempt = 1; !await acquireLock(lockPath); attempt++) {
92255
92305
  if (attempt === INDEX_LOCK_ATTEMPTS) return { status: "busy", lockPath };
@@ -106388,7 +106438,7 @@ __export(source_exports, {
106388
106438
  sourceSyncWarnings: () => sourceSyncWarnings
106389
106439
  });
106390
106440
  import path158 from "path";
106391
- import { createHash as createHash24 } from "crypto";
106441
+ import { createHash as createHash25 } from "crypto";
106392
106442
  function sourceLockPath() {
106393
106443
  return path158.join(getUserHome(), ".teamai", ".source-lifecycle-lock");
106394
106444
  }
@@ -106428,7 +106478,7 @@ async function listSourceNames() {
106428
106478
  }
106429
106479
  }
106430
106480
  function getSourceRepoId(source) {
106431
- return createHash24("sha256").update(source.repo.trim()).digest("hex");
106481
+ return createHash25("sha256").update(source.repo.trim()).digest("hex");
106432
106482
  }
106433
106483
  function getSourceRepoCacheDir(source) {
106434
106484
  return path158.join(getUserHome(), ".teamai", "source-repos", getSourceRepoId(source));
@@ -106450,7 +106500,7 @@ async function listInstallationRecords() {
106450
106500
  return records2;
106451
106501
  }
106452
106502
  function getSourceManifestPath(sourceName, localConfig2) {
106453
- const installationId = createHash24("sha256").update(JSON.stringify([
106503
+ const installationId = createHash25("sha256").update(JSON.stringify([
106454
106504
  path158.resolve(resolveBaseDir(localConfig2)),
106455
106505
  path158.resolve(localConfig2.repo.localPath)
106456
106506
  ])).digest("hex");
@@ -106838,6 +106888,7 @@ async function sourceAddHttp(endpoint, options2) {
106838
106888
  endpoint: trimmed2,
106839
106889
  token: options2.token,
106840
106890
  force: true,
106891
+ filterAgents: normalizeAgentList(options2.agent),
106841
106892
  workspacePath: mainConfig?.projectRoot ?? process.cwd()
106842
106893
  });
106843
106894
  log.success(`HTTP source added (${trimmed2}).`);
@@ -107566,6 +107617,7 @@ var init_source2 = __esm({
107566
107617
  init_builtin_skills();
107567
107618
  init_home();
107568
107619
  init_update();
107620
+ init_known_agents();
107569
107621
  init_path_safety();
107570
107622
  init_types5();
107571
107623
  }
@@ -108077,13 +108129,13 @@ __export(builtin_skills_exports, {
108077
108129
  skillsGuardBase: () => skillsGuardBase
108078
108130
  });
108079
108131
  import fs95 from "fs";
108080
- import { createHash as createHash25 } from "crypto";
108132
+ import { createHash as createHash26 } from "crypto";
108081
108133
  import path161 from "path";
108082
108134
  function isCliOwnedSkillName(name) {
108083
108135
  return BUILTIN_SKILL_NAMES.has(name) || LEGACY_BUILTIN_SKILL_NAMES.has(name);
108084
108136
  }
108085
108137
  function packagedSkillDigest(content) {
108086
- return createHash25("sha256").update(content).digest("hex");
108138
+ return createHash26("sha256").update(content).digest("hex");
108087
108139
  }
108088
108140
  async function ownedSkillFiles(skillName, paths) {
108089
108141
  const owned = /* @__PURE__ */ new Map();
@@ -108209,7 +108261,7 @@ async function removeOwnedFiles(dir, owned, baseDir, backupDir) {
108209
108261
  }
108210
108262
  function skillBackupDir(baseDir, tool, skillRoot, skillName) {
108211
108263
  const rootSlug = skillRoot.replace(/[\\/:]+/g, "-").replace(/^-+/, "");
108212
- const baseSlug = `${path161.basename(baseDir) || "root"}-${createHash25("sha256").update(baseDir).digest("hex").slice(0, 8)}`;
108264
+ const baseSlug = `${path161.basename(baseDir) || "root"}-${createHash26("sha256").update(baseDir).digest("hex").slice(0, 8)}`;
108213
108265
  return path161.join(getUserHome(), ".teamai", "removed-skills", PRUNE_RUN_ID, baseSlug, tool, rootSlug, skillName);
108214
108266
  }
108215
108267
  async function skillsGuardBase(scopeRoot, skillsDir) {
@@ -113355,7 +113407,7 @@ __export(types_exports, {
113355
113407
  usesBranchWorktree: () => usesBranchWorktree
113356
113408
  });
113357
113409
  import path171 from "path";
113358
- import { createHash as createHash26 } from "crypto";
113410
+ import { createHash as createHash27 } from "crypto";
113359
113411
  function parseInstallScope(value) {
113360
113412
  const parsed = InstallScopeSchema.safeParse(value);
113361
113413
  if (!parsed.success) throw new Error(`Invalid scope "${value}". Use "workspace" or "user".`);
@@ -113423,7 +113475,7 @@ function managedMcpManifestKey(tool, projectScope) {
113423
113475
  return projectScope ? `${owner}:project` : owner;
113424
113476
  }
113425
113477
  function managedMcpWorkspaceId(workspaceRoot) {
113426
- return createHash26("sha1").update(workspaceRoot).digest("hex").slice(0, 12);
113478
+ return createHash27("sha1").update(workspaceRoot).digest("hex").slice(0, 12);
113427
113479
  }
113428
113480
  function managedMcpManifestPath(dataHome, workspaceRoot) {
113429
113481
  if (workspaceRoot) {
@@ -114884,7 +114936,7 @@ var init_types5 = __esm({
114884
114936
 
114885
114937
  // src/claude-hook-host.ts
114886
114938
  import fs99 from "fs";
114887
- import { createHash as createHash27 } from "crypto";
114939
+ import { createHash as createHash28 } from "crypto";
114888
114940
  import path172 from "path";
114889
114941
  function copilotRunsEntries(data) {
114890
114942
  const file = data;
@@ -114892,7 +114944,7 @@ function copilotRunsEntries(data) {
114892
114944
  return Object.entries(file.hooks).flatMap(([event, entries2]) => !Array.isArray(entries2) ? [] : entries2.filter((entry) => entry?.type === "command").flatMap((entry) => [entry.bash, entry.powershell, entry.command].filter((command) => typeof command === "string").map((command) => [event, entry.matcher, command])));
114893
114945
  }
114894
114946
  function copilotCopyKey(event, matcher, command) {
114895
- return createHash27("sha256").update(JSON.stringify([event, matcher && matcher !== "*" ? matcher : "", command])).digest("hex");
114947
+ return createHash28("sha256").update(JSON.stringify([event, matcher && matcher !== "*" ? matcher : "", command])).digest("hex");
114896
114948
  }
114897
114949
  function claudeHookOtherHostSkip(copilotEvent, copilotMatcher, command) {
114898
114950
  if (!copilotEvent) return CURSOR_HOOK_SKIP;
@@ -114974,7 +115026,7 @@ var init_claude_hook_host = __esm({
114974
115026
  });
114975
115027
 
114976
115028
  // src/copilot-hook-claim.ts
114977
- import { createHash as createHash28 } from "crypto";
115029
+ import { createHash as createHash29 } from "crypto";
114978
115030
  import fs100 from "fs";
114979
115031
  import path173 from "path";
114980
115032
  function claimDay(raw) {
@@ -115002,7 +115054,7 @@ function claimCopilotHookEvent(event, matcher, raw, env3 = process.env) {
115002
115054
  if (!day) return true;
115003
115055
  const root = path173.join(getTeamaiHomeDir(), CLAIMS_DIR);
115004
115056
  removeOldClaims(root, day);
115005
- const key = createHash28("sha256").update(JSON.stringify([env3.COPILOT_AGENT_SESSION_ID ?? "", event, matcher, raw])).digest("hex");
115057
+ const key = createHash29("sha256").update(JSON.stringify([env3.COPILOT_AGENT_SESSION_ID ?? "", event, matcher, raw])).digest("hex");
115006
115058
  try {
115007
115059
  fs100.mkdirSync(path173.join(root, day), { recursive: true });
115008
115060
  fs100.writeFileSync(path173.join(root, day, key), "", { flag: "wx" });
@@ -115641,7 +115693,7 @@ __export(hooks_exports2, {
115641
115693
  unregisterCheckoutFromSharedManifest: () => unregisterCheckoutFromSharedManifest
115642
115694
  });
115643
115695
  import path176 from "path";
115644
- import { createHash as createHash29 } from "crypto";
115696
+ import { createHash as createHash30 } from "crypto";
115645
115697
  import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
115646
115698
  import { realpathSync as realpathSync6 } from "fs";
115647
115699
  import { lstat as lstat2, readdir as readdir2, rm as rm2 } from "fs/promises";
@@ -116386,7 +116438,7 @@ function copilotPowershellCommand(command) {
116386
116438
  return match[1] === "bash" ? `${match[2]} 2>$null; exit 0` : `& ${match[1]} -lc "${match[2]} 2>/dev/null"; exit 0`;
116387
116439
  }
116388
116440
  function copilotGlobalSource(file) {
116389
- return createHash29("sha256").update(path176.resolve(file)).digest("hex");
116441
+ return createHash30("sha256").update(path176.resolve(file)).digest("hex");
116390
116442
  }
116391
116443
  function toCopilotEntry(def, globalSource) {
116392
116444
  const command = globalSource && def.source === "builtin" ? def.command.replace("--tool copilot", `--tool copilot --copilot-global ${copilotGlobalSource(globalSource)}`) : def.command;
@@ -117704,7 +117756,7 @@ async function codexTrustTargets(teamConfig, localConfig2, selectedAgents) {
117704
117756
  };
117705
117757
  }
117706
117758
  async function codexTrustFingerprint(targets) {
117707
- const hash2 = createHash29("sha256").update(JSON.stringify({ ...targets, inputs: void 0 }));
117759
+ const hash2 = createHash30("sha256").update(JSON.stringify({ ...targets, inputs: void 0 }));
117708
117760
  for (const file of targets.inputs) hash2.update(`\0${file}\0${await readFileSafe(file) ?? ""}`);
117709
117761
  hash2.update(`\0${codexBuildsIdentity()}`);
117710
117762
  return hash2.digest("hex");
@@ -124183,7 +124235,7 @@ var init_overlay = __esm({
124183
124235
  });
124184
124236
 
124185
124237
  // src/workflow/place/worktree-snapshot.ts
124186
- import { createHash as createHash30 } from "crypto";
124238
+ import { createHash as createHash31 } from "crypto";
124187
124239
  import { execFile as execFile9 } from "child_process";
124188
124240
  import fs106 from "fs/promises";
124189
124241
  import path185 from "path";
@@ -124211,7 +124263,7 @@ async function hashPath(workspace, relative3) {
124211
124263
  try {
124212
124264
  const stats = await fs106.lstat(full);
124213
124265
  const bytes = stats.isSymbolicLink() ? await fs106.readlink(full) : await fs106.readFile(full);
124214
- return createHash30("sha256").update(bytes).digest("hex");
124266
+ return createHash31("sha256").update(bytes).digest("hex");
124215
124267
  } catch {
124216
124268
  return "missing";
124217
124269
  }
@@ -124541,7 +124593,7 @@ var init_setup = __esm({
124541
124593
  });
124542
124594
 
124543
124595
  // src/workflow/place/writer.ts
124544
- import { createHash as createHash31 } from "crypto";
124596
+ import { createHash as createHash32 } from "crypto";
124545
124597
  import fs109 from "fs/promises";
124546
124598
  import path188 from "path";
124547
124599
  async function mergeBranches({ dir, run: run3, step, executor, merge }) {
@@ -124574,7 +124626,7 @@ async function conflictsPath(dir) {
124574
124626
  return path188.resolve(dir, printed);
124575
124627
  }
124576
124628
  function hashText(text2) {
124577
- return createHash31("sha256").update(text2).digest("hex");
124629
+ return createHash32("sha256").update(text2).digest("hex");
124578
124630
  }
124579
124631
  async function rememberConflicts(dir) {
124580
124632
  const current = await conflictContents(dir);
@@ -131242,7 +131294,7 @@ var init_session_cmd = __esm({
131242
131294
  // src/dashboard/workspaces.ts
131243
131295
  import fs119 from "fs/promises";
131244
131296
  import path206 from "path";
131245
- import { createHash as createHash32 } from "crypto";
131297
+ import { createHash as createHash33 } from "crypto";
131246
131298
  async function configAt(home) {
131247
131299
  try {
131248
131300
  return LocalConfigSchema.parse(import_yaml42.default.parse(await fs119.readFile(path206.join(home, "config.yaml"), "utf8")));
@@ -131277,7 +131329,7 @@ async function dashboardWorkspaces(events) {
131277
131329
  if (!config) return;
131278
131330
  seen.add(anchor);
131279
131331
  result.push({
131280
- id: createHash32("sha256").update(anchor).digest("hex").slice(0, 24),
131332
+ id: createHash33("sha256").update(anchor).digest("hex").slice(0, 24),
131281
131333
  label: path206.basename(root),
131282
131334
  scope: "project",
131283
131335
  root,
@@ -132342,7 +132394,7 @@ __export(distribution_exports, {
132342
132394
  saveProjects: () => saveProjects,
132343
132395
  saveRoles: () => saveRoles
132344
132396
  });
132345
- import { createHash as createHash33 } from "crypto";
132397
+ import { createHash as createHash34 } from "crypto";
132346
132398
  function assertRolesEditable(ownConfig) {
132347
132399
  if (!ownConfig.roleAssignment) return;
132348
132400
  throw new HttpError(409, `Roles are assigned in ${ROLE_ASSIGNMENTS_FILE} in the team repo and cannot be changed here. Open a merge request against that file, or ask a code owner of manifest/ to.`);
@@ -132356,7 +132408,7 @@ function selectionOf2(config) {
132356
132408
  }
132357
132409
  function revisionOf(config) {
132358
132410
  const identity2 = { scope: config.scope, dataHome: config.dataHome ?? null, projectRoot: config.projectRoot ?? null };
132359
- return createHash33("sha256").update(JSON.stringify({ identity: identity2, selection: selectionOf2(config) })).digest("hex").slice(0, 16);
132411
+ return createHash34("sha256").update(JSON.stringify({ identity: identity2, selection: selectionOf2(config) })).digest("hex").slice(0, 16);
132360
132412
  }
132361
132413
  async function readManifest3(kind, load2, repoPath) {
132362
132414
  try {
@@ -135264,7 +135316,7 @@ var init_graph_index_schema = __esm({
135264
135316
  });
135265
135317
 
135266
135318
  // src/wiki-engine/code-knowledge/code-collector.ts
135267
- import { createHash as createHash34 } from "crypto";
135319
+ import { createHash as createHash35 } from "crypto";
135268
135320
  import { execFile as execFile10 } from "child_process";
135269
135321
  import { readFile as readFile7, readdir as readdir4, stat as stat5 } from "fs/promises";
135270
135322
  import path215 from "path";
@@ -135308,7 +135360,7 @@ async function collectCode(options2) {
135308
135360
  path: filePath,
135309
135361
  relativePath,
135310
135362
  language,
135311
- sha256: createHash34("sha256").update(content).digest("hex"),
135363
+ sha256: createHash35("sha256").update(content).digest("hex"),
135312
135364
  content,
135313
135365
  isKeyFile: isKeyFile(relativePath, language)
135314
135366
  });
@@ -142868,7 +142920,7 @@ var init_code_knowledge_recall = __esm({
142868
142920
  });
142869
142921
 
142870
142922
  // src/codebase-freshness.ts
142871
- import { createHash as createHash35 } from "crypto";
142923
+ import { createHash as createHash36 } from "crypto";
142872
142924
  import { lstat as lstat5, readFile as readFile16, readdir as readdir10, realpath as realpath10, stat as stat11 } from "fs/promises";
142873
142925
  import path235 from "path";
142874
142926
  function errorCode(error2) {
@@ -143054,7 +143106,7 @@ async function checkOneSource(source, manifest, checkout) {
143054
143106
  if (!fileStat.isFile()) return unknown();
143055
143107
  try {
143056
143108
  const content = await readFile16(actualPath, "utf8");
143057
- const actualHash = createHash35("sha256").update(content).digest("hex");
143109
+ const actualHash = createHash36("sha256").update(content).digest("hex");
143058
143110
  return {
143059
143111
  ...source,
143060
143112
  freshness: actualHash === expectedHash ? "current" : "stale",
@@ -153491,7 +153543,7 @@ sourceCmd.command("remove <name>").description("Remove a source and clean up its
153491
153543
  const { sourceRemove: sourceRemove2 } = await Promise.resolve().then(() => (init_source2(), source_exports));
153492
153544
  await sourceRemove2(name, globalOpts);
153493
153545
  });
153494
- sourceCmd.command("add-http <endpoint>").description("Add an HTTP source (report/sync/ack) alongside a git main repo").option("--token <key>", "API token for the HTTP endpoint (stored 0600, never committed)").option("--force", "Overwrite an existing HTTP source config").action(async (endpoint, cmdOpts) => {
153546
+ sourceCmd.command("add-http <endpoint>").description("Add an HTTP source (report/sync/ack) alongside a git main repo").option("--token <key>", "API token for the HTTP endpoint (stored 0600, never committed)").option("--agent <name>", "Agent instance that owns this token (repeat setup for each agent)", (val, acc) => acc.concat(val), []).option("--force", "Overwrite an existing HTTP source config").action(async (endpoint, cmdOpts) => {
153495
153547
  const globalOpts = program2.opts();
153496
153548
  const { sourceAddHttp: sourceAddHttp2 } = await Promise.resolve().then(() => (init_source2(), source_exports));
153497
153549
  await sourceAddHttp2(endpoint, { ...globalOpts, ...cmdOpts });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-ai/teamai-cli",
3
- "version": "0.30.3",
3
+ "version": "0.30.5",
4
4
  "description": "TeamAI — Make Every Team AI Native (skill sync + shared knowledge base, powered by Git)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -175,6 +175,7 @@ Generated: do not edit by hand. Regenerate with
175
175
  - `teamai source remove <name>` — Remove a source and clean up its skills
176
176
  - `teamai source add-http <endpoint>` — Add an HTTP source (report/sync/ack) alongside a git main repo
177
177
  - `--token <key>` — API token for the HTTP endpoint (stored 0600, never committed)
178
+ - `--agent <name>` — Agent instance that owns this token (repeat setup for each agent)
178
179
  - `--force` — Overwrite an existing HTTP source config
179
180
  - `teamai source remove-http` — Remove the HTTP source and clean up its resources
180
181
  - `teamai source list` — List all configured sources
@@ -104,11 +104,19 @@ was skipped and why.
104
104
  cannot use Git. If the admin provides an HTTP endpoint + API key instead, use:
105
105
 
106
106
  ```bash
107
- teamai init --http https://your-team-host/api --token <api-key>
107
+ teamai init --http https://your-team-host/api --token <api-key> --agent <tool>
108
108
  ```
109
109
 
110
110
  This is a read-only consumer mode — `push` / `contribute` are not available, but
111
- skills and rules still sync.
111
+ skills and rules still sync. Run it from each workspace that should be managed:
112
+ the user-level hooks are shared by the machine, but an unrelated workspace is
113
+ not reported or allowed to receive workspace resources. ClawPro project binding
114
+ is optional; skipping it keeps general workspace sync enabled while declining
115
+ resources tied to a specific project. Treat each linked Git worktree as a
116
+ separate workspace and run HTTP setup in each one that should be managed.
117
+ ClawPro instance tokens are Agent-specific. On a machine with multiple AI tools,
118
+ run setup once per tool with its own `--agent` and token; TeamAI keeps those
119
+ credentials separate while allowing them to manage the same workspace.
112
120
 
113
121
  **Claude Code or Codex kept in a different directory (`CLAUDE_CONFIG_DIR`,
114
122
  `CODEX_HOME`):** `init` records that directory (as `toolRoots.claude` /