@agent-team-foundation/first-tree-hub 0.14.9-alpha.302.1 → 0.14.9-alpha.303.1
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/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as cleanWorkspaces, A as printResults, B as ClientRuntime, C as migrateLocalAgentDirs, D as checkNodeVersion, E as checkClientConfig, G as removeLocalAgent, I as restartClientService, J as ClientOrgMismatchError, K as fail, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, Q as SessionRegistry, R as stopClientService, S as createApiNameResolver, T as checkBackgroundService, U as findStaleAliases, V as handleClientOrgMismatch, W as formatStaleReason, X as FirstTreeHubSDK, Y as ClientUserMismatchError, Z as SdkError, _ as loadOnboardState, a as declineUpdate, b as saveOnboardState, c as detectInstallMode, d as reconcileLocalRuntimeProviders, et as probeCapabilities, f as uploadClientCapabilities, g as formatCheckReport, h as promptMissingFields, i as createExecuteUpdate, j as reconcileAgentConfigs, k as checkWebSocket, l as fetchLatestVersion, m as promptAddAgent, nt as configureClientLoggerForService, o as promptUpdate, p as isInteractive, q as success, r as registerSaaSConnectCommand, s as PACKAGE_NAME, tt as applyClientLoggerConfig, u as installGlobalLatest, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate } from "../saas-connect-
|
|
2
|
+
import { $ as cleanWorkspaces, A as printResults, B as ClientRuntime, C as migrateLocalAgentDirs, D as checkNodeVersion, E as checkClientConfig, G as removeLocalAgent, I as restartClientService, J as ClientOrgMismatchError, K as fail, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, Q as SessionRegistry, R as stopClientService, S as createApiNameResolver, T as checkBackgroundService, U as findStaleAliases, V as handleClientOrgMismatch, W as formatStaleReason, X as FirstTreeHubSDK, Y as ClientUserMismatchError, Z as SdkError, _ as loadOnboardState, a as declineUpdate, b as saveOnboardState, c as detectInstallMode, d as reconcileLocalRuntimeProviders, et as probeCapabilities, f as uploadClientCapabilities, g as formatCheckReport, h as promptMissingFields, i as createExecuteUpdate, j as reconcileAgentConfigs, k as checkWebSocket, l as fetchLatestVersion, m as promptAddAgent, nt as configureClientLoggerForService, o as promptUpdate, p as isInteractive, q as success, r as registerSaaSConnectCommand, s as PACKAGE_NAME, tt as applyClientLoggerConfig, u as installGlobalLatest, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate } from "../saas-connect-AuYDI1jx.mjs";
|
|
3
3
|
import { C as resolveConfigReadonly, S as resetConfigMeta, _ as initConfig, a as loadCredentials, b as readConfigFile, c as saveAgentConfig, d as DEFAULT_DATA_DIR, f as DEFAULT_HOME_DIR, g as getConfigValue, i as ensureFreshAdminToken, m as clientConfigSchema, p as agentConfigSchema, r as ensureFreshAccessToken, s as resolveServerUrl, u as DEFAULT_CONFIG_DIR, v as loadAgents, w as setConfigValue, x as resetConfig } from "../bootstrap-DdLoOQI5.mjs";
|
|
4
4
|
import { a as print, n as CLI_USER_AGENT, o as setJsonMode, r as COMMAND_VERSION, t as cliFetch } from "../cli-fetch-BGVItZxo.mjs";
|
|
5
5
|
import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-CKj6zuvQ.mjs";
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as printResults, B as ClientRuntime, D as checkNodeVersion, E as checkClientConfig, F as resolveCliInvocation, H as rotateClientIdWithBackup, I as restartClientService, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, R as stopClientService, V as handleClientOrgMismatch, X as FirstTreeHubSDK, Z as SdkError, g as formatCheckReport, h as promptMissingFields, k as checkWebSocket, m as promptAddAgent, n as deriveHubUrlFromToken, p as isInteractive, t as HubUrlDerivationError, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate, z as uninstallClientService } from "./saas-connect-
|
|
1
|
+
import { A as printResults, B as ClientRuntime, D as checkNodeVersion, E as checkClientConfig, F as resolveCliInvocation, H as rotateClientIdWithBackup, I as restartClientService, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, R as stopClientService, V as handleClientOrgMismatch, X as FirstTreeHubSDK, Z as SdkError, g as formatCheckReport, h as promptMissingFields, k as checkWebSocket, m as promptAddAgent, n as deriveHubUrlFromToken, p as isInteractive, t as HubUrlDerivationError, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate, z as uninstallClientService } from "./saas-connect-AuYDI1jx.mjs";
|
|
2
2
|
import { i as ensureFreshAdminToken, n as AuthRefreshRateLimitedError, o as resolveAccessToken, r as ensureFreshAccessToken, s as resolveServerUrl, t as AuthRefreshFailedError } from "./bootstrap-DdLoOQI5.mjs";
|
|
3
3
|
import { i as blank, s as status } from "./cli-fetch-BGVItZxo.mjs";
|
|
4
4
|
import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-CKj6zuvQ.mjs";
|
|
@@ -3880,6 +3880,34 @@ function isCanonicalDocLinkPath(path) {
|
|
|
3880
3880
|
const normalized = normalizeDocLinkPath(path);
|
|
3881
3881
|
return normalized !== null && normalized === path;
|
|
3882
3882
|
}
|
|
3883
|
+
/**
|
|
3884
|
+
* Cross-agent workspace doc key.
|
|
3885
|
+
*
|
|
3886
|
+
* doc-preview snapshots from the SENDER's own workspace keep a bare,
|
|
3887
|
+
* base-relative key (`docs/foo.md`) — unchanged, zero-regression. A snapshot
|
|
3888
|
+
* of a file in ANOTHER agent's workspace needs a globally-unique key so web's
|
|
3889
|
+
* cache lookup is unambiguous: `<agentSlug>/<chatId>/<rel>`, i.e. the path
|
|
3890
|
+
* relative to the shared `workspaces/` common root (minus that root). Runtime
|
|
3891
|
+
* builds it when it snapshots a sibling-workspace file; web reconstructs the
|
|
3892
|
+
* same key from a scanned token to match the snapshot; server parses it to
|
|
3893
|
+
* re-check the owner is a chat participant. All three must agree, so the
|
|
3894
|
+
* build/parse logic lives here next to `normalizeDocLinkPath`.
|
|
3895
|
+
*
|
|
3896
|
+
* `chatId` scopes the key to a single chat: a `workspaces/<X>/<chatId>` dir
|
|
3897
|
+
* only exists when X has run a session in that chat, so the chatId segment is
|
|
3898
|
+
* the structural fence that keeps one chat from previewing another chat's
|
|
3899
|
+
* private workspace docs.
|
|
3900
|
+
*/
|
|
3901
|
+
function buildWorkspaceDocKey(agentSlug, chatId, rel) {
|
|
3902
|
+
const slug = agentSlug.trim();
|
|
3903
|
+
const chat = chatId.trim();
|
|
3904
|
+
if (!slug || !chat || slug.includes("/") || chat.includes("/")) return null;
|
|
3905
|
+
if (slug.startsWith(".") || chat.startsWith(".")) return null;
|
|
3906
|
+
const relNorm = normalizeDocLinkPath(rel);
|
|
3907
|
+
if (!relNorm) return null;
|
|
3908
|
+
const key = `${slug}/${chat}/${relNorm}`;
|
|
3909
|
+
return isCanonicalDocLinkPath(key) ? key : null;
|
|
3910
|
+
}
|
|
3883
3911
|
const adapterPlatformSchema = z.enum([
|
|
3884
3912
|
"feishu",
|
|
3885
3913
|
"slack",
|
|
@@ -10192,38 +10220,7 @@ var Deduplicator = class {
|
|
|
10192
10220
|
return this.seen.size;
|
|
10193
10221
|
}
|
|
10194
10222
|
};
|
|
10195
|
-
|
|
10196
|
-
* Scan an outbound agent message for `.md` path mentions (inline markdown
|
|
10197
|
-
* links `[text](path.md)` AND bare `path.md` tokens) and turn each
|
|
10198
|
-
* safely-resolvable target into a snapshot of the file's contents at send
|
|
10199
|
-
* time.
|
|
10200
|
-
*
|
|
10201
|
-
* Resolution is constrained to `root`. A path resolves when its real target
|
|
10202
|
-
* is a regular `.md` file physically inside the worktree with no hidden
|
|
10203
|
-
* segment. Two written forms resolve:
|
|
10204
|
-
* - workspace-relative (`docs/foo.md`, `./docs/foo.md`); and
|
|
10205
|
-
* - **absolute paths that land inside `root`** (`<root>/docs/foo.md`).
|
|
10206
|
-
* Web cannot map an absolute path back to a snapshot key (it does not
|
|
10207
|
-
* know `root`), so for every resolved token whose written form is not
|
|
10208
|
-
* already the canonical relative path, we **rewrite that span in the
|
|
10209
|
-
* outbound text** to the canonical relative path (preserving any
|
|
10210
|
-
* `:line[:col]` suffix). The caller sends `rewrittenText`, and web's
|
|
10211
|
-
* unchanged re-scan then sees a relative token, matches the snapshot,
|
|
10212
|
-
* and renders the preview link. This keeps web/server/schema untouched
|
|
10213
|
-
* and is immune to server-side body rewrites (e.g. `@mention` prepend)
|
|
10214
|
-
* because web re-scans rather than trusting byte offsets.
|
|
10215
|
-
*
|
|
10216
|
-
* Anything that escapes the worktree (absolute path resolving OUTSIDE root,
|
|
10217
|
-
* `..` traversal, symlink pointing to a hidden segment inside or outside
|
|
10218
|
-
* root), hides (`.dotfile` / `.dotdir`, `.agent/`), or is missing is dropped
|
|
10219
|
-
* — the caller's message still goes through, the offending mention simply
|
|
10220
|
-
* stays plain text in the UI (and is left untouched in `rewrittenText`).
|
|
10221
|
-
*
|
|
10222
|
-
* The shared schema enforces canonical path form and the server re-validates
|
|
10223
|
-
* byte budgets + sha256 so a misbehaving runtime cannot lodge mismatched
|
|
10224
|
-
* data.
|
|
10225
|
-
*/
|
|
10226
|
-
async function buildMessageDocumentSnapshots(text, root) {
|
|
10223
|
+
async function buildMessageDocumentSnapshots(text, root, fence) {
|
|
10227
10224
|
const occurrences = collectDocPathOccurrences(text);
|
|
10228
10225
|
if (occurrences.length === 0) return {
|
|
10229
10226
|
docs: [],
|
|
@@ -10236,28 +10233,52 @@ async function buildMessageDocumentSnapshots(text, root) {
|
|
|
10236
10233
|
skipped: occurrences.length,
|
|
10237
10234
|
rewrittenText: text
|
|
10238
10235
|
};
|
|
10239
|
-
const
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10236
|
+
const workspacesRootReal = fence ? await safeRealpath(fence.workspacesRoot) : null;
|
|
10237
|
+
const resolved = await Promise.all(occurrences.map(async (occ) => {
|
|
10238
|
+
const selfKey = await canonicalizeWorkspacePath(rootReal, occ.writtenPath);
|
|
10239
|
+
if (selfKey) return {
|
|
10240
|
+
...occ,
|
|
10241
|
+
kind: "self",
|
|
10242
|
+
key: selfKey,
|
|
10243
|
+
file: null,
|
|
10244
|
+
shortForm: ""
|
|
10245
|
+
};
|
|
10246
|
+
if (workspacesRootReal && fence && isAbsolute(occ.writtenPath)) {
|
|
10247
|
+
const cross = await resolveCrossWorkspaceDoc(workspacesRootReal, fence, occ.writtenPath);
|
|
10248
|
+
if (cross) return {
|
|
10249
|
+
...occ,
|
|
10250
|
+
kind: "cross",
|
|
10251
|
+
key: cross.key,
|
|
10252
|
+
file: cross.file,
|
|
10253
|
+
shortForm: cross.shortForm
|
|
10254
|
+
};
|
|
10255
|
+
}
|
|
10256
|
+
return {
|
|
10257
|
+
...occ,
|
|
10258
|
+
kind: null,
|
|
10259
|
+
key: null,
|
|
10260
|
+
file: null,
|
|
10261
|
+
shortForm: ""
|
|
10262
|
+
};
|
|
10263
|
+
}));
|
|
10243
10264
|
const docs = [];
|
|
10244
10265
|
let totalBytes = 0;
|
|
10245
10266
|
let skipped = 0;
|
|
10246
10267
|
const seen = /* @__PURE__ */ new Set();
|
|
10247
10268
|
const snapshotted = /* @__PURE__ */ new Set();
|
|
10248
10269
|
for (const occ of resolved) {
|
|
10249
|
-
const
|
|
10250
|
-
if (!
|
|
10270
|
+
const key = occ.key;
|
|
10271
|
+
if (!key || !key.toLowerCase().endsWith(".md")) {
|
|
10251
10272
|
skipped += 1;
|
|
10252
10273
|
continue;
|
|
10253
10274
|
}
|
|
10254
|
-
if (seen.has(
|
|
10255
|
-
seen.add(
|
|
10275
|
+
if (seen.has(key)) continue;
|
|
10276
|
+
seen.add(key);
|
|
10256
10277
|
if (docs.length >= 5) {
|
|
10257
10278
|
skipped += 1;
|
|
10258
10279
|
continue;
|
|
10259
10280
|
}
|
|
10260
|
-
const file = await resolveWorkspaceFile(rootReal,
|
|
10281
|
+
const file = occ.kind === "cross" ? occ.file : await resolveWorkspaceFile(rootReal, key);
|
|
10261
10282
|
if (!file) {
|
|
10262
10283
|
skipped += 1;
|
|
10263
10284
|
continue;
|
|
@@ -10280,23 +10301,36 @@ async function buildMessageDocumentSnapshots(text, root) {
|
|
|
10280
10301
|
}
|
|
10281
10302
|
const sha256 = createHash("sha256").update(content, "utf8").digest("hex");
|
|
10282
10303
|
docs.push({
|
|
10283
|
-
path:
|
|
10304
|
+
path: key,
|
|
10284
10305
|
sha256,
|
|
10285
10306
|
size,
|
|
10286
10307
|
content
|
|
10287
10308
|
});
|
|
10288
10309
|
totalBytes += size;
|
|
10289
|
-
snapshotted.add(
|
|
10310
|
+
snapshotted.add(key);
|
|
10290
10311
|
} catch {
|
|
10291
10312
|
skipped += 1;
|
|
10292
10313
|
}
|
|
10293
10314
|
}
|
|
10315
|
+
const selfKeys = /* @__PURE__ */ new Set();
|
|
10316
|
+
for (const occ of resolved) if (occ.kind === "self" && occ.key && snapshotted.has(occ.key)) selfKeys.add(occ.key);
|
|
10294
10317
|
const rewrites = [];
|
|
10295
|
-
for (const occ of resolved)
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10318
|
+
for (const occ of resolved) {
|
|
10319
|
+
if (!occ.key || !snapshotted.has(occ.key)) continue;
|
|
10320
|
+
if (occ.kind === "self" && isAbsolute(occ.writtenPath)) rewrites.push({
|
|
10321
|
+
start: occ.start,
|
|
10322
|
+
end: occ.end,
|
|
10323
|
+
replacement: `${occ.key}${occ.lineSuffix}`
|
|
10324
|
+
});
|
|
10325
|
+
else if (occ.kind === "cross") {
|
|
10326
|
+
const replacement = selfKeys.has(occ.shortForm) ? occ.key : occ.shortForm;
|
|
10327
|
+
rewrites.push({
|
|
10328
|
+
start: occ.start,
|
|
10329
|
+
end: occ.end,
|
|
10330
|
+
replacement: `${replacement}${occ.lineSuffix}`
|
|
10331
|
+
});
|
|
10332
|
+
}
|
|
10333
|
+
}
|
|
10300
10334
|
return {
|
|
10301
10335
|
docs,
|
|
10302
10336
|
skipped,
|
|
@@ -10395,6 +10429,44 @@ async function canonicalizeWorkspacePath(rootReal, writtenPath) {
|
|
|
10395
10429
|
return normalizeDocLinkPath(relative(rootReal, real));
|
|
10396
10430
|
}
|
|
10397
10431
|
/**
|
|
10432
|
+
* Resolve an ABSOLUTE `.md` path that points into a DIFFERENT agent's
|
|
10433
|
+
* workspace under the shared common root. Returns the global snapshot key
|
|
10434
|
+
* `<ownerSlug>/<chatId>/<rel>`, the realpath'd file to read, and the short
|
|
10435
|
+
* `<ownerSlug>/<rel>` form to rewrite into the outbound text — or null when
|
|
10436
|
+
* the path is outside the common root, belongs to another chat, hides, is the
|
|
10437
|
+
* sender's own workspace (handled as a self path), is not a regular `.md`
|
|
10438
|
+
* file, or cannot be realpath'd.
|
|
10439
|
+
*
|
|
10440
|
+
* realpath runs BEFORE the containment check so an ancestor symlink cannot
|
|
10441
|
+
* fake "inside the common root" — same discipline as the self-path resolver.
|
|
10442
|
+
*/
|
|
10443
|
+
async function resolveCrossWorkspaceDoc(workspacesRootReal, fence, absPath) {
|
|
10444
|
+
const real = await safeRealpath(absPath);
|
|
10445
|
+
if (!real) return null;
|
|
10446
|
+
const prefix = workspacesRootReal.endsWith(sep) ? workspacesRootReal : workspacesRootReal + sep;
|
|
10447
|
+
if (!real.startsWith(prefix)) return null;
|
|
10448
|
+
const segments = relative(workspacesRootReal, real).split(sep).filter((s) => s.length > 0 && s !== ".");
|
|
10449
|
+
if (segments.length < 3) return null;
|
|
10450
|
+
if (segments.some((s) => s.startsWith("."))) return null;
|
|
10451
|
+
const [ownerSlug, segChatId, ...rest] = segments;
|
|
10452
|
+
if (!ownerSlug || !segChatId) return null;
|
|
10453
|
+
if (segChatId !== fence.chatId) return null;
|
|
10454
|
+
if (ownerSlug === fence.selfSlug) return null;
|
|
10455
|
+
const rel = rest.join("/");
|
|
10456
|
+
const key = buildWorkspaceDocKey(ownerSlug, segChatId, rel);
|
|
10457
|
+
if (!key || !key.toLowerCase().endsWith(".md")) return null;
|
|
10458
|
+
try {
|
|
10459
|
+
if (!(await stat(real)).isFile()) return null;
|
|
10460
|
+
} catch {
|
|
10461
|
+
return null;
|
|
10462
|
+
}
|
|
10463
|
+
return {
|
|
10464
|
+
key,
|
|
10465
|
+
file: real,
|
|
10466
|
+
shortForm: `${ownerSlug}/${rel}`
|
|
10467
|
+
};
|
|
10468
|
+
}
|
|
10469
|
+
/**
|
|
10398
10470
|
* Resolve a canonical workspace-relative link target against `rootReal`.
|
|
10399
10471
|
* Returns the real path of a regular `.md` file that is **physically** inside
|
|
10400
10472
|
* the worktree AND whose real path contains no hidden segments. The second
|
|
@@ -10434,7 +10506,11 @@ function createResultSink(deps) {
|
|
|
10434
10506
|
let content = text;
|
|
10435
10507
|
const documentBasePath = await deps.getDocumentBasePath?.();
|
|
10436
10508
|
if (documentBasePath) try {
|
|
10437
|
-
const { docs, skipped, rewrittenText } = await buildMessageDocumentSnapshots(text, documentBasePath
|
|
10509
|
+
const { docs, skipped, rewrittenText } = await buildMessageDocumentSnapshots(text, documentBasePath, deps.workspacesRoot && deps.selfSlug ? {
|
|
10510
|
+
workspacesRoot: deps.workspacesRoot,
|
|
10511
|
+
chatId: deps.chatId,
|
|
10512
|
+
selfSlug: deps.selfSlug
|
|
10513
|
+
} : void 0);
|
|
10438
10514
|
content = rewrittenText;
|
|
10439
10515
|
if (docs.length > 0) metadata.documentContext = documentContextSchema.parse({
|
|
10440
10516
|
kind: "snapshot",
|
|
@@ -11071,7 +11147,9 @@ var SessionManager = class {
|
|
|
11071
11147
|
this.currentTrigger.delete(chatId);
|
|
11072
11148
|
},
|
|
11073
11149
|
log,
|
|
11074
|
-
getDocumentBasePath: () => this.resolveDocumentBasePath(log, chatId)
|
|
11150
|
+
getDocumentBasePath: () => this.resolveDocumentBasePath(log, chatId),
|
|
11151
|
+
workspacesRoot: dirname(this.config.handlerConfig.workspaceRoot),
|
|
11152
|
+
selfSlug: basename(this.config.handlerConfig.workspaceRoot)
|
|
11075
11153
|
});
|
|
11076
11154
|
const envCtx = {
|
|
11077
11155
|
sdk: this.config.sdk,
|