@cleocode/core 2026.9.15 → 2026.9.17
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/changesets/parser.d.ts.map +1 -1
- package/dist/changesets/parser.js +67 -0
- package/dist/changesets/parser.js.map +1 -1
- package/dist/crypto/credentials.d.ts +128 -26
- package/dist/crypto/credentials.d.ts.map +1 -1
- package/dist/crypto/credentials.js +183 -142
- package/dist/crypto/credentials.js.map +1 -1
- package/dist/docs/export-document.js +14705 -12949
- package/dist/docs/export-document.js.map +3 -3
- package/dist/doctor/index.d.ts +1 -1
- package/dist/doctor/index.d.ts.map +1 -1
- package/dist/doctor/index.js +1 -1
- package/dist/doctor/index.js.map +1 -1
- package/dist/doctor/nexus-schema-residency.d.ts +1 -1
- package/dist/doctor/nexus-schema-residency.d.ts.map +1 -1
- package/dist/doctor/nexus-schema-residency.js +1 -0
- package/dist/doctor/nexus-schema-residency.js.map +1 -1
- package/dist/doctor/project-identity.d.ts +124 -0
- package/dist/doctor/project-identity.d.ts.map +1 -0
- package/dist/doctor/project-identity.js +307 -0
- package/dist/doctor/project-identity.js.map +1 -0
- package/dist/doctor/superseded-store.d.ts +8 -0
- package/dist/doctor/superseded-store.d.ts.map +1 -1
- package/dist/doctor/superseded-store.js +48 -3
- package/dist/doctor/superseded-store.js.map +1 -1
- package/dist/init.d.ts +7 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +17 -2
- package/dist/init.js.map +1 -1
- package/dist/internal.d.ts +5 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -2
- package/dist/internal.js.map +1 -1
- package/dist/llm/credentials-store.d.ts +10 -0
- package/dist/llm/credentials-store.d.ts.map +1 -1
- package/dist/llm/credentials-store.js +7 -1
- package/dist/llm/credentials-store.js.map +1 -1
- package/dist/llm/plugin-facade.js +13368 -1550
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/nexus/analyze-orchestrator.d.ts +20 -3
- package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
- package/dist/nexus/analyze-orchestrator.js +159 -10
- package/dist/nexus/analyze-orchestrator.js.map +1 -1
- package/dist/nexus/assessment-store.d.ts +57 -0
- package/dist/nexus/assessment-store.d.ts.map +1 -0
- package/dist/nexus/assessment-store.js +73 -0
- package/dist/nexus/assessment-store.js.map +1 -0
- package/dist/nexus/freshness.d.ts +131 -0
- package/dist/nexus/freshness.d.ts.map +1 -0
- package/dist/nexus/freshness.js +391 -0
- package/dist/nexus/freshness.js.map +1 -0
- package/dist/nexus/graph-manifest.d.ts +88 -0
- package/dist/nexus/graph-manifest.d.ts.map +1 -0
- package/dist/nexus/graph-manifest.js +114 -0
- package/dist/nexus/graph-manifest.js.map +1 -0
- package/dist/nexus/knowledge.d.ts +21 -1
- package/dist/nexus/knowledge.d.ts.map +1 -1
- package/dist/nexus/knowledge.js +76 -37
- package/dist/nexus/knowledge.js.map +1 -1
- package/dist/nexus/path-map.d.ts +55 -0
- package/dist/nexus/path-map.d.ts.map +1 -0
- package/dist/nexus/path-map.js +87 -0
- package/dist/nexus/path-map.js.map +1 -0
- package/dist/nexus/projects-clean.d.ts +21 -28
- package/dist/nexus/projects-clean.d.ts.map +1 -1
- package/dist/nexus/projects-clean.js +168 -85
- package/dist/nexus/projects-clean.js.map +1 -1
- package/dist/nexus/query.d.ts.map +1 -1
- package/dist/nexus/query.js +3 -2
- package/dist/nexus/query.js.map +1 -1
- package/dist/nexus/registry-hygiene.d.ts +70 -0
- package/dist/nexus/registry-hygiene.d.ts.map +1 -0
- package/dist/nexus/registry-hygiene.js +113 -0
- package/dist/nexus/registry-hygiene.js.map +1 -0
- package/dist/nexus/registry.d.ts +2 -2
- package/dist/nexus/registry.d.ts.map +1 -1
- package/dist/nexus/registry.js +48 -12
- package/dist/nexus/registry.js.map +1 -1
- package/dist/paths.d.ts +26 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +99 -6
- package/dist/paths.js.map +1 -1
- package/dist/render/nexus/graph/index.d.ts.map +1 -1
- package/dist/render/nexus/graph/index.js +8 -0
- package/dist/render/nexus/graph/index.js.map +1 -1
- package/dist/resources/spawn-wrapper.d.ts +0 -11
- package/dist/resources/spawn-wrapper.d.ts.map +1 -1
- package/dist/resources/spawn-wrapper.js +62 -6
- package/dist/resources/spawn-wrapper.js.map +1 -1
- package/dist/scaffold/ensure-config.d.ts +12 -3
- package/dist/scaffold/ensure-config.d.ts.map +1 -1
- package/dist/scaffold/ensure-config.js +61 -38
- package/dist/scaffold/ensure-config.js.map +1 -1
- package/dist/scaffold/project-identity.d.ts +136 -0
- package/dist/scaffold/project-identity.d.ts.map +1 -0
- package/dist/scaffold/project-identity.js +225 -0
- package/dist/scaffold/project-identity.js.map +1 -0
- package/dist/selfimprove/fix-gen.js +13319 -1556
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/store/agent-api-key.d.ts +114 -0
- package/dist/store/agent-api-key.d.ts.map +1 -0
- package/dist/store/agent-api-key.js +136 -0
- package/dist/store/agent-api-key.js.map +1 -0
- package/dist/store/agent-registry-accessor.d.ts +4 -2
- package/dist/store/agent-registry-accessor.d.ts.map +1 -1
- package/dist/store/agent-registry-accessor.js +39 -89
- package/dist/store/agent-registry-accessor.js.map +1 -1
- package/dist/store/api-key-kdf.d.ts +7 -0
- package/dist/store/api-key-kdf.d.ts.map +1 -1
- package/dist/store/api-key-kdf.js +7 -0
- package/dist/store/api-key-kdf.js.map +1 -1
- package/dist/store/backup-crypto.d.ts +49 -0
- package/dist/store/backup-crypto.d.ts.map +1 -1
- package/dist/store/backup-crypto.js +154 -0
- package/dist/store/backup-crypto.js.map +1 -1
- package/dist/store/credential-transfer.d.ts +303 -0
- package/dist/store/credential-transfer.d.ts.map +1 -0
- package/dist/store/credential-transfer.js +774 -0
- package/dist/store/credential-transfer.js.map +1 -0
- package/dist/store/exodus/column-transforms.d.ts +79 -4
- package/dist/store/exodus/column-transforms.d.ts.map +1 -1
- package/dist/store/exodus/column-transforms.js +121 -4
- package/dist/store/exodus/column-transforms.js.map +1 -1
- package/dist/store/exodus/index.d.ts +1 -0
- package/dist/store/exodus/index.d.ts.map +1 -1
- package/dist/store/exodus/index.js +1 -0
- package/dist/store/exodus/index.js.map +1 -1
- package/dist/store/exodus/migrate.d.ts +17 -1
- package/dist/store/exodus/migrate.d.ts.map +1 -1
- package/dist/store/exodus/migrate.js +155 -33
- package/dist/store/exodus/migrate.js.map +1 -1
- package/dist/store/exodus/on-open.d.ts +14 -0
- package/dist/store/exodus/on-open.d.ts.map +1 -1
- package/dist/store/exodus/on-open.js +115 -6
- package/dist/store/exodus/on-open.js.map +1 -1
- package/dist/store/exodus/reconcile.d.ts +103 -0
- package/dist/store/exodus/reconcile.d.ts.map +1 -0
- package/dist/store/exodus/reconcile.js +728 -0
- package/dist/store/exodus/reconcile.js.map +1 -0
- package/dist/store/exodus/recovery.d.ts.map +1 -1
- package/dist/store/exodus/recovery.js +14 -2
- package/dist/store/exodus/recovery.js.map +1 -1
- package/dist/store/exodus/runtime-targets.d.ts +39 -0
- package/dist/store/exodus/runtime-targets.d.ts.map +1 -0
- package/dist/store/exodus/runtime-targets.js +63 -0
- package/dist/store/exodus/runtime-targets.js.map +1 -0
- package/dist/store/exodus/table-name-map.d.ts.map +1 -1
- package/dist/store/exodus/table-name-map.js +5 -0
- package/dist/store/exodus/table-name-map.js.map +1 -1
- package/dist/store/exodus/table-order.d.ts +35 -0
- package/dist/store/exodus/table-order.d.ts.map +1 -0
- package/dist/store/exodus/table-order.js +62 -0
- package/dist/store/exodus/table-order.js.map +1 -0
- package/dist/store/exodus/verify-migration.d.ts +4 -1
- package/dist/store/exodus/verify-migration.d.ts.map +1 -1
- package/dist/store/exodus/verify-migration.js +142 -10
- package/dist/store/exodus/verify-migration.js.map +1 -1
- package/dist/store/global-salt.d.ts +16 -0
- package/dist/store/global-salt.d.ts.map +1 -1
- package/dist/store/global-salt.js +22 -7
- package/dist/store/global-salt.js.map +1 -1
- package/dist/store/legacy-tasks-lineage.d.ts +86 -0
- package/dist/store/legacy-tasks-lineage.d.ts.map +1 -0
- package/dist/store/legacy-tasks-lineage.js +285 -0
- package/dist/store/legacy-tasks-lineage.js.map +1 -0
- package/dist/store/migration-manager.d.ts +27 -0
- package/dist/store/migration-manager.d.ts.map +1 -1
- package/dist/store/migration-manager.js +63 -0
- package/dist/store/migration-manager.js.map +1 -1
- package/dist/store/portable-bundle-import.d.ts +117 -0
- package/dist/store/portable-bundle-import.d.ts.map +1 -0
- package/dist/store/portable-bundle-import.js +589 -0
- package/dist/store/portable-bundle-import.js.map +1 -0
- package/dist/store/portable-bundle-relocate.d.ts +66 -0
- package/dist/store/portable-bundle-relocate.d.ts.map +1 -0
- package/dist/store/portable-bundle-relocate.js +304 -0
- package/dist/store/portable-bundle-relocate.js.map +1 -0
- package/dist/store/portable-bundle-scan.d.ts +167 -0
- package/dist/store/portable-bundle-scan.d.ts.map +1 -0
- package/dist/store/portable-bundle-scan.js +569 -0
- package/dist/store/portable-bundle-scan.js.map +1 -0
- package/dist/store/portable-bundle.d.ts +146 -0
- package/dist/store/portable-bundle.d.ts.map +1 -0
- package/dist/store/portable-bundle.js +674 -0
- package/dist/store/portable-bundle.js.map +1 -0
- package/dist/store/regenerators.d.ts +3 -1
- package/dist/store/regenerators.d.ts.map +1 -1
- package/dist/store/regenerators.js +6 -2
- package/dist/store/regenerators.js.map +1 -1
- package/dist/store/schema/cleo-global/nexus.d.ts +98 -0
- package/dist/store/schema/cleo-global/nexus.d.ts.map +1 -1
- package/dist/store/schema/cleo-global/nexus.js +19 -0
- package/dist/store/schema/cleo-global/nexus.js.map +1 -1
- package/dist/store/schema/nexus-schema.d.ts +102 -0
- package/dist/store/schema/nexus-schema.d.ts.map +1 -1
- package/dist/store/schema/nexus-schema.js +24 -0
- package/dist/store/schema/nexus-schema.js.map +1 -1
- package/dist/store/split-brain-import.d.ts +176 -0
- package/dist/store/split-brain-import.d.ts.map +1 -0
- package/dist/store/split-brain-import.js +680 -0
- package/dist/store/split-brain-import.js.map +1 -0
- package/dist/store/sqlite.d.ts +29 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +57 -5
- package/dist/store/sqlite.js.map +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +30 -5
- package/dist/system/health.js.map +1 -1
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +60 -0
- package/dist/upgrade.js.map +1 -1
- package/dist/validation/doctor/checks.d.ts +14 -0
- package/dist/validation/doctor/checks.d.ts.map +1 -1
- package/dist/validation/doctor/checks.js +33 -0
- package/dist/validation/doctor/checks.js.map +1 -1
- package/migrations/drizzle-cleo-global/20260924000000_t12354-project-paths/migration.sql +40 -0
- package/migrations/drizzle-nexus/20260924000000_t12315-nexus-parse-cache/migration.sql +21 -0
- package/package.json +13 -13
- package/templates/CLEO-INJECTION.md +3 -3
- package/templates/cleo-gitignore +7 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write-once portable project identity: decide, re-link, adopt (T12325).
|
|
3
|
+
*
|
|
4
|
+
* The tracked `.cleo/project-id` file (read by `@cleocode/paths`) is the one
|
|
5
|
+
* project identifier that survives a fresh clone, a move, and a new device.
|
|
6
|
+
* This module owns the two write-side rules ADR-094 (amending ADR-013 §9) depends on:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Write once.** The file is created with `O_EXCL` and never rewritten. A
|
|
9
|
+
* conflicting or malformed file is reported, not repaired.
|
|
10
|
+
* 2. **Never silently re-mint.** When no local or tracked id exists, the
|
|
11
|
+
* global registry is searched for the project's previous identity (same
|
|
12
|
+
* path, its canonical-fingerprint alias, or a live checkout of the same remote). A
|
|
13
|
+
* unique candidate is re-linked; several candidates refuse to guess; a new
|
|
14
|
+
* id is minted only when nothing can be re-linked or when the caller asks
|
|
15
|
+
* for one explicitly.
|
|
16
|
+
*
|
|
17
|
+
* Precedence when both files exist and disagree: the local
|
|
18
|
+
* `project-info.json` id wins and the conflict is reported. Local state
|
|
19
|
+
* (registry row, aliases, brain) is keyed by that id, so switching silently
|
|
20
|
+
* would re-key it — the data-loss geometry this whole design avoids. Two ids
|
|
21
|
+
* for one repository means two lineages (typically two devices that each ran
|
|
22
|
+
* `cleo init` before this file existed); reconciling them is an explicit act.
|
|
23
|
+
*
|
|
24
|
+
* @task T12325
|
|
25
|
+
*/
|
|
26
|
+
import { randomUUID } from 'node:crypto';
|
|
27
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
28
|
+
import { writeFile } from 'node:fs/promises';
|
|
29
|
+
import { join, resolve } from 'node:path';
|
|
30
|
+
import { ExitCode } from '@cleocode/contracts';
|
|
31
|
+
import { canonicalizePath, computeCanonicalProjectId, formatPortableProjectId, getCleoHome, isValidPortableProjectId, portableProjectIdPath, readPortableProjectId, } from '@cleocode/paths';
|
|
32
|
+
import { CleoError } from '../errors.js';
|
|
33
|
+
/**
|
|
34
|
+
* Normalise a git remote URL so `git@host:o/r.git` and `https://host/o/r`
|
|
35
|
+
* compare equal. Returns `null` for empty input.
|
|
36
|
+
*
|
|
37
|
+
* @param url - Raw `git remote get-url` output or a stored `remoteUrl`.
|
|
38
|
+
* @returns A `host/owner/repo` style key, or `null`.
|
|
39
|
+
*/
|
|
40
|
+
export function normalizeRemoteUrl(url) {
|
|
41
|
+
if (!url)
|
|
42
|
+
return null;
|
|
43
|
+
let key = url.trim();
|
|
44
|
+
if (!key)
|
|
45
|
+
return null;
|
|
46
|
+
key = key.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '');
|
|
47
|
+
key = key.replace(/^[^@/]+@/, '');
|
|
48
|
+
key = key.replace(/^([^/:]+):(?!\d+\/)/, '$1/');
|
|
49
|
+
key = key.replace(/\/+$/, '').replace(/\.git$/i, '');
|
|
50
|
+
return key.toLowerCase() || null;
|
|
51
|
+
}
|
|
52
|
+
/** Read `remoteUrl` from a checkout's `project-info.json`; null when absent or unusable. */
|
|
53
|
+
function readInfoRemote(projectPath) {
|
|
54
|
+
try {
|
|
55
|
+
const data = JSON.parse(readFileSync(join(projectPath, '.cleo', 'project-info.json'), 'utf-8'));
|
|
56
|
+
return typeof data['remoteUrl'] === 'string' ? data['remoteUrl'] : null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Search the global registry for identities this project root previously had.
|
|
64
|
+
*
|
|
65
|
+
* Tiers, strongest first: a row registered at this exact path; an alias row
|
|
66
|
+
* for this path's derived ids; a row whose LIVE checkout declares the same
|
|
67
|
+
* git remote. Stranded rows (path gone) cannot be matched by remote because
|
|
68
|
+
* the registry stores no remote — their checkout is the only record of it.
|
|
69
|
+
*
|
|
70
|
+
* @param projectRoot - Absolute project root.
|
|
71
|
+
* @param cleoHome - Global CLEO home whose registry to read.
|
|
72
|
+
* @returns Candidates plus diagnostics; an unreadable registry yields no
|
|
73
|
+
* candidates and a diagnostic saying coverage is missing.
|
|
74
|
+
*/
|
|
75
|
+
export async function findRelinkCandidates(projectRoot, cleoHome = getCleoHome()) {
|
|
76
|
+
const diagnostics = [];
|
|
77
|
+
const candidates = [];
|
|
78
|
+
const lexicalRoot = resolve(projectRoot);
|
|
79
|
+
const realRoot = canonicalizePath(projectRoot);
|
|
80
|
+
const rootForms = new Set([lexicalRoot, realRoot]);
|
|
81
|
+
try {
|
|
82
|
+
const { getNexusRegistryDb } = await import('../store/nexus-sqlite.js');
|
|
83
|
+
const { projectIdAliases, projectRegistry } = await import('../store/schema/nexus-schema.js');
|
|
84
|
+
const { inArray } = await import('drizzle-orm');
|
|
85
|
+
const db = await getNexusRegistryDb(cleoHome);
|
|
86
|
+
const rows = db
|
|
87
|
+
.select({ projectId: projectRegistry.projectId, projectPath: projectRegistry.projectPath })
|
|
88
|
+
.from(projectRegistry)
|
|
89
|
+
.all();
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
if (rootForms.has(row.projectPath))
|
|
92
|
+
candidates.push({ ...row, via: 'registry-path' });
|
|
93
|
+
}
|
|
94
|
+
// Only the full canonical fingerprint is a re-link key. The legacy
|
|
95
|
+
// base64url alias is truncated to 32 chars (24 path bytes), so every
|
|
96
|
+
// checkout under a shared prefix collides on it — measured: two sibling
|
|
97
|
+
// temp dirs re-linked to each other through it.
|
|
98
|
+
const derived = [computeCanonicalProjectId(realRoot)];
|
|
99
|
+
const aliasRows = db
|
|
100
|
+
.select()
|
|
101
|
+
.from(projectIdAliases)
|
|
102
|
+
.where(inArray(projectIdAliases.legacyId, derived))
|
|
103
|
+
.all();
|
|
104
|
+
for (const alias of aliasRows) {
|
|
105
|
+
const owner = rows.find((row) => row.projectId === alias.canonicalId);
|
|
106
|
+
candidates.push({
|
|
107
|
+
projectId: alias.canonicalId,
|
|
108
|
+
projectPath: owner?.projectPath ?? '',
|
|
109
|
+
via: 'registry-alias',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const { findGitRemoteUrl } = await import('../nexus/identity.js');
|
|
113
|
+
const remote = normalizeRemoteUrl(await findGitRemoteUrl(realRoot));
|
|
114
|
+
if (remote) {
|
|
115
|
+
for (const row of rows) {
|
|
116
|
+
if (rootForms.has(row.projectPath) || !existsSync(row.projectPath))
|
|
117
|
+
continue;
|
|
118
|
+
if (normalizeRemoteUrl(readInfoRemote(row.projectPath)) === remote)
|
|
119
|
+
candidates.push({ ...row, via: 'registry-remote' });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
diagnostics.push(`registry unavailable, re-link coverage missing: ${error instanceof Error ? error.message : String(error)}`);
|
|
125
|
+
}
|
|
126
|
+
return { candidates, diagnostics };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Decide the identity a scaffold step writes into `project-info.json`.
|
|
130
|
+
*
|
|
131
|
+
* @param projectRoot - Absolute project root.
|
|
132
|
+
* @param localInfoId - The id already in `project-info.json`, if any.
|
|
133
|
+
* @param options - Explicit-mint request and registry home.
|
|
134
|
+
* @returns The decision with provenance and diagnostics.
|
|
135
|
+
* @throws {CleoError} `CONFIG_ERROR` when the tracked file is malformed, or
|
|
136
|
+
* when the registry offers several candidates and no explicit mint was asked.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const { projectId, source } = await decideProjectIdentity(root, undefined);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export async function decideProjectIdentity(projectRoot, localInfoId, options = {}) {
|
|
144
|
+
const tracked = readPortableProjectId(projectRoot);
|
|
145
|
+
const diagnostics = [];
|
|
146
|
+
if (localInfoId) {
|
|
147
|
+
if (tracked.status === 'valid' && tracked.projectId !== localInfoId) {
|
|
148
|
+
diagnostics.push(`identity conflict: .cleo/project-id is '${tracked.projectId}' but project-info.json is '${localInfoId}'; keeping the local id — reconcile explicitly`);
|
|
149
|
+
}
|
|
150
|
+
return { projectId: localInfoId, source: 'project-info', diagnostics };
|
|
151
|
+
}
|
|
152
|
+
if (tracked.status === 'valid') {
|
|
153
|
+
return { projectId: tracked.projectId, source: 'tracked', diagnostics };
|
|
154
|
+
}
|
|
155
|
+
if (tracked.status === 'invalid') {
|
|
156
|
+
throw new CleoError(ExitCode.CONFIG_ERROR, `Tracked project identity ${portableProjectIdPath(projectRoot)} is unusable (${tracked.reason}); refusing to mint a replacement`, { fix: 'Restore it from version control: `git checkout -- .cleo/project-id`' });
|
|
157
|
+
}
|
|
158
|
+
if (options.mintNewIdentity) {
|
|
159
|
+
diagnostics.push('minted a new identity on explicit request');
|
|
160
|
+
return { projectId: randomUUID(), source: 'minted', diagnostics };
|
|
161
|
+
}
|
|
162
|
+
const found = await findRelinkCandidates(projectRoot, options.cleoHome);
|
|
163
|
+
diagnostics.push(...found.diagnostics);
|
|
164
|
+
for (const via of ['registry-path', 'registry-alias', 'registry-remote']) {
|
|
165
|
+
const tier = found.candidates.filter((candidate) => candidate.via === via);
|
|
166
|
+
const ids = [...new Set(tier.map((candidate) => candidate.projectId))].filter(isValidPortableProjectId);
|
|
167
|
+
if (ids.length === 1) {
|
|
168
|
+
const [projectId] = ids;
|
|
169
|
+
diagnostics.push(`re-linked identity '${projectId}' from ${via} (${tier[0]?.projectPath || 'alias'})`);
|
|
170
|
+
return { projectId, source: via, diagnostics };
|
|
171
|
+
}
|
|
172
|
+
if (ids.length > 1) {
|
|
173
|
+
throw new CleoError(ExitCode.CONFIG_ERROR, `Cannot re-link project identity for ${projectRoot}: ${ids.length} registered identities match by ${via} (${tier
|
|
174
|
+
.map((candidate) => `${candidate.projectId} @ ${candidate.projectPath}`)
|
|
175
|
+
.join(', ')})`, {
|
|
176
|
+
fix: 'Write the intended id to .cleo/project-id, or mint explicitly with `cleo init --new-identity`',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
diagnostics.push('minted a new identity: no local, tracked or registered identity to re-link');
|
|
181
|
+
return { projectId: randomUUID(), source: 'minted', diagnostics };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Make sure `<projectRoot>/.cleo/project-id` records `projectId` — write-once.
|
|
185
|
+
*
|
|
186
|
+
* Creates the file only when absent (`O_EXCL`); a present file is compared,
|
|
187
|
+
* never rewritten.
|
|
188
|
+
*
|
|
189
|
+
* @param projectRoot - Absolute project root.
|
|
190
|
+
* @param projectId - The identity to adopt.
|
|
191
|
+
* @returns What happened; `conflict`/`invalid` are for the caller to report.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* const outcome = await ensurePortableProjectId(root, info.projectId);
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export async function ensurePortableProjectId(projectRoot, projectId) {
|
|
199
|
+
const current = readPortableProjectId(projectRoot);
|
|
200
|
+
if (current.status === 'valid')
|
|
201
|
+
return current.projectId === projectId ? 'present' : 'conflict';
|
|
202
|
+
if (current.status === 'invalid')
|
|
203
|
+
return 'invalid';
|
|
204
|
+
if (!isValidPortableProjectId(projectId))
|
|
205
|
+
return 'invalid';
|
|
206
|
+
if (!existsSync(join(projectRoot, '.cleo')))
|
|
207
|
+
return 'no-cleo-dir';
|
|
208
|
+
try {
|
|
209
|
+
await writeFile(portableProjectIdPath(projectRoot), formatPortableProjectId(projectId), {
|
|
210
|
+
flag: 'wx',
|
|
211
|
+
});
|
|
212
|
+
return 'written';
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (error instanceof Error && 'code' in error && error.code === 'EEXIST') {
|
|
216
|
+
// Lost a race with another writer: judge what they wrote, never overwrite it.
|
|
217
|
+
const raced = readPortableProjectId(projectRoot);
|
|
218
|
+
if (raced.status === 'valid')
|
|
219
|
+
return raced.projectId === projectId ? 'present' : 'conflict';
|
|
220
|
+
return 'invalid';
|
|
221
|
+
}
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=project-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-identity.js","sourceRoot":"","sources":["../../src/scaffold/project-identity.ts"],"names":[],"mappings":"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"}
|