@anhlp/alp 0.0.0-beta.2 → 0.0.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/acp.js +104 -21
- package/dist/alpd.js +705 -326
- package/docs/modular-monorepo.md +1 -1
- package/package.json +4 -4
- package/packages/cli/dist/acp/main.js +1 -1
- package/packages/cli/dist/acp.js +104 -21
- package/packages/cli/dist/alpd.js +705 -326
- package/packages/cli/dist/backup.js +1 -1
- package/packages/cli/dist/build-manifest.json +1 -1
- package/packages/cli/dist/cli.js +6 -2
- package/packages/cli/dist/daemon.js +6 -2
- package/packages/client/dist/index.d.ts +16 -1
- package/packages/client/dist/index.js +83 -5
- package/packages/client/dist/supervise.js +18 -12
- package/packages/core/dist/library-edit.d.ts +7 -4
- package/packages/core/dist/library-edit.js +59 -6
package/docs/modular-monorepo.md
CHANGED
|
@@ -138,7 +138,7 @@ Restarting is non-destructive. `alp daemon restart` (or the plugin's supervision
|
|
|
138
138
|
|
|
139
139
|
## Independent versions and publishing
|
|
140
140
|
|
|
141
|
-
> **For the beta `0.0.
|
|
141
|
+
> **For the beta `0.0.1-beta.3`, do not use the commands in this section.** The beta gives all
|
|
142
142
|
> eight published packages one version, packs them once and publishes those tarballs with
|
|
143
143
|
> `--tag beta`, following the manual checklist in [beta-scope.md](alp/beta-scope.md) §7. The
|
|
144
144
|
> `npm publish` below has no `--tag`, so it would set `latest`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anhlp/alp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.1-beta.3",
|
|
4
4
|
"description": "ALP command line and alpd, the per-user daemon that runs filesystem-defined Codex and Claude Code agent teams",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -91,9 +91,9 @@
|
|
|
91
91
|
"zod": "4.4.3"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"@anhlp/alp-client": "0.0.
|
|
95
|
-
"@anhlp/alp-core": "0.0.
|
|
96
|
-
"@anhlp/alp-protocol": "0.0.
|
|
94
|
+
"@anhlp/alp-client": "0.0.1-beta.3",
|
|
95
|
+
"@anhlp/alp-core": "0.0.1-beta.3",
|
|
96
|
+
"@anhlp/alp-protocol": "0.0.1-beta.3",
|
|
97
97
|
"@anthropic-ai/claude-agent-sdk": "^0.3.292",
|
|
98
98
|
"smol-toml": "1.9.0"
|
|
99
99
|
},
|
|
@@ -6,7 +6,7 @@ import { startDaemon } from "@anhlp/alp-client/supervise";
|
|
|
6
6
|
import { createLogger } from "@anhlp/alp-core/log";
|
|
7
7
|
import { createAcpAgent } from "./agent.js";
|
|
8
8
|
async function runAcp({ daemonEntry }) {
|
|
9
|
-
const version = true ? "0.0.
|
|
9
|
+
const version = true ? "0.0.1-beta.3" : "0.0.0";
|
|
10
10
|
const logger = createLogger({ component: "acp" });
|
|
11
11
|
const log = (message, fields) => logger.warn(message, fields);
|
|
12
12
|
const app = createAcpAgent({
|
package/packages/cli/dist/acp.js
CHANGED
|
@@ -18419,8 +18419,8 @@ import { Readable, Writable } from "node:stream";
|
|
|
18419
18419
|
|
|
18420
18420
|
// packages/client/src/index.js
|
|
18421
18421
|
import { spawn } from "node:child_process";
|
|
18422
|
-
import { createHash } from "node:crypto";
|
|
18423
|
-
import { open, readFile, realpath, stat, lstat, mkdir, readlink, unlink } from "node:fs/promises";
|
|
18422
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
18423
|
+
import { open, readFile, writeFile, realpath, stat, lstat, mkdir, readlink, unlink, readdir, rename, rmdir, link } from "node:fs/promises";
|
|
18424
18424
|
import net from "node:net";
|
|
18425
18425
|
import os from "node:os";
|
|
18426
18426
|
import path from "node:path";
|
|
@@ -18552,7 +18552,7 @@ var AlpRpcError = class extends Error {
|
|
|
18552
18552
|
};
|
|
18553
18553
|
var ERROR = Object.freeze({ failed: 1e3, notFound: 1001, forbidden: 1003, conflict: 1004, unavailable: 1005, protocol: 1006 });
|
|
18554
18554
|
var RPC_ERROR = Object.freeze({ parseError: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602 });
|
|
18555
|
-
var CAPABILITY = Object.freeze({ attachAfter: "attach.after", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", settingsLanguage: "settings.language", sessionListPage: "session.list.page", questionWatch: "question.watch", questionWatchTurns: "question.watch.turns", workspaceFiles: "workspace.files", workspaceFilesRead: "workspace.files.read", promptImages: "prompt.images" });
|
|
18555
|
+
var CAPABILITY = Object.freeze({ attachAfter: "attach.after", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", libraryProjectWrite: "library.project.write", settingsLanguage: "settings.language", sessionListPage: "session.list.page", questionWatch: "question.watch", questionWatchTurns: "question.watch.turns", workspaceFiles: "workspace.files", workspaceFilesRead: "workspace.files.read", promptImages: "prompt.images" });
|
|
18556
18556
|
var PROMPT_IMAGES = Object.freeze({ mimeTypes: Object.freeze(["image/png", "image/jpeg", "image/gif", "image/webp"]), maxCount: 8, maxBytes: 5 * 1024 * 1024, maxTotalBytes: 11 * 1024 * 1024 });
|
|
18557
18557
|
var RPC_METHODS = Object.freeze(["assignment.recall", "board.list", "catalog.get", "daemon.hello", "daemon.pause", "daemon.pauses", "daemon.resume", "daemon.shutdown", "daemon.status", "daemon.usage", "extension.register", "extension.unregister", "library.delete", "library.duplicate", "library.get", "library.list", "library.models", "library.rename", "library.save", "library.skills.set", "question.answer", "question.list", "question.watch", "session.archive", "session.attach", "session.close", "session.configure", "session.create", "session.get", "session.interrupt", "session.list", "session.log", "session.message", "session.preview", "session.prompt", "session.release", "session.rename", "session.status", "settings.language.get", "settings.language.set", "task.create", "task.gate.resolve", "task.list", "task.update", "team.list", "workspace.files.list", "workspace.files.read", "workspace.files.search"]);
|
|
18558
18558
|
var WORKSPACE_FILES = Object.freeze({ maxReadBytes: 1024 * 1024, maxListEntries: 1e3, maxPathLength: 4096 });
|
|
@@ -19158,10 +19158,86 @@ async function tail(file2, lines = 30) {
|
|
|
19158
19158
|
return "";
|
|
19159
19159
|
}
|
|
19160
19160
|
}
|
|
19161
|
+
async function lifecycleOwners(home) {
|
|
19162
|
+
const directory = path.join(home, "state", "alpd.starting");
|
|
19163
|
+
const names = await readdir(directory).catch((error51) => {
|
|
19164
|
+
if (error51.code === "ENOENT") return [];
|
|
19165
|
+
throw error51;
|
|
19166
|
+
});
|
|
19167
|
+
return names.map((token) => {
|
|
19168
|
+
const match = /^owner-(\d+)-(\d+)-(start|stop)-[a-f0-9-]+$/.exec(token);
|
|
19169
|
+
if (!match) throw new Error("Invalid alpd startup admission owner");
|
|
19170
|
+
return { token, pid: Number(match[1]), bootTime: Number(match[2]), action: match[3] };
|
|
19171
|
+
});
|
|
19172
|
+
}
|
|
19173
|
+
async function readDaemonLifecycle(home) {
|
|
19174
|
+
return (await lifecycleOwners(home)).find((owner) => lockAlive(owner));
|
|
19175
|
+
}
|
|
19176
|
+
async function withDaemonLifecycle(home, action, run, { joinStart = false } = {}) {
|
|
19177
|
+
try {
|
|
19178
|
+
await assertPrivateDirectory(home, { create: true });
|
|
19179
|
+
} catch (cause) {
|
|
19180
|
+
throw Object.assign(new Error(`ALP_HOME_UNUSABLE, alpd failed to start while preparing its home: ${cause.message}`, { cause }), { code: "ALP_HOME_UNUSABLE" });
|
|
19181
|
+
}
|
|
19182
|
+
const state = path.join(home, "state");
|
|
19183
|
+
await assertPrivateDirectory(state, { create: true });
|
|
19184
|
+
const directory = path.join(state, "alpd.starting");
|
|
19185
|
+
const token = `owner-${process.pid}-${bootTime()}-${action}-${randomUUID()}`;
|
|
19186
|
+
const candidate = path.join(state, `alpd.starting-${token}`);
|
|
19187
|
+
await mkdir(candidate, { mode: 448 });
|
|
19188
|
+
await writeFile(path.join(candidate, token), JSON.stringify({ pid: process.pid, bootTime: bootTime(), action }), { mode: 384 });
|
|
19189
|
+
try {
|
|
19190
|
+
let joined = false;
|
|
19191
|
+
if (joinStart) {
|
|
19192
|
+
const owner = await readDaemonLifecycle(home);
|
|
19193
|
+
if (owner?.action === "start") {
|
|
19194
|
+
try {
|
|
19195
|
+
await link(path.join(directory, owner.token), path.join(directory, token));
|
|
19196
|
+
joined = true;
|
|
19197
|
+
} catch (error51) {
|
|
19198
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
19199
|
+
}
|
|
19200
|
+
}
|
|
19201
|
+
}
|
|
19202
|
+
while (!joined) {
|
|
19203
|
+
try {
|
|
19204
|
+
await rename(candidate, directory);
|
|
19205
|
+
break;
|
|
19206
|
+
} catch (error51) {
|
|
19207
|
+
if (!["EEXIST", "ENOTEMPTY", "EPERM", "EACCES"].includes(error51.code)) throw error51;
|
|
19208
|
+
for (const owner of await lifecycleOwners(home)) {
|
|
19209
|
+
if (!lockAlive(owner)) await unlink(path.join(directory, owner.token)).catch((error52) => {
|
|
19210
|
+
if (error52.code !== "ENOENT") throw error52;
|
|
19211
|
+
});
|
|
19212
|
+
}
|
|
19213
|
+
await rmdir(directory).catch((error52) => {
|
|
19214
|
+
if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error52.code)) throw error52;
|
|
19215
|
+
});
|
|
19216
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
19217
|
+
}
|
|
19218
|
+
}
|
|
19219
|
+
try {
|
|
19220
|
+
return await run();
|
|
19221
|
+
} finally {
|
|
19222
|
+
await unlink(path.join(directory, token));
|
|
19223
|
+
await rmdir(directory).catch((error51) => {
|
|
19224
|
+
if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error51.code)) throw error51;
|
|
19225
|
+
});
|
|
19226
|
+
}
|
|
19227
|
+
} finally {
|
|
19228
|
+
await unlink(path.join(candidate, token)).catch((error51) => {
|
|
19229
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
19230
|
+
});
|
|
19231
|
+
await rmdir(candidate).catch((error51) => {
|
|
19232
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
19233
|
+
});
|
|
19234
|
+
}
|
|
19235
|
+
}
|
|
19161
19236
|
async function ensureDaemon({ home = alpHome(), entry = (
|
|
19162
19237
|
/** @type {string | undefined} */
|
|
19163
19238
|
void 0
|
|
19164
|
-
), env = process.env, execPath = process.execPath, timeoutMs = 15e3 } = {}) {
|
|
19239
|
+
), env = process.env, execPath = process.execPath, timeoutMs = 15e3, lifecycleLocked = false } = {}) {
|
|
19240
|
+
if (!lifecycleLocked) return withDaemonLifecycle(home, "start", () => ensureDaemon({ home, entry, env, execPath, timeoutMs, lifecycleLocked: true }));
|
|
19165
19241
|
const ready = async () => {
|
|
19166
19242
|
const lock = await readLock(home);
|
|
19167
19243
|
return lockAlive(lock) && lock.ready ? lock.socket : void 0;
|
|
@@ -19174,7 +19250,7 @@ async function ensureDaemon({ home = alpHome(), entry = (
|
|
|
19174
19250
|
entry ??= await findDaemonEntry({ home, env });
|
|
19175
19251
|
if (!entry) throw new Error("ALP daemon is not running and alpd.js was not found. Install the ALP CLI and run `alp daemon start`, or set ALP_DAEMON_ENTRY to alpd.js");
|
|
19176
19252
|
await new Promise((resolve, reject) => {
|
|
19177
|
-
const launcher = spawn(execPath, [entry, "--detach"], {
|
|
19253
|
+
const launcher = spawn(execPath, [entry, "--detach", `--startup-timeout=${timeoutMs}`], {
|
|
19178
19254
|
env: { ...env, ALP_HOME: home, ELECTRON_RUN_AS_NODE: "1" },
|
|
19179
19255
|
stdio: ["ignore", "ignore", "pipe"],
|
|
19180
19256
|
windowsHide: true
|
|
@@ -19199,12 +19275,12 @@ ${log}` : ""}`);
|
|
|
19199
19275
|
}
|
|
19200
19276
|
|
|
19201
19277
|
// packages/client/src/supervise.js
|
|
19202
|
-
import { mkdir as mkdir3, unlink as unlink3, writeFile as
|
|
19278
|
+
import { mkdir as mkdir3, unlink as unlink3, writeFile as writeFile3, stat as stat2 } from "node:fs/promises";
|
|
19203
19279
|
import path3 from "node:path";
|
|
19204
19280
|
|
|
19205
19281
|
// packages/client/src/service.js
|
|
19206
19282
|
import { createHash as createHash2 } from "node:crypto";
|
|
19207
|
-
import { mkdir as mkdir2, readFile as readFile2, unlink as unlink2, writeFile } from "node:fs/promises";
|
|
19283
|
+
import { mkdir as mkdir2, readFile as readFile2, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
|
|
19208
19284
|
import os2 from "node:os";
|
|
19209
19285
|
import path2 from "node:path";
|
|
19210
19286
|
|
|
@@ -19301,6 +19377,9 @@ async function releaseHold(home) {
|
|
|
19301
19377
|
await unlink3(holdFile(home)).catch(() => {
|
|
19302
19378
|
});
|
|
19303
19379
|
}
|
|
19380
|
+
async function daemonHeld(home) {
|
|
19381
|
+
return stat2(holdFile(home)).then(() => true, () => false);
|
|
19382
|
+
}
|
|
19304
19383
|
async function whenReady(home, timeoutMs) {
|
|
19305
19384
|
const deadline = Date.now() + timeoutMs;
|
|
19306
19385
|
while (Date.now() < deadline) {
|
|
@@ -19310,20 +19389,24 @@ async function whenReady(home, timeoutMs) {
|
|
|
19310
19389
|
}
|
|
19311
19390
|
throw new Error(`alpd did not become ready within ${timeoutMs / 1e3} s; see ${path3.join(home, "logs")}`);
|
|
19312
19391
|
}
|
|
19313
|
-
async function startDaemon({ home, entry, env = process.env, timeoutMs = 15e3 }) {
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19392
|
+
async function startDaemon({ home, entry, env = process.env, timeoutMs = 15e3, automatic = false }) {
|
|
19393
|
+
return withDaemonLifecycle(home, "start", async () => {
|
|
19394
|
+
if (automatic) {
|
|
19395
|
+
if (await daemonHeld(home)) return;
|
|
19396
|
+
} else await releaseHold(home);
|
|
19397
|
+
const lock = await readLock(home);
|
|
19398
|
+
if (lockAlive(lock)) return lock.ready ? lock.socket : whenReady(home, timeoutMs);
|
|
19399
|
+
const service = serviceFor({ home, env });
|
|
19400
|
+
if (service && await installedProgram(service)) {
|
|
19401
|
+
await startService(service);
|
|
19402
|
+
return whenReady(home, timeoutMs);
|
|
19403
|
+
}
|
|
19404
|
+
return ensureDaemon({ home, entry, env, timeoutMs, lifecycleLocked: true });
|
|
19405
|
+
});
|
|
19323
19406
|
}
|
|
19324
19407
|
|
|
19325
19408
|
// packages/cli/src/acp/agent.ts
|
|
19326
|
-
import { randomUUID } from "node:crypto";
|
|
19409
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
19327
19410
|
import path4 from "node:path";
|
|
19328
19411
|
var unlistedOf = ({ teams, team, teamLabel }) => teams.some((entry) => entry.id === team) ? void 0 : { id: team, label: teamLabel ?? (team === "custom" ? "Custom" : team) };
|
|
19329
19412
|
var MODES = [
|
|
@@ -19681,7 +19764,7 @@ ${item2.text}`;
|
|
|
19681
19764
|
send(session, { sessionUpdate: "config_option_update", configOptions: configOptions(session) });
|
|
19682
19765
|
}
|
|
19683
19766
|
if (session.waiting) throw RequestError.invalidRequest(void 0, "A prompt is already running in this session");
|
|
19684
|
-
const clientMessageId =
|
|
19767
|
+
const clientMessageId = randomUUID2();
|
|
19685
19768
|
session.ours.add(clientMessageId);
|
|
19686
19769
|
let done;
|
|
19687
19770
|
const ended = new Promise((resolve) => {
|
|
@@ -19726,7 +19809,7 @@ ${item2.text}`;
|
|
|
19726
19809
|
})).onRequest("authenticate", () => ({})).onRequest("session/new", async ({ params }) => {
|
|
19727
19810
|
const cwd = absolute(params.cwd);
|
|
19728
19811
|
const mcpServers = hostServers(params.mcpServers);
|
|
19729
|
-
const session = track(`acp-${
|
|
19812
|
+
const session = track(`acp-${randomUUID2()}`, cwd, mcpServers, await preview(cwd, mcpServers), false);
|
|
19730
19813
|
queueMicrotask(() => send(session, { sessionUpdate: "available_commands_update", availableCommands: COMMANDS }));
|
|
19731
19814
|
return { sessionId: session.id, configOptions: configOptions(session), modes: modeState(session) };
|
|
19732
19815
|
}).onRequest("session/load", async ({ params }) => {
|
|
@@ -19803,7 +19886,7 @@ ${item2.text}`;
|
|
|
19803
19886
|
|
|
19804
19887
|
// packages/cli/src/acp/main.ts
|
|
19805
19888
|
async function runAcp({ daemonEntry }) {
|
|
19806
|
-
const version2 = true ? "0.0.
|
|
19889
|
+
const version2 = true ? "0.0.1-beta.3" : "0.0.0";
|
|
19807
19890
|
const logger = createLogger({ component: "acp" });
|
|
19808
19891
|
const log = (message, fields) => logger.warn(message, fields);
|
|
19809
19892
|
const app = createAcpAgent({
|