orbitmap 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -13
- package/dist/adapters/cloud.d.ts +53 -21
- package/dist/adapters/cloud.js +95 -29
- package/dist/adapters/cloud.js.map +1 -1
- package/dist/adapters/factory.d.ts +3 -0
- package/dist/adapters/factory.js +8 -1
- package/dist/adapters/factory.js.map +1 -1
- package/dist/adapters/local/adapter.d.ts +0 -20
- package/dist/adapters/local/adapter.js +0 -18
- package/dist/adapters/local/adapter.js.map +1 -1
- package/dist/adapters/local/context.d.ts +1 -1
- package/dist/adapters/local/context.js +10 -2
- package/dist/adapters/local/context.js.map +1 -1
- package/dist/adapters/local/entities/tasks.d.ts +9 -1
- package/dist/adapters/local/entities/tasks.js +47 -5
- package/dist/adapters/local/entities/tasks.js.map +1 -1
- package/dist/adapters/local/entities/work-items.d.ts +14 -30
- package/dist/adapters/local/entities/work-items.js +63 -47
- package/dist/adapters/local/entities/work-items.js.map +1 -1
- package/dist/adapters/local/rules.d.ts +4 -4
- package/dist/adapters/local/rules.js +4 -19
- package/dist/adapters/local/rules.js.map +1 -1
- package/dist/adapters/local/shapes.d.ts +27 -7
- package/dist/adapters/local/shapes.js +41 -5
- package/dist/adapters/local/shapes.js.map +1 -1
- package/dist/adapters/local/store.d.ts +55 -6
- package/dist/adapters/local/store.js +122 -8
- package/dist/adapters/local/store.js.map +1 -1
- package/dist/adapters/local/types.d.ts +9 -2
- package/dist/adapters/local/types.js +9 -2
- package/dist/adapters/local/types.js.map +1 -1
- package/dist/adapters/types.d.ts +7 -20
- package/dist/agent-instructions.d.ts +21 -8
- package/dist/agent-instructions.js +91 -35
- package/dist/agent-instructions.js.map +1 -1
- package/dist/area-directories.d.ts +75 -0
- package/dist/area-directories.js +162 -0
- package/dist/area-directories.js.map +1 -0
- package/dist/area-grouping.d.ts +55 -0
- package/dist/area-grouping.js +72 -0
- package/dist/area-grouping.js.map +1 -0
- package/dist/commands/areas.js +30 -2
- package/dist/commands/areas.js.map +1 -1
- package/dist/commands/assign.d.ts +1 -0
- package/dist/commands/assign.js +2 -0
- package/dist/commands/assign.js.map +1 -1
- package/dist/commands/context.d.ts +95 -0
- package/dist/commands/context.js +104 -2
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/create.d.ts +1 -0
- package/dist/commands/create.js +19 -14
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/dep.js +5 -8
- package/dist/commands/dep.js.map +1 -1
- package/dist/commands/doc-import.d.ts +1 -0
- package/dist/commands/doc-import.js +8 -26
- package/dist/commands/doc-import.js.map +1 -1
- package/dist/commands/doc-patch.js +16 -52
- package/dist/commands/doc-patch.js.map +1 -1
- package/dist/commands/doc-share.js +4 -0
- package/dist/commands/doc-share.js.map +1 -1
- package/dist/commands/doc-update.js +4 -8
- package/dist/commands/doc-update.js.map +1 -1
- package/dist/commands/doc.js +7 -12
- package/dist/commands/doc.js.map +1 -1
- package/dist/commands/docs-cache.js +23 -10
- package/dist/commands/docs-cache.js.map +1 -1
- package/dist/commands/docs.js +29 -10
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/get.js +4 -10
- package/dist/commands/get.js.map +1 -1
- package/dist/commands/ideas.d.ts +3 -1
- package/dist/commands/ideas.js +26 -49
- package/dist/commands/ideas.js.map +1 -1
- package/dist/commands/init.d.ts +24 -1
- package/dist/commands/init.js +0 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/intent.js +10 -14
- package/dist/commands/intent.js.map +1 -1
- package/dist/commands/issues.d.ts +3 -1
- package/dist/commands/issues.js +47 -72
- package/dist/commands/issues.js.map +1 -1
- package/dist/commands/log.d.ts +1 -0
- package/dist/commands/log.js +18 -17
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/mission.js +3 -12
- package/dist/commands/mission.js.map +1 -1
- package/dist/commands/orbit.d.ts +7 -1
- package/dist/commands/orbit.js +13 -24
- package/dist/commands/orbit.js.map +1 -1
- package/dist/commands/setup-agent.d.ts +97 -4
- package/dist/commands/setup-agent.js +265 -38
- package/dist/commands/setup-agent.js.map +1 -1
- package/dist/commands/setup-mcp.d.ts +2 -0
- package/dist/commands/setup-mcp.js +17 -40
- package/dist/commands/setup-mcp.js.map +1 -1
- package/dist/commands/start.d.ts +43 -0
- package/dist/commands/start.js +158 -3
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +5 -15
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/subtask.d.ts +1 -0
- package/dist/commands/subtask.js +9 -2
- package/dist/commands/subtask.js.map +1 -1
- package/dist/commands/task-edit-content.js +4 -8
- package/dist/commands/task-edit-content.js.map +1 -1
- package/dist/commands/task.d.ts +7 -0
- package/dist/commands/task.js +5 -2
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/tasks.d.ts +35 -0
- package/dist/commands/tasks.js +45 -9
- package/dist/commands/tasks.js.map +1 -1
- package/dist/commands/vibes.d.ts +3 -1
- package/dist/commands/vibes.js +41 -31
- package/dist/commands/vibes.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +14 -1
- package/dist/config.js.map +1 -1
- package/dist/doc-cache.d.ts +74 -5
- package/dist/doc-cache.js +106 -22
- package/dist/doc-cache.js.map +1 -1
- package/dist/enums.d.ts +26 -0
- package/dist/enums.js +53 -0
- package/dist/enums.js.map +1 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.js +82 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.js +134 -56
- package/dist/index.js.map +1 -1
- package/dist/mcp-config.d.ts +42 -1
- package/dist/mcp-config.js +82 -2
- package/dist/mcp-config.js.map +1 -1
- package/dist/oauth.js +10 -10
- package/dist/output.d.ts +12 -0
- package/dist/output.js +39 -1
- package/dist/output.js.map +1 -1
- package/dist/paths.d.ts +40 -0
- package/dist/paths.js +174 -9
- package/dist/paths.js.map +1 -1
- package/dist/project-config.d.ts +42 -1
- package/dist/project-config.js +51 -0
- package/dist/project-config.js.map +1 -1
- package/dist/workspace-resolve.d.ts +179 -9
- package/dist/workspace-resolve.js +288 -20
- package/dist/workspace-resolve.js.map +1 -1
- package/dist/write-target.d.ts +56 -0
- package/dist/write-target.js +170 -0
- package/dist/write-target.js.map +1 -0
- package/package.json +1 -1
- package/dist/commands/create-project.d.ts +0 -6
- package/dist/commands/create-project.js +0 -28
- package/dist/commands/create-project.js.map +0 -1
- package/dist/commands/projects.d.ts +0 -3
- package/dist/commands/projects.js +0 -28
- package/dist/commands/projects.js.map +0 -1
package/dist/paths.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
4
|
/** Name of the per-project OrbitMap directory (`<project>/.orbitmap`). */
|
|
5
5
|
export const ORBITMAP_DIR = '.orbitmap';
|
|
6
6
|
/**
|
|
@@ -12,6 +12,28 @@ export const ORBITMAP_DIR = '.orbitmap';
|
|
|
12
12
|
export const CONFIG_FILENAME = 'config.json';
|
|
13
13
|
/** Filename of the machine-local secret store (`~/.orbitmap/credentials.json`). */
|
|
14
14
|
export const CREDENTIALS_FILENAME = 'credentials.json';
|
|
15
|
+
/**
|
|
16
|
+
* Filename of the gitignored, machine-specific link file (`<project>/.orbitmap/link.json`).
|
|
17
|
+
*
|
|
18
|
+
* Duplicated from `workspace-resolve.ts`'s `LINK_FILE` rather than imported: that module
|
|
19
|
+
* sits above this one, and this is a leaf.
|
|
20
|
+
*/
|
|
21
|
+
const LINK_FILENAME = 'link.json';
|
|
22
|
+
/**
|
|
23
|
+
* The one directory holding documents, in **both** modes (IN-rwb5qu).
|
|
24
|
+
*
|
|
25
|
+
* Cloud mode: `<project>/.orbitmap/docs/` is the download cache. Local mode: the same
|
|
26
|
+
* `docs/` inside the workspace directory is the domain data itself — and for the usual
|
|
27
|
+
* in-project workspace (`<project>/.orbitmap`) that is literally the same path. Hence one
|
|
28
|
+
* name here rather than one per layer: there is exactly ONE copy of a document on disk,
|
|
29
|
+
* globally, with **no per-area subdirectories** (the data model does not record a
|
|
30
|
+
* document's home area, so the directory tree must not pretend it does).
|
|
31
|
+
*/
|
|
32
|
+
export const DOCS_DIR = 'docs';
|
|
33
|
+
/** Pre-v2 local-workspace name of {@link DOCS_DIR}; migrated away by `LocalStore.migrate()`. */
|
|
34
|
+
export const LEGACY_DOCUMENTS_DIR = 'documents';
|
|
35
|
+
/** Cache bookkeeping inside {@link DOCS_DIR}; written in cloud mode only. */
|
|
36
|
+
export const DOC_CACHE_MANIFEST = '.manifest.json';
|
|
15
37
|
let homeCache;
|
|
16
38
|
/**
|
|
17
39
|
* The resolved OrbitMap home, memoised.
|
|
@@ -141,22 +163,165 @@ export function findProjectConfigFiles(startDir = process.cwd()) {
|
|
|
141
163
|
const found = [];
|
|
142
164
|
const globals = new Set(globalConfigPaths().map(canonical));
|
|
143
165
|
const homeStop = canonical(homedir());
|
|
166
|
+
const seenFiles = new Set();
|
|
167
|
+
const seenStarts = new Set();
|
|
168
|
+
// Directories whose walk-up still has to run: `startDir` first, then any workspace root a
|
|
169
|
+
// `link.json` back-pointer found along the way names (see the doc comment above).
|
|
170
|
+
const queue = [resolve(startDir)];
|
|
171
|
+
while (queue.length > 0) {
|
|
172
|
+
const start = queue.shift();
|
|
173
|
+
if (seenStarts.has(canonical(start)))
|
|
174
|
+
continue;
|
|
175
|
+
seenStarts.add(canonical(start));
|
|
176
|
+
let dir = start;
|
|
177
|
+
for (;;) {
|
|
178
|
+
const orbitmapDir = join(dir, ORBITMAP_DIR);
|
|
179
|
+
const candidate = join(orbitmapDir, CONFIG_FILENAME);
|
|
180
|
+
if (existsSync(candidate) &&
|
|
181
|
+
!globals.has(canonical(candidate)) &&
|
|
182
|
+
!isOrbitMapHomeDir(orbitmapDir)) {
|
|
183
|
+
const key = canonical(candidate);
|
|
184
|
+
if (!seenFiles.has(key)) {
|
|
185
|
+
seenFiles.add(key);
|
|
186
|
+
found.push(candidate);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// An out-of-tree area cannot reach its workspace root by walking up; its machine-local
|
|
190
|
+
// `link.json` says where the root is, so the walk hops there and continues rather than
|
|
191
|
+
// ending with `agent`/`mode`/`workspace` unresolved (IS-ktfg5z).
|
|
192
|
+
const pointer = workspacePointer(orbitmapDir);
|
|
193
|
+
if (pointer && !seenStarts.has(canonical(pointer)))
|
|
194
|
+
queue.push(pointer);
|
|
195
|
+
if (canonical(dir) === homeStop)
|
|
196
|
+
break;
|
|
197
|
+
const parent = dirname(dir);
|
|
198
|
+
if (parent === dir)
|
|
199
|
+
break;
|
|
200
|
+
dir = parent;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return found;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The nearest directory at or above `startDir` that owns an `.orbitmap/` — the project (or
|
|
207
|
+
* workspace) root the doc cache and the `.gitignore` entries belong to. `undefined` when the
|
|
208
|
+
* walk finds none.
|
|
209
|
+
*
|
|
210
|
+
* The same walk and the same guards as {@link findProjectConfigFiles}, for the same reasons:
|
|
211
|
+
* an OrbitMap *home* is never a project (a relocated or mis-`$HOME`-ed `~/.orbitmap` sits on
|
|
212
|
+
* the walk path as an ordinary ancestor), and nothing above the user's home is that user's
|
|
213
|
+
* project. Only the *directory* is required to exist here — a project that has never had a
|
|
214
|
+
* `config.json` still owns its `.orbitmap/link.json` or `.orbitmap/docs/`.
|
|
215
|
+
*
|
|
216
|
+
* This is what replaces the doc cache's old `join('.orbitmap', 'docs')`, which resolved
|
|
217
|
+
* against `process.cwd()` with no walk-up at all and therefore scattered a fresh copy of
|
|
218
|
+
* every document into every subdirectory a command happened to run from.
|
|
219
|
+
*
|
|
220
|
+
* **One workspace = one directory = one `.orbitmap`.** The walk is not a search for any
|
|
221
|
+
* `.orbitmap` that happens to look like a workspace: it stops at the first ancestor that
|
|
222
|
+
* declares one ({@link declaresWorkspace}) and returns it, or follows an explicit
|
|
223
|
+
* back-pointer ({@link workspacePointer}) when the area lives outside the tree. Climbing
|
|
224
|
+
* past the first declaration used to hand the deepest directory to the *furthest* ancestor
|
|
225
|
+
* declaring a workspace — by construction not its owner — so two workspaces side by side
|
|
226
|
+
* under a third pooled their documents into the outermost one's flat `<slug>.md` namespace
|
|
227
|
+
* and overwrote each other (IS-8hv2wn).
|
|
228
|
+
*/
|
|
229
|
+
export function findProjectOrbitMapDir(startDir = process.cwd()) {
|
|
230
|
+
const globals = new Set(globalConfigPaths().map(canonical));
|
|
231
|
+
const homeStop = canonical(homedir());
|
|
232
|
+
let nearest;
|
|
144
233
|
let dir = resolve(startDir);
|
|
145
234
|
for (;;) {
|
|
146
235
|
const orbitmapDir = join(dir, ORBITMAP_DIR);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
!globals.has(canonical(
|
|
150
|
-
|
|
151
|
-
|
|
236
|
+
if (existsSync(orbitmapDir) &&
|
|
237
|
+
!isOrbitMapHomeDir(orbitmapDir) &&
|
|
238
|
+
!globals.has(canonical(join(orbitmapDir, CONFIG_FILENAME)))) {
|
|
239
|
+
nearest ??= dir;
|
|
240
|
+
// A back-pointer wins outright: this directory has been told, in writing, where its
|
|
241
|
+
// workspace lives, so there is nothing left to search for.
|
|
242
|
+
const pointer = workspacePointer(orbitmapDir);
|
|
243
|
+
if (pointer)
|
|
244
|
+
return pointer;
|
|
245
|
+
// The FIRST declaring config is the owner. Climbing further would hand `startDir`
|
|
246
|
+
// to whichever unrelated ancestor happens to declare a workspace too — by
|
|
247
|
+
// definition not the one that owns it (IS-8hv2wn).
|
|
248
|
+
if (declaresWorkspace(orbitmapDir))
|
|
249
|
+
return dir;
|
|
152
250
|
}
|
|
153
251
|
if (canonical(dir) === homeStop)
|
|
154
|
-
|
|
252
|
+
break;
|
|
155
253
|
const parent = dirname(dir);
|
|
156
254
|
if (parent === dir)
|
|
157
|
-
|
|
255
|
+
break;
|
|
158
256
|
dir = parent;
|
|
159
257
|
}
|
|
258
|
+
return nearest;
|
|
259
|
+
}
|
|
260
|
+
/** Parse a JSON file into a plain object, or `undefined` when it is missing/unreadable. */
|
|
261
|
+
function readJsonObject(file) {
|
|
262
|
+
try {
|
|
263
|
+
const raw = JSON.parse(readFileSync(file, 'utf-8'));
|
|
264
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
265
|
+
return undefined;
|
|
266
|
+
return raw;
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The workspace **directory** this `.orbitmap/` points at, when it points at one elsewhere.
|
|
274
|
+
*
|
|
275
|
+
* An area directory living outside the workspace tree cannot reach the workspace root by
|
|
276
|
+
* walking up — there is nothing above it but unrelated directories. `init` therefore writes
|
|
277
|
+
* `{"workspace": "<path>"}` into that area's own machine-local `link.json` (the same field,
|
|
278
|
+
* the same meaning, as in local mode: *where* the workspace is). Following it is what keeps
|
|
279
|
+
* such an area sharing the one global `docs/` instead of quietly starting a second copy.
|
|
280
|
+
*
|
|
281
|
+
* Accepts either the workspace root or its `.orbitmap/` directory — `resolveWorkspacePath`
|
|
282
|
+
* hands back the latter for a `workspace.yml`-marked local workspace, so both spellings of
|
|
283
|
+
* "the workspace is over there" exist in the wild, and both must mean the same directory
|
|
284
|
+
* here. Relative paths resolve against the link file, exactly as in `resolveWorkspacePath`.
|
|
285
|
+
*/
|
|
286
|
+
function workspacePointer(orbitmapDir) {
|
|
287
|
+
const link = readJsonObject(join(orbitmapDir, LINK_FILENAME));
|
|
288
|
+
const target = link?.['workspace'];
|
|
289
|
+
if (typeof target !== 'string' || !target)
|
|
290
|
+
return undefined;
|
|
291
|
+
const resolved = resolve(orbitmapDir, target);
|
|
292
|
+
return basename(resolved) === ORBITMAP_DIR ? dirname(resolved) : resolved;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Does this `.orbitmap/` belong to a *workspace* root rather than an area directory?
|
|
296
|
+
*
|
|
297
|
+
* The distinction is **location**, not identity. An out-of-tree area marker also carries a
|
|
298
|
+
* `workspace` field, because it needs the slug for the `X-Orbitmap-Workspace` header and has
|
|
299
|
+
* no root config to inherit it from — but naming your workspace is not the same as being its
|
|
300
|
+
* root, and reading it as one gave that directory its own second copy of every document.
|
|
301
|
+
* A file that pins an `area` is a marker, full stop; a root is what maps `areas` (or names a
|
|
302
|
+
* workspace without claiming to be an area). Read directly rather than through
|
|
303
|
+
* `project-config.ts`, which sits above this module.
|
|
304
|
+
*
|
|
305
|
+
* `link.json` is deliberately *not* enough on its own: its `workspace` field is a pointer at
|
|
306
|
+
* a workspace somewhere else (see {@link workspacePointer}) — the opposite of being one. Only
|
|
307
|
+
* its `areas` map, which lists the out-of-tree areas *of this workspace*, marks a root.
|
|
308
|
+
*/
|
|
309
|
+
function declaresWorkspace(orbitmapDir) {
|
|
310
|
+
const link = readJsonObject(join(orbitmapDir, LINK_FILENAME));
|
|
311
|
+
if (isPlainObject(link?.['areas']))
|
|
312
|
+
return true;
|
|
313
|
+
const raw = readJsonObject(join(orbitmapDir, CONFIG_FILENAME));
|
|
314
|
+
if (!raw)
|
|
315
|
+
return false;
|
|
316
|
+
if (isPlainObject(raw['areas']))
|
|
317
|
+
return true;
|
|
318
|
+
// `typeof null === 'object'` used to make `"areas": null` a workspace root; hence the
|
|
319
|
+
// explicit shape check above and the `area`-marker exclusion here.
|
|
320
|
+
return typeof raw['workspace'] === 'string' && typeof raw['area'] !== 'string';
|
|
321
|
+
}
|
|
322
|
+
/** A non-null, non-array object — what an `areas` map has to be to mean anything. */
|
|
323
|
+
function isPlainObject(value) {
|
|
324
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
160
325
|
}
|
|
161
326
|
/**
|
|
162
327
|
* Per-field walk-up: hand each project config file, nearest first, to `read` and return the
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|
package/dist/project-config.d.ts
CHANGED
|
@@ -41,11 +41,52 @@ export interface OrbitMapConfig {
|
|
|
41
41
|
workspace_path?: string;
|
|
42
42
|
/** Name of the agent profile to use — looked up in `~/.orbitmap/credentials.json`. */
|
|
43
43
|
agent?: string;
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Workspace this config belongs to — a workspace **slug or UUID**, never a path (the
|
|
46
|
+
* *directory* is `workspace_path` / the `link.json` pointer / the marker walk-up).
|
|
47
|
+
*
|
|
48
|
+
* Written into the workspace directory's own `.orbitmap/config.json` (schema v2), which is
|
|
49
|
+
* committable and holds no secrets. Purely declarative here: this field says *which*
|
|
50
|
+
* workspace the tree belongs to, it does not participate in the workspace-directory ladder.
|
|
51
|
+
*/
|
|
52
|
+
workspace?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Directory → area slug map (schema v2): "the code for area `x` lives in this directory".
|
|
55
|
+
*
|
|
56
|
+
* **Keys are relative directory paths, resolved against the parent of the `.orbitmap`
|
|
57
|
+
* directory that holds this config file** — i.e. against the workspace/project root, not
|
|
58
|
+
* against the current directory and not against `.orbitmap/` itself. `"."` is that root.
|
|
59
|
+
* Keys are normalised on read ({@link normalizeAreaPathKey}): backslashes become `/`,
|
|
60
|
+
* `./` prefixes and trailing slashes are dropped, and an empty result becomes `"."`.
|
|
61
|
+
*
|
|
62
|
+
* Values are area slugs.
|
|
63
|
+
*
|
|
64
|
+
* **Absolute keys and keys that escape upward (`..`) are rejected**, deliberately: a
|
|
65
|
+
* directory outside the tree is machine-specific and therefore belongs in the gitignored
|
|
66
|
+
* `link.json` (see `WorkspaceLink.areas` in `workspace-resolve.ts`), not in a file meant to
|
|
67
|
+
* be committed and to mean the same thing in every checkout.
|
|
68
|
+
*/
|
|
69
|
+
areas?: Record<string, string>;
|
|
70
|
+
/**
|
|
71
|
+
* Default area slug/id.
|
|
72
|
+
*
|
|
73
|
+
* Still read on every rung for back-compat — an existing project config that pins `area`
|
|
74
|
+
* keeps working unchanged. Schema v2 replaces it with the directory-scoped {@link areas}
|
|
75
|
+
* map plus a per-area marker config, so `orbitmap init` no longer *writes* this field.
|
|
76
|
+
*/
|
|
45
77
|
area?: string;
|
|
46
78
|
/** @deprecated Legacy secret, read once for migration then dropped. Never written. */
|
|
47
79
|
api_key?: string;
|
|
48
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Canonical form of one {@link OrbitMapConfig.areas} key, or `undefined` when the key is not
|
|
83
|
+
* a usable relative directory path.
|
|
84
|
+
*
|
|
85
|
+
* Normalises what a hand-written (or Windows-written) config may contain — `.\packages\api`,
|
|
86
|
+
* `packages/api/`, `./` — into one comparable form (`packages/api`, `.`). Rejects absolute
|
|
87
|
+
* paths and any `..` segment; see {@link OrbitMapConfig.areas} for why.
|
|
88
|
+
*/
|
|
89
|
+
export declare function normalizeAreaPathKey(key: string): string | undefined;
|
|
49
90
|
/**
|
|
50
91
|
* Parse and validate a raw config object (either file) — mistyped fields drop out instead
|
|
51
92
|
* of propagating a garbage value into the resolution ladders. Unknown keys are not
|
package/dist/project-config.js
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { resolveProjectField } from './paths.js';
|
|
3
|
+
/** Matches a Windows drive prefix (`C:`), which `node:path`'s POSIX `isAbsolute` does not —
|
|
4
|
+
* an `areas` key is a portable, committable path and must be rejected on every platform. */
|
|
5
|
+
const WINDOWS_DRIVE = /^[a-zA-Z]:/;
|
|
6
|
+
/**
|
|
7
|
+
* Canonical form of one {@link OrbitMapConfig.areas} key, or `undefined` when the key is not
|
|
8
|
+
* a usable relative directory path.
|
|
9
|
+
*
|
|
10
|
+
* Normalises what a hand-written (or Windows-written) config may contain — `.\packages\api`,
|
|
11
|
+
* `packages/api/`, `./` — into one comparable form (`packages/api`, `.`). Rejects absolute
|
|
12
|
+
* paths and any `..` segment; see {@link OrbitMapConfig.areas} for why.
|
|
13
|
+
*/
|
|
14
|
+
export function normalizeAreaPathKey(key) {
|
|
15
|
+
const slashed = key.replace(/\\/g, '/').trim();
|
|
16
|
+
if (!slashed)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (slashed.startsWith('/') || WINDOWS_DRIVE.test(slashed))
|
|
19
|
+
return undefined;
|
|
20
|
+
const segments = slashed.split('/').filter((part) => part.length > 0 && part !== '.');
|
|
21
|
+
if (segments.some((part) => part === '..'))
|
|
22
|
+
return undefined;
|
|
23
|
+
return segments.length === 0 ? '.' : segments.join('/');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validate the `areas` map: a plain object of relative-directory → non-empty area slug.
|
|
27
|
+
*
|
|
28
|
+
* **Rejected per entry, not per file.** One unusable key (absolute, `..`, non-string value)
|
|
29
|
+
* must not take the rest of a repository's mapping down with it — the same reasoning as the
|
|
30
|
+
* per-field validation in {@link parseConfig}. When nothing valid is left the field itself is
|
|
31
|
+
* dropped, so a ladder sees "not set" rather than an empty map. On a collision (two keys
|
|
32
|
+
* normalising to the same directory) the first entry wins.
|
|
33
|
+
*/
|
|
34
|
+
function parseAreasMap(raw) {
|
|
35
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
36
|
+
return undefined;
|
|
37
|
+
const areas = {};
|
|
38
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
39
|
+
if (typeof value !== 'string' || !value)
|
|
40
|
+
continue;
|
|
41
|
+
const dir = normalizeAreaPathKey(key);
|
|
42
|
+
if (dir === undefined)
|
|
43
|
+
continue;
|
|
44
|
+
if (!(dir in areas))
|
|
45
|
+
areas[dir] = value;
|
|
46
|
+
}
|
|
47
|
+
return Object.keys(areas).length > 0 ? areas : undefined;
|
|
48
|
+
}
|
|
3
49
|
/**
|
|
4
50
|
* Parse and validate a raw config object (either file) — mistyped fields drop out instead
|
|
5
51
|
* of propagating a garbage value into the resolution ladders. Unknown keys are not
|
|
@@ -19,6 +65,11 @@ export function parseConfig(raw) {
|
|
|
19
65
|
}
|
|
20
66
|
if (typeof r['agent'] === 'string' && r['agent'])
|
|
21
67
|
config.agent = r['agent'];
|
|
68
|
+
if (typeof r['workspace'] === 'string' && r['workspace'])
|
|
69
|
+
config.workspace = r['workspace'];
|
|
70
|
+
const areas = parseAreasMap(r['areas']);
|
|
71
|
+
if (areas)
|
|
72
|
+
config.areas = areas;
|
|
22
73
|
if (typeof r['area'] === 'string' && r['area'])
|
|
23
74
|
config.area = r['area'];
|
|
24
75
|
if (typeof r['api_key'] === 'string' && r['api_key'])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-config.js","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"project-config.js","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,12 +9,37 @@ export { ORBITMAP_DIR };
|
|
|
9
9
|
export declare const LINK_FILE = "link.json";
|
|
10
10
|
/** Marker file that identifies a directory as a local workspace. */
|
|
11
11
|
export declare const WORKSPACE_FILE = "workspace.yml";
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Contents of a `.orbitmap/link.json` — the **machine-specific, gitignored** half of the
|
|
14
|
+
* OrbitMap files, holding everything that is a path on *this* disk.
|
|
15
|
+
*
|
|
16
|
+
* Two shapes, both valid, distinguished only by which fields are present:
|
|
17
|
+
*
|
|
18
|
+
* - **project-level** (the original): `workspace` points at the workspace directory living
|
|
19
|
+
* elsewhere on this machine, `area` names the area this project is.
|
|
20
|
+
* - **workspace-level** (schema v2): the file sits in the workspace's own `.orbitmap/` and
|
|
21
|
+
* carries {@link WorkspaceLink.areas} — the areas whose code is *not* under the workspace
|
|
22
|
+
* tree, which therefore cannot be expressed in the committable
|
|
23
|
+
* `config.json` `areas` map (see `OrbitMapConfig.areas`).
|
|
24
|
+
*
|
|
25
|
+
* Nothing forbids a single file from having all three fields; they are independent.
|
|
26
|
+
*/
|
|
13
27
|
export interface WorkspaceLink {
|
|
14
28
|
/** Absolute path, or a path relative to the link file itself. */
|
|
15
29
|
workspace?: string;
|
|
16
30
|
/** Area slug within that workspace. */
|
|
17
31
|
area?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Area slug → the directory holding that area's code, for areas that live **outside the
|
|
34
|
+
* workspace tree** (the inverse direction of `OrbitMapConfig.areas`, which maps
|
|
35
|
+
* in-tree directory → slug).
|
|
36
|
+
*
|
|
37
|
+
* Values are absolute paths; a relative one is resolved against the link file's own
|
|
38
|
+
* directory, exactly like {@link WorkspaceLink.workspace}. This is why the mapping is
|
|
39
|
+
* split across two files: an absolute path is true only on one machine, so it must not
|
|
40
|
+
* land in the committable `config.json`.
|
|
41
|
+
*/
|
|
42
|
+
areas?: Record<string, string>;
|
|
18
43
|
}
|
|
19
44
|
/** A `link.json` that was found on disk, with the file it came from. */
|
|
20
45
|
export interface FoundWorkspaceLink {
|
|
@@ -24,6 +49,8 @@ export interface FoundWorkspaceLink {
|
|
|
24
49
|
}
|
|
25
50
|
export declare function setWorkspaceFlag(value?: string): void;
|
|
26
51
|
export declare function getWorkspaceFlag(): string | undefined;
|
|
52
|
+
export declare function setWorkspaceIdFlag(value?: string): void;
|
|
53
|
+
export declare function getWorkspaceIdFlag(): string | undefined;
|
|
27
54
|
/**
|
|
28
55
|
* Walk up from `startDir` to the filesystem root looking for `.orbitmap/link.json`
|
|
29
56
|
* (same discovery behaviour as git). Returns the nearest one, or `undefined`.
|
|
@@ -38,9 +65,22 @@ export declare function findLinkFile(startDir?: string): FoundWorkspaceLink | un
|
|
|
38
65
|
* subdirectory is both what makes the mode local and what the path ladder returns.
|
|
39
66
|
*/
|
|
40
67
|
export declare function findWorkspaceDir(startDir?: string): string | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Parse and validate a raw `link.json` object — **per field**, like `parseConfig`: a
|
|
70
|
+
* mistyped `area` must not stop the `workspace` pointer next to it from working, and a
|
|
71
|
+
* single bad entry in {@link WorkspaceLink.areas} must not discard the rest of the map.
|
|
72
|
+
* Unknown keys are not represented here; nothing in the CLI rewrites this file wholesale.
|
|
73
|
+
*/
|
|
74
|
+
export declare function parseWorkspaceLink(raw: unknown): WorkspaceLink | undefined;
|
|
41
75
|
export interface ResolveOptions {
|
|
42
76
|
/** Explicit `--workspace <path>`; defaults to the captured global flag. */
|
|
43
77
|
workspace?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Explicit `--workspace-id <slug-or-uuid>`; defaults to the captured global flag.
|
|
80
|
+
* Identifies a workspace in the backend — unrelated to {@link ResolveOptions.workspace},
|
|
81
|
+
* which is a directory on this disk.
|
|
82
|
+
*/
|
|
83
|
+
workspaceId?: string;
|
|
44
84
|
/** Explicit `--area <slug>`. */
|
|
45
85
|
area?: string;
|
|
46
86
|
/** Directory the resolution starts from (defaults to `process.cwd()`). */
|
|
@@ -105,13 +145,54 @@ export declare function resolveWorkspacePath(options?: ResolveOptions): string;
|
|
|
105
145
|
/**
|
|
106
146
|
* Which rung of the area ladder produced a slug.
|
|
107
147
|
*
|
|
108
|
-
* `flag`/`env`/`link`/`project-config` are **explicit**: the user, the project's
|
|
109
|
-
* `link.json`, or the project's own `.orbitmap/config.json` named that area
|
|
110
|
-
* does not exist must fail loudly.
|
|
111
|
-
*
|
|
112
|
-
* a local workspace is allowed to
|
|
148
|
+
* `flag`/`env`/`link`/`project-config`/`cwd-map` are **explicit**: the user, the project's
|
|
149
|
+
* own `link.json`, or the project's own `.orbitmap/config.json` named that area (directly or
|
|
150
|
+
* by mapping the current directory to it), so a slug that does not exist must fail loudly.
|
|
151
|
+
* `config` is the machine-wide default (the deprecated global `~/.orbitmap/config.json`
|
|
152
|
+
* `area` — ADR 0002) — the weakest rung, and the only one a local workspace is allowed to
|
|
153
|
+
* override or ignore (see `createLocalContext`).
|
|
154
|
+
*/
|
|
155
|
+
export type AreaSource = 'flag' | 'env' | 'link' | 'project-config' | 'cwd-map' | 'config';
|
|
156
|
+
/** One entry of the directory ↔ area mapping: "the code for `area` lives in `dir`". */
|
|
157
|
+
export interface AreaDirectoryMapping {
|
|
158
|
+
/** Area slug. */
|
|
159
|
+
area: string;
|
|
160
|
+
/** Absolute directory holding that area's code. */
|
|
161
|
+
dir: string;
|
|
162
|
+
/** Which file declared it: the committable `config.json` or the machine-local `link.json`. */
|
|
163
|
+
from: 'config' | 'link';
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* True when `child` is `dir` itself or lives underneath it. Segment-wise (via `relative`)
|
|
167
|
+
* rather than a string prefix, so `/repo/app` does not swallow `/repo/app-legacy`, and
|
|
168
|
+
* case-folded on win32 via {@link comparablePath}, where `C:\Work\Repo` and `c:\work\repo`
|
|
169
|
+
* are one directory.
|
|
170
|
+
*
|
|
171
|
+
* Exported because `init` asks the same question about the same paths on the write side. It
|
|
172
|
+
* had grown its own weaker copy — no case-fold, and a `/^[a-zA-Z]:/` test on the *relative*
|
|
173
|
+
* result standing in for "different drive" — under which a root recorded as `C:\Work\Repo`
|
|
174
|
+
* did not contain `c:\work\repo\pkg`, so `init` silently laid down a second, competing
|
|
175
|
+
* `areas` map for the same workspace (TS-5c4yg9). One question, one answer.
|
|
113
176
|
*/
|
|
114
|
-
export
|
|
177
|
+
export declare function isWithin(dir: string, child: string): boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Every directory → area mapping visible from `cwd`, **deepest first** (so the first match is
|
|
180
|
+
* the most specific one, exactly like the nearest-file rule everywhere else).
|
|
181
|
+
*
|
|
182
|
+
* Two sources, both schema v2, pointing in opposite directions on purpose:
|
|
183
|
+
*
|
|
184
|
+
* - `OrbitMapConfig.areas` (directory → slug) from the nearest `.orbitmap/config.json` that
|
|
185
|
+
* **sets `areas`** (per-field walk-up, ADR 0002 §2). Its keys are relative to the parent of
|
|
186
|
+
* the `.orbitmap` directory holding that file — the workspace/project root — so they mean
|
|
187
|
+
* the same thing in every checkout, which is why that file is the committable one.
|
|
188
|
+
* - `WorkspaceLink.areas` (slug → directory) from the nearest `.orbitmap/link.json`, for
|
|
189
|
+
* areas whose code sits *outside* the tree. Absolute, or relative to the link file itself.
|
|
190
|
+
*
|
|
191
|
+
* Purely lexical: no directory is required to exist. Used both by the `cwd-map` rung of
|
|
192
|
+
* {@link resolveArea} and by the `AREA_CONTEXT_REQUIRED` details, so the error always lists
|
|
193
|
+
* exactly the directories that would have resolved an area.
|
|
194
|
+
*/
|
|
195
|
+
export declare function resolveAreaDirectoryMap(options?: ResolveOptions): AreaDirectoryMapping[];
|
|
115
196
|
/** An area slug together with the rung it came from. */
|
|
116
197
|
export interface ResolvedAreaSlug {
|
|
117
198
|
area?: string;
|
|
@@ -120,11 +201,25 @@ export interface ResolvedAreaSlug {
|
|
|
120
201
|
/**
|
|
121
202
|
* Area resolution (ADR 0002, extending spec §3.3), explicit sources only:
|
|
122
203
|
* `--area` → `ORBITMAP_AREA` → `link.json.area` → nearest `.orbitmap/config.json`
|
|
123
|
-
* **that sets `area`** **[ADR 0002]** →
|
|
204
|
+
* **that sets `area`** **[ADR 0002]** → **cwd matched against the `areas` directory map**
|
|
205
|
+
* (schema v2) → global config `area` (deprecated).
|
|
124
206
|
*
|
|
125
207
|
* The project-config rung is per-field (`resolveProjectConfigField`): a nested config that
|
|
126
208
|
* sets only `agent` does not hide an `area` pinned by the repository root.
|
|
127
209
|
*
|
|
210
|
+
* Every file-reading rung is bounded by {@link workspaceBoundary}: the config that names the
|
|
211
|
+
* `workspace` is the furthest ancestor allowed to name the area, because an area named above
|
|
212
|
+
* it belongs to another workspace (IS-e6q7je). The machine-wide `config` rung is left alone —
|
|
213
|
+
* it is already deprecated and warns on use.
|
|
214
|
+
*
|
|
215
|
+
* The `cwd-map` rung is what lets one workspace cover a whole monorepo without a config file
|
|
216
|
+
* in every package: the workspace's `config.json` says which directory is which area, and the
|
|
217
|
+
* current directory picks one (deepest mapping wins — see {@link resolveAreaDirectoryMap}).
|
|
218
|
+
* It sits *below* the `area` field on purpose, so a per-directory marker — a
|
|
219
|
+
* `.orbitmap/config.json` holding nothing but `area`, which is all schema v2 needs to pin one
|
|
220
|
+
* directory — still overrides the map, and every pre-v2 config that pins `area` keeps
|
|
221
|
+
* behaving exactly as it did.
|
|
222
|
+
*
|
|
128
223
|
* The two remaining rungs — the workspace's own `workspace.yml` `default_area` (which
|
|
129
224
|
* sits *between* the project config and the global config) and "workspace has exactly one
|
|
130
225
|
* area → auto-select it" — require reading the workspace tree and therefore live in the
|
|
@@ -138,15 +233,90 @@ export declare function resolveAreaSlug(options?: ResolveOptions): string | unde
|
|
|
138
233
|
/**
|
|
139
234
|
* Assert that an area was resolved. Adapters call this after their own last-resort
|
|
140
235
|
* auto-selection; `availableAreas` (when known) is surfaced in the error details.
|
|
236
|
+
*
|
|
237
|
+
* Missing area context is **not** a global error — most of the CLI (missions, intents, the
|
|
238
|
+
* workspace-level document tree) works without one, so `resolveWorkspaceContext` returns
|
|
239
|
+
* `scope: 'workspace'` rather than throwing. Only the operations that genuinely need an area
|
|
240
|
+
* come here, which is why the details carry `mapped_directories`: when an area exists but the
|
|
241
|
+
* current directory is not inside it, the fix is to `cd` into one of the listed directories
|
|
242
|
+
* (or pass `--area`), and the error is the only place that can say which ones those are.
|
|
243
|
+
*
|
|
244
|
+
* `options` scopes that lookup; omitted, it is computed from `process.cwd()` — the right
|
|
245
|
+
* answer for a real invocation, and what lets callers deep in the adapters (which have no
|
|
246
|
+
* `ResolveOptions` of their own) still produce a useful error.
|
|
247
|
+
*/
|
|
248
|
+
export declare function requireArea(area: string | undefined, availableAreas?: string[], options?: ResolveOptions): string;
|
|
249
|
+
/**
|
|
250
|
+
* The same "which areas, and how to get into one" advice, for an `AREA_CONTEXT_REQUIRED`
|
|
251
|
+
* that arrived **from the server** (TS-jt6q36 follow-up).
|
|
252
|
+
*
|
|
253
|
+
* The API answers with its own text ("Pass area_id parameter or set the X-Orbitmap-Area
|
|
254
|
+
* header. Call GET /api/agent/areas…"), which describes the HTTP contract, not the CLI a
|
|
255
|
+
* person is actually holding — and it used to reach the terminal verbatim, so the improved
|
|
256
|
+
* local wording only ever showed up in local mode. This is what the cloud adapter wraps it
|
|
257
|
+
* with. Unlike {@link requireArea} it never knows the workspace's full area list (that would
|
|
258
|
+
* cost a request), so it falls back to the ways out alone, which are always actionable.
|
|
141
259
|
*/
|
|
142
|
-
export declare function
|
|
260
|
+
export declare function areaContextHint(options?: ResolveOptions): {
|
|
261
|
+
message: string;
|
|
262
|
+
mappedDirectories: Array<{
|
|
263
|
+
area: string;
|
|
264
|
+
dir: string;
|
|
265
|
+
}>;
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Which rung of the workspace-identity ladder produced the slug/uuid — mirrors
|
|
269
|
+
* {@link AreaSource}, minus the rungs that only ever name an area.
|
|
270
|
+
*/
|
|
271
|
+
export type WorkspaceSource = 'flag' | 'env' | 'project-config' | 'config';
|
|
272
|
+
/** A workspace slug/uuid together with the rung it came from. */
|
|
273
|
+
export interface ResolvedWorkspaceId {
|
|
274
|
+
workspace?: string;
|
|
275
|
+
source?: WorkspaceSource;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Workspace **identity** resolution (schema v2), first match wins:
|
|
279
|
+
* 1. `--workspace-id <slug-or-uuid>` flag
|
|
280
|
+
* 2. `ORBITMAP_WORKSPACE_ID` env var
|
|
281
|
+
* 3. nearest `.orbitmap/config.json` **that sets `workspace`** (per-field, ADR 0002 §2)
|
|
282
|
+
* 4. global config `workspace`
|
|
283
|
+
*
|
|
284
|
+
* Strictly separate from {@link resolveWorkspacePath}, which answers a different question —
|
|
285
|
+
* *which directory on this disk* — and keeps its own unchanged ladder (`--workspace` →
|
|
286
|
+
* `ORBITMAP_WORKSPACE` → `link.json` → `workspace.yml` → `workspace_path`). A slug is never a
|
|
287
|
+
* path and a path is never a slug: `--workspace <path>` still means local mode and this rung
|
|
288
|
+
* never touches it, `--workspace-id` never implies a mode.
|
|
289
|
+
*
|
|
290
|
+
* Undefined is a normal outcome (an un-migrated cloud project, or local mode, where the
|
|
291
|
+
* workspace is a directory), not an error.
|
|
292
|
+
*/
|
|
293
|
+
export declare function resolveWorkspaceId(options?: ResolveOptions): ResolvedWorkspaceId;
|
|
294
|
+
/**
|
|
295
|
+
* What the current invocation is scoped to.
|
|
296
|
+
*
|
|
297
|
+
* `'area'` when an area resolved — the historical behaviour, and what area-scoped commands
|
|
298
|
+
* need. `'workspace'` when none did: not an error, just a wider scope (missions, intents and
|
|
299
|
+
* workspace-level documents live there). Commands that cannot work without an area call
|
|
300
|
+
* {@link requireArea} and get `AREA_CONTEXT_REQUIRED`.
|
|
301
|
+
*/
|
|
302
|
+
export type ContextScope = 'area' | 'workspace';
|
|
143
303
|
export interface ResolvedWorkspaceContext {
|
|
144
304
|
mode: OrbitMapMode;
|
|
145
305
|
/** Absolute workspace directory; only resolved in local mode. */
|
|
146
306
|
workspacePath?: string;
|
|
307
|
+
/**
|
|
308
|
+
* Workspace **identity** — slug or uuid, never a path (see {@link resolveWorkspaceId}).
|
|
309
|
+
* Independent of {@link ResolvedWorkspaceContext.workspacePath}; either, both or neither
|
|
310
|
+
* may be set.
|
|
311
|
+
*/
|
|
312
|
+
workspace?: string;
|
|
313
|
+
/** Which rung produced {@link ResolvedWorkspaceContext.workspace}. */
|
|
314
|
+
workspaceSource?: WorkspaceSource;
|
|
147
315
|
area?: string;
|
|
148
316
|
/** Which rung of the area ladder produced {@link ResolvedWorkspaceContext.area}. */
|
|
149
317
|
areaSource?: AreaSource;
|
|
318
|
+
/** `'area'` when an area resolved, `'workspace'` otherwise. Never absent. */
|
|
319
|
+
scope: ContextScope;
|
|
150
320
|
link?: FoundWorkspaceLink;
|
|
151
321
|
config: OrbitMapConfig;
|
|
152
322
|
}
|