@alpacakit/agents 0.1.0-beta.37 → 0.1.0-beta.39
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/internal/controlled-process/host-controller.d.ts.map +1 -1
- package/dist/internal/controlled-process/host-controller.js +23 -1
- package/dist/internal/controlled-process/supervisor-runtime.js +14 -1
- package/package.json +2 -2
- package/dist/auth/profile-store.d.ts +0 -14
- package/dist/auth/profile-store.d.ts.map +0 -1
- package/dist/auth/profile-store.js +0 -102
- package/dist/keyring/index.d.ts +0 -2
- package/dist/keyring/index.d.ts.map +0 -1
- package/dist/keyring/index.js +0 -1
- package/dist/keyring/resolver.d.ts +0 -9
- package/dist/keyring/resolver.d.ts.map +0 -1
- package/dist/keyring/resolver.js +0 -64
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-controller.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/host-controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EAErB,6BAA6B,EAC9B,MAAM,kCAAkC,CAAC;AAQ1C,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA6BtE,OAAO,KAAK,EACV,uBAAuB,EAEvB,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAqC/B,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;CAC3C,CAAC;AAEF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACrD,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACvD;AAsED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,2BAA2B,CAAC,CAoJtC;
|
|
1
|
+
{"version":3,"file":"host-controller.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/host-controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EAErB,6BAA6B,EAC9B,MAAM,kCAAkC,CAAC;AAQ1C,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA6BtE,OAAO,KAAK,EACV,uBAAuB,EAEvB,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAqC/B,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;CAC3C,CAAC;AAEF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACrD,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACvD;AAsED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,2BAA2B,CAAC,CAoJtC;AA6oBD,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,CAAC;CACrE,GAAG,OAAO,CAAC,6BAA6B,CAAC,CA4BzC"}
|
|
@@ -366,7 +366,17 @@ async function requestTerminationBeforeDeadline(input) {
|
|
|
366
366
|
if (raced.kind === "closed") {
|
|
367
367
|
return null;
|
|
368
368
|
}
|
|
369
|
-
if (raced.kind === "
|
|
369
|
+
if (raced.kind === "deadline") {
|
|
370
|
+
throw raced.error;
|
|
371
|
+
}
|
|
372
|
+
if (raced.kind === "failure") {
|
|
373
|
+
// A supervisor that rolled back before reporting its launch failure has
|
|
374
|
+
// already unlinked its socket, so this request can fail while its close is
|
|
375
|
+
// the only remaining evidence. Join that close within the same deadline;
|
|
376
|
+
// the caller still has to prove absence from the signed record.
|
|
377
|
+
if (await closedBeforeDeadline(input.supervisorClose, input.deadline)) {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
370
380
|
throw raced.error;
|
|
371
381
|
}
|
|
372
382
|
if (raced.response.kind !== SUPERVISOR_RESPONSE_KIND.termination) {
|
|
@@ -379,6 +389,18 @@ async function requestTerminationBeforeDeadline(input) {
|
|
|
379
389
|
supervisorExit: null,
|
|
380
390
|
};
|
|
381
391
|
}
|
|
392
|
+
function closedBeforeDeadline(supervisorClose, deadline) {
|
|
393
|
+
if (supervisorClose.didClose()) {
|
|
394
|
+
return Promise.resolve(true);
|
|
395
|
+
}
|
|
396
|
+
return new Promise((resolve) => {
|
|
397
|
+
const timeout = setTimeout(() => resolve(false), Math.max(0, deadline - Date.now()));
|
|
398
|
+
void supervisorClose.promise.then(() => {
|
|
399
|
+
clearTimeout(timeout);
|
|
400
|
+
resolve(true);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
}
|
|
382
404
|
async function raceSupervisorRequestWithClose(input) {
|
|
383
405
|
if (input.supervisorClose.didClose()) {
|
|
384
406
|
return {
|
|
@@ -238,7 +238,6 @@ async function runSupervisorSession(input) {
|
|
|
238
238
|
shimControl = shim.stdio[SHIM_CONTROL_FD];
|
|
239
239
|
shimStatus = shim.stdio[SHIM_STATUS_FD];
|
|
240
240
|
shimInput = shim.stdio.at(SHIM_INPUT_FD);
|
|
241
|
-
shimInput.on("error", () => { });
|
|
242
241
|
const pid = requireShimPid(shim);
|
|
243
242
|
shimPid = pid;
|
|
244
243
|
const record = {
|
|
@@ -701,9 +700,23 @@ function spawnShim(bootstrap) {
|
|
|
701
700
|
env: {},
|
|
702
701
|
stdio: ["ignore", "pipe", "pipe", "pipe", "pipe", "pipe"],
|
|
703
702
|
});
|
|
703
|
+
containShimStdioErrors(shim);
|
|
704
704
|
shim.once("error", () => { });
|
|
705
705
|
return shim;
|
|
706
706
|
}
|
|
707
|
+
/**
|
|
708
|
+
* The supervisor owns every shim stdio error from spawn until the shim closes.
|
|
709
|
+
* Framed writes, status reads, and output pumps keep their own listeners and
|
|
710
|
+
* remain responsible for their outcomes; this owner only contains the pipe
|
|
711
|
+
* errors a dying shim raises once those operations have settled, so a late
|
|
712
|
+
* reset can never end supervisor custody before its failure is reported.
|
|
713
|
+
*/
|
|
714
|
+
function containShimStdioErrors(shim) {
|
|
715
|
+
for (const stream of shim.stdio) {
|
|
716
|
+
stream?.on("error", consumeShimStdioError);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
function consumeShimStdioError() { }
|
|
707
720
|
function waitForShimClose(shim) {
|
|
708
721
|
return new Promise((resolve) => {
|
|
709
722
|
shim.once("close", () => resolve());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alpacakit/agents",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.39",
|
|
4
4
|
"description": "Run AI coding agents programmatically: execute, stream, classify failures, fail over.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"zod": "^4.3.6",
|
|
48
|
-
"@alpacakit/core": "0.1.0-beta.
|
|
48
|
+
"@alpacakit/core": "0.1.0-beta.39"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"typescript": "^5.9.3",
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal file-backed profile store (A11): rows follow rule 2 and never
|
|
3
|
-
* contain secret values (A9) — materials carry refs and paths only.
|
|
4
|
-
* Profile management UX is consumer territory. Unlike fleet state, this
|
|
5
|
-
* is durable user data: malformed or unknown-version files are typed
|
|
6
|
-
* errors, never silent resets.
|
|
7
|
-
*/
|
|
8
|
-
import { type AuthProfile } from "./material.js";
|
|
9
|
-
export interface ProfileStore {
|
|
10
|
-
list(): Promise<AuthProfile[]>;
|
|
11
|
-
save(profiles: AuthProfile[]): Promise<void>;
|
|
12
|
-
}
|
|
13
|
-
export declare function fileProfileStore(filePath: string): ProfileStore;
|
|
14
|
-
//# sourceMappingURL=profile-store.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"profile-store.d.ts","sourceRoot":"","sources":["../../src/auth/profile-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAcrE,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AA+FD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAE/D"}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal file-backed profile store (A11): rows follow rule 2 and never
|
|
3
|
-
* contain secret values (A9) — materials carry refs and paths only.
|
|
4
|
-
* Profile management UX is consumer territory. Unlike fleet state, this
|
|
5
|
-
* is durable user data: malformed or unknown-version files are typed
|
|
6
|
-
* errors, never silent resets.
|
|
7
|
-
*/
|
|
8
|
-
import { readFile } from "node:fs/promises";
|
|
9
|
-
import { AGENT_ERROR_CODE, AgentError } from "../core/error.js";
|
|
10
|
-
import { isErrnoCode, writeFileAtomically } from "../core/fs.js";
|
|
11
|
-
import { readUnknownRecord } from "../core/json.js";
|
|
12
|
-
import { TEXT_ENCODING } from "../core/values.js";
|
|
13
|
-
import { AUTH_MATERIAL_KIND } from "./material.js";
|
|
14
|
-
const PROFILE_FILE_VERSION = 1;
|
|
15
|
-
const PROFILE_STORE_ERROR_REASON = {
|
|
16
|
-
unreadable: "unreadable file",
|
|
17
|
-
malformedJson: "malformed JSON",
|
|
18
|
-
rootMustBeObject: "root must be an object",
|
|
19
|
-
unsupportedVersion: "unsupported version",
|
|
20
|
-
invalidProfiles: "profiles must be auth profile records",
|
|
21
|
-
};
|
|
22
|
-
function profileStoreError(reason, cause) {
|
|
23
|
-
return new AgentError({
|
|
24
|
-
code: AGENT_ERROR_CODE.invalidInput,
|
|
25
|
-
classification: AgentError.agentFailure(AGENT_ERROR_CODE.invalidInput),
|
|
26
|
-
message: `Invalid auth profile store: ${reason}`,
|
|
27
|
-
data: { reason },
|
|
28
|
-
...(cause === undefined ? {} : { cause }),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
function isSecretRef(value) {
|
|
32
|
-
const record = readUnknownRecord(value);
|
|
33
|
-
if (record === null) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
const { resolver, id } = record;
|
|
37
|
-
return typeof resolver === "string" && typeof id === "string";
|
|
38
|
-
}
|
|
39
|
-
function isAuthProfile(value) {
|
|
40
|
-
const record = readUnknownRecord(value);
|
|
41
|
-
if (record === null) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
const { id, agentId, material: materialValue } = record;
|
|
45
|
-
const material = readUnknownRecord(materialValue);
|
|
46
|
-
if (material === null) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
const { kind, path, secret } = material;
|
|
50
|
-
const materialValid = (kind === AUTH_MATERIAL_KIND.homeDir && typeof path === "string") ||
|
|
51
|
-
(kind === AUTH_MATERIAL_KIND.apiKey && isSecretRef(secret));
|
|
52
|
-
return typeof id === "string" && typeof agentId === "string" && materialValid;
|
|
53
|
-
}
|
|
54
|
-
function parseProfileFile(raw) {
|
|
55
|
-
let parsed;
|
|
56
|
-
try {
|
|
57
|
-
parsed = JSON.parse(raw);
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
throw profileStoreError(PROFILE_STORE_ERROR_REASON.malformedJson, error);
|
|
61
|
-
}
|
|
62
|
-
const root = readUnknownRecord(parsed);
|
|
63
|
-
if (root === null) {
|
|
64
|
-
throw profileStoreError(PROFILE_STORE_ERROR_REASON.rootMustBeObject);
|
|
65
|
-
}
|
|
66
|
-
const { version, profiles } = root;
|
|
67
|
-
if (version !== PROFILE_FILE_VERSION) {
|
|
68
|
-
throw profileStoreError(PROFILE_STORE_ERROR_REASON.unsupportedVersion);
|
|
69
|
-
}
|
|
70
|
-
if (!Array.isArray(profiles) || !profiles.every(isAuthProfile)) {
|
|
71
|
-
throw profileStoreError(PROFILE_STORE_ERROR_REASON.invalidProfiles);
|
|
72
|
-
}
|
|
73
|
-
return profiles;
|
|
74
|
-
}
|
|
75
|
-
class FileProfileStore {
|
|
76
|
-
filePath;
|
|
77
|
-
constructor(filePath) {
|
|
78
|
-
this.filePath = filePath;
|
|
79
|
-
}
|
|
80
|
-
async list() {
|
|
81
|
-
let raw;
|
|
82
|
-
try {
|
|
83
|
-
raw = await readFile(this.filePath, TEXT_ENCODING);
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
// Durable user data: only "not created yet" reads as empty; any
|
|
87
|
-
// other read failure surfaces instead of masking real profiles.
|
|
88
|
-
if (isErrnoCode(error, "ENOENT")) {
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
throw profileStoreError(PROFILE_STORE_ERROR_REASON.unreadable, error);
|
|
92
|
-
}
|
|
93
|
-
return parseProfileFile(raw);
|
|
94
|
-
}
|
|
95
|
-
async save(profiles) {
|
|
96
|
-
const file = { version: PROFILE_FILE_VERSION, profiles };
|
|
97
|
-
await writeFileAtomically(this.filePath, `${JSON.stringify(file, null, 2)}\n`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
export function fileProfileStore(filePath) {
|
|
101
|
-
return new FileProfileStore(filePath);
|
|
102
|
-
}
|
package/dist/keyring/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC"}
|
package/dist/keyring/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { keyringResolver, } from "./resolver.js";
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { SecretResolver } from "../core/ports.js";
|
|
2
|
-
export type KeyringResolverOptions = {
|
|
3
|
-
readonly service: string;
|
|
4
|
-
};
|
|
5
|
-
type KeyringModuleLoader = () => Promise<unknown>;
|
|
6
|
-
export declare function keyringResolver(options: KeyringResolverOptions): SecretResolver;
|
|
7
|
-
export declare function createKeyringResolver(options: KeyringResolverOptions, loader: KeyringModuleLoader): SecretResolver;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/keyring/resolver.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,KAAK,mBAAmB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;AAiBlD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,cAAc,CAEhB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE,mBAAmB,GAC1B,cAAc,CA4BhB"}
|
package/dist/keyring/resolver.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// biome-ignore-all lint/style/useNamingConvention: Keyring peer exports mirror upstream PascalCase names.
|
|
2
|
-
import { AGENT_ERROR_CODE, AgentError, NOT_AVAILABLE_CAUSE, } from "../core/error.js";
|
|
3
|
-
const KEYRING_PEER_PACKAGE = "@napi-rs/keyring";
|
|
4
|
-
const KEYRING_ENTRY_EXPORT = "Entry";
|
|
5
|
-
const EMPTY_SECRET_VALUE = "";
|
|
6
|
-
const defaultKeyringModuleLoader = () => import(KEYRING_PEER_PACKAGE);
|
|
7
|
-
export function keyringResolver(options) {
|
|
8
|
-
return createKeyringResolver(options, defaultKeyringModuleLoader);
|
|
9
|
-
}
|
|
10
|
-
export function createKeyringResolver(options, loader) {
|
|
11
|
-
const service = options.service;
|
|
12
|
-
let modulePromise = null;
|
|
13
|
-
const load = () => {
|
|
14
|
-
modulePromise ??= loadKeyringModule(loader);
|
|
15
|
-
return modulePromise;
|
|
16
|
-
};
|
|
17
|
-
return {
|
|
18
|
-
resolve: async (id) => {
|
|
19
|
-
const module = await load();
|
|
20
|
-
try {
|
|
21
|
-
// Absent entries are the `null` return (`Entry.getPassword():
|
|
22
|
-
// string | null` in @napi-rs/keyring); anything thrown is a real
|
|
23
|
-
// keychain failure, never "missing secret".
|
|
24
|
-
const value = await new module.Entry(service, id).getPassword();
|
|
25
|
-
return value === EMPTY_SECRET_VALUE ? null : value;
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
throw new AgentError({
|
|
29
|
-
code: AGENT_ERROR_CODE.failed,
|
|
30
|
-
classification: AgentError.agentFailure(AGENT_ERROR_CODE.failed),
|
|
31
|
-
message: `Keyring secret "${id}" could not be read`,
|
|
32
|
-
data: { exitCode: null, stdoutTail: null, stderrTail: null },
|
|
33
|
-
cause: error,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
async function loadKeyringModule(loader) {
|
|
40
|
-
let loaded;
|
|
41
|
-
try {
|
|
42
|
-
loaded = await loader();
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
throw keyringNotInstalledError(error);
|
|
46
|
-
}
|
|
47
|
-
if (typeof loaded !== "object" || loaded === null) {
|
|
48
|
-
throw keyringNotInstalledError(null);
|
|
49
|
-
}
|
|
50
|
-
const entry = Reflect.get(loaded, KEYRING_ENTRY_EXPORT);
|
|
51
|
-
if (typeof entry !== "function") {
|
|
52
|
-
throw keyringNotInstalledError(null);
|
|
53
|
-
}
|
|
54
|
-
return { Entry: entry };
|
|
55
|
-
}
|
|
56
|
-
function keyringNotInstalledError(cause) {
|
|
57
|
-
return new AgentError({
|
|
58
|
-
code: AGENT_ERROR_CODE.notAvailable,
|
|
59
|
-
classification: AgentError.agentFailure(AGENT_ERROR_CODE.notAvailable),
|
|
60
|
-
message: "Keyring peer is not installed",
|
|
61
|
-
data: { cause: NOT_AVAILABLE_CAUSE.notInstalled },
|
|
62
|
-
cause,
|
|
63
|
-
});
|
|
64
|
-
}
|