@wairon/cli 5.0.2-dev.4 → 5.0.2-dev.6
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/cli/index.js +216 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +152 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.0.2-dev.
|
|
68
|
+
WAIRON_VERSION = "5.0.2-dev.6";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
71
71
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -3304,8 +3304,17 @@ var MODEL = __MODEL_JSON__;
|
|
|
3304
3304
|
var configuredLineStyle = ['bezier', 'straight', 'taxi'].indexOf(diagramConfig.lineStyle) >= 0 ? diagramConfig.lineStyle : 'bezier';
|
|
3305
3305
|
var defaultViewKind = diagramConfig.defaultView === 'types' ? 'types' : (diagramConfig.defaultView === 'databases' && showDatabaseTab ? 'databases' : 'system');
|
|
3306
3306
|
|
|
3307
|
+
// Stage J: a deep link (opts.initialRoute) seeds the initial view directly so a
|
|
3308
|
+
// refresh / shared URL renders the right scope with NO root-first flash. Parsed
|
|
3309
|
+
// by the same resolver as openRoute (hoisted below). No onViewChange fires for
|
|
3310
|
+
// this initial seed.
|
|
3311
|
+
var initialView = { kind: defaultViewKind, id: null };
|
|
3312
|
+
if (typeof opts !== 'undefined' && opts && typeof opts.initialRoute === 'string' && opts.initialRoute.length) {
|
|
3313
|
+
initialView = resolveRoute(opts.initialRoute);
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3307
3316
|
var state = {
|
|
3308
|
-
view:
|
|
3317
|
+
view: initialView,
|
|
3309
3318
|
internals: typeof saved.internals === 'boolean' ? saved.internals : false,
|
|
3310
3319
|
externals: typeof saved.externals === 'boolean' ? saved.externals : true,
|
|
3311
3320
|
dataCoupling: typeof saved.dataCoupling === 'boolean' ? saved.dataCoupling : false,
|
|
@@ -3324,6 +3333,9 @@ var MODEL = __MODEL_JSON__;
|
|
|
3324
3333
|
// Set by buildTypeElements when the ERD is degraded for performance (huge
|
|
3325
3334
|
// scopes); consumed by renderTypesNotice to explain the level-of-detail.
|
|
3326
3335
|
var typesNotice = '';
|
|
3336
|
+
// Stage J: true while openRoute is applying a URL-driven view change, so the
|
|
3337
|
+
// onViewChange callback is suppressed and we do not loop URL -> engine -> URL.
|
|
3338
|
+
var applyingRoute = false;
|
|
3327
3339
|
|
|
3328
3340
|
function viewKey() {
|
|
3329
3341
|
// 'types2' + detail level: table sizes differ per detail, and the prefix
|
|
@@ -4753,6 +4765,78 @@ var MODEL = __MODEL_JSON__;
|
|
|
4753
4765
|
state.typesRenderAll = false; // a fresh scope re-evaluates the LOD budget
|
|
4754
4766
|
rebuild(true);
|
|
4755
4767
|
renderPanel();
|
|
4768
|
+
notifyViewChange();
|
|
4769
|
+
}
|
|
4770
|
+
|
|
4771
|
+
// ---- Stage J: URL <-> view routing ----------------------------------------
|
|
4772
|
+
// The canvas navigation lives in the URL path (refresh-safe + shareable). A
|
|
4773
|
+
// route is the string AFTER /canvas/<project>: '' = system root; a '/'-joined
|
|
4774
|
+
// NAMESPACE (e.g. 'a/b') that resolves against the model to a subsystem or a
|
|
4775
|
+
// component; or the 'types'/'databases' view modes with an optional scope.
|
|
4776
|
+
// A segment is the '::' namespace with '/' as separator, each part encoded.
|
|
4777
|
+
function encSeg(s) { return encodeURIComponent(String(s)); }
|
|
4778
|
+
function routeOf(view) {
|
|
4779
|
+
if (!view) return '';
|
|
4780
|
+
var k = view.kind;
|
|
4781
|
+
if (k === 'system') return '';
|
|
4782
|
+
if (k === 'types' || k === 'databases') {
|
|
4783
|
+
return view.id ? k + '/' + view.id.split('::').map(encSeg).join('/') : k;
|
|
4784
|
+
}
|
|
4785
|
+
if (k === 'subsystem') {
|
|
4786
|
+
return view.id ? view.id.split('::').map(encSeg).join('/') : '';
|
|
4787
|
+
}
|
|
4788
|
+
if (k === 'component') {
|
|
4789
|
+
// A component's full namespace path IS its id: a chained subproject carries
|
|
4790
|
+
// the subsystem prefix in the id (a::b::comp), a flat project uses the bare
|
|
4791
|
+
// id (comp). Serializing comp.id split on '::' round-trips exactly via the
|
|
4792
|
+
// compById lookup in resolveRoute. Owner-pattern nesting is deliberately NOT
|
|
4793
|
+
// encoded (ownership is a separate axis; comp.id does not embed the owner).
|
|
4794
|
+
var c = compById[view.id];
|
|
4795
|
+
var full = c ? c.id : view.id;
|
|
4796
|
+
return full.split('::').map(encSeg).join('/');
|
|
4797
|
+
}
|
|
4798
|
+
return '';
|
|
4799
|
+
}
|
|
4800
|
+
function resolveRoute(routeStr) {
|
|
4801
|
+
var parts = String(routeStr || '').split('/').filter(function (s) { return s.length > 0; }).map(decodeURIComponent);
|
|
4802
|
+
if (!parts.length) return { kind: 'system', id: null };
|
|
4803
|
+
// NOTE: a subsystem literally named 'types'/'databases' is SHADOWED by these
|
|
4804
|
+
// view-mode routes (acceptable \u2014 the modes own those first segments).
|
|
4805
|
+
if (parts[0] === 'types') {
|
|
4806
|
+
return { kind: 'types', id: parts.length > 1 ? parts.slice(1).join('::') : null };
|
|
4807
|
+
}
|
|
4808
|
+
if (parts[0] === 'databases') {
|
|
4809
|
+
if (!showDatabaseTab) return { kind: 'system', id: null };
|
|
4810
|
+
return { kind: 'databases', id: parts.length > 1 ? parts.slice(1).join('::') : null };
|
|
4811
|
+
}
|
|
4812
|
+
var joined = parts.join('::');
|
|
4813
|
+
if (subById[joined]) return { kind: 'subsystem', id: joined };
|
|
4814
|
+
if (compById[joined]) return { kind: 'component', id: joined };
|
|
4815
|
+
return { kind: 'system', id: null }; // unknown id -> fall back to the root
|
|
4816
|
+
}
|
|
4817
|
+
// Apply a route (URL -> engine) WITHOUT echoing back through onViewChange.
|
|
4818
|
+
function openRoute(routeStr) {
|
|
4819
|
+
var v = resolveRoute(routeStr);
|
|
4820
|
+
if (state.view.kind === v.kind && state.view.id === v.id) return;
|
|
4821
|
+
applyingRoute = true;
|
|
4822
|
+
try {
|
|
4823
|
+
state.view = { kind: v.kind, id: v.id };
|
|
4824
|
+
state.selected = null;
|
|
4825
|
+
state.selectedKind = null;
|
|
4826
|
+
state.typesRenderAll = false;
|
|
4827
|
+
rebuild(true);
|
|
4828
|
+
renderPanel();
|
|
4829
|
+
} finally {
|
|
4830
|
+
applyingRoute = false;
|
|
4831
|
+
}
|
|
4832
|
+
}
|
|
4833
|
+
// Notify the embedder (engine -> URL) of the current view; suppressed while a
|
|
4834
|
+
// route is being applied so the URL is not driven in a loop.
|
|
4835
|
+
function notifyViewChange() {
|
|
4836
|
+
if (applyingRoute) return;
|
|
4837
|
+
if (typeof opts !== 'undefined' && opts && typeof opts.onViewChange === 'function') {
|
|
4838
|
+
try { opts.onViewChange(routeOf(state.view)); } catch (e) { /* ignore */ }
|
|
4839
|
+
}
|
|
4756
4840
|
}
|
|
4757
4841
|
// Explain (and offer to override) a performance-degraded ERD.
|
|
4758
4842
|
function renderTypesNotice() {
|
|
@@ -4903,6 +4987,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
4903
4987
|
if (t.ghost) {
|
|
4904
4988
|
state.view = parentViewOf(t.kind, t.id);
|
|
4905
4989
|
rebuild(true);
|
|
4990
|
+
notifyViewChange();
|
|
4906
4991
|
select(t.kind, t.id, true);
|
|
4907
4992
|
return;
|
|
4908
4993
|
}
|
|
@@ -5923,10 +6008,12 @@ var MODEL = __MODEL_JSON__;
|
|
|
5923
6008
|
if (kind === 'type' && state.view.kind !== 'types' && state.view.kind !== 'databases') {
|
|
5924
6009
|
state.view = { kind: 'types', id: typesScopeFromView() };
|
|
5925
6010
|
rebuild(true);
|
|
6011
|
+
notifyViewChange();
|
|
5926
6012
|
} else if (kind === 'component' && (state.view.kind === 'types' || state.view.kind === 'databases')) {
|
|
5927
6013
|
var c = compById[id];
|
|
5928
6014
|
state.view = { kind: 'subsystem', id: c ? c.subsystem : null };
|
|
5929
6015
|
rebuild(true);
|
|
6016
|
+
notifyViewChange();
|
|
5930
6017
|
}
|
|
5931
6018
|
state.selectedKind = kind;
|
|
5932
6019
|
state.selected = id;
|
|
@@ -5985,6 +6072,14 @@ var MODEL = __MODEL_JSON__;
|
|
|
5985
6072
|
if (!exposes || !openApiAllowed()) return '';
|
|
5986
6073
|
return '<div class="openbtn"><button class="tbtn" data-openapi-tag="' + esc(tag || '') + '">\\u25A4 View OpenAPI \\u2197</button></div>';
|
|
5987
6074
|
}
|
|
6075
|
+
// "Open in Specs" \u2014 deep-links the focused spec into the hosted Specs value
|
|
6076
|
+
// editor via a host hook (opts.onOpenSpec). Hidden when no host provides it
|
|
6077
|
+
// (standalone file, shared page): those have no editor to open. kind is the
|
|
6078
|
+
// spec layer, id its qualified spec id.
|
|
6079
|
+
function openSpecButton(kind, id) {
|
|
6080
|
+
if (typeof opts === 'undefined' || !opts || typeof opts.onOpenSpec !== 'function') return '';
|
|
6081
|
+
return '<div class="openbtn"><button class="tbtn" data-openspec-kind="' + kind + '" data-openspec-id="' + esc(id) + '">\\u270E Open in Specs \\u2197</button></div>';
|
|
6082
|
+
}
|
|
5988
6083
|
|
|
5989
6084
|
function renderPanel() {
|
|
5990
6085
|
var head = '', body = '';
|
|
@@ -6007,7 +6102,8 @@ var MODEL = __MODEL_JSON__;
|
|
|
6007
6102
|
+ (scopeFocus ? staticChip('current view') : '')
|
|
6008
6103
|
+ chip(c.subsystem, 'subsystem', c.subsystem)
|
|
6009
6104
|
+ (scopeFocus ? '' : openViewButton('component', c.id, c.owns.length > 0))
|
|
6010
|
-
+ openApiButton(componentExposesApi(c), c.apiTag)
|
|
6105
|
+
+ openApiButton(componentExposesApi(c), c.apiTag)
|
|
6106
|
+
+ openSpecButton('component', c.id);
|
|
6011
6107
|
|
|
6012
6108
|
var linkedTypes = MODEL.types.filter(function (t) { return t.componentClass === c.id; });
|
|
6013
6109
|
if (linkedTypes.length) {
|
|
@@ -6091,7 +6187,8 @@ var MODEL = __MODEL_JSON__;
|
|
|
6091
6187
|
MODEL.types.forEach(function (t2) { if (t2.id === focusId) ty = t2; });
|
|
6092
6188
|
if (ty) {
|
|
6093
6189
|
head = '<h2>' + esc(ty.name) + '</h2>' + staticChip('\\u00AB' + ty.kind + '\\u00BB')
|
|
6094
|
-
+ (ty.subsystem ? chip(ty.subsystem, 'subsystem', ty.subsystem) : staticChip('system-level shared'))
|
|
6190
|
+
+ (ty.subsystem ? chip(ty.subsystem, 'subsystem', ty.subsystem) : staticChip('system-level shared'))
|
|
6191
|
+
+ openSpecButton('type', ty.id);
|
|
6095
6192
|
|
|
6096
6193
|
if (ty.componentClass) {
|
|
6097
6194
|
head += '<div style="margin-top:6px"><b style="font-size:11px">Class Component:</b> ' + chip(ty.componentClass, 'component', ty.componentClass) + '</div>';
|
|
@@ -6167,7 +6264,8 @@ var MODEL = __MODEL_JSON__;
|
|
|
6167
6264
|
+ (s.status ? staticChip(s.status) : '')
|
|
6168
6265
|
+ (scopeFocus ? staticChip('current view') : '')
|
|
6169
6266
|
+ (scopeFocus ? '' : openViewButton('subsystem', s.id, subKids > 0))
|
|
6170
|
-
+ openApiButton(subsystemExposesApi(s.id), '')
|
|
6267
|
+
+ openApiButton(subsystemExposesApi(s.id), '')
|
|
6268
|
+
+ openSpecButton('subsystem', s.id);
|
|
6171
6269
|
body += '<p class="desc">' + esc(s.description) + '</p>';
|
|
6172
6270
|
if (s.trustedLinks.length) {
|
|
6173
6271
|
body += section('Trusted links (fast lanes)', s.trustedLinks.length, s.trustedLinks.map(function (t2) {
|
|
@@ -6225,6 +6323,16 @@ var MODEL = __MODEL_JSON__;
|
|
|
6225
6323
|
});
|
|
6226
6324
|
})(oapis[oi]);
|
|
6227
6325
|
}
|
|
6326
|
+
var ospecs = panel.querySelectorAll('[data-openspec-kind]');
|
|
6327
|
+
for (var si = 0; si < ospecs.length; si++) {
|
|
6328
|
+
(function (b) {
|
|
6329
|
+
b.addEventListener('click', function () {
|
|
6330
|
+
if (typeof opts !== 'undefined' && opts && typeof opts.onOpenSpec === 'function') {
|
|
6331
|
+
opts.onOpenSpec(b.getAttribute('data-openspec-kind'), b.getAttribute('data-openspec-id') || '');
|
|
6332
|
+
}
|
|
6333
|
+
});
|
|
6334
|
+
})(ospecs[si]);
|
|
6335
|
+
}
|
|
6228
6336
|
var flows = panel.querySelectorAll('[data-flow-comp]');
|
|
6229
6337
|
for (var j = 0; j < flows.length; j++) {
|
|
6230
6338
|
(function (b) {
|
|
@@ -6237,6 +6345,23 @@ var MODEL = __MODEL_JSON__;
|
|
|
6237
6345
|
}
|
|
6238
6346
|
|
|
6239
6347
|
renderPanel();
|
|
6348
|
+
// Stage G: a deep link may focus a component and/or open a method's narrative
|
|
6349
|
+
// modal once the seeded view + DOM + cy graph exist. The host parses the URL hash
|
|
6350
|
+
// into opts.initialSelect / opts.initialFlow \u2014 both carry the component id, so this
|
|
6351
|
+
// works whether the seeded view is the component itself or its parent subsystem
|
|
6352
|
+
// (a leaf component has no meaningful "inside", so Specs deep-links open the parent
|
|
6353
|
+
// and focus the component here).
|
|
6354
|
+
(function () {
|
|
6355
|
+
if (typeof opts === 'undefined' || !opts) return;
|
|
6356
|
+
var f = opts.initialFlow, s = opts.initialSelect;
|
|
6357
|
+
var focusComp = (f && f.comp) || (s && s.comp);
|
|
6358
|
+
if (focusComp && compById[focusComp]) {
|
|
6359
|
+
try { select('component', focusComp, true); } catch (e) { /* ignore */ }
|
|
6360
|
+
}
|
|
6361
|
+
if (f && f.comp && f.method && compById[f.comp]) {
|
|
6362
|
+
try { openFlow(f.comp, f.method, f.mode === 'steps' ? 'steps' : 'flow'); } catch (e) { /* ignore */ }
|
|
6363
|
+
}
|
|
6364
|
+
})();
|
|
6240
6365
|
})();
|
|
6241
6366
|
</script>
|
|
6242
6367
|
</body>
|
|
@@ -12618,6 +12743,28 @@ function buildGraphModel(level) {
|
|
|
12618
12743
|
...t.subsystem ? { parentId: t.subsystem } : {}
|
|
12619
12744
|
});
|
|
12620
12745
|
}
|
|
12746
|
+
const componentByInterface = /* @__PURE__ */ new Map();
|
|
12747
|
+
for (const c of model.components) {
|
|
12748
|
+
for (const intf of c.interfaces) componentByInterface.set(intf.id, c.id);
|
|
12749
|
+
}
|
|
12750
|
+
for (const impl of loadImplementationSpecs()) {
|
|
12751
|
+
const componentId = componentByInterface.get(impl.contract);
|
|
12752
|
+
if (!componentId) continue;
|
|
12753
|
+
nodes.push({
|
|
12754
|
+
id: impl.id,
|
|
12755
|
+
label: impl.name,
|
|
12756
|
+
kind: "implementation",
|
|
12757
|
+
level: 4,
|
|
12758
|
+
parentId: componentId,
|
|
12759
|
+
...impl.status ? { status: impl.status } : {}
|
|
12760
|
+
});
|
|
12761
|
+
candidates.push({ from: componentId, to: impl.id, edgeKind: "owns" });
|
|
12762
|
+
}
|
|
12763
|
+
for (const c of model.components) {
|
|
12764
|
+
for (const memberId of c.owns) {
|
|
12765
|
+
candidates.push({ from: c.id, to: memberId, edgeKind: "owns" });
|
|
12766
|
+
}
|
|
12767
|
+
}
|
|
12621
12768
|
for (const e of model.edges) {
|
|
12622
12769
|
candidates.push({ from: e.from, to: e.to, edgeKind: "depends_on" });
|
|
12623
12770
|
}
|
|
@@ -27583,6 +27730,7 @@ function removeIdentityProviderRecord(dataDir, id) {
|
|
|
27583
27730
|
}
|
|
27584
27731
|
var PROJECT_CREATE_CAPABILITY = "project:create";
|
|
27585
27732
|
var PROJECT_WRITE_CAPABILITY = "project:write";
|
|
27733
|
+
var PROJECT_READ_CAPABILITY = "project:read";
|
|
27586
27734
|
var POLICY_MANAGE_CAPABILITY = "project:admin";
|
|
27587
27735
|
function carriesInstancePermission(cfg, principal, capability) {
|
|
27588
27736
|
return authorize(cfg.dataDir, principal, capability, "instance", "").value === "yes";
|
|
@@ -27647,6 +27795,19 @@ function recordProjectProfileSelection(root, selection) {
|
|
|
27647
27795
|
writeYamlFile(AI_PATHS.projectConfig(), raw);
|
|
27648
27796
|
});
|
|
27649
27797
|
}
|
|
27798
|
+
function readProjectType(root) {
|
|
27799
|
+
return runWithProjectRoot(root, () => {
|
|
27800
|
+
const raw = readYamlFile(AI_PATHS.projectConfig());
|
|
27801
|
+
return raw?.projectType ?? "backend";
|
|
27802
|
+
});
|
|
27803
|
+
}
|
|
27804
|
+
function writeProjectType(root, projectType) {
|
|
27805
|
+
runWithProjectRoot(root, () => {
|
|
27806
|
+
const raw = readYamlFile(AI_PATHS.projectConfig()) ?? {};
|
|
27807
|
+
raw["projectType"] = projectType;
|
|
27808
|
+
writeYamlFile(AI_PATHS.projectConfig(), raw);
|
|
27809
|
+
});
|
|
27810
|
+
}
|
|
27650
27811
|
function requestPackNames(request) {
|
|
27651
27812
|
const sel = request.profileSelection;
|
|
27652
27813
|
if (!sel) return [];
|
|
@@ -27841,6 +28002,32 @@ function reconcileProjectPolicy(cfg, credential, projectId) {
|
|
|
27841
28002
|
);
|
|
27842
28003
|
return result;
|
|
27843
28004
|
}
|
|
28005
|
+
function getProjectConfig(cfg, credential, projectId) {
|
|
28006
|
+
const principal = requirePrincipal4(cfg, credential);
|
|
28007
|
+
if (authorize(cfg.dataDir, principal, PROJECT_READ_CAPABILITY, "project", projectId).value !== "yes") {
|
|
28008
|
+
throw new ForbiddenError(
|
|
28009
|
+
"reading a project's configuration requires project:read over the project"
|
|
28010
|
+
);
|
|
28011
|
+
}
|
|
28012
|
+
const root = resolveProjectRoot(cfg.dataDir, principal, projectId);
|
|
28013
|
+
if (!root) throw new Error(`Unknown project "${projectId}".`);
|
|
28014
|
+
const projectType = readProjectType(root);
|
|
28015
|
+
const locked = runWithProjectRoot(root, () => hostCore.readLockRecord() !== null);
|
|
28016
|
+
return { projectType, locked };
|
|
28017
|
+
}
|
|
28018
|
+
function setProjectType(cfg, credential, projectId, projectType) {
|
|
28019
|
+
const principal = requirePrincipal4(cfg, credential);
|
|
28020
|
+
if (authorize(cfg.dataDir, principal, PROJECT_WRITE_CAPABILITY, "project", projectId).value !== "yes") {
|
|
28021
|
+
throw new ForbiddenError(
|
|
28022
|
+
"changing a project's configuration requires project:write over the project"
|
|
28023
|
+
);
|
|
28024
|
+
}
|
|
28025
|
+
const root = resolveProjectRoot(cfg.dataDir, principal, projectId);
|
|
28026
|
+
if (!root) throw new Error(`Unknown project "${projectId}".`);
|
|
28027
|
+
writeProjectType(root, projectType);
|
|
28028
|
+
const locked = runWithProjectRoot(root, () => hostCore.readLockRecord() !== null);
|
|
28029
|
+
return { projectType, locked };
|
|
28030
|
+
}
|
|
27844
28031
|
function getPackPolicy(cfg, credential) {
|
|
27845
28032
|
requirePrincipal4(cfg, credential);
|
|
27846
28033
|
return getPackPolicyRecord(cfg.dataDir) ?? PERMISSIVE_DEFAULT_POLICY;
|
|
@@ -27904,7 +28091,7 @@ function handlePolicyRequest(cfg, credential, req, res, body, url) {
|
|
|
27904
28091
|
|
|
27905
28092
|
// src/server/identity.ts
|
|
27906
28093
|
var PROJECT_ADMIN_CAPABILITY2 = "project:admin";
|
|
27907
|
-
var
|
|
28094
|
+
var PROJECT_READ_CAPABILITY2 = "project:read";
|
|
27908
28095
|
var PROJECT_WRITE_CAPABILITY2 = "project:write";
|
|
27909
28096
|
function requireInstanceProjectAdmin(cfg, principal, what) {
|
|
27910
28097
|
if (authorize(cfg.dataDir, principal, PROJECT_ADMIN_CAPABILITY2, "instance", "").value !== "yes") {
|
|
@@ -28050,7 +28237,7 @@ function revokeToken(cfg, credential, tokenId) {
|
|
|
28050
28237
|
}
|
|
28051
28238
|
function mintSelfToken(cfg, credential, projectId, write) {
|
|
28052
28239
|
const principal = requirePrincipal5(cfg, credential);
|
|
28053
|
-
if (authorize(cfg.dataDir, principal,
|
|
28240
|
+
if (authorize(cfg.dataDir, principal, PROJECT_READ_CAPABILITY2, "project", projectId).value !== "yes") {
|
|
28054
28241
|
throw new ForbiddenError("caller lacks project:read on the requested project");
|
|
28055
28242
|
}
|
|
28056
28243
|
if (write && authorize(cfg.dataDir, principal, PROJECT_WRITE_CAPABILITY2, "project", projectId).value !== "yes") {
|
|
@@ -28696,7 +28883,7 @@ function audienceDistance(resolution, targetProjectId) {
|
|
|
28696
28883
|
// src/server/landscape.ts
|
|
28697
28884
|
var yamlLib = __toESM(require("js-yaml"));
|
|
28698
28885
|
var PROJECT_ADMIN_CAPABILITY3 = "project:admin";
|
|
28699
|
-
var
|
|
28886
|
+
var PROJECT_READ_CAPABILITY3 = "project:read";
|
|
28700
28887
|
var PROJECT_WRITE_CAPABILITY3 = "project:write";
|
|
28701
28888
|
function requirePrincipal6(cfg, credential) {
|
|
28702
28889
|
const principal = authenticateCredential(cfg.dataDir, credential);
|
|
@@ -28710,7 +28897,7 @@ function readView(cfg, principal) {
|
|
|
28710
28897
|
if (isInstanceAdmin(principal)) {
|
|
28711
28898
|
return { all: true, projectIds: /* @__PURE__ */ new Set(), unitIds: /* @__PURE__ */ new Set(), contextUnitIds: /* @__PURE__ */ new Set() };
|
|
28712
28899
|
}
|
|
28713
|
-
const read = visibleScopes(cfg.dataDir, principal,
|
|
28900
|
+
const read = visibleScopes(cfg.dataDir, principal, PROJECT_READ_CAPABILITY3);
|
|
28714
28901
|
const admin = visibleScopes(cfg.dataDir, principal, PROJECT_ADMIN_CAPABILITY3);
|
|
28715
28902
|
const unitIds = /* @__PURE__ */ new Set([...actionableUnitIds(read), ...actionableUnitIds(admin)]);
|
|
28716
28903
|
const contextUnitIds = new Set(
|
|
@@ -28725,13 +28912,13 @@ function readView(cfg, principal) {
|
|
|
28725
28912
|
}
|
|
28726
28913
|
function requireLandscapeReach(cfg, principal, view, what) {
|
|
28727
28914
|
if (view.all || view.projectIds.size > 0 || view.unitIds.size > 0) return;
|
|
28728
|
-
if (permitsCap(cfg, principal,
|
|
28915
|
+
if (permitsCap(cfg, principal, PROJECT_READ_CAPABILITY3, "instance", "") || permitsCap(cfg, principal, PROJECT_ADMIN_CAPABILITY3, "instance", "")) {
|
|
28729
28916
|
return;
|
|
28730
28917
|
}
|
|
28731
28918
|
throw new ForbiddenError(`${what} requires project:read reach`);
|
|
28732
28919
|
}
|
|
28733
28920
|
function requireObserverScope(cfg, principal, observerProjectId) {
|
|
28734
|
-
const covered = permitsCap(cfg, principal,
|
|
28921
|
+
const covered = permitsCap(cfg, principal, PROJECT_READ_CAPABILITY3, "project", observerProjectId) || permitsCap(cfg, principal, PROJECT_WRITE_CAPABILITY3, "project", observerProjectId);
|
|
28735
28922
|
if (!covered) {
|
|
28736
28923
|
throw new ForbiddenError("caller lacks scope over the current project");
|
|
28737
28924
|
}
|
|
@@ -30789,6 +30976,12 @@ function evaluateProjectPolicy2(cfg, credential, projectId) {
|
|
|
30789
30976
|
function reconcileProjectPolicy2(cfg, credential, projectId) {
|
|
30790
30977
|
return reconcileProjectPolicy(cfg, credential, projectId);
|
|
30791
30978
|
}
|
|
30979
|
+
function getProjectConfig2(cfg, credential, projectId) {
|
|
30980
|
+
return getProjectConfig(cfg, credential, projectId);
|
|
30981
|
+
}
|
|
30982
|
+
function setProjectType2(cfg, credential, projectId, projectType) {
|
|
30983
|
+
return setProjectType(cfg, credential, projectId, projectType);
|
|
30984
|
+
}
|
|
30792
30985
|
function listProducers2(cfg, credential, project2) {
|
|
30793
30986
|
return listProducers(cfg, credential, project2);
|
|
30794
30987
|
}
|
|
@@ -34091,6 +34284,12 @@ function opsPolicyEvaluate(cfg, sessionId, url, res) {
|
|
|
34091
34284
|
function opsPolicyReconcile(cfg, sessionId, body, res) {
|
|
34092
34285
|
sendJson(res, 200, reconcileProjectPolicy2(cfg, sessionId, String(body?.projectId ?? "")));
|
|
34093
34286
|
}
|
|
34287
|
+
function opsGetProjectConfig(cfg, sessionId, url, res) {
|
|
34288
|
+
sendJson(res, 200, getProjectConfig2(cfg, sessionId, q(url, "projectId") ?? ""));
|
|
34289
|
+
}
|
|
34290
|
+
function opsSetProjectConfig(cfg, sessionId, body, res) {
|
|
34291
|
+
sendJson(res, 200, setProjectType2(cfg, sessionId, String(body?.projectId ?? ""), String(body?.projectType ?? "")));
|
|
34292
|
+
}
|
|
34094
34293
|
function opsListProducers(cfg, sessionId, url, res) {
|
|
34095
34294
|
sendJson(res, 200, { producers: listProducers2(cfg, sessionId, q(url, "projectId") ?? "") });
|
|
34096
34295
|
}
|
|
@@ -34323,6 +34522,12 @@ async function handleWebRequest(cfg, req, res, body, url, ctx) {
|
|
|
34323
34522
|
if (req.method === "POST" && parts.length === 4 && parts[2] === "packs" && parts[3] === "adopt") {
|
|
34324
34523
|
return opsAdoptProjectPack(cfg, sessionId, body, res);
|
|
34325
34524
|
}
|
|
34525
|
+
if (req.method === "GET" && parts.length === 3 && parts[2] === "config") {
|
|
34526
|
+
return opsGetProjectConfig(cfg, sessionId, url, res);
|
|
34527
|
+
}
|
|
34528
|
+
if (req.method === "POST" && parts.length === 3 && parts[2] === "config") {
|
|
34529
|
+
return opsSetProjectConfig(cfg, sessionId, body, res);
|
|
34530
|
+
}
|
|
34326
34531
|
if (req.method === "GET" && parts.length === 3 && parts[2] === "policy") {
|
|
34327
34532
|
return opsPolicyEvaluate(cfg, sessionId, url, res);
|
|
34328
34533
|
}
|