@agentconnect.md/daemon 1.0.0-rc.66 → 1.0.0-rc.67
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/index.js +159 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13322,6 +13322,50 @@ const WorkspaceReadContent = object({
|
|
|
13322
13322
|
nextOffset: number().int().nonnegative().optional(),
|
|
13323
13323
|
truncated: boolean().optional()
|
|
13324
13324
|
});
|
|
13325
|
+
/**
|
|
13326
|
+
* Agent workspace git ops (C→D REQ → REP) — the console's on-demand controls for
|
|
13327
|
+
* a git-repo workspace. Like list/read these run daemon-local; the CP proxies the
|
|
13328
|
+
* outcome and stores nothing.
|
|
13329
|
+
*
|
|
13330
|
+
* These are DATA-oriented: a non-repo workspace (`isRepo:false`), a dirty tree,
|
|
13331
|
+
* or a pull that can't fast-forward (offline, diverged, local edits) all come
|
|
13332
|
+
* back as a normal REP — NOT an `error` frame. Only an unknown agent
|
|
13333
|
+
* (`BAD_PAYLOAD`) or an unexpected git/fs failure (`INTERNAL`) is an error.
|
|
13334
|
+
*/
|
|
13335
|
+
/** C→D REQ: report `git status` of the agent's workspace (is it clean?). */
|
|
13336
|
+
const WorkspaceGitStatusReq = object({ agentId: string().min(1) });
|
|
13337
|
+
/** One changed path in a `git status`. `index`/`workingDir` are git's per-file XY
|
|
13338
|
+
* status chars (' ', 'M', 'A', 'D', 'R', 'C', 'U', '?', …). */
|
|
13339
|
+
const WorkspaceGitFile = object({
|
|
13340
|
+
path: string(),
|
|
13341
|
+
index: string(),
|
|
13342
|
+
workingDir: string()
|
|
13343
|
+
});
|
|
13344
|
+
/** D→C REP (corr = the req id): the working-tree status. */
|
|
13345
|
+
const WorkspaceGitStatus = object({
|
|
13346
|
+
agentId: string(),
|
|
13347
|
+
isRepo: boolean(),
|
|
13348
|
+
clean: boolean(),
|
|
13349
|
+
branch: string().optional(),
|
|
13350
|
+
tracking: string().optional(),
|
|
13351
|
+
ahead: number().int().nonnegative().optional(),
|
|
13352
|
+
behind: number().int().nonnegative().optional(),
|
|
13353
|
+
files: array(WorkspaceGitFile).optional(),
|
|
13354
|
+
truncated: boolean().optional()
|
|
13355
|
+
});
|
|
13356
|
+
/** C→D REQ: force a fast-forward-only `git pull` in the agent's workspace now. */
|
|
13357
|
+
const WorkspaceGitPullReq = object({ agentId: string().min(1) });
|
|
13358
|
+
/** D→C REP (corr = the req id): the pull outcome. A failed pull is DATA
|
|
13359
|
+
* (`ok:false` + `detail`), not an error frame (see the section docblock). */
|
|
13360
|
+
const WorkspaceGitPullResult = object({
|
|
13361
|
+
agentId: string(),
|
|
13362
|
+
isRepo: boolean(),
|
|
13363
|
+
ok: boolean(),
|
|
13364
|
+
detail: string().optional(),
|
|
13365
|
+
changed: number().int().nonnegative().optional(),
|
|
13366
|
+
insertions: number().int().nonnegative().optional(),
|
|
13367
|
+
deletions: number().int().nonnegative().optional()
|
|
13368
|
+
});
|
|
13325
13369
|
//#endregion
|
|
13326
13370
|
//#region ../protocol/dist/frames/telemetry.js
|
|
13327
13371
|
/**
|
|
@@ -13446,6 +13490,10 @@ const FRAME_SCHEMAS = {
|
|
|
13446
13490
|
"workspace/list/page": WorkspaceListPage,
|
|
13447
13491
|
"workspace/read": WorkspaceReadReq,
|
|
13448
13492
|
"workspace/read/content": WorkspaceReadContent,
|
|
13493
|
+
"workspace/gitstatus": WorkspaceGitStatusReq,
|
|
13494
|
+
"workspace/gitstatus/result": WorkspaceGitStatus,
|
|
13495
|
+
"workspace/gitpull": WorkspaceGitPullReq,
|
|
13496
|
+
"workspace/gitpull/result": WorkspaceGitPullResult,
|
|
13449
13497
|
"config/push": object({ keys: record(string(), unknown()) }),
|
|
13450
13498
|
"daemon/restart": object({
|
|
13451
13499
|
reason: string(),
|
|
@@ -13545,6 +13593,10 @@ discriminatedUnion("type", [
|
|
|
13545
13593
|
frame("workspace/list/page", FRAME_SCHEMAS["workspace/list/page"]),
|
|
13546
13594
|
frame("workspace/read", FRAME_SCHEMAS["workspace/read"]),
|
|
13547
13595
|
frame("workspace/read/content", FRAME_SCHEMAS["workspace/read/content"]),
|
|
13596
|
+
frame("workspace/gitstatus", FRAME_SCHEMAS["workspace/gitstatus"]),
|
|
13597
|
+
frame("workspace/gitstatus/result", FRAME_SCHEMAS["workspace/gitstatus/result"]),
|
|
13598
|
+
frame("workspace/gitpull", FRAME_SCHEMAS["workspace/gitpull"]),
|
|
13599
|
+
frame("workspace/gitpull/result", FRAME_SCHEMAS["workspace/gitpull/result"]),
|
|
13548
13600
|
frame("config/push", FRAME_SCHEMAS["config/push"]),
|
|
13549
13601
|
frame("daemon/restart", FRAME_SCHEMAS["daemon/restart"]),
|
|
13550
13602
|
frame("daemon/upgrade", FRAME_SCHEMAS["daemon/upgrade"]),
|
|
@@ -13671,7 +13723,7 @@ function isFrame(type) {
|
|
|
13671
13723
|
}
|
|
13672
13724
|
//#endregion
|
|
13673
13725
|
//#region src/workspace/workspace-manager.ts
|
|
13674
|
-
const PULL_TIMEOUT_MS = 4500;
|
|
13726
|
+
const PULL_TIMEOUT_MS$1 = 4500;
|
|
13675
13727
|
const cloneInFlight = /* @__PURE__ */ new Map();
|
|
13676
13728
|
async function prepareWorkspace(agent) {
|
|
13677
13729
|
const cwd = agent.workspace.path;
|
|
@@ -13686,7 +13738,7 @@ async function prepareWorkspace(agent) {
|
|
|
13686
13738
|
return cwd;
|
|
13687
13739
|
}
|
|
13688
13740
|
if (agent.workspace.pullOnNewSession) try {
|
|
13689
|
-
await Promise.race([simpleGit(cwd).pull(["--ff-only"]), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("pull timeout")), PULL_TIMEOUT_MS))]);
|
|
13741
|
+
await Promise.race([simpleGit(cwd).pull(["--ff-only"]), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("pull timeout")), PULL_TIMEOUT_MS$1))]);
|
|
13690
13742
|
} catch {}
|
|
13691
13743
|
return cwd;
|
|
13692
13744
|
}
|
|
@@ -82763,6 +82815,12 @@ var CpClient = class {
|
|
|
82763
82815
|
case "workspace/read":
|
|
82764
82816
|
this.deps.workspaceRead.read(frame.payload).then((content) => this.reply(frame, "workspace/read/content", content)).catch((err) => this.workspaceError(frame.id, "workspace/read", err));
|
|
82765
82817
|
return;
|
|
82818
|
+
case "workspace/gitstatus":
|
|
82819
|
+
this.deps.workspaceGit.status(frame.payload.agentId).then((status) => this.reply(frame, "workspace/gitstatus/result", status)).catch((err) => this.workspaceError(frame.id, "workspace/gitstatus", err));
|
|
82820
|
+
return;
|
|
82821
|
+
case "workspace/gitpull":
|
|
82822
|
+
this.deps.workspaceGit.pull(frame.payload.agentId).then((result) => this.reply(frame, "workspace/gitpull/result", result)).catch((err) => this.workspaceError(frame.id, "workspace/gitpull", err));
|
|
82823
|
+
return;
|
|
82766
82824
|
case "agent/prompt":
|
|
82767
82825
|
this.sendError(frame.id, "INTERNAL", "agent/prompt not implemented", false);
|
|
82768
82826
|
return;
|
|
@@ -82860,6 +82918,104 @@ function createSessionReader(store) {
|
|
|
82860
82918
|
};
|
|
82861
82919
|
}
|
|
82862
82920
|
//#endregion
|
|
82921
|
+
//#region src/cp/workspace-git.ts
|
|
82922
|
+
/**
|
|
82923
|
+
* `WorkspaceGit` — the seam answering the CP's `workspace/gitstatus` and
|
|
82924
|
+
* `workspace/gitpull` REQs against an agent's local git-repo workspace. Sibling
|
|
82925
|
+
* to {@link WorkspaceReader}: git runs daemon-local and only the outcome is
|
|
82926
|
+
* proxied to the console (§1/§12), never the repo.
|
|
82927
|
+
*
|
|
82928
|
+
* DATA vs error: a from-scratch workspace (`isRepo:false`), a dirty tree, or a
|
|
82929
|
+
* pull that can't fast-forward (offline, diverged, would clobber local edits) are
|
|
82930
|
+
* all normal REPs — the caller renders them. Only an unknown agentId throws
|
|
82931
|
+
* ({@link WorkspaceViolationError} → `BAD_PAYLOAD`); an unexpected git/fs failure
|
|
82932
|
+
* on `status` propagates (→ `INTERNAL`). Git-provided text is scrubbed of the
|
|
82933
|
+
* absolute workspace path before it leaves the daemon (raw git errors embed host
|
|
82934
|
+
* paths that must not reach the CP/UI).
|
|
82935
|
+
*/
|
|
82936
|
+
/** On-demand pull is interactive, so allow more headroom than the 4.5s
|
|
82937
|
+
* best-effort pull at session start (workspace-manager.ts). */
|
|
82938
|
+
const PULL_TIMEOUT_MS = 2e4;
|
|
82939
|
+
/** Cap the changed-file list so a huge working tree can't overflow the frame. */
|
|
82940
|
+
const MAX_STATUS_FILES = 500;
|
|
82941
|
+
function createWorkspaceGit(workspaceRootByAgent) {
|
|
82942
|
+
function rootFor(agentId) {
|
|
82943
|
+
const root = workspaceRootByAgent(agentId);
|
|
82944
|
+
if (!root) throw new WorkspaceViolationError(`unknown agent "${agentId}"`);
|
|
82945
|
+
return root;
|
|
82946
|
+
}
|
|
82947
|
+
/** A git-repo workspace has a `.git` at its root; from-scratch does not. */
|
|
82948
|
+
function isRepo(root) {
|
|
82949
|
+
return existsSync(join(root, ".git"));
|
|
82950
|
+
}
|
|
82951
|
+
/** Strip the absolute workspace path out of git-provided text so no host path
|
|
82952
|
+
* leaks to the CP/UI. Repo-relative paths in the message are kept. */
|
|
82953
|
+
function scrub(root, msg) {
|
|
82954
|
+
return msg.split(root).join("<workspace>").trim();
|
|
82955
|
+
}
|
|
82956
|
+
return {
|
|
82957
|
+
async status(agentId) {
|
|
82958
|
+
const root = rootFor(agentId);
|
|
82959
|
+
if (!isRepo(root)) return {
|
|
82960
|
+
agentId,
|
|
82961
|
+
isRepo: false,
|
|
82962
|
+
clean: true
|
|
82963
|
+
};
|
|
82964
|
+
const s = await simpleGit(root).status();
|
|
82965
|
+
const files = s.files.slice(0, MAX_STATUS_FILES).map((f) => ({
|
|
82966
|
+
path: f.path,
|
|
82967
|
+
index: f.index,
|
|
82968
|
+
workingDir: f.working_dir
|
|
82969
|
+
}));
|
|
82970
|
+
return {
|
|
82971
|
+
agentId,
|
|
82972
|
+
isRepo: true,
|
|
82973
|
+
clean: s.isClean(),
|
|
82974
|
+
...s.current ? { branch: s.current } : {},
|
|
82975
|
+
...s.tracking ? { tracking: s.tracking } : {},
|
|
82976
|
+
ahead: s.ahead,
|
|
82977
|
+
behind: s.behind,
|
|
82978
|
+
...files.length ? { files } : {},
|
|
82979
|
+
...s.files.length > files.length ? { truncated: true } : {}
|
|
82980
|
+
};
|
|
82981
|
+
},
|
|
82982
|
+
async pull(agentId) {
|
|
82983
|
+
const root = rootFor(agentId);
|
|
82984
|
+
if (!isRepo(root)) return {
|
|
82985
|
+
agentId,
|
|
82986
|
+
isRepo: false,
|
|
82987
|
+
ok: false,
|
|
82988
|
+
detail: "workspace is not a git checkout"
|
|
82989
|
+
};
|
|
82990
|
+
let timer;
|
|
82991
|
+
try {
|
|
82992
|
+
const res = await Promise.race([simpleGit(root).pull(["--ff-only"]), new Promise((_, rej) => {
|
|
82993
|
+
timer = setTimeout(() => rej(/* @__PURE__ */ new Error("pull timed out")), PULL_TIMEOUT_MS);
|
|
82994
|
+
})]);
|
|
82995
|
+
const changed = res.files.length;
|
|
82996
|
+
return {
|
|
82997
|
+
agentId,
|
|
82998
|
+
isRepo: true,
|
|
82999
|
+
ok: true,
|
|
83000
|
+
changed,
|
|
83001
|
+
insertions: res.summary.insertions,
|
|
83002
|
+
deletions: res.summary.deletions,
|
|
83003
|
+
detail: changed > 0 ? `Fast-forwarded — updated ${changed} file${changed === 1 ? "" : "s"}.` : "Already up to date."
|
|
83004
|
+
};
|
|
83005
|
+
} catch (err) {
|
|
83006
|
+
return {
|
|
83007
|
+
agentId,
|
|
83008
|
+
isRepo: true,
|
|
83009
|
+
ok: false,
|
|
83010
|
+
detail: scrub(root, err?.message ?? "pull failed")
|
|
83011
|
+
};
|
|
83012
|
+
} finally {
|
|
83013
|
+
if (timer) clearTimeout(timer);
|
|
83014
|
+
}
|
|
83015
|
+
}
|
|
83016
|
+
};
|
|
83017
|
+
}
|
|
83018
|
+
//#endregion
|
|
82863
83019
|
//#region src/cp/cp-cron.ts
|
|
82864
83020
|
/**
|
|
82865
83021
|
* `CpCronRegistry` — the daemon's set of CP-pushed crons, keyed by `cronId`
|
|
@@ -84368,6 +84524,7 @@ var Daemon = class Daemon {
|
|
|
84368
84524
|
configApply: this.cpConfigApply(),
|
|
84369
84525
|
sessionRead: createSessionReader(this.store),
|
|
84370
84526
|
workspaceRead: createWorkspaceReader((id) => this.agents.get(id)?.workspace.path),
|
|
84527
|
+
workspaceGit: createWorkspaceGit((id) => this.agents.get(id)?.workspace.path),
|
|
84371
84528
|
clock: systemClock,
|
|
84372
84529
|
connect: () => ClientTransport.dial(url),
|
|
84373
84530
|
log: this.log
|