@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 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.0-beta.2` (until it is on npm, `@beta` installs
8
- `0.0.0-beta.1`; check with `npm view @anhlp/alp dist-tags`): see the
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.0-beta.2`); a plain `npm install -g @anhlp/alp`
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 beta supports Paseo `>=0.11.1 <0.12.0`. 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.
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 a later 0.11 release (the Tasks panel needs its 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`.
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 writeFile2, stat as stat2 } from "node:fs/promises";
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
- await releaseHold(home);
19315
- const lock = await readLock(home);
19316
- if (lockAlive(lock)) return lock.ready ? lock.socket : whenReady(home, timeoutMs);
19317
- const service = serviceFor({ home, env });
19318
- if (service && await installedProgram(service)) {
19319
- await startService(service);
19320
- return whenReady(home, timeoutMs);
19321
- }
19322
- return ensureDaemon({ home, entry, env, timeoutMs });
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 = randomUUID();
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-${randomUUID()}`, cwd, mcpServers, await preview(cwd, mcpServers), false);
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.0-beta.2" : "0.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({