@asc-agent/runtime 0.1.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/LICENSE +15 -0
- package/README.md +22 -0
- package/dist/adapters/claude-code/binding.d.ts +5 -0
- package/dist/adapters/claude-code/binding.js +14 -0
- package/dist/adapters/claude-code/guard.d.ts +54 -0
- package/dist/adapters/claude-code/guard.js +295 -0
- package/dist/adapters/claude-code/install.d.ts +67 -0
- package/dist/adapters/claude-code/install.js +231 -0
- package/dist/adapters/claude-code/observer.d.ts +38 -0
- package/dist/adapters/claude-code/observer.js +77 -0
- package/dist/adapters/claude-code/probe.d.ts +53 -0
- package/dist/adapters/claude-code/probe.js +129 -0
- package/dist/adapters/claude-code/skill.d.ts +8 -0
- package/dist/adapters/claude-code/skill.js +267 -0
- package/dist/adapters/fixture-work/index.d.ts +34 -0
- package/dist/adapters/fixture-work/index.js +78 -0
- package/dist/adapters/github/adapter.d.ts +32 -0
- package/dist/adapters/github/adapter.js +95 -0
- package/dist/adapters/github/client.d.ts +30 -0
- package/dist/adapters/github/client.js +90 -0
- package/dist/adapters/github/context.d.ts +44 -0
- package/dist/adapters/github/context.js +170 -0
- package/dist/adapters/github/event-source.d.ts +55 -0
- package/dist/adapters/github/event-source.js +183 -0
- package/dist/adapters/github/scm.d.ts +50 -0
- package/dist/adapters/github/scm.js +104 -0
- package/dist/adapters/gitlab/adapter.d.ts +36 -0
- package/dist/adapters/gitlab/adapter.js +117 -0
- package/dist/adapters/gitlab/client.d.ts +30 -0
- package/dist/adapters/gitlab/client.js +47 -0
- package/dist/adapters/gitlab/ports.d.ts +40 -0
- package/dist/adapters/gitlab/ports.js +186 -0
- package/dist/adapters/jam/adapter.d.ts +53 -0
- package/dist/adapters/jam/adapter.js +144 -0
- package/dist/adapters/jam/event-source.d.ts +28 -0
- package/dist/adapters/jam/event-source.js +65 -0
- package/dist/adapters/jam/mcp-client.d.ts +50 -0
- package/dist/adapters/jam/mcp-client.js +218 -0
- package/dist/adapters/jam/ports.d.ts +51 -0
- package/dist/adapters/jam/ports.js +167 -0
- package/dist/adapters/local/identity.d.ts +27 -0
- package/dist/adapters/local/identity.js +40 -0
- package/dist/adapters/local/presentation.d.ts +14 -0
- package/dist/adapters/local/presentation.js +42 -0
- package/dist/adapters/markdown/layout.d.ts +15 -0
- package/dist/adapters/markdown/layout.js +49 -0
- package/dist/adapters/markdown/serialize.d.ts +9 -0
- package/dist/adapters/markdown/serialize.js +120 -0
- package/dist/adapters/markdown/state-store.d.ts +23 -0
- package/dist/adapters/markdown/state-store.js +316 -0
- package/dist/adapters/mattermost/client.d.ts +23 -0
- package/dist/adapters/mattermost/client.js +54 -0
- package/dist/adapters/mattermost/presentation.d.ts +43 -0
- package/dist/adapters/mattermost/presentation.js +88 -0
- package/dist/adapters/memory/mocks.d.ts +87 -0
- package/dist/adapters/memory/mocks.js +163 -0
- package/dist/adapters/memory/runtime-binding.d.ts +23 -0
- package/dist/adapters/memory/runtime-binding.js +118 -0
- package/dist/adapters/memory/state-store.d.ts +17 -0
- package/dist/adapters/memory/state-store.js +125 -0
- package/dist/adapters/text/renderer.d.ts +7 -0
- package/dist/adapters/text/renderer.js +81 -0
- package/dist/adapters/webhook/ingress.d.ts +103 -0
- package/dist/adapters/webhook/ingress.js +150 -0
- package/dist/cli/asc.d.ts +16 -0
- package/dist/cli/asc.js +2914 -0
- package/dist/cli/identity-config.d.ts +7 -0
- package/dist/cli/identity-config.js +31 -0
- package/dist/composition/observe.d.ts +21 -0
- package/dist/composition/observe.js +72 -0
- package/dist/composition/registry.d.ts +33 -0
- package/dist/composition/registry.js +62 -0
- package/dist/composition/runtime.d.ts +63 -0
- package/dist/composition/runtime.js +155 -0
- package/dist/core/approval/service.d.ts +17 -0
- package/dist/core/approval/service.js +129 -0
- package/dist/core/attach/bootstrap.d.ts +102 -0
- package/dist/core/attach/bootstrap.js +178 -0
- package/dist/core/attach/init.d.ts +29 -0
- package/dist/core/attach/init.js +100 -0
- package/dist/core/attach/setup-plan.d.ts +125 -0
- package/dist/core/attach/setup-plan.js +177 -0
- package/dist/core/attach/setup.d.ts +40 -0
- package/dist/core/attach/setup.js +140 -0
- package/dist/core/binding/types.d.ts +93 -0
- package/dist/core/binding/types.js +74 -0
- package/dist/core/distribution/release.d.ts +15 -0
- package/dist/core/distribution/release.js +27 -0
- package/dist/core/distribution/runtime-install.d.ts +50 -0
- package/dist/core/distribution/runtime-install.js +90 -0
- package/dist/core/distribution/runtime-select.d.ts +74 -0
- package/dist/core/distribution/runtime-select.js +149 -0
- package/dist/core/execution/executor.d.ts +51 -0
- package/dist/core/execution/executor.js +106 -0
- package/dist/core/execution/grant.d.ts +42 -0
- package/dist/core/execution/grant.js +78 -0
- package/dist/core/model/entities.d.ts +872 -0
- package/dist/core/model/entities.js +285 -0
- package/dist/core/model/ids.d.ts +41 -0
- package/dist/core/model/ids.js +50 -0
- package/dist/core/model/transitions.d.ts +26 -0
- package/dist/core/model/transitions.js +124 -0
- package/dist/core/monitor/coverage.d.ts +111 -0
- package/dist/core/monitor/coverage.js +129 -0
- package/dist/core/monitor/engine.d.ts +134 -0
- package/dist/core/monitor/engine.js +574 -0
- package/dist/core/monitor/health-alerts.d.ts +35 -0
- package/dist/core/monitor/health-alerts.js +88 -0
- package/dist/core/monitor/investigation.d.ts +82 -0
- package/dist/core/monitor/investigation.js +232 -0
- package/dist/core/monitor/observation.d.ts +79 -0
- package/dist/core/monitor/observation.js +110 -0
- package/dist/core/monitor/relevance.d.ts +47 -0
- package/dist/core/monitor/relevance.js +100 -0
- package/dist/core/monitor/signals.d.ts +59 -0
- package/dist/core/monitor/signals.js +105 -0
- package/dist/core/operator/local-operator.d.ts +60 -0
- package/dist/core/operator/local-operator.js +82 -0
- package/dist/core/operator/preflight.d.ts +60 -0
- package/dist/core/operator/preflight.js +139 -0
- package/dist/core/operator/proceed.d.ts +98 -0
- package/dist/core/operator/proceed.js +167 -0
- package/dist/core/operator/progress.d.ts +124 -0
- package/dist/core/operator/progress.js +135 -0
- package/dist/core/operator/render.d.ts +34 -0
- package/dist/core/operator/render.js +170 -0
- package/dist/core/operator/runtime-binding.d.ts +61 -0
- package/dist/core/operator/runtime-binding.js +22 -0
- package/dist/core/policy/ownership.d.ts +44 -0
- package/dist/core/policy/ownership.js +46 -0
- package/dist/core/policy/policy.d.ts +99 -0
- package/dist/core/policy/policy.js +147 -0
- package/dist/core/policy/remote-freeze.d.ts +101 -0
- package/dist/core/policy/remote-freeze.js +151 -0
- package/dist/core/policy/scope.d.ts +23 -0
- package/dist/core/policy/scope.js +93 -0
- package/dist/core/presentation/digest.d.ts +98 -0
- package/dist/core/presentation/digest.js +160 -0
- package/dist/core/resolver/load.d.ts +121 -0
- package/dist/core/resolver/load.js +246 -0
- package/dist/core/resolver/render.d.ts +4 -0
- package/dist/core/resolver/render.js +62 -0
- package/dist/core/resolver/resolve.d.ts +42 -0
- package/dist/core/resolver/resolve.js +51 -0
- package/dist/core/resolver/version.d.ts +22 -0
- package/dist/core/resolver/version.js +49 -0
- package/dist/core/runtime/audit.d.ts +374 -0
- package/dist/core/runtime/audit.js +454 -0
- package/dist/core/runtime/claims.d.ts +115 -0
- package/dist/core/runtime/claims.js +153 -0
- package/dist/core/runtime/closure.d.ts +73 -0
- package/dist/core/runtime/closure.js +162 -0
- package/dist/core/runtime/controller.d.ts +40 -0
- package/dist/core/runtime/controller.js +121 -0
- package/dist/core/runtime/escalation.d.ts +188 -0
- package/dist/core/runtime/escalation.js +322 -0
- package/dist/core/runtime/execution-state.d.ts +43 -0
- package/dist/core/runtime/execution-state.js +81 -0
- package/dist/core/runtime/front.d.ts +95 -0
- package/dist/core/runtime/front.js +144 -0
- package/dist/core/runtime/orchestrator.d.ts +54 -0
- package/dist/core/runtime/orchestrator.js +98 -0
- package/dist/core/runtime/query.d.ts +184 -0
- package/dist/core/runtime/query.js +213 -0
- package/dist/core/runtime/report.d.ts +33 -0
- package/dist/core/runtime/report.js +108 -0
- package/dist/core/runtime/session.d.ts +156 -0
- package/dist/core/runtime/session.js +281 -0
- package/dist/core/runtime/store-ops.d.ts +22 -0
- package/dist/core/runtime/store-ops.js +26 -0
- package/dist/core/view/build-view.d.ts +36 -0
- package/dist/core/view/build-view.js +131 -0
- package/dist/core/view/decision-view.d.ts +564 -0
- package/dist/core/view/decision-view.js +103 -0
- package/dist/core/workspace/identity.d.ts +82 -0
- package/dist/core/workspace/identity.js +133 -0
- package/dist/core/workspace/index-store.d.ts +140 -0
- package/dist/core/workspace/index-store.js +125 -0
- package/dist/core/workspace/migrate.d.ts +73 -0
- package/dist/core/workspace/migrate.js +123 -0
- package/dist/core/workspace/resolve.d.ts +34 -0
- package/dist/core/workspace/resolve.js +89 -0
- package/dist/ports/adapter.d.ts +35 -0
- package/dist/ports/adapter.js +12 -0
- package/dist/ports/approval.d.ts +98 -0
- package/dist/ports/approval.js +7 -0
- package/dist/ports/change-context.d.ts +22 -0
- package/dist/ports/change-context.js +8 -0
- package/dist/ports/event-source.d.ts +42 -0
- package/dist/ports/event-source.js +6 -0
- package/dist/ports/inventory.d.ts +53 -0
- package/dist/ports/inventory.js +12 -0
- package/dist/ports/presentation.d.ts +62 -0
- package/dist/ports/presentation.js +13 -0
- package/dist/ports/renderer.d.ts +23 -0
- package/dist/ports/renderer.js +7 -0
- package/dist/ports/resource-context.d.ts +51 -0
- package/dist/ports/resource-context.js +9 -0
- package/dist/ports/scm.d.ts +40 -0
- package/dist/ports/scm.js +6 -0
- package/dist/ports/state-store.d.ts +97 -0
- package/dist/ports/state-store.js +12 -0
- package/dist/presets/balanced.json +8 -0
- package/dist/presets/conservative.json +9 -0
- package/dist/presets/lightweight.json +8 -0
- package/dist/profiles/example-team/profile.json +101 -0
- package/dist/profiles/pilot-local/profile.json +56 -0
- package/dist/schemas/profile.d.ts +631 -0
- package/dist/schemas/profile.js +287 -0
- package/package.json +55 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { AdapterRuntime, BindingPlan, Capability, ResolvedBinding } from '../binding/types.ts';
|
|
2
|
+
export type ProfileChoice =
|
|
3
|
+
/** 사람이 지정했다. */
|
|
4
|
+
{
|
|
5
|
+
kind: 'GIVEN';
|
|
6
|
+
id: string;
|
|
7
|
+
}
|
|
8
|
+
/** 이미 붙어 있고 lock이 무엇으로 붙었는지 안다. */
|
|
9
|
+
| {
|
|
10
|
+
kind: 'ALREADY_ATTACHED';
|
|
11
|
+
id: string;
|
|
12
|
+
}
|
|
13
|
+
/** 후보는 있는데 정해지지 않았다. 하나뿐이어도 여기 머문다. */
|
|
14
|
+
| {
|
|
15
|
+
kind: 'UNDECIDED';
|
|
16
|
+
candidates: string[];
|
|
17
|
+
}
|
|
18
|
+
/** 붙어 있는데 무엇으로 붙었는지 읽지 못했다. 붙이다 만 상태다. */
|
|
19
|
+
| {
|
|
20
|
+
kind: 'ATTACHED_UNKNOWN';
|
|
21
|
+
};
|
|
22
|
+
export type HostState = {
|
|
23
|
+
id: string;
|
|
24
|
+
installed: boolean;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* 정책은 사람이 정한다 (C-09 §7.1). adapter가 찾아 주는 것과 그것이 무슨 뜻인지 정하는 것은
|
|
28
|
+
* 다른 일이며, 후자를 대신 정하면 그건 wizard다 (C-06 §2).
|
|
29
|
+
*/
|
|
30
|
+
export declare const POLICY_QUESTIONS: readonly [{
|
|
31
|
+
readonly id: "canonical";
|
|
32
|
+
readonly question: "어느 branch·ref가 canonical인가";
|
|
33
|
+
}, {
|
|
34
|
+
readonly id: "work-ssot";
|
|
35
|
+
readonly question: "작업 항목의 정본이 어디인가";
|
|
36
|
+
}, {
|
|
37
|
+
readonly id: "ownership";
|
|
38
|
+
readonly question: "ownership path는 무엇인가";
|
|
39
|
+
}, {
|
|
40
|
+
readonly id: "authority";
|
|
41
|
+
readonly question: "decision authority는 누구인가";
|
|
42
|
+
}, {
|
|
43
|
+
readonly id: "digest-channel";
|
|
44
|
+
readonly question: "기본 전달 채널은 무엇인가";
|
|
45
|
+
}];
|
|
46
|
+
export type PolicyId = (typeof POLICY_QUESTIONS)[number]['id'];
|
|
47
|
+
export type BootstrapPlan = {
|
|
48
|
+
projectRoot: string;
|
|
49
|
+
git: boolean;
|
|
50
|
+
attached: boolean;
|
|
51
|
+
profile: ProfileChoice;
|
|
52
|
+
hosts: HostState[];
|
|
53
|
+
/** 발견된 외부 연결 후보와 실측 상태 (C-09 §7). 없으면 로컬 루프만 도는 것이다. */
|
|
54
|
+
bindings: readonly ResolvedBinding[];
|
|
55
|
+
/**
|
|
56
|
+
* adapter 자체의 상태. binding과 나눠서 보여 준다 — "도구는 쓸 수 있는데 이 프로젝트가
|
|
57
|
+
* 아직 붙어 있지 않다"와 "도구가 없다"는 사람이 할 일이 다르다.
|
|
58
|
+
*/
|
|
59
|
+
runtimes: readonly AdapterRuntime[];
|
|
60
|
+
/** 지금 실제로 쓸 수 있는 semantic capability. adapter 이름이 아니라 할 수 있는 일이다. */
|
|
61
|
+
capabilities: readonly Capability[];
|
|
62
|
+
/** 사람이 실행할 다음 명령. 문자열이다 — 자동으로 태우지 않는다. */
|
|
63
|
+
steps: string[];
|
|
64
|
+
/** 사람이 정해야 남은 것. 비어 있지 않으면 계획은 아직 계획일 뿐이다. */
|
|
65
|
+
undecided: string[];
|
|
66
|
+
};
|
|
67
|
+
export type BootstrapInput = {
|
|
68
|
+
cwd: string;
|
|
69
|
+
/** ASC 설치 경로. `profiles/` 를 여기서 읽는다. */
|
|
70
|
+
installRoot: string;
|
|
71
|
+
/**
|
|
72
|
+
* 이 위치가 붙어 있다면 그 runtime 뿌리. **Surface가 정해서 넘긴다** — Core가 여기서
|
|
73
|
+
* 다시 찾으면 저장소 안의 `.asc` 만 보게 되고, 그것이 기본이 아닌 지금은
|
|
74
|
+
* local scope로 붙은 workspace를 "안 붙었다"고 답하게 된다 (C-11 §3).
|
|
75
|
+
*/
|
|
76
|
+
ascRoot?: string;
|
|
77
|
+
/** 사람이 지정한 Profile. 없으면 후보만 제시한다. */
|
|
78
|
+
profileId?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Host 설치 상태. Core는 어떤 Host가 있는지 모른다 — Surface가 알아내 넘긴다.
|
|
81
|
+
* 이름이 이 파일에 들어오는 순간 Core가 provider를 아는 셈이 된다.
|
|
82
|
+
*/
|
|
83
|
+
hosts?: readonly HostState[];
|
|
84
|
+
/**
|
|
85
|
+
* Composition이 조립한 Binding Plan (C-09 §7). 같은 이유로 여기서 adapter를 부르지 않는다 —
|
|
86
|
+
* 이 파일은 결과를 계획에 싣기만 한다.
|
|
87
|
+
*/
|
|
88
|
+
bindings?: BindingPlan;
|
|
89
|
+
/**
|
|
90
|
+
* 이미 선언된 정책. **붙어 있다는 것이 정책이 전부 정해졌다는 뜻은 아니다** —
|
|
91
|
+
* Profile이 무엇을 선언했는지는 Surface가 읽어 알려 주고, 남은 것을 여기서 묻는다.
|
|
92
|
+
*/
|
|
93
|
+
declaredPolicies?: readonly PolicyId[];
|
|
94
|
+
/** 정책 질문 자체를 생략할지. 계획만 보려는 호출용이다. */
|
|
95
|
+
askPolicy?: boolean;
|
|
96
|
+
};
|
|
97
|
+
/** 설치 경로의 Profile 후보. 없으면 빈 목록이다 — 그것도 사람이 알아야 할 사실이다. */
|
|
98
|
+
export declare function availableProfiles(installRoot: string): Promise<string[]>;
|
|
99
|
+
/** 감지하고 계획을 세운다. 파일은 하나도 건드리지 않는다. */
|
|
100
|
+
export declare function planBootstrap(input: BootstrapInput): Promise<BootstrapPlan>;
|
|
101
|
+
/** 사람이 읽는 계획. 무엇이 정해지지 않았는지가 먼저 오도록 짠다. */
|
|
102
|
+
export declare function renderPlan(plan: BootstrapPlan): string;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Bootstrap Resolver — 붙이기 전에 무엇이 있고 무엇이 정해지지 않았는지 본다 (C-06).
|
|
2
|
+
//
|
|
3
|
+
// 두 진입(CLI의 `asc init` / 자연어 "ASC로 진행해")이 여기서 만난다. 진입만 다르고 판정은 하나여야
|
|
4
|
+
// 한다 — 자연어 경로가 CLI 경로보다 더 할 수 있는 것도, 덜 할 수 있는 것도 없다.
|
|
5
|
+
//
|
|
6
|
+
// 이 모듈은 **아무것도 쓰지 않는다.** 감지하고, 무엇을 할지 적고, 돌려준다. 실행은 기존
|
|
7
|
+
// 표면(init / host install / profile resolve)이 그대로 한다 — 새 orchestration 계층을
|
|
8
|
+
// 만들면 같은 일을 하는 경로가 둘이 되고, 그때부터 둘이 조금씩 달라진다.
|
|
9
|
+
//
|
|
10
|
+
// 후보를 찾아 주는 것과 그중 하나를 대신 고르는 것은 다른 일이다. 후보가 하나뿐이어도
|
|
11
|
+
// 고르지 않는다 (C-06 §2) — Profile 선택은 그 프로젝트가 어떤 규칙 아래 돌지를 정하는
|
|
12
|
+
// 결정이고, 그건 사람 몫이다.
|
|
13
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { availableCapabilities } from "../binding/types.js";
|
|
16
|
+
import { discoverProjectRoot, exists } from "./init.js";
|
|
17
|
+
/**
|
|
18
|
+
* 정책은 사람이 정한다 (C-09 §7.1). adapter가 찾아 주는 것과 그것이 무슨 뜻인지 정하는 것은
|
|
19
|
+
* 다른 일이며, 후자를 대신 정하면 그건 wizard다 (C-06 §2).
|
|
20
|
+
*/
|
|
21
|
+
export const POLICY_QUESTIONS = [
|
|
22
|
+
{ id: 'canonical', question: '어느 branch·ref가 canonical인가' },
|
|
23
|
+
{ id: 'work-ssot', question: '작업 항목의 정본이 어디인가' },
|
|
24
|
+
{ id: 'ownership', question: 'ownership path는 무엇인가' },
|
|
25
|
+
{ id: 'authority', question: 'decision authority는 누구인가' },
|
|
26
|
+
{ id: 'digest-channel', question: '기본 전달 채널은 무엇인가' },
|
|
27
|
+
];
|
|
28
|
+
/** 설치 경로의 Profile 후보. 없으면 빈 목록이다 — 그것도 사람이 알아야 할 사실이다. */
|
|
29
|
+
export async function availableProfiles(installRoot) {
|
|
30
|
+
try {
|
|
31
|
+
const entries = await readdir(join(installRoot, 'profiles'), { withFileTypes: true });
|
|
32
|
+
const ids = [];
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (!entry.isDirectory())
|
|
35
|
+
continue;
|
|
36
|
+
if (await exists(join(installRoot, 'profiles', entry.name, 'profile.json')))
|
|
37
|
+
ids.push(entry.name);
|
|
38
|
+
}
|
|
39
|
+
return ids.sort();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** 붙어 있다면 무엇으로 붙었는가. lock을 읽지 못하면 붙이다 만 상태로 본다. */
|
|
46
|
+
async function attachedProfile(ascRoot) {
|
|
47
|
+
try {
|
|
48
|
+
const lock = JSON.parse(await readFile(join(ascRoot, 'profile.lock'), 'utf8'));
|
|
49
|
+
return typeof lock.profile?.id === 'string' ? lock.profile.id : null;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** 감지하고 계획을 세운다. 파일은 하나도 건드리지 않는다. */
|
|
56
|
+
export async function planBootstrap(input) {
|
|
57
|
+
const { root: projectRoot, git } = await discoverProjectRoot(input.cwd);
|
|
58
|
+
// 넘겨받은 것이 없을 때만 저장소 안을 본다 — 옛 경로를 계속 지원하되 기본으로 두지 않는다.
|
|
59
|
+
const ascRoot = input.ascRoot ?? join(projectRoot, '.asc');
|
|
60
|
+
const attached = await exists(ascRoot);
|
|
61
|
+
const candidates = await availableProfiles(input.installRoot);
|
|
62
|
+
const hosts = [...(input.hosts ?? [])];
|
|
63
|
+
const plan = input.bindings ?? { bindings: [] };
|
|
64
|
+
const locked = attached ? await attachedProfile(ascRoot) : null;
|
|
65
|
+
const profile = input.profileId
|
|
66
|
+
? { kind: 'GIVEN', id: input.profileId }
|
|
67
|
+
: locked
|
|
68
|
+
? { kind: 'ALREADY_ATTACHED', id: locked }
|
|
69
|
+
: attached
|
|
70
|
+
? { kind: 'ATTACHED_UNKNOWN' }
|
|
71
|
+
: { kind: 'UNDECIDED', candidates };
|
|
72
|
+
const steps = [];
|
|
73
|
+
const undecided = [];
|
|
74
|
+
if (profile.kind === 'UNDECIDED') {
|
|
75
|
+
undecided.push(candidates.length === 0
|
|
76
|
+
? '쓸 수 있는 Profile이 없다 — 설치 경로에 profiles/<id>/profile.json 이 있어야 한다'
|
|
77
|
+
: `어떤 Profile로 붙일지 (후보: ${candidates.join(', ')})`);
|
|
78
|
+
if (candidates.length > 0)
|
|
79
|
+
steps.push('asc init --profile <위 후보 중 하나>');
|
|
80
|
+
}
|
|
81
|
+
else if (profile.kind === 'ATTACHED_UNKNOWN') {
|
|
82
|
+
// 통과시키면 무엇으로 도는지 모르는 채 굴러간다 — bootstrapGuard가 막는 것과 같은 상태다
|
|
83
|
+
undecided.push('.asc/ 는 있는데 profile.lock 을 읽지 못했다 — 붙이다 만 상태다');
|
|
84
|
+
steps.push('asc init --profile <id> 로 다시 붙이거나 asc profile resolve --write 로 고정한다');
|
|
85
|
+
}
|
|
86
|
+
else if (!attached) {
|
|
87
|
+
steps.push(`asc init --profile ${profile.id}`);
|
|
88
|
+
}
|
|
89
|
+
for (const host of hosts) {
|
|
90
|
+
if (!host.installed)
|
|
91
|
+
steps.push(`asc host ${host.id} install`);
|
|
92
|
+
}
|
|
93
|
+
// 설정만 채우면 열리는 것과 지금 닿지 않는 것을 나눠 말한다 — 합치면 사람이 무엇을
|
|
94
|
+
// 해야 하는지 알 수 없다 (C-09 §5.1).
|
|
95
|
+
for (const binding of plan.bindings) {
|
|
96
|
+
if (binding.state === 'UNCONFIGURED') {
|
|
97
|
+
undecided.push(`${binding.adapterId} (${binding.resource}): ${binding.detail ?? '설정이 필요하다'}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (input.askPolicy !== false) {
|
|
101
|
+
const declared = new Set(input.declaredPolicies ?? []);
|
|
102
|
+
for (const policy of POLICY_QUESTIONS) {
|
|
103
|
+
if (!declared.has(policy.id))
|
|
104
|
+
undecided.push(policy.question);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
steps.push('asc setup status');
|
|
108
|
+
if (attached)
|
|
109
|
+
steps.push('asc proceed');
|
|
110
|
+
return {
|
|
111
|
+
projectRoot,
|
|
112
|
+
git,
|
|
113
|
+
attached,
|
|
114
|
+
profile,
|
|
115
|
+
hosts,
|
|
116
|
+
bindings: plan.bindings,
|
|
117
|
+
runtimes: plan.runtimes ?? [],
|
|
118
|
+
capabilities: availableCapabilities(plan),
|
|
119
|
+
steps,
|
|
120
|
+
undecided,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** 사람이 읽는 계획. 무엇이 정해지지 않았는지가 먼저 오도록 짠다. */
|
|
124
|
+
export function renderPlan(plan) {
|
|
125
|
+
const lines = [];
|
|
126
|
+
lines.push(`프로젝트: ${plan.projectRoot}${plan.git ? '' : ' (git 저장소가 아니다)'}`);
|
|
127
|
+
lines.push(plan.attached ? 'ASC: 이미 붙어 있다' : 'ASC: 아직 붙지 않았다');
|
|
128
|
+
if (plan.profile.kind === 'UNDECIDED') {
|
|
129
|
+
lines.push(plan.profile.candidates.length > 0
|
|
130
|
+
? `Profile 후보: ${plan.profile.candidates.join(', ')}`
|
|
131
|
+
: 'Profile 후보: 없음');
|
|
132
|
+
}
|
|
133
|
+
else if (plan.profile.kind === 'ATTACHED_UNKNOWN') {
|
|
134
|
+
lines.push('Profile: 알 수 없음 (profile.lock 을 읽지 못했다)');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
lines.push(`Profile: ${plan.profile.id}`);
|
|
138
|
+
}
|
|
139
|
+
for (const host of plan.hosts) {
|
|
140
|
+
lines.push(`Host ${host.id}: ${host.installed ? '설치됨' : '설치되지 않음'}`);
|
|
141
|
+
}
|
|
142
|
+
if (plan.runtimes.length > 0) {
|
|
143
|
+
lines.push('', '도구 상태 (이 프로젝트와 무관한 사실):');
|
|
144
|
+
for (const runtime of plan.runtimes) {
|
|
145
|
+
lines.push(` ${runtime.adapterId} ${runtime.state}${runtime.detail ? ` — ${runtime.detail}` : ''}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
lines.push('', '발견된 외부 연결:');
|
|
149
|
+
if (plan.bindings.length === 0) {
|
|
150
|
+
lines.push(' (없음)');
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
for (const binding of plan.bindings) {
|
|
154
|
+
const role = binding.role ? ` [${binding.role}]` : '';
|
|
155
|
+
const detail = binding.detail ? ` — ${binding.detail}` : '';
|
|
156
|
+
lines.push(` ${binding.adapterId} ${binding.resource}${role} ${binding.state}${detail}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// 도구는 되는데 이 프로젝트가 안 붙어 있는 경우를 따로 말한다. 그러지 않으면
|
|
160
|
+
// "도구는 쓸 수 있다는데 왜 아무것도 안 보이지"에서 사람이 멈춘다.
|
|
161
|
+
for (const runtime of plan.runtimes) {
|
|
162
|
+
if (runtime.state !== 'AVAILABLE')
|
|
163
|
+
continue;
|
|
164
|
+
if (plan.bindings.some((binding) => binding.adapterId === runtime.adapterId))
|
|
165
|
+
continue;
|
|
166
|
+
lines.push(` ${runtime.adapterId} NOT DISCOVERED — 도구는 쓸 수 있으나 이 프로젝트에 연결 선언이 없다`);
|
|
167
|
+
}
|
|
168
|
+
lines.push('', `지금 쓸 수 있는 것: ${plan.capabilities.join(', ') || '없음'}`);
|
|
169
|
+
if (plan.undecided.length > 0) {
|
|
170
|
+
lines.push('', '정해지지 않은 것 (ASC가 대신 정하지 않는다):');
|
|
171
|
+
for (const item of plan.undecided)
|
|
172
|
+
lines.push(` - ${item}`);
|
|
173
|
+
}
|
|
174
|
+
lines.push('', '다음 순서:');
|
|
175
|
+
for (const step of plan.steps)
|
|
176
|
+
lines.push(` ${step}`);
|
|
177
|
+
return lines.join('\n');
|
|
178
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare function exists(path: string): Promise<boolean>;
|
|
2
|
+
/** `.git`이 있는 곳까지 올라간다. 없으면 여기가 프로젝트 뿌리다. */
|
|
3
|
+
export declare function discoverProjectRoot(start: string): Promise<{
|
|
4
|
+
root: string;
|
|
5
|
+
git: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
export type ExcludeOutcome = 'added' | 'already' | 'no-git';
|
|
8
|
+
/**
|
|
9
|
+
* `.asc/`를 Git 추적에서 뺀다. 프로젝트의 `.gitignore`는 팀 파일이라 손대지 않는다 —
|
|
10
|
+
* ASC를 쓰지 않는 사람에게 ASC의 흔적이 보이면 안 된다 (OM §1.2).
|
|
11
|
+
*
|
|
12
|
+
* **project scope에서만 부른다** (C-11 §5). local scope는 저장소 파일을 한 바이트도
|
|
13
|
+
* 건드리지 않으므로 여기에 오지 않는다 — `.git/info/exclude` 자기등록은 legacy·전환기
|
|
14
|
+
* 안전장치이지 개인 사용의 정상 경로가 아니다.
|
|
15
|
+
*/
|
|
16
|
+
export declare function excludeFromGit(projectRoot: string): Promise<ExcludeOutcome>;
|
|
17
|
+
/** 개인 몫 서식. 처음 한 번만 만들고 이후엔 사람 것이다 (OM §4.4). */
|
|
18
|
+
export declare function overrideTemplate(): string;
|
|
19
|
+
/**
|
|
20
|
+
* 승인자 매핑 서식. 비어 있으면 어떤 승인도 통과하지 않는다 (OM §11.6).
|
|
21
|
+
*
|
|
22
|
+
* 예시를 `$example` 안에 두는 이유: loadIdentityMap은 값이 배열인 항목만 매핑으로 받으므로
|
|
23
|
+
* 이 키는 조용히 무시된다. 반대로 예시를 실제 매핑 형태로 바로 적으면 **그 이름이 유효한
|
|
24
|
+
* 승인자가 되어** 아무도 지정하지 않은 사람이 승인을 통과시킨다. 형식을 보여주되 살아나지
|
|
25
|
+
* 않는 자리에 둔다.
|
|
26
|
+
*/
|
|
27
|
+
export declare function identitiesTemplate(): string;
|
|
28
|
+
/** 이미 있으면 건드리지 않는다. 사람이 채운 것을 덮지 않기 위해서다. */
|
|
29
|
+
export declare function writeIfAbsent(path: string, content: string): Promise<boolean>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Project Attach — 프로젝트 하나에 Runtime을 붙인다 (OM §3.3~3.4, C-11 §2·§5).
|
|
2
|
+
//
|
|
3
|
+
// **기본은 local scope이고, 그때 프로젝트에는 아무것도 생기지 않는다.** runtime은 사용자
|
|
4
|
+
// 소유 공간(`ASC_HOME/workspaces/<W-id>`)에 있다. 프로젝트 안의 `.asc/`는 팀이 그렇게
|
|
5
|
+
// 하기로 정한 project scope이거나 아직 옮기지 않은 legacy일 때만 존재한다.
|
|
6
|
+
//
|
|
7
|
+
// 프로젝트는 ASC를 몰라야 하며, runtime을 지우면 아무 일도 없었던 것이 되어야 한다.
|
|
8
|
+
import { access, appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
9
|
+
import { constants } from 'node:fs';
|
|
10
|
+
import { dirname, join, resolve } from 'node:path';
|
|
11
|
+
const EXCLUDE_LINE = '.asc/';
|
|
12
|
+
export async function exists(path) {
|
|
13
|
+
try {
|
|
14
|
+
await access(path, constants.F_OK);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** `.git`이 있는 곳까지 올라간다. 없으면 여기가 프로젝트 뿌리다. */
|
|
22
|
+
export async function discoverProjectRoot(start) {
|
|
23
|
+
let dir = resolve(start);
|
|
24
|
+
for (;;) {
|
|
25
|
+
if (await exists(join(dir, '.git')))
|
|
26
|
+
return { root: dir, git: true };
|
|
27
|
+
const parent = dirname(dir);
|
|
28
|
+
if (parent === dir)
|
|
29
|
+
return { root: resolve(start), git: false };
|
|
30
|
+
dir = parent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `.asc/`를 Git 추적에서 뺀다. 프로젝트의 `.gitignore`는 팀 파일이라 손대지 않는다 —
|
|
35
|
+
* ASC를 쓰지 않는 사람에게 ASC의 흔적이 보이면 안 된다 (OM §1.2).
|
|
36
|
+
*
|
|
37
|
+
* **project scope에서만 부른다** (C-11 §5). local scope는 저장소 파일을 한 바이트도
|
|
38
|
+
* 건드리지 않으므로 여기에 오지 않는다 — `.git/info/exclude` 자기등록은 legacy·전환기
|
|
39
|
+
* 안전장치이지 개인 사용의 정상 경로가 아니다.
|
|
40
|
+
*/
|
|
41
|
+
export async function excludeFromGit(projectRoot) {
|
|
42
|
+
const gitDir = join(projectRoot, '.git');
|
|
43
|
+
if (!(await exists(gitDir)))
|
|
44
|
+
return 'no-git';
|
|
45
|
+
const excludePath = join(gitDir, 'info', 'exclude');
|
|
46
|
+
await mkdir(dirname(excludePath), { recursive: true });
|
|
47
|
+
let current = '';
|
|
48
|
+
try {
|
|
49
|
+
current = await readFile(excludePath, 'utf8');
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error.code !== 'ENOENT')
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
if (current.split('\n').some((line) => line.trim() === EXCLUDE_LINE))
|
|
56
|
+
return 'already';
|
|
57
|
+
const prefix = current.length > 0 && !current.endsWith('\n') ? '\n' : '';
|
|
58
|
+
await appendFile(excludePath, `${prefix}${EXCLUDE_LINE}\n`, 'utf8');
|
|
59
|
+
return 'added';
|
|
60
|
+
}
|
|
61
|
+
/** 개인 몫 서식. 처음 한 번만 만들고 이후엔 사람 것이다 (OM §4.4). */
|
|
62
|
+
export function overrideTemplate() {
|
|
63
|
+
return `${JSON.stringify({
|
|
64
|
+
$comment: '개인 몫입니다. 팀원마다 이 파일만 다릅니다. 토큰은 값이 아니라 이름으로만 적습니다.',
|
|
65
|
+
// 이 파일은 profile.lock의 digest에 들어간다. 고치고 재고정하지 않으면 다음 명령이
|
|
66
|
+
// LOCK_DRIFT로 멈추는데, 그때 가서 알면 늦다 — 파일을 열었을 때 여기 적혀 있어야 한다.
|
|
67
|
+
$afterEdit: '이 파일을 고친 뒤에는 `asc profile resolve --write` 로 재고정하세요.',
|
|
68
|
+
schemaVersion: 1,
|
|
69
|
+
monitorIdentities: [],
|
|
70
|
+
controller: { identities: {} },
|
|
71
|
+
approval: { preferredChannel: 'local' },
|
|
72
|
+
}, null, 2)}\n`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 승인자 매핑 서식. 비어 있으면 어떤 승인도 통과하지 않는다 (OM §11.6).
|
|
76
|
+
*
|
|
77
|
+
* 예시를 `$example` 안에 두는 이유: loadIdentityMap은 값이 배열인 항목만 매핑으로 받으므로
|
|
78
|
+
* 이 키는 조용히 무시된다. 반대로 예시를 실제 매핑 형태로 바로 적으면 **그 이름이 유효한
|
|
79
|
+
* 승인자가 되어** 아무도 지정하지 않은 사람이 승인을 통과시킨다. 형식을 보여주되 살아나지
|
|
80
|
+
* 않는 자리에 둔다.
|
|
81
|
+
*/
|
|
82
|
+
export function identitiesTemplate() {
|
|
83
|
+
return `${JSON.stringify({
|
|
84
|
+
$comment: '승인 권한자 이름과, 그 사람을 알아볼 채널:계정을 잇습니다. 비밀·토큰은 넣지 않습니다. ' +
|
|
85
|
+
'아래 $example 을 참고해 같은 형태로 항목을 추가하세요 ($example 자체는 무시됩니다).',
|
|
86
|
+
$example: { 'controller-이름': ['local:내-계정', '<채널>:@내-계정'] },
|
|
87
|
+
}, null, 2)}\n`;
|
|
88
|
+
}
|
|
89
|
+
/** 이미 있으면 건드리지 않는다. 사람이 채운 것을 덮지 않기 위해서다. */
|
|
90
|
+
export async function writeIfAbsent(path, content) {
|
|
91
|
+
try {
|
|
92
|
+
await writeFile(path, content, { encoding: 'utf8', flag: 'wx' });
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (error.code === 'EEXIST')
|
|
97
|
+
return false;
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { StableInstallState } from '../distribution/runtime-install.ts';
|
|
2
|
+
/** 세상의 스냅샷. 읽기만 해서 만든다. */
|
|
3
|
+
export type SetupState = {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
git: boolean;
|
|
6
|
+
/** 이미 붙어 있으면 그 runtime 뿌리. 없으면 안 붙은 것이다. */
|
|
7
|
+
ascRoot?: string;
|
|
8
|
+
/** 붙어 있다면 무엇으로 붙었는가. */
|
|
9
|
+
attachedProfile?: string;
|
|
10
|
+
/** 사람이 `--profile` 로 지정한 것. */
|
|
11
|
+
requestedProfile?: string;
|
|
12
|
+
/** 이 설치본이 아는 Profile 후보. */
|
|
13
|
+
profileCandidates: readonly string[];
|
|
14
|
+
/** 채택 범위. 기본은 개인이다 (C-11 §2). */
|
|
15
|
+
scope: 'local' | 'project';
|
|
16
|
+
/** Host 설치 상태 — L-5 판정을 그대로 쓴다. */
|
|
17
|
+
host: {
|
|
18
|
+
id: string;
|
|
19
|
+
status: string;
|
|
20
|
+
}[];
|
|
21
|
+
/**
|
|
22
|
+
* 이 machine에 stable `asc` 가 있는가 (C-14 §3).
|
|
23
|
+
*
|
|
24
|
+
* **bootstrap이 들고 온 임시 runtime과 다른 축이다** — 없으면 이 축을 그리지 않고,
|
|
25
|
+
* 그때는 설치된 `asc` 를 전제하지 않는다.
|
|
26
|
+
*/
|
|
27
|
+
stableRuntime?: StableInstallState;
|
|
28
|
+
};
|
|
29
|
+
export type SetupChange =
|
|
30
|
+
/**
|
|
31
|
+
* 이 machine에 stable `asc` 를 놓는다 (C-14 §3.1).
|
|
32
|
+
*
|
|
33
|
+
* **설치도 plan에 드러나는 mutation이다** — bootstrap이 몰래 하지 않는다 (불변식 ⑩).
|
|
34
|
+
*/
|
|
35
|
+
{
|
|
36
|
+
target: 'runtime-install';
|
|
37
|
+
package: string;
|
|
38
|
+
version: string;
|
|
39
|
+
strategy: 'npm-global';
|
|
40
|
+
from: string;
|
|
41
|
+
}
|
|
42
|
+
/** 이 checkout에 runtime을 붙인다. local scope면 저장소에는 아무것도 만들지 않는다. */
|
|
43
|
+
| {
|
|
44
|
+
target: 'attach-workspace';
|
|
45
|
+
scope: 'local' | 'project';
|
|
46
|
+
profile: string;
|
|
47
|
+
}
|
|
48
|
+
/** Host 설치물을 지금 source에 맞춘다. 왜 필요한지까지 든다. */
|
|
49
|
+
| {
|
|
50
|
+
target: 'host-install';
|
|
51
|
+
host: string;
|
|
52
|
+
from: string;
|
|
53
|
+
};
|
|
54
|
+
export type SetupStatus = 'already_configured' | 'ready_to_apply' | 'user_action_required';
|
|
55
|
+
export type SetupCode =
|
|
56
|
+
/** 후보가 여럿이거나 없다 — 고르는 것은 사람이다 (C-06 §2). */
|
|
57
|
+
'ASC_PROFILE_SELECTION_REQUIRED'
|
|
58
|
+
/** 저장소에 두는 것은 팀의 결정이다 (C-11 불변식 ⑤). */
|
|
59
|
+
| 'ASC_PROJECT_SCOPE_REQUIRES_CONSENT'
|
|
60
|
+
/** 설치물을 사람이 고쳤다 — 덮는 것은 사람이 정한다 (L-5). */
|
|
61
|
+
| 'ASC_HOST_INSTALL_MODIFIED';
|
|
62
|
+
/**
|
|
63
|
+
* 다음에 할 일 하나. **두 형태를 함께 든다** (C-14 §3.4, 불변식 ⑯).
|
|
64
|
+
*
|
|
65
|
+
* `display` 는 사람이 읽는 짧은 형태이고, `portable` 은 **지금 이 machine 상태에서
|
|
66
|
+
* 그대로 실행되는** 형태다. runtime이 아직 없으면 둘이 다르고, 설치된 뒤에는 같다.
|
|
67
|
+
* agent는 `portable` 만 실행하면 되고, 산문을 읽을 필요가 없다.
|
|
68
|
+
*/
|
|
69
|
+
export type NextAction = {
|
|
70
|
+
type: 'select_profile' | 'install_runtime' | 'apply_setup' | 'proceed' | 'force_host_install';
|
|
71
|
+
display: string;
|
|
72
|
+
portable: string;
|
|
73
|
+
};
|
|
74
|
+
export type SetupPlan = {
|
|
75
|
+
status: SetupStatus;
|
|
76
|
+
code?: SetupCode;
|
|
77
|
+
/** apply가 할 일. 여기 없는 변경은 일어나지 않는다 (C-14 불변식 ⑩). */
|
|
78
|
+
changes: SetupChange[];
|
|
79
|
+
requiresUserAction: boolean;
|
|
80
|
+
/** 사람이 골라야 할 때 무엇 중에서 고르는가. 대신 고르지 않는다. */
|
|
81
|
+
profiles?: readonly string[];
|
|
82
|
+
/**
|
|
83
|
+
* 명령 문자열. **설치 이전에는 `asc …` 를 전제하지 않는다** — 아래 `actions` 의
|
|
84
|
+
* `portable` 과 같은 값이다. 기존 소비자를 위해 유지한다.
|
|
85
|
+
*/
|
|
86
|
+
nextActions: string[];
|
|
87
|
+
/** 같은 것을 두 형태로. agent는 `portable`, 사람은 `display`. */
|
|
88
|
+
actions: NextAction[];
|
|
89
|
+
/** 지금 명령을 어디서 실행하고 있는가 — 설치 전이면 bootstrap이다. */
|
|
90
|
+
executionMode: 'bootstrap' | 'installed-runtime';
|
|
91
|
+
/** 왜 이 판정인가. 산문이 아니라 사실이다. */
|
|
92
|
+
evidence: string[];
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* 무엇을 바꿀 것인가. **아무것도 바꾸지 않는다.**
|
|
96
|
+
*
|
|
97
|
+
* 사람이 답해야 하는 것(profile 선택·project 채택·설치물 수정)은 여기서 멈춘다 —
|
|
98
|
+
* agent라고 해서 대신 추측하지 않는다 (C-13 · C-14 §7.1).
|
|
99
|
+
*/
|
|
100
|
+
export declare function computeSetupPlan(state: SetupState): SetupPlan;
|
|
101
|
+
/** apply가 실제로 부를 것들. Core는 무엇이 그것을 하는지 모른다. */
|
|
102
|
+
export type SetupEffects = {
|
|
103
|
+
installRuntime(change: Extract<SetupChange, {
|
|
104
|
+
target: 'runtime-install';
|
|
105
|
+
}>): Promise<void>;
|
|
106
|
+
attachWorkspace(change: Extract<SetupChange, {
|
|
107
|
+
target: 'attach-workspace';
|
|
108
|
+
}>): Promise<void>;
|
|
109
|
+
installHost(change: Extract<SetupChange, {
|
|
110
|
+
target: 'host-install';
|
|
111
|
+
}>): Promise<void>;
|
|
112
|
+
};
|
|
113
|
+
export type ApplyResult = {
|
|
114
|
+
applied: SetupChange[];
|
|
115
|
+
changesApplied: boolean;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* plan에 적힌 것만 실행한다. **다시 판단하지 않는다** (C-14 불변식 ⑩).
|
|
119
|
+
*
|
|
120
|
+
* 여기서 상태를 다시 보고 마음을 바꾸면, 사람이 승인한 plan과 실제로 일어난 일이
|
|
121
|
+
* 달라진다. 그 순간 plan은 아무것도 보장하지 않는 문서가 된다.
|
|
122
|
+
*/
|
|
123
|
+
export declare function applySetupPlan(plan: SetupPlan, effects: SetupEffects): Promise<ApplyResult>;
|
|
124
|
+
/** 사람이 읽는 줄. 같은 plan에서 나온다 — agent가 보는 JSON과 다른 판단이 아니다. */
|
|
125
|
+
export declare function renderSetupPlan(plan: SetupPlan): string[];
|