alp-code 0.9.0
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/CHANGELOG.md +770 -0
- package/LICENSE +21 -0
- package/README.md +295 -0
- package/alp.config.yaml +5 -0
- package/dist/src/agents/agent-definition.js +28 -0
- package/dist/src/agents/capability-catalog.js +33 -0
- package/dist/src/agents/compaction.js +36 -0
- package/dist/src/agents/errors.js +12 -0
- package/dist/src/agents/librarian.js +38 -0
- package/dist/src/agents/main.js +37 -0
- package/dist/src/agents/memory-grant.js +29 -0
- package/dist/src/agents/model-context.js +70 -0
- package/dist/src/agents/modes.js +134 -0
- package/dist/src/agents/oracle.js +36 -0
- package/dist/src/agents/read-thread.js +38 -0
- package/dist/src/agents/registry.js +238 -0
- package/dist/src/agents/render-identity.js +38 -0
- package/dist/src/agents/review.js +37 -0
- package/dist/src/agents/search.js +37 -0
- package/dist/src/agents/shared/house-rules.js +33 -0
- package/dist/src/agents/shared/principal.js +18 -0
- package/dist/src/agents/shared/voice.js +29 -0
- package/dist/src/agents/titling.js +32 -0
- package/dist/src/agents/types.js +15 -0
- package/dist/src/backend/execution-backend.js +2 -0
- package/dist/src/backend/local-execution-store.js +144 -0
- package/dist/src/backend/local-process-backend.js +533 -0
- package/dist/src/backend/local-supervisor.js +104 -0
- package/dist/src/cli/alp.js +380 -0
- package/dist/src/cli/commands/context.js +203 -0
- package/dist/src/cli/commands/delegate.js +136 -0
- package/dist/src/cli/commands/identity-sync.js +31 -0
- package/dist/src/cli/commands/init.js +184 -0
- package/dist/src/cli/commands/mode.js +22 -0
- package/dist/src/cli/commands/principal.js +114 -0
- package/dist/src/cli/commands/run-main.js +90 -0
- package/dist/src/cli/commands/runtime.js +21 -0
- package/dist/src/cli/mode-preference-store.js +62 -0
- package/dist/src/cli/mode-selector.js +178 -0
- package/dist/src/cli/update-check.js +77 -0
- package/dist/src/context/checkpoint.js +134 -0
- package/dist/src/context/compact-journal.js +153 -0
- package/dist/src/context/compact-payload.js +121 -0
- package/dist/src/context/continuity.js +70 -0
- package/dist/src/context/types.js +2 -0
- package/dist/src/delegation/backend-registry.js +40 -0
- package/dist/src/delegation/delegation-service.js +300 -0
- package/dist/src/delegation/types.js +12 -0
- package/dist/src/execution/execution-policy.js +96 -0
- package/dist/src/execution/execution-service.js +115 -0
- package/dist/src/execution/execution-store.js +78 -0
- package/dist/src/execution/identity-capsule.js +65 -0
- package/dist/src/execution/types.js +12 -0
- package/dist/src/hooks/execution-bridge.js +84 -0
- package/dist/src/index.js +4 -0
- package/dist/src/memory/adapters/markdown-file-store.js +257 -0
- package/dist/src/memory/adapters/memory-api-client.js +2 -0
- package/dist/src/memory/adapters/memory-path-mapper.js +76 -0
- package/dist/src/memory/adapters/remote-api-store.js +25 -0
- package/dist/src/memory/context-ranker.js +21 -0
- package/dist/src/memory/errors.js +58 -0
- package/dist/src/memory/memory-service.js +149 -0
- package/dist/src/memory/memory-store.js +2 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/policy/capability-policy.js +29 -0
- package/dist/src/policy/delegation-policy.js +25 -0
- package/dist/src/policy/errors.js +10 -0
- package/dist/src/policy/invariants.js +31 -0
- package/dist/src/policy/memory-policy.js +22 -0
- package/dist/src/policy/policy-engine.js +85 -0
- package/dist/src/policy/types.js +8 -0
- package/dist/src/policy/workspace-policy.js +77 -0
- package/dist/src/principal/principal-profile-store.js +89 -0
- package/dist/src/runtime/adapter-files.js +147 -0
- package/dist/src/runtime/claude-adapter.js +177 -0
- package/dist/src/runtime/codex-adapter.js +169 -0
- package/dist/src/runtime/permission-rules.js +156 -0
- package/dist/src/runtime/render-session-context.js +124 -0
- package/dist/src/runtime/render-task-input.js +33 -0
- package/dist/src/runtime/runtime-adapter.js +2 -0
- package/dist/src/runtime/runtime-preference-store.js +66 -0
- package/dist/src/runtime/runtime-selector.js +178 -0
- package/dist/src/runtime/types.js +2 -0
- package/dist/src/runtime/windows-shim.js +57 -0
- package/dist/src/state-paths.js +49 -0
- package/dist/src/workflow/output-validator.js +27 -0
- package/dist/src/workflow/repair-policy.js +8 -0
- package/dist/src/workflow/types.js +22 -0
- package/dist/src/workflow/workflow-runner.js +81 -0
- package/hooks/compact-record.cjs +109 -0
- package/hooks/session-boot.cjs +112 -0
- package/hooks/session-end.cjs +34 -0
- package/package.json +48 -0
- package/scaffold/memory/INDEX.md +27 -0
- package/scaffold/memory/README.md +76 -0
- package/scaffold/memory/projects/INDEX.md +22 -0
- package/scaffold/memory/projects/PROTOCOL.md +128 -0
- package/scaffold/memory/projects/_template/PROJECT.md +45 -0
- package/scripts/alp.cjs +126 -0
- package/scripts/alp.ps1 +4 -0
- package/scripts/alp.sh +3 -0
- package/scripts/bootstrap.cjs +144 -0
- package/scripts/checkout-release.cjs +30 -0
- package/scripts/delegate.cjs +19 -0
- package/scripts/doctor.cjs +158 -0
- package/scripts/doctor.sh +3 -0
- package/scripts/ensure-state.cjs +22 -0
- package/scripts/lib/cli-link.cjs +375 -0
- package/scripts/lib/codex-role.cjs +18 -0
- package/scripts/lib/delegation/command-runner.cjs +108 -0
- package/scripts/lib/delegation/config.cjs +81 -0
- package/scripts/lib/install-paths.cjs +154 -0
- package/scripts/lib/release-manifest.cjs +42 -0
- package/scripts/lib/semver-lite.cjs +20 -0
- package/scripts/lib/state.cjs +274 -0
- package/scripts/lib/uninstall.cjs +252 -0
- package/scripts/lib/update-check-worker.cjs +21 -0
- package/scripts/lib/update.cjs +395 -0
- package/scripts/run-role.cjs +42 -0
- package/scripts/run-role.ps1 +4 -0
- package/scripts/run-role.sh +3 -0
- package/scripts/sync-project-index.sh +167 -0
- package/skills/agent-memory/SKILL.md +109 -0
- package/skills/alp-debug/SKILL.md +90 -0
- package/skills/alp-debug/references/defense-in-depth.md +118 -0
- package/skills/alp-debug/references/investigation-methodology.md +106 -0
- package/skills/alp-debug/references/log-and-ci-analysis.md +96 -0
- package/skills/alp-debug/references/performance-diagnostics.md +112 -0
- package/skills/alp-debug/references/reporting-standards.md +120 -0
- package/skills/alp-debug/references/root-cause-tracing.md +134 -0
- package/skills/alp-debug/references/systematic-debugging.md +93 -0
- package/skills/alp-debug/references/verification.md +86 -0
- package/skills/alp-debug/scripts/find-polluter.sh +63 -0
- package/skills/alp-debug/scripts/find-polluter.test.md +102 -0
- package/skills/alp-plan/SKILL.md +128 -0
- package/skills/alp-plan/references/archive-workflow.md +77 -0
- package/skills/alp-plan/references/codebase-understanding.md +55 -0
- package/skills/alp-plan/references/output-standards.md +96 -0
- package/skills/alp-plan/references/plan-organization.md +129 -0
- package/skills/alp-plan/references/red-team-personas.md +76 -0
- package/skills/alp-plan/references/red-team-workflow.md +81 -0
- package/skills/alp-plan/references/research-phase.md +57 -0
- package/skills/alp-plan/references/scope-challenge.md +82 -0
- package/skills/alp-plan/references/solution-design.md +76 -0
- package/skills/alp-plan/references/validate-question-framework.md +89 -0
- package/skills/alp-plan/references/validate-workflow.md +83 -0
- package/skills/alp-predict/SKILL.md +98 -0
- package/skills/alp-scenario/SKILL.md +86 -0
- package/skills/code-review/SKILL.md +111 -0
- package/skills/code-review/references/code-review-reception.md +114 -0
- package/skills/code-review/references/edge-case-scouting.md +78 -0
- package/skills/code-review/references/verification-before-completion.md +117 -0
- package/skills/delegation/SKILL.md +46 -0
- package/skills/docs-seeker/.env.example +15 -0
- package/skills/docs-seeker/SKILL.md +87 -0
- package/skills/docs-seeker/package.json +25 -0
- package/skills/docs-seeker/references/advanced.md +82 -0
- package/skills/docs-seeker/references/context7-patterns.md +68 -0
- package/skills/docs-seeker/references/errors.md +72 -0
- package/skills/docs-seeker/scripts/analyze-llms-txt.js +211 -0
- package/skills/docs-seeker/scripts/detect-topic.js +172 -0
- package/skills/docs-seeker/scripts/fetch-docs.js +213 -0
- package/skills/docs-seeker/scripts/tests/run-tests.js +72 -0
- package/skills/docs-seeker/scripts/tests/test-analyze-llms.js +119 -0
- package/skills/docs-seeker/scripts/tests/test-detect-topic.js +112 -0
- package/skills/docs-seeker/scripts/tests/test-fetch-docs.js +84 -0
- package/skills/docs-seeker/scripts/utils/env-loader.js +94 -0
- package/skills/docs-seeker/workflows/library-search.md +73 -0
- package/skills/docs-seeker/workflows/repo-analysis.md +90 -0
- package/skills/docs-seeker/workflows/topic-search.md +69 -0
- package/skills/git/SKILL.md +121 -0
- package/skills/git/references/branch-management.md +90 -0
- package/skills/git/references/commit-standards.md +82 -0
- package/skills/git/references/gh-cli-guide.md +132 -0
- package/skills/git/references/safety-protocols.md +86 -0
- package/skills/git/references/workflow-commit.md +89 -0
- package/skills/git/references/workflow-merge.md +63 -0
- package/skills/git/references/workflow-pr.md +70 -0
- package/skills/git/references/workflow-push.md +62 -0
- package/skills/gkg/SKILL.md +87 -0
- package/skills/gkg/references/cli-commands.md +92 -0
- package/skills/gkg/references/http-api.md +99 -0
- package/skills/gkg/references/language-support.md +54 -0
- package/skills/problem-solving/SKILL.md +86 -0
- package/skills/problem-solving/references/attribution.md +48 -0
- package/skills/problem-solving/references/collision-zone-thinking.md +71 -0
- package/skills/problem-solving/references/inversion-exercise.md +88 -0
- package/skills/problem-solving/references/meta-pattern-recognition.md +80 -0
- package/skills/problem-solving/references/scale-game.md +82 -0
- package/skills/problem-solving/references/simplification-cascades.md +83 -0
- package/skills/problem-solving/references/when-stuck.md +76 -0
- package/skills/repomix/SKILL.md +94 -0
- package/skills/repomix/references/configuration.md +134 -0
- package/skills/repomix/references/usage-patterns.md +106 -0
- package/skills/repomix/scripts/.coverage +0 -0
- package/skills/repomix/scripts/README.md +179 -0
- package/skills/repomix/scripts/repomix_batch.py +455 -0
- package/skills/repomix/scripts/repos.example.json +15 -0
- package/skills/repomix/scripts/requirements.txt +15 -0
- package/skills/repomix/scripts/tests/test_repomix_batch.py +531 -0
- package/skills/research/SKILL.md +107 -0
- package/skills/security-scan/SKILL.md +101 -0
- package/skills/security-scan/references/secret-patterns.md +75 -0
- package/skills/security-scan/references/vulnerability-patterns.md +136 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryEntryAlreadyExistsError = exports.MemoryEntryNotFoundError = exports.MemoryVersionConflictError = exports.UnauthorizedMemoryAccessError = exports.InvalidMemoryIdError = void 0;
|
|
4
|
+
class InvalidMemoryIdError extends Error {
|
|
5
|
+
id;
|
|
6
|
+
constructor(id) {
|
|
7
|
+
super(`invalid logical memory ID \`${id}\``);
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.name = "InvalidMemoryIdError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.InvalidMemoryIdError = InvalidMemoryIdError;
|
|
13
|
+
class UnauthorizedMemoryAccessError extends Error {
|
|
14
|
+
actor;
|
|
15
|
+
operation;
|
|
16
|
+
scope;
|
|
17
|
+
code;
|
|
18
|
+
constructor(actor, operation, scope, code) {
|
|
19
|
+
super(`\`${actor}\` cannot ${operation} memory scope \`${scope}\`: ${code}`);
|
|
20
|
+
this.actor = actor;
|
|
21
|
+
this.operation = operation;
|
|
22
|
+
this.scope = scope;
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.name = "UnauthorizedMemoryAccessError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.UnauthorizedMemoryAccessError = UnauthorizedMemoryAccessError;
|
|
28
|
+
class MemoryVersionConflictError extends Error {
|
|
29
|
+
id;
|
|
30
|
+
expectedVersion;
|
|
31
|
+
actualVersion;
|
|
32
|
+
constructor(id, expectedVersion, actualVersion) {
|
|
33
|
+
super(`memory \`${id}\` version conflict: expected ${expectedVersion}, actual ${actualVersion}`);
|
|
34
|
+
this.id = id;
|
|
35
|
+
this.expectedVersion = expectedVersion;
|
|
36
|
+
this.actualVersion = actualVersion;
|
|
37
|
+
this.name = "MemoryVersionConflictError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.MemoryVersionConflictError = MemoryVersionConflictError;
|
|
41
|
+
class MemoryEntryNotFoundError extends Error {
|
|
42
|
+
id;
|
|
43
|
+
constructor(id) {
|
|
44
|
+
super(`memory \`${id}\` does not exist`);
|
|
45
|
+
this.id = id;
|
|
46
|
+
this.name = "MemoryEntryNotFoundError";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.MemoryEntryNotFoundError = MemoryEntryNotFoundError;
|
|
50
|
+
class MemoryEntryAlreadyExistsError extends Error {
|
|
51
|
+
id;
|
|
52
|
+
constructor(id) {
|
|
53
|
+
super(`memory \`${id}\` already exists`);
|
|
54
|
+
this.id = id;
|
|
55
|
+
this.name = "MemoryEntryAlreadyExistsError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.MemoryEntryAlreadyExistsError = MemoryEntryAlreadyExistsError;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryService = void 0;
|
|
4
|
+
exports.parseMemoryId = parseMemoryId;
|
|
5
|
+
const memory_grant_1 = require("../agents/memory-grant");
|
|
6
|
+
const context_ranker_1 = require("./context-ranker");
|
|
7
|
+
const errors_1 = require("./errors");
|
|
8
|
+
function validSegment(segment) {
|
|
9
|
+
return segment.length > 0 && segment !== "." && segment !== ".." && !segment.includes("/") && !segment.includes("\\");
|
|
10
|
+
}
|
|
11
|
+
function parseMemoryId(id) {
|
|
12
|
+
const segments = id.split(":");
|
|
13
|
+
if (segments[0] === "shared" && segments.length >= 2 && segments.slice(1).every(validSegment)) {
|
|
14
|
+
const pathSegments = Object.freeze(segments.slice(1));
|
|
15
|
+
return { id, scope: "shared", grant: id, name: pathSegments.join(":"), pathSegments };
|
|
16
|
+
}
|
|
17
|
+
if (segments[0] === "project" &&
|
|
18
|
+
segments.length >= 3 &&
|
|
19
|
+
segments.slice(1).every(validSegment)) {
|
|
20
|
+
const pathSegments = Object.freeze(segments.slice(2));
|
|
21
|
+
return {
|
|
22
|
+
id,
|
|
23
|
+
scope: "project",
|
|
24
|
+
grant: id,
|
|
25
|
+
projectId: segments[1],
|
|
26
|
+
name: pathSegments.join(":"),
|
|
27
|
+
pathSegments,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (segments[0] === "private" &&
|
|
31
|
+
segments.length >= 3 &&
|
|
32
|
+
segments.slice(1).every(validSegment)) {
|
|
33
|
+
const pathSegments = Object.freeze(segments.slice(2));
|
|
34
|
+
return {
|
|
35
|
+
id,
|
|
36
|
+
scope: "private",
|
|
37
|
+
grant: id,
|
|
38
|
+
ownerRole: segments[1],
|
|
39
|
+
name: pathSegments.join(":"),
|
|
40
|
+
pathSegments,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
throw new errors_1.InvalidMemoryIdError(id);
|
|
44
|
+
}
|
|
45
|
+
class MemoryService {
|
|
46
|
+
store;
|
|
47
|
+
policy;
|
|
48
|
+
audit;
|
|
49
|
+
ranker;
|
|
50
|
+
now;
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.store = options.store;
|
|
53
|
+
this.policy = options.policy;
|
|
54
|
+
this.audit = options.audit;
|
|
55
|
+
this.ranker = options.ranker ?? new context_ranker_1.DeterministicContextRanker();
|
|
56
|
+
this.now = options.now ?? (() => new Date());
|
|
57
|
+
}
|
|
58
|
+
async record(actor, operation, logicalId, result, detail) {
|
|
59
|
+
await this.audit.record({
|
|
60
|
+
actor,
|
|
61
|
+
operation,
|
|
62
|
+
logicalId,
|
|
63
|
+
result,
|
|
64
|
+
timestamp: this.now().toISOString(),
|
|
65
|
+
...(detail === undefined ? {} : { detail }),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async authorize(actor, operation, access, scope, logicalId) {
|
|
69
|
+
const authorization = this.policy.authorize({ type: "memory", actor, operation: access, scope });
|
|
70
|
+
if (!authorization.allowed) {
|
|
71
|
+
await this.record(actor, operation, logicalId, "denied", authorization.code);
|
|
72
|
+
throw new errors_1.UnauthorizedMemoryAccessError(actor, access, scope, authorization.code);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async call(actor, operation, logicalId, action) {
|
|
76
|
+
try {
|
|
77
|
+
const result = await action();
|
|
78
|
+
await this.record(actor, operation, logicalId, "allowed");
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const detail = error instanceof Error ? error.name : String(error);
|
|
83
|
+
await this.record(actor, operation, logicalId, "error", detail);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async search(actor, query) {
|
|
88
|
+
await this.authorize(actor, "search", "read", query.scope, query.scope);
|
|
89
|
+
return this.call(actor, "search", query.scope, async () => {
|
|
90
|
+
const entries = await this.store.search(query);
|
|
91
|
+
return entries.filter((entry) => (0, memory_grant_1.memoryGrantCovers)(query.scope, entry.id));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async get(actor, id) {
|
|
95
|
+
const parsed = parseMemoryId(id);
|
|
96
|
+
await this.authorize(actor, "get", "read", parsed.grant, id);
|
|
97
|
+
return this.call(actor, "get", id, () => this.store.get(id));
|
|
98
|
+
}
|
|
99
|
+
async create(actor, input) {
|
|
100
|
+
const parsed = parseMemoryId(input.id);
|
|
101
|
+
await this.authorize(actor, "create", "write", parsed.grant, input.id);
|
|
102
|
+
return this.call(actor, "create", input.id, () => this.store.create(input));
|
|
103
|
+
}
|
|
104
|
+
async update(actor, id, input) {
|
|
105
|
+
const parsed = parseMemoryId(id);
|
|
106
|
+
await this.authorize(actor, "update", "write", parsed.grant, id);
|
|
107
|
+
return this.call(actor, "update", id, () => this.store.update(id, input));
|
|
108
|
+
}
|
|
109
|
+
async delete(actor, id, expectedVersion) {
|
|
110
|
+
const parsed = parseMemoryId(id);
|
|
111
|
+
await this.authorize(actor, "delete", "write", parsed.grant, id);
|
|
112
|
+
return this.call(actor, "delete", id, () => this.store.delete(id, expectedVersion));
|
|
113
|
+
}
|
|
114
|
+
async buildContext(input) {
|
|
115
|
+
if (!Number.isInteger(input.characterBudget) || input.characterBudget < 0) {
|
|
116
|
+
throw new RangeError("characterBudget must be a non-negative integer");
|
|
117
|
+
}
|
|
118
|
+
const byId = new Map();
|
|
119
|
+
for (const query of input.queries) {
|
|
120
|
+
for (const entry of await this.search(input.actor, query))
|
|
121
|
+
byId.set(entry.id, entry);
|
|
122
|
+
}
|
|
123
|
+
const ranked = this.ranker.rank([...byId.values()], input.queries);
|
|
124
|
+
const entries = [];
|
|
125
|
+
const omittedEntryIds = [];
|
|
126
|
+
let charactersUsed = 0;
|
|
127
|
+
for (const entry of ranked) {
|
|
128
|
+
if (charactersUsed + entry.content.length <= input.characterBudget) {
|
|
129
|
+
entries.push(entry);
|
|
130
|
+
charactersUsed += entry.content.length;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
omittedEntryIds.push(entry.id);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return Object.freeze({
|
|
137
|
+
invariantContext: input.invariantContext,
|
|
138
|
+
policyContext: input.policyContext,
|
|
139
|
+
entries: Object.freeze(entries),
|
|
140
|
+
diagnostics: Object.freeze({
|
|
141
|
+
characterBudget: input.characterBudget,
|
|
142
|
+
charactersUsed,
|
|
143
|
+
truncated: omittedEntryIds.length > 0,
|
|
144
|
+
omittedEntryIds: Object.freeze(omittedEntryIds),
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
exports.MemoryService = MemoryService;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CapabilityPolicy = void 0;
|
|
4
|
+
exports.mcpServerOf = mcpServerOf;
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
/**
|
|
7
|
+
* The three grants a definition declares by name: skills, in-process subagents, MCP servers.
|
|
8
|
+
*
|
|
9
|
+
* Each is answered from the definition, the same way tools and memory are — the grant is a
|
|
10
|
+
* list of names, and anything not on it is denied with the name in the message, so a blocked
|
|
11
|
+
* run says which grant would unblock it rather than only that something was refused.
|
|
12
|
+
*/
|
|
13
|
+
class CapabilityPolicy {
|
|
14
|
+
authorize(actor, kind, name, granted, code) {
|
|
15
|
+
return granted.includes(name)
|
|
16
|
+
? types_1.ALLOW
|
|
17
|
+
: (0, types_1.deny)(code, `${kind} \`${name}\` is not granted to \`${actor.id}\``);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.CapabilityPolicy = CapabilityPolicy;
|
|
21
|
+
/**
|
|
22
|
+
* Both runtimes name an MCP tool `mcp__<server>__<tool>`. The server segment is what policy
|
|
23
|
+
* decided on, so the tool request is routed to that grant rather than to `TOOL_CATALOG`,
|
|
24
|
+
* which does not contain it and could only ever answer `TOOL_NOT_GRANTED` — true today, and
|
|
25
|
+
* still true on the day the server *is* granted.
|
|
26
|
+
*/
|
|
27
|
+
function mcpServerOf(tool) {
|
|
28
|
+
return /^mcp__(.+?)__(.+)$/.exec(tool)?.[1] ?? null;
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DelegationPolicy = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
class DelegationPolicy {
|
|
6
|
+
registry;
|
|
7
|
+
constructor(registry) {
|
|
8
|
+
this.registry = registry;
|
|
9
|
+
}
|
|
10
|
+
authorize(actor, target) {
|
|
11
|
+
const parent = this.registry.get(actor);
|
|
12
|
+
if (!this.registry.has(target)) {
|
|
13
|
+
return (0, types_1.deny)("UNKNOWN_TARGET", `unknown delegation target \`${target}\``);
|
|
14
|
+
}
|
|
15
|
+
if (!parent.delegatesTo.includes(target)) {
|
|
16
|
+
return (0, types_1.deny)("DELEGATION_NOT_ALLOWED", `\`${actor}\` cannot delegate to \`${target}\``);
|
|
17
|
+
}
|
|
18
|
+
const child = this.registry.get(target);
|
|
19
|
+
if (child.reportsTo !== actor) {
|
|
20
|
+
return (0, types_1.deny)("DELEGATION_PARENT_MISMATCH", `\`${target}\` reports to \`${child.reportsTo}\`, not \`${actor}\``);
|
|
21
|
+
}
|
|
22
|
+
return types_1.ALLOW;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.DelegationPolicy = DelegationPolicy;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidPolicyStateError = void 0;
|
|
4
|
+
class InvalidPolicyStateError extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message, options);
|
|
7
|
+
this.name = "InvalidPolicyStateError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.InvalidPolicyStateError = InvalidPolicyStateError;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POLICY_GUARDRAIL_LIMITATION = void 0;
|
|
4
|
+
exports.isRawRuntimeTool = isRawRuntimeTool;
|
|
5
|
+
exports.hasIndirectCommand = hasIndirectCommand;
|
|
6
|
+
exports.invokesRawRuntime = invokesRawRuntime;
|
|
7
|
+
const RAW_RUNTIME_TOOL = /(?:^|__)(?:herdr|paseo|create_agent|spawn_agent)(?:__|$)/i;
|
|
8
|
+
const INDIRECT_COMMAND = /(^|[^\w])eval[^\w]|`|\$(?:\(|[A-Za-z_{])|[<>]\(|\bbase64\b|\bxxd\b|\b(?:sh|bash|zsh|python3?|node)\s+-c\b|\bperl\s+-e\b|\bruby\s+-e\b|\bxargs\b/;
|
|
9
|
+
const RUNTIME_BINARY = /^(?:herdr|paseo(?:\.exe)?)$/i;
|
|
10
|
+
const SHELL_WRAPPER = /^(?:env|command|sudo)$/;
|
|
11
|
+
exports.POLICY_GUARDRAIL_LIMITATION = "Command inspection is a guardrail, not hostile-process isolation; use an OS sandbox or container for adversarial code.";
|
|
12
|
+
function isRawRuntimeTool(tool) {
|
|
13
|
+
return RAW_RUNTIME_TOOL.test(tool);
|
|
14
|
+
}
|
|
15
|
+
function hasIndirectCommand(command) {
|
|
16
|
+
return INDIRECT_COMMAND.test(command);
|
|
17
|
+
}
|
|
18
|
+
function invokesRawRuntime(command) {
|
|
19
|
+
for (const segment of command.split(/[;\n]|&&|\|\||\|/)) {
|
|
20
|
+
const words = segment.trim().split(/\s+/).filter(Boolean);
|
|
21
|
+
let index = 0;
|
|
22
|
+
while (index < words.length && /^[A-Za-z_]\w*=/.test(words[index]))
|
|
23
|
+
index++;
|
|
24
|
+
while (index < words.length && SHELL_WRAPPER.test(words[index]))
|
|
25
|
+
index++;
|
|
26
|
+
const binary = words[index]?.replace(/^['"]|['"]$/g, "").split("/").at(-1);
|
|
27
|
+
if (binary && RUNTIME_BINARY.test(binary))
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryPolicy = void 0;
|
|
4
|
+
const memory_grant_1 = require("../agents/memory-grant");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
function privateOwner(scope) {
|
|
7
|
+
return scope.startsWith("private:") ? scope.split(":")[1] ?? null : null;
|
|
8
|
+
}
|
|
9
|
+
class MemoryPolicy {
|
|
10
|
+
authorize(actor, operation, scope) {
|
|
11
|
+
const owner = privateOwner(scope);
|
|
12
|
+
if (owner !== null && owner !== actor.id) {
|
|
13
|
+
return (0, types_1.deny)("PRIVATE_MEMORY_DENIED", `\`${actor.id}\` cannot ${operation} private memory for \`${owner}\``);
|
|
14
|
+
}
|
|
15
|
+
const grants = actor.capabilities.memory[operation];
|
|
16
|
+
if (!grants.some((grant) => (0, memory_grant_1.memoryGrantCovers)(grant, scope))) {
|
|
17
|
+
return (0, types_1.deny)("MEMORY_NOT_GRANTED", `\`${actor.id}\` has no ${operation} grant for \`${scope}\``);
|
|
18
|
+
}
|
|
19
|
+
return types_1.ALLOW;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.MemoryPolicy = MemoryPolicy;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PolicyEngine = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const capability_policy_1 = require("./capability-policy");
|
|
7
|
+
const delegation_policy_1 = require("./delegation-policy");
|
|
8
|
+
const invariants_1 = require("./invariants");
|
|
9
|
+
const memory_policy_1 = require("./memory-policy");
|
|
10
|
+
const types_1 = require("./types");
|
|
11
|
+
const workspace_policy_1 = require("./workspace-policy");
|
|
12
|
+
function canonicalizePath(value) {
|
|
13
|
+
const absolute = (0, node_path_1.resolve)(value);
|
|
14
|
+
try {
|
|
15
|
+
return (0, node_fs_1.realpathSync)(absolute);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
try {
|
|
19
|
+
return (0, node_path_1.join)((0, node_fs_1.realpathSync)((0, node_path_1.dirname)(absolute)), (0, node_path_1.basename)(absolute));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return absolute;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
class PolicyEngine {
|
|
27
|
+
registry;
|
|
28
|
+
delegation;
|
|
29
|
+
memory = new memory_policy_1.MemoryPolicy();
|
|
30
|
+
capability = new capability_policy_1.CapabilityPolicy();
|
|
31
|
+
workspace;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this.registry = options.registry;
|
|
34
|
+
this.delegation = new delegation_policy_1.DelegationPolicy(options.registry);
|
|
35
|
+
this.workspace = new workspace_policy_1.WorkspacePolicy(options.registry, options.canonicalizePath ?? canonicalizePath);
|
|
36
|
+
}
|
|
37
|
+
authorize(request) {
|
|
38
|
+
if (!request || typeof request !== "object" || !("actor" in request)) {
|
|
39
|
+
return (0, types_1.deny)("UNKNOWN_REQUEST", "unrecognized policy request");
|
|
40
|
+
}
|
|
41
|
+
if (!this.registry.has(request.actor)) {
|
|
42
|
+
return (0, types_1.deny)("UNKNOWN_ACTOR", `unknown actor \`${request.actor}\``);
|
|
43
|
+
}
|
|
44
|
+
const actor = this.registry.get(request.actor);
|
|
45
|
+
switch (request.type) {
|
|
46
|
+
case "delegation":
|
|
47
|
+
return this.delegation.authorize(request.actor, request.target);
|
|
48
|
+
case "memory":
|
|
49
|
+
return this.memory.authorize(actor, request.operation, request.scope);
|
|
50
|
+
case "workspace":
|
|
51
|
+
return this.workspace.authorize(request.actor, request.operation, request.path, request.execution);
|
|
52
|
+
case "configuration":
|
|
53
|
+
return request.target.kind === "policy-source"
|
|
54
|
+
? (0, types_1.deny)("POLICY_MUTATION_DENIED", `\`${request.actor}\` cannot mutate policy source`)
|
|
55
|
+
: (0, types_1.deny)("DEFINITION_MUTATION_DENIED", `\`${request.actor}\` cannot mutate agent definition \`${request.target.agentId}\``);
|
|
56
|
+
case "skill":
|
|
57
|
+
return this.capability.authorize(actor, "skill", request.skill, actor.capabilities.skills, "SKILL_NOT_GRANTED");
|
|
58
|
+
case "subagent":
|
|
59
|
+
return this.capability.authorize(actor, "subagent", request.subagent, actor.capabilities.subagents, "SUBAGENT_NOT_GRANTED");
|
|
60
|
+
case "mcp":
|
|
61
|
+
return this.capability.authorize(actor, "mcp server", request.server, actor.capabilities.mcpServers, "MCP_SERVER_NOT_GRANTED");
|
|
62
|
+
case "tool": {
|
|
63
|
+
if ((0, invariants_1.isRawRuntimeTool)(request.tool) ||
|
|
64
|
+
(request.command !== undefined && (0, invariants_1.invokesRawRuntime)(request.command))) {
|
|
65
|
+
return (0, types_1.deny)("RAW_RUNTIME_TOOL_DENIED", `raw runtime tool \`${request.tool}\` is not part of the ALP delegation API`);
|
|
66
|
+
}
|
|
67
|
+
if (request.command !== undefined &&
|
|
68
|
+
(0, invariants_1.hasIndirectCommand)(request.command)) {
|
|
69
|
+
return (0, types_1.deny)("INDIRECT_TOOL_REQUEST", `indirect command cannot be authorized safely`);
|
|
70
|
+
}
|
|
71
|
+
const server = (0, capability_policy_1.mcpServerOf)(request.tool);
|
|
72
|
+
if (server !== null) {
|
|
73
|
+
return this.capability.authorize(actor, "mcp server", server, actor.capabilities.mcpServers, "MCP_SERVER_NOT_GRANTED");
|
|
74
|
+
}
|
|
75
|
+
if (!actor.capabilities.tools.some((tool) => tool === request.tool)) {
|
|
76
|
+
return (0, types_1.deny)("TOOL_NOT_GRANTED", `\`${request.tool}\` is not granted to \`${request.actor}\``);
|
|
77
|
+
}
|
|
78
|
+
return types_1.ALLOW;
|
|
79
|
+
}
|
|
80
|
+
default:
|
|
81
|
+
return (0, types_1.deny)("UNKNOWN_REQUEST", "unrecognized policy request");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.PolicyEngine = PolicyEngine;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ALLOW = void 0;
|
|
4
|
+
exports.deny = deny;
|
|
5
|
+
exports.ALLOW = Object.freeze({ allowed: true });
|
|
6
|
+
function deny(code, reason) {
|
|
7
|
+
return Object.freeze({ allowed: false, code, reason });
|
|
8
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkspacePolicy = void 0;
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const types_1 = require("./types");
|
|
7
|
+
function within(root, target) {
|
|
8
|
+
const relation = (0, node_path_1.relative)(root, target);
|
|
9
|
+
return relation === "" || (!relation.startsWith("..") && !(0, node_path_1.isAbsolute)(relation));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a role's declared roots for *this* execution.
|
|
13
|
+
*
|
|
14
|
+
* A relative root — `"."`, which every code-native role declares — means "the workspace
|
|
15
|
+
* this execution was launched against", not "wherever the launcher process happened to be
|
|
16
|
+
* standing". Resolving it once, at construction, against the launcher's cwd is what made
|
|
17
|
+
* `alp delegate review --project ~/code/api` deny every path inside the project it was
|
|
18
|
+
* pointed at, from any directory but the launcher's own. Absolute roots are unaffected and
|
|
19
|
+
* still mean exactly what they say.
|
|
20
|
+
*
|
|
21
|
+
* Per-request rather than cached: an execution's active workspace is what a grant is
|
|
22
|
+
* relative to, so there is nothing stable to cache under the role's id.
|
|
23
|
+
*/
|
|
24
|
+
function canonicalizeRoots(definition, activeWorkspace, canonicalizePath) {
|
|
25
|
+
const resolve = (root) => canonicalizePath((0, node_path_1.isAbsolute)(root) ? root : (0, node_path_1.join)(activeWorkspace, root));
|
|
26
|
+
try {
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
readRoots: Object.freeze(definition.capabilities.workspace.readRoots.map(resolve)),
|
|
29
|
+
writeRoots: Object.freeze(definition.capabilities.workspace.writeRoots.map(resolve)),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new errors_1.InvalidPolicyStateError(`cannot canonicalize workspace grants for \`${definition.id}\``, { cause: error });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class WorkspacePolicy {
|
|
37
|
+
canonicalizePath;
|
|
38
|
+
definitions = new Map();
|
|
39
|
+
constructor(registry, canonicalizePath) {
|
|
40
|
+
this.canonicalizePath = canonicalizePath;
|
|
41
|
+
for (const definition of registry.list()) {
|
|
42
|
+
this.definitions.set(definition.id, definition);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
authorize(actor, operation, inputPath, execution) {
|
|
46
|
+
if (operation === "write" && execution.workspaceMode === "read-only") {
|
|
47
|
+
return (0, types_1.deny)("WORKSPACE_READ_ONLY", `execution workspace \`${execution.activeWorkspace}\` is read-only`);
|
|
48
|
+
}
|
|
49
|
+
let target;
|
|
50
|
+
let activeWorkspace;
|
|
51
|
+
try {
|
|
52
|
+
target = this.canonicalizePath(inputPath);
|
|
53
|
+
activeWorkspace = this.canonicalizePath(execution.activeWorkspace);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
57
|
+
return (0, types_1.deny)("PATH_RESOLUTION_FAILED", `cannot canonicalize workspace path: ${reason}`);
|
|
58
|
+
}
|
|
59
|
+
const definition = this.definitions.get(actor);
|
|
60
|
+
if (!definition) {
|
|
61
|
+
throw new errors_1.InvalidPolicyStateError(`missing workspace grants for \`${actor}\``);
|
|
62
|
+
}
|
|
63
|
+
const grants = canonicalizeRoots(definition, activeWorkspace, this.canonicalizePath);
|
|
64
|
+
if (execution.delegated && !within(activeWorkspace, target)) {
|
|
65
|
+
return (0, types_1.deny)("WORKSPACE_SCOPE_MISMATCH", `delegated execution is scoped to \`${activeWorkspace}\`, not \`${target}\``);
|
|
66
|
+
}
|
|
67
|
+
if (!grants.readRoots.some((root) => within(root, target))) {
|
|
68
|
+
return (0, types_1.deny)("WORKSPACE_NOT_GRANTED", `\`${actor}\` cannot read workspace path \`${target}\``);
|
|
69
|
+
}
|
|
70
|
+
if (operation === "write" &&
|
|
71
|
+
!grants.writeRoots.some((root) => within(root, target))) {
|
|
72
|
+
return (0, types_1.deny)("WORKSPACE_NOT_GRANTED", `\`${actor}\` cannot write workspace path \`${target}\``);
|
|
73
|
+
}
|
|
74
|
+
return types_1.ALLOW;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.WorkspacePolicy = WorkspacePolicy;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRINCIPAL_FIELD_LIMIT = void 0;
|
|
4
|
+
exports.principalProfileFile = principalProfileFile;
|
|
5
|
+
exports.normalizePrincipalField = normalizePrincipalField;
|
|
6
|
+
exports.normalizePrincipalProfile = normalizePrincipalProfile;
|
|
7
|
+
exports.readPrincipalProfile = readPrincipalProfile;
|
|
8
|
+
exports.writePrincipalProfile = writePrincipalProfile;
|
|
9
|
+
const node_crypto_1 = require("node:crypto");
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const promises_1 = require("node:fs/promises");
|
|
12
|
+
const node_os_1 = require("node:os");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
/** Long enough for a full name, short enough that a stray paste cannot flood every prompt. */
|
|
15
|
+
exports.PRINCIPAL_FIELD_LIMIT = 60;
|
|
16
|
+
function principalProfileFile(env = process.env) {
|
|
17
|
+
return (0, node_path_1.join)(env.HOME || (0, node_os_1.homedir)(), ".alp", "principal.json");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Collapses whitespace and rejects empties — these strings are interpolated straight into
|
|
21
|
+
* every role's instructions, so a newline here would silently become a new prompt line.
|
|
22
|
+
*/
|
|
23
|
+
function normalizePrincipalField(label, value) {
|
|
24
|
+
const cleaned = value.replace(/[\p{Cc}\p{Cf}]/gu, " ").replace(/\s+/g, " ").trim();
|
|
25
|
+
if (cleaned === "")
|
|
26
|
+
throw new Error(`${label} is required`);
|
|
27
|
+
if (cleaned.length > exports.PRINCIPAL_FIELD_LIMIT) {
|
|
28
|
+
throw new Error(`${label} must be at most ${exports.PRINCIPAL_FIELD_LIMIT} characters`);
|
|
29
|
+
}
|
|
30
|
+
return cleaned;
|
|
31
|
+
}
|
|
32
|
+
function normalizePrincipalProfile(input) {
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
name: normalizePrincipalField("name", input.name),
|
|
35
|
+
addressAs: normalizePrincipalField("form of address", input.addressAs),
|
|
36
|
+
selfAs: normalizePrincipalField("agent self-reference", input.selfAs),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Synchronous on purpose: `AgentDefinition.instructions()` is sync and is called while
|
|
41
|
+
* rendering identity documents and identity capsules. The file is a few dozen bytes and is
|
|
42
|
+
* read at most once per role render, so there is no cache to go stale after `alp init`
|
|
43
|
+
* writes the profile in the same process.
|
|
44
|
+
*/
|
|
45
|
+
function readPrincipalProfile(file = principalProfileFile()) {
|
|
46
|
+
let content;
|
|
47
|
+
try {
|
|
48
|
+
content = (0, node_fs_1.readFileSync)(file, "utf8");
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error.code === "ENOENT")
|
|
52
|
+
return { profile: null };
|
|
53
|
+
return { profile: null, warning: `cannot read principal profile at ${file}` };
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(content);
|
|
57
|
+
if (parsed === null || typeof parsed !== "object" || parsed.version !== 1) {
|
|
58
|
+
throw new Error("unsupported principal profile");
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
profile: normalizePrincipalProfile({
|
|
62
|
+
name: String(parsed.name ?? ""),
|
|
63
|
+
addressAs: String(parsed.addressAs ?? ""),
|
|
64
|
+
selfAs: String(parsed.selfAs ?? ""),
|
|
65
|
+
}),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return { profile: null, warning: `invalid principal profile at ${file}; run \`alp principal set\`` };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function writePrincipalProfile(profile, file = principalProfileFile()) {
|
|
73
|
+
const normalized = normalizePrincipalProfile(profile);
|
|
74
|
+
const directory = (0, node_path_1.dirname)(file);
|
|
75
|
+
await (0, promises_1.mkdir)(directory, { recursive: true, mode: 0o700 });
|
|
76
|
+
await (0, promises_1.chmod)(directory, 0o700);
|
|
77
|
+
const temporary = (0, node_path_1.join)(directory, `.${(0, node_crypto_1.randomUUID)()}.principal.tmp`);
|
|
78
|
+
try {
|
|
79
|
+
await (0, promises_1.writeFile)(temporary, `${JSON.stringify({ version: 1, ...normalized }, null, 2)}\n`, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
80
|
+
await (0, promises_1.chmod)(temporary, 0o600);
|
|
81
|
+
await (0, promises_1.rename)(temporary, file);
|
|
82
|
+
await (0, promises_1.chmod)(file, 0o600);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
await (0, promises_1.rm)(temporary, { force: true });
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
return normalized;
|
|
89
|
+
}
|