@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/README.md
CHANGED
|
@@ -4,11 +4,11 @@ Provider-neutral filesystem agent resolution with a server-side Paseo provider.
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
The current release is the beta `0.0.
|
|
8
|
-
`0.0.0-beta.
|
|
7
|
+
The current release is the beta `0.0.1-beta.3` (until it is on npm, `@beta` installs
|
|
8
|
+
`0.0.0-beta.2`; check with `npm view @anhlp/alp dist-tags`): see the
|
|
9
9
|
[release notes](docs/alp/beta-release-notes.md) for what it includes and its known issues, and
|
|
10
10
|
[installing, upgrading and removing the beta](docs/alp/beta-install.md) for every step. Name the
|
|
11
|
-
beta when you install (`@beta`, or exactly `@0.0.
|
|
11
|
+
beta when you install (`@beta`, or exactly `@0.0.1-beta.3`); a plain `npm install -g @anhlp/alp`
|
|
12
12
|
installs whatever npm's `latest` tag points at, which moves to a new beta only after that
|
|
13
13
|
beta's post-publish checks.
|
|
14
14
|
|
|
@@ -334,13 +334,13 @@ Details: [ALPD §60](plans/reference/ALPD.md#60-alp-as-an-acp-agent-2026-10-10).
|
|
|
334
334
|
|
|
335
335
|
## Paseo
|
|
336
336
|
|
|
337
|
-
To use the published plugin, follow [the plugin's README](integrations/paseo/README.md) or [beta-install.md](docs/alp/beta-install.md#paseo-plugin): `paseo plugin add npm:@anhlp/alp-paseo@beta` (from `0.0.0-beta.2`; in beta.1 the `npm:` source fails, [#111](https://github.com/phucanh08/alp/issues/111), so there run `npm install -g @anhlp/alp-paseo@beta`, then `paseo plugin add "$(npm root -g)/@anhlp/alp-paseo"`). The
|
|
337
|
+
To use the published plugin, follow [the plugin's README](integrations/paseo/README.md) or [beta-install.md](docs/alp/beta-install.md#paseo-plugin): `paseo plugin add npm:@anhlp/alp-paseo@beta` (from `0.0.0-beta.2`; in beta.1 the `npm:` source fails, [#111](https://github.com/phucanh08/alp/issues/111), so there run `npm install -g @anhlp/alp-paseo@beta`, then `paseo plugin add "$(npm root -g)/@anhlp/alp-paseo"`). The plugin supports Paseo `>=0.11.1 <0.13.0` (`0.0.0-beta.2` and earlier: `<0.12.0`, so Paseo 0.12 needs `0.0.1-beta.3` or later). See [installation and runtime behavior](docs/alp/paseo-plugin.md) and the historical [phase acceptance evidence](docs/alp/phase-2-5-results.md). The plugin was verified with Paseo 0.11.1 and live Codex and Claude Code permission changes before the beta; the beta's own runs are in [beta-scope.md](docs/alp/beta-scope.md) §6.
|
|
338
338
|
|
|
339
339
|
**Settings → ALP** manages your library of teams, agents, skills, MCP servers, hooks and ACP providers, and the **ALP project** workspace panel manages a project's overrides ([settings screen](docs/alp/paseo-plugin.md#alp-settings-and-the-project-panel)).
|
|
340
340
|
|
|
341
341
|
### Build and add the plugin to Paseo
|
|
342
342
|
|
|
343
|
-
This section builds the plugin from a checkout of this repository. Prerequisites: Node.js 22 (22.12 or later) or 24, Paseo 0.11.1 or
|
|
343
|
+
This section builds the plugin from a checkout of this repository. Prerequisites: Node.js 22 (22.12 or later) or 24, Paseo 0.11.1 or later, up to 0.12.x (the Tasks panel needs the 0.11.1 plugin client API), and a logged-in Codex CLI and/or Claude Code installation. Set `ALP_CODEX_BIN` or `ALP_CLAUDE_BIN` to an absolute native executable path when it is not available on `PATH`.
|
|
344
344
|
|
|
345
345
|
Install dependencies, type-check the plugin, and build its server bundle:
|
|
346
346
|
|
package/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({
|