@aifabrix/builder 2.51.0 → 2.52.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/jest.isolated-projects.js +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +43 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/work.api.js +283 -0
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +216 -0
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +15 -9
- package/lib/app/run.js +18 -2
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +13 -9
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +14 -2
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +25 -1
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +65 -56
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +62 -12
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/channel-add-setup.js +8 -2
- package/lib/commands/channel.js +16 -5
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +19 -17
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/evidence-kind.js +65 -15
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/lifecycle-external.js +21 -8
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-sync.js +34 -17
- package/lib/commands/role-assistant.js +371 -0
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +25 -7
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +31 -10
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/datasource-validate-summary.js +2 -2
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +39 -1
- package/lib/external-system/deploy.js +25 -11
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +43 -7
- package/lib/generator/builders.js +12 -15
- package/lib/generator/wizard.js +51 -4
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-spec.js +47 -28
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-template-workspace.js +28 -1
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync.js +485 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +221 -0
- package/lib/role-assistant/test-runner-workhub.js +399 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +24 -28
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +38 -4
- package/lib/schema/external-system.schema.json +4 -4
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/utils/api.js +52 -27
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/docker-run-ephemeral.js +115 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +34 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +80 -38
- package/lib/utils/pipeline-upload-auth-retry.js +15 -3
- package/lib/utils/role-assistant-paths.js +160 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +38 -29
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate-external-file.js +8 -1
- package/lib/validation/validate.js +54 -19
- package/package.json +2 -1
- package/scripts/export-builder-api-evidence.js +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +24 -17
- package/templates/applications/dataplane/env.template +2 -2
- package/templates/applications/dataplane/rbac.yaml +24 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +3 -3
- package/templates/applications/miso-controller/env.template +17 -2
- package/templates/channels/chatgpt/install.template.md.hbs +82 -26
- package/templates/channels/chatgpt/integration/README.md +11 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- package/templates/channels/slack/install.template.md.hbs +6 -1
- package/templates/python/docker-compose.hbs +10 -0
- package/templates/typescript/docker-compose.hbs +10 -0
- /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"date": "2026-06-15T00:00:00Z",
|
|
73
73
|
"changes": [
|
|
74
74
|
"Added systemKind digitalWorker to x-systemKindCatalog (enterpriseContext group).",
|
|
75
|
-
"Extended subscriptionFilter with roleKeys, includeSystemKinds, and excludeSystemKinds for
|
|
75
|
+
"Extended subscriptionFilter with roleKeys, includeSystemKinds, and excludeSystemKinds for Role Assistant generation inputs."
|
|
76
76
|
],
|
|
77
77
|
"breaking": false
|
|
78
78
|
},
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
"$defs": {
|
|
185
185
|
"subscriptionFilter": {
|
|
186
186
|
"type": "object",
|
|
187
|
-
"description": "Shared filter engine shape for subscription triggers and
|
|
187
|
+
"description": "Shared filter engine shape for subscription triggers and Role Assistant generation inputs.",
|
|
188
188
|
"properties": {
|
|
189
189
|
"resourceTypes": {
|
|
190
190
|
"type": "array",
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
"minLength": 1
|
|
218
218
|
},
|
|
219
219
|
"minItems": 1,
|
|
220
|
-
"description": "Role keys that scope
|
|
220
|
+
"description": "Role keys that scope Role Assistant or subscription generation."
|
|
221
221
|
},
|
|
222
222
|
"includeSystemKinds": {
|
|
223
223
|
"type": "array",
|
|
@@ -978,7 +978,7 @@
|
|
|
978
978
|
},
|
|
979
979
|
{
|
|
980
980
|
"key": "digitalWorker",
|
|
981
|
-
"displayName": "
|
|
981
|
+
"displayName": "Role Assistant",
|
|
982
982
|
"group": "enterpriseContext"
|
|
983
983
|
},
|
|
984
984
|
{
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "aifabrix://schema/role-assistant-settings",
|
|
4
|
+
"title": "Role Assistant Settings",
|
|
5
|
+
"description": "Schema for role-assistant.yaml used by digitalWorker packages.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"apiVersion",
|
|
9
|
+
"version",
|
|
10
|
+
"kind",
|
|
11
|
+
"roleKey"
|
|
12
|
+
],
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"properties": {
|
|
15
|
+
"apiVersion": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"const": "aifabrix.ai/v1"
|
|
18
|
+
},
|
|
19
|
+
"version": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
22
|
+
},
|
|
23
|
+
"kind": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"const": "RoleAssistantSettings"
|
|
26
|
+
},
|
|
27
|
+
"roleKey": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"pattern": "^[a-z0-9-_]+$"
|
|
30
|
+
},
|
|
31
|
+
"dev": {
|
|
32
|
+
"$ref": "#/$defs/environmentSettings"
|
|
33
|
+
},
|
|
34
|
+
"tst": {
|
|
35
|
+
"$ref": "#/$defs/environmentSettings"
|
|
36
|
+
},
|
|
37
|
+
"pro": {
|
|
38
|
+
"$ref": "#/$defs/environmentSettings"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"$defs": {
|
|
42
|
+
"environmentSettings": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"additionalProperties": false,
|
|
45
|
+
"properties": {
|
|
46
|
+
"runtime": {
|
|
47
|
+
"$ref": "#/$defs/runtime"
|
|
48
|
+
},
|
|
49
|
+
"debug": {
|
|
50
|
+
"$ref": "#/$defs/debug"
|
|
51
|
+
},
|
|
52
|
+
"knowledge": {
|
|
53
|
+
"$ref": "#/$defs/knowledge"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"runtime": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"provider": {
|
|
62
|
+
"type": "string"
|
|
63
|
+
},
|
|
64
|
+
"model": {
|
|
65
|
+
"type": "string"
|
|
66
|
+
},
|
|
67
|
+
"temperature": {
|
|
68
|
+
"type": "number",
|
|
69
|
+
"minimum": 0,
|
|
70
|
+
"maximum": 2
|
|
71
|
+
},
|
|
72
|
+
"maxLoops": {
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"minimum": 1,
|
|
75
|
+
"maximum": 1000
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"debug": {
|
|
80
|
+
"type": "object",
|
|
81
|
+
"additionalProperties": false,
|
|
82
|
+
"properties": {
|
|
83
|
+
"enabled": {
|
|
84
|
+
"type": "boolean"
|
|
85
|
+
},
|
|
86
|
+
"includeRuntimeLog": {
|
|
87
|
+
"type": "boolean"
|
|
88
|
+
},
|
|
89
|
+
"includePromptTrace": {
|
|
90
|
+
"type": "boolean"
|
|
91
|
+
},
|
|
92
|
+
"includeCapabilityTrace": {
|
|
93
|
+
"type": "boolean"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"knowledge": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"additionalProperties": false,
|
|
100
|
+
"properties": {
|
|
101
|
+
"documentStorageKey": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"pattern": "^[a-z0-9-_]+$"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
package/lib/utils/api.js
CHANGED
|
@@ -381,12 +381,11 @@ function extractControllerUrl(url) {
|
|
|
381
381
|
return match ? match[1] : url;
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
|
-
|
|
385
384
|
/**
|
|
386
|
-
* Set auth header on headers object: Bearer for user token, x-client-token for
|
|
385
|
+
* Set auth header on headers object: Bearer for user token, x-client-token for app token.
|
|
387
386
|
* @param {Object} headers - Headers object to mutate
|
|
388
387
|
* @param {string} token - Token value
|
|
389
|
-
* @param {string} authType - 'bearer'
|
|
388
|
+
* @param {string} authType - 'bearer' | 'client-token'
|
|
390
389
|
*/
|
|
391
390
|
function setAuthHeader(headers, token, authType) {
|
|
392
391
|
if (!token) return;
|
|
@@ -396,7 +395,6 @@ function setAuthHeader(headers, token, authType) {
|
|
|
396
395
|
headers['Authorization'] = `Bearer ${token}`;
|
|
397
396
|
}
|
|
398
397
|
}
|
|
399
|
-
|
|
400
398
|
/**
|
|
401
399
|
* Make an authenticated API call with user token (Bearer) or application token (x-client-token).
|
|
402
400
|
* Automatically refreshes device token on 401 when user Bearer was used.
|
|
@@ -408,7 +406,48 @@ function setAuthHeader(headers, token, authType) {
|
|
|
408
406
|
* @param {string} [tokenOrAuthConfig.controller] - Controller URL for token refresh (if object)
|
|
409
407
|
* @returns {Promise<Object>} Response object
|
|
410
408
|
*/
|
|
411
|
-
|
|
409
|
+
/**
|
|
410
|
+
* Retry once after device-token refresh on 401 (Bearer only; never for client-token/preserveBearer).
|
|
411
|
+
* @param {Object} response
|
|
412
|
+
* @param {string} url
|
|
413
|
+
* @param {Object} options
|
|
414
|
+
* @param {Object} headers
|
|
415
|
+
* @param {string|null} authControllerUrl
|
|
416
|
+
* @returns {Promise<Object>}
|
|
417
|
+
*/
|
|
418
|
+
async function retryApiCallAfterDeviceTokenRefresh(
|
|
419
|
+
response,
|
|
420
|
+
url,
|
|
421
|
+
options,
|
|
422
|
+
headers,
|
|
423
|
+
authControllerUrl
|
|
424
|
+
) {
|
|
425
|
+
try {
|
|
426
|
+
const { forceRefreshDeviceToken } = require('./token-manager');
|
|
427
|
+
const refreshedToken = await forceRefreshDeviceToken(
|
|
428
|
+
authControllerUrl || extractControllerUrl(url)
|
|
429
|
+
);
|
|
430
|
+
if (refreshedToken?.token) {
|
|
431
|
+
headers.Authorization = `Bearer ${refreshedToken.token}`;
|
|
432
|
+
return await makeApiCall(url, { ...options, headers });
|
|
433
|
+
}
|
|
434
|
+
const authError =
|
|
435
|
+
'Authentication failed: Token expired and refresh failed. Please login again using: aifabrix login';
|
|
436
|
+
return { ...response, error: authError, formattedError: authError };
|
|
437
|
+
} catch (refreshError) {
|
|
438
|
+
const authError = `Authentication failed: ${refreshError.message || String(refreshError)}. Please login again using: aifabrix login`;
|
|
439
|
+
return { ...response, error: authError, formattedError: authError };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function shouldRefreshBearerOn401(response, authType, tokenOrAuthConfig) {
|
|
443
|
+
return (
|
|
444
|
+
!response.success &&
|
|
445
|
+
response.status === 401 &&
|
|
446
|
+
authType !== 'client-token' &&
|
|
447
|
+
!(typeof tokenOrAuthConfig === 'object' && tokenOrAuthConfig?.preserveBearer)
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
412
451
|
async function authenticatedApiCall(url, options = {}, tokenOrAuthConfig) {
|
|
413
452
|
const isStringToken = typeof tokenOrAuthConfig === 'string';
|
|
414
453
|
const token = isStringToken ? tokenOrAuthConfig : tokenOrAuthConfig?.token;
|
|
@@ -426,32 +465,18 @@ async function authenticatedApiCall(url, options = {}, tokenOrAuthConfig) {
|
|
|
426
465
|
headers
|
|
427
466
|
});
|
|
428
467
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
try {
|
|
438
|
-
const { forceRefreshDeviceToken } = require('./token-manager');
|
|
439
|
-
const refreshedToken = await forceRefreshDeviceToken(authControllerUrl || extractControllerUrl(url));
|
|
440
|
-
if (refreshedToken?.token) {
|
|
441
|
-
headers['Authorization'] = `Bearer ${refreshedToken.token}`;
|
|
442
|
-
return await makeApiCall(url, { ...options, headers });
|
|
443
|
-
}
|
|
444
|
-
const authError = 'Authentication failed: Token expired and refresh failed. Please login again using: aifabrix login';
|
|
445
|
-
return { ...response, error: authError, formattedError: authError };
|
|
446
|
-
} catch (refreshError) {
|
|
447
|
-
const authError = `Authentication failed: ${refreshError.message || String(refreshError)}. Please login again using: aifabrix login`;
|
|
448
|
-
return { ...response, error: authError, formattedError: authError };
|
|
449
|
-
}
|
|
468
|
+
if (shouldRefreshBearerOn401(response, authType, tokenOrAuthConfig)) {
|
|
469
|
+
return retryApiCallAfterDeviceTokenRefresh(
|
|
470
|
+
response,
|
|
471
|
+
url,
|
|
472
|
+
options,
|
|
473
|
+
headers,
|
|
474
|
+
authControllerUrl
|
|
475
|
+
);
|
|
450
476
|
}
|
|
451
477
|
|
|
452
478
|
return response;
|
|
453
479
|
}
|
|
454
|
-
|
|
455
480
|
const {
|
|
456
481
|
initiateDeviceCodeFlow,
|
|
457
482
|
pollDeviceCodeToken,
|
|
@@ -15,6 +15,11 @@ const {
|
|
|
15
15
|
SYSTEM_KIND_ENTERPRISE_KNOWLEDGE,
|
|
16
16
|
getEnterpriseKnowledgePath
|
|
17
17
|
} = require('./enterprise-knowledge-paths');
|
|
18
|
+
const {
|
|
19
|
+
WORKSPACE_ROLE_ASSISTANT,
|
|
20
|
+
SYSTEM_KIND_DIGITAL_WORKER,
|
|
21
|
+
getRoleAssistantPath
|
|
22
|
+
} = require('./role-assistant-paths');
|
|
18
23
|
|
|
19
24
|
const WORKSPACE_INTEGRATION = 'integration';
|
|
20
25
|
|
|
@@ -62,10 +67,14 @@ function normalizeWorkspaceOverride(raw) {
|
|
|
62
67
|
return null;
|
|
63
68
|
}
|
|
64
69
|
const v = String(raw).trim().toLowerCase();
|
|
65
|
-
if (
|
|
70
|
+
if (
|
|
71
|
+
v === WORKSPACE_INTEGRATION ||
|
|
72
|
+
v === WORKSPACE_ENTERPRISE_KNOWLEDGE ||
|
|
73
|
+
v === WORKSPACE_ROLE_ASSISTANT
|
|
74
|
+
) {
|
|
66
75
|
return v;
|
|
67
76
|
}
|
|
68
|
-
throw new Error(`Unknown workspace "${raw}". Use integration or
|
|
77
|
+
throw new Error(`Unknown workspace "${raw}". Use integration, enterprise-knowledge, or role-assistant.`);
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
function workspacePair(appPath, baseDir) {
|
|
@@ -76,47 +85,84 @@ function resolveFromWorkspaceOverride(workspaceOverride, appName, getIntegration
|
|
|
76
85
|
if (workspaceOverride === WORKSPACE_ENTERPRISE_KNOWLEDGE) {
|
|
77
86
|
return workspacePair(getEnterpriseKnowledgePath(appName), WORKSPACE_ENTERPRISE_KNOWLEDGE);
|
|
78
87
|
}
|
|
88
|
+
if (workspaceOverride === WORKSPACE_ROLE_ASSISTANT) {
|
|
89
|
+
return workspacePair(getRoleAssistantPath(appName), WORKSPACE_ROLE_ASSISTANT);
|
|
90
|
+
}
|
|
79
91
|
if (workspaceOverride === WORKSPACE_INTEGRATION) {
|
|
80
92
|
return workspacePair(getIntegrationPath(appName), WORKSPACE_INTEGRATION);
|
|
81
93
|
}
|
|
82
94
|
return null;
|
|
83
95
|
}
|
|
84
96
|
|
|
85
|
-
function
|
|
86
|
-
const kind =
|
|
87
|
-
explicitKind ||
|
|
88
|
-
readSystemKindFromAppDir(ekPath, appName) ||
|
|
89
|
-
readSystemKindFromAppDir(intPath, appName);
|
|
97
|
+
function resolveWorkspaceFromSystemKind(kind, ekPath, intPath, roleAssistantPath) {
|
|
90
98
|
if (kind === SYSTEM_KIND_ENTERPRISE_KNOWLEDGE) {
|
|
91
99
|
return workspacePair(ekPath, WORKSPACE_ENTERPRISE_KNOWLEDGE);
|
|
92
100
|
}
|
|
101
|
+
if (kind === SYSTEM_KIND_DIGITAL_WORKER) {
|
|
102
|
+
return workspacePair(roleAssistantPath, WORKSPACE_ROLE_ASSISTANT);
|
|
103
|
+
}
|
|
93
104
|
return workspacePair(intPath, WORKSPACE_INTEGRATION);
|
|
94
105
|
}
|
|
95
106
|
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
function resolveAmbiguousWorkspace(ekPath, intPath, roleAssistantPath, explicitKind, appName) {
|
|
108
|
+
const kind =
|
|
109
|
+
explicitKind ||
|
|
110
|
+
readSystemKindFromAppDir(roleAssistantPath, appName) ||
|
|
111
|
+
readSystemKindFromAppDir(ekPath, appName) ||
|
|
112
|
+
readSystemKindFromAppDir(intPath, appName);
|
|
113
|
+
return resolveWorkspaceFromSystemKind(kind, ekPath, intPath, roleAssistantPath);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function resolveFromExistingPaths(paths, existence, explicitKind, appName) {
|
|
117
|
+
const { ekPath, intPath, roleAssistantPath } = paths;
|
|
118
|
+
const { ekExists, intExists, roleAssistantExists } = existence;
|
|
119
|
+
const existingCount = [ekExists, intExists, roleAssistantExists].filter(Boolean).length;
|
|
120
|
+
if (existingCount === 1) {
|
|
121
|
+
if (ekExists) {
|
|
122
|
+
return workspacePair(ekPath, WORKSPACE_ENTERPRISE_KNOWLEDGE);
|
|
123
|
+
}
|
|
124
|
+
if (roleAssistantExists) {
|
|
125
|
+
return workspacePair(roleAssistantPath, WORKSPACE_ROLE_ASSISTANT);
|
|
126
|
+
}
|
|
101
127
|
return workspacePair(intPath, WORKSPACE_INTEGRATION);
|
|
102
128
|
}
|
|
103
|
-
if (
|
|
104
|
-
return resolveAmbiguousWorkspace(ekPath, intPath, explicitKind, appName);
|
|
129
|
+
if (existingCount > 1) {
|
|
130
|
+
return resolveAmbiguousWorkspace(ekPath, intPath, roleAssistantPath, explicitKind, appName);
|
|
105
131
|
}
|
|
106
|
-
const kind =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
132
|
+
const kind =
|
|
133
|
+
explicitKind ||
|
|
134
|
+
readSystemKindFromAppDir(roleAssistantPath, appName) ||
|
|
135
|
+
readSystemKindFromAppDir(ekPath, appName) ||
|
|
136
|
+
readSystemKindFromAppDir(intPath, appName);
|
|
137
|
+
return resolveWorkspaceFromSystemKind(kind, ekPath, intPath, roleAssistantPath);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function resolveFromExistingPathsBackwardCompatible(
|
|
141
|
+
ekPath,
|
|
142
|
+
intPath,
|
|
143
|
+
roleAssistantPath,
|
|
144
|
+
explicitKind,
|
|
145
|
+
appName
|
|
146
|
+
) {
|
|
147
|
+
return resolveFromExistingPaths(
|
|
148
|
+
{ ekPath, intPath, roleAssistantPath },
|
|
149
|
+
{
|
|
150
|
+
ekExists: isExistingDirectory(ekPath),
|
|
151
|
+
intExists: isExistingDirectory(intPath),
|
|
152
|
+
roleAssistantExists: isExistingDirectory(roleAssistantPath)
|
|
153
|
+
},
|
|
154
|
+
explicitKind,
|
|
155
|
+
appName
|
|
156
|
+
);
|
|
111
157
|
}
|
|
112
158
|
|
|
113
159
|
/**
|
|
114
160
|
* Resolves authoring tree for an external system or Enterprise Knowledge anchor.
|
|
115
161
|
* @param {string} appName - App or anchor key
|
|
116
162
|
* @param {Object} [options]
|
|
117
|
-
* @param {string} [options.workspace] - integration | enterprise-knowledge
|
|
163
|
+
* @param {string} [options.workspace] - integration | enterprise-knowledge | role-assistant
|
|
118
164
|
* @param {string} [options.systemKind] - Explicit systemKind override
|
|
119
|
-
* @returns {{ appPath: string, baseDir: 'integration' | 'enterprise-knowledge' }}
|
|
165
|
+
* @returns {{ appPath: string, baseDir: 'integration' | 'enterprise-knowledge' | 'role-assistant' }}
|
|
120
166
|
*/
|
|
121
167
|
function resolveAppWorkspace(appName, options = {}) {
|
|
122
168
|
if (!appName || typeof appName !== 'string') {
|
|
@@ -137,11 +183,11 @@ function resolveAppWorkspace(appName, options = {}) {
|
|
|
137
183
|
|
|
138
184
|
const ekPath = getEnterpriseKnowledgePath(appName);
|
|
139
185
|
const intPath = getIntegrationPath(appName);
|
|
140
|
-
|
|
186
|
+
const roleAssistantPath = getRoleAssistantPath(appName);
|
|
187
|
+
return resolveFromExistingPathsBackwardCompatible(
|
|
141
188
|
ekPath,
|
|
142
189
|
intPath,
|
|
143
|
-
|
|
144
|
-
isExistingDirectory(intPath),
|
|
190
|
+
roleAssistantPath,
|
|
145
191
|
explicitKind,
|
|
146
192
|
appName
|
|
147
193
|
);
|
|
@@ -160,7 +206,9 @@ function resolveExternalSystemPath(appName, options = {}) {
|
|
|
160
206
|
module.exports = {
|
|
161
207
|
WORKSPACE_INTEGRATION,
|
|
162
208
|
WORKSPACE_ENTERPRISE_KNOWLEDGE,
|
|
209
|
+
WORKSPACE_ROLE_ASSISTANT,
|
|
163
210
|
SYSTEM_KIND_ENTERPRISE_KNOWLEDGE,
|
|
211
|
+
SYSTEM_KIND_DIGITAL_WORKER,
|
|
164
212
|
readSystemKindFromAppDir,
|
|
165
213
|
normalizeWorkspaceOverride,
|
|
166
214
|
resolveAppWorkspace,
|
|
@@ -64,7 +64,9 @@ function buildFlatCredentialTemplate(systemKey, authType) {
|
|
|
64
64
|
security: { clientId: kvPath('clientId'), clientSecret: kvPath('clientSecret') }
|
|
65
65
|
},
|
|
66
66
|
apikey: {
|
|
67
|
-
|
|
67
|
+
// Match credential-field-registry placeholders (Authorization + Bearer).
|
|
68
|
+
// Integrations that need a literal custom header set headerName after repair.
|
|
69
|
+
variables: { baseUrl: base, headerName: 'Authorization', prefix: 'Bearer' },
|
|
68
70
|
security: { apiKey: kvPath('apiKey') }
|
|
69
71
|
},
|
|
70
72
|
bearerToken: {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Workspace roots allowed for external certify / verify CLI commands.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const CERTIFY_EXTERNAL_BASE_DIRS = new Set([
|
|
8
|
+
'integration',
|
|
9
|
+
'role-assistant',
|
|
10
|
+
'enterprise-knowledge'
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string|null|undefined} baseDir
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function isCertifyExternalWorkspace(baseDir) {
|
|
18
|
+
return CERTIFY_EXTERNAL_BASE_DIRS.has(baseDir);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string|null|undefined} baseDir
|
|
23
|
+
* @param {string} commandLabel - e.g. verify-operations
|
|
24
|
+
* @returns {void}
|
|
25
|
+
* @throws {Error}
|
|
26
|
+
*/
|
|
27
|
+
function assertCertifyExternalWorkspace(baseDir, commandLabel) {
|
|
28
|
+
if (!isCertifyExternalWorkspace(baseDir)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`${commandLabel} applies to external authoring folders only ` +
|
|
31
|
+
'(integration/<systemKey>/, role-assistant/<roleKey>/, or enterprise-knowledge/<key>/).'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
CERTIFY_EXTERNAL_BASE_DIRS,
|
|
38
|
+
isCertifyExternalWorkspace,
|
|
39
|
+
assertCertifyExternalWorkspace
|
|
40
|
+
};
|
|
@@ -8,6 +8,27 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* True when URL is http to a Docker Compose service DNS name (single hostname label).
|
|
13
|
+
* Example: http://miso-controller:3000/miso — used by builder-api in compose, not public HTTP.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} url
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function isDockerComposeServiceHttpUrl(url) {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = new URL(url);
|
|
21
|
+
if (parsed.protocol !== 'http:') {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const host = parsed.hostname || '';
|
|
25
|
+
// Compose DNS: one label, no dots (miso-controller, dataplane, …)
|
|
26
|
+
return Boolean(host) && !host.includes('.') && host !== 'localhost';
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
/**
|
|
12
33
|
* Validates and sanitizes controller URL
|
|
13
34
|
* Enforces HTTPS-only communication for security
|
|
@@ -20,9 +41,13 @@ function validateControllerUrl(url) {
|
|
|
20
41
|
throw new Error('Controller URL is required and must be a string');
|
|
21
42
|
}
|
|
22
43
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
// HTTPS required in public networks; allow localhost and compose-service HTTP for local Docker
|
|
45
|
+
const allowedHttp =
|
|
46
|
+
url.startsWith('http://localhost') || isDockerComposeServiceHttpUrl(url);
|
|
47
|
+
if (!url.startsWith('https://') && !allowedHttp) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
'Controller URL must use HTTPS (https://), http://localhost, or a Docker Compose service HTTP URL'
|
|
50
|
+
);
|
|
26
51
|
}
|
|
27
52
|
|
|
28
53
|
// Basic URL format validation
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ephemeral docker run (--rm) for one-shot commands (test, install, deployJob).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Shared docker run --rm helper
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawn } = require('child_process');
|
|
10
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Run a shell command in an ephemeral container; optional --env-file / --network / bind mounts.
|
|
14
|
+
* Attach to the developer infra bridge when the command needs compose DNS
|
|
15
|
+
* (e.g. hostname `postgres` in DATABASE_URL).
|
|
16
|
+
* @param {string} fullImage - Image:tag
|
|
17
|
+
* @param {string} cmd - Shell command (passed to sh -c)
|
|
18
|
+
* @param {string|null} [envFilePath] - Path for docker run --env-file
|
|
19
|
+
* @param {{
|
|
20
|
+
* network?: string|null,
|
|
21
|
+
* volumeMounts?: Array<{ host: string, container: string }>,
|
|
22
|
+
* quiet?: boolean
|
|
23
|
+
* }} [options] - quiet=true pipes stdio (installer-friendly; dump on failure)
|
|
24
|
+
* @returns {Promise<number|{ code: number, output: string }>} Exit code, or code+output when quiet
|
|
25
|
+
*/
|
|
26
|
+
async function runDockerRunEphemeral(fullImage, cmd, envFilePath = null, options = {}) {
|
|
27
|
+
const dockerEnv = await getDockerExecEnv();
|
|
28
|
+
const network =
|
|
29
|
+
options && typeof options.network === 'string' && options.network.trim()
|
|
30
|
+
? options.network.trim()
|
|
31
|
+
: null;
|
|
32
|
+
const volumeMounts = Array.isArray(options?.volumeMounts) ? options.volumeMounts : [];
|
|
33
|
+
const quiet = options?.quiet === true;
|
|
34
|
+
const args = _buildDockerRunArgs(fullImage, cmd, envFilePath, network, volumeMounts);
|
|
35
|
+
if (quiet) {
|
|
36
|
+
return _spawnDockerQuiet(args, dockerEnv);
|
|
37
|
+
}
|
|
38
|
+
return _spawnDockerInherit(args, dockerEnv);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {string} fullImage
|
|
43
|
+
* @param {string} cmd
|
|
44
|
+
* @param {string|null} envFilePath
|
|
45
|
+
* @param {string|null} network
|
|
46
|
+
* @param {Array<{ host: string, container: string }>} volumeMounts
|
|
47
|
+
* @returns {string[]}
|
|
48
|
+
*/
|
|
49
|
+
function _buildDockerRunArgs(fullImage, cmd, envFilePath, network, volumeMounts) {
|
|
50
|
+
const args = ['run', '--rm'];
|
|
51
|
+
if (network) {
|
|
52
|
+
args.push('--network', network);
|
|
53
|
+
}
|
|
54
|
+
if (envFilePath) {
|
|
55
|
+
args.push('--env-file', envFilePath);
|
|
56
|
+
}
|
|
57
|
+
for (const mount of volumeMounts) {
|
|
58
|
+
const host = typeof mount?.host === 'string' ? mount.host.trim() : '';
|
|
59
|
+
const container = typeof mount?.container === 'string' ? mount.container.trim() : '';
|
|
60
|
+
if (host && container) {
|
|
61
|
+
args.push('-v', `${host}:${container}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
args.push(fullImage, 'sh', '-c', cmd);
|
|
65
|
+
return args;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string[]} args
|
|
70
|
+
* @param {NodeJS.ProcessEnv} dockerEnv
|
|
71
|
+
* @returns {Promise<number>}
|
|
72
|
+
*/
|
|
73
|
+
function _spawnDockerInherit(args, dockerEnv) {
|
|
74
|
+
return new Promise((resolve) => {
|
|
75
|
+
const proc = spawn('docker', args, {
|
|
76
|
+
stdio: 'inherit',
|
|
77
|
+
shell: false,
|
|
78
|
+
env: dockerEnv
|
|
79
|
+
});
|
|
80
|
+
proc.on('close', (code) => resolve(code !== null ? code : 1));
|
|
81
|
+
proc.on('error', () => resolve(1));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {string[]} args
|
|
87
|
+
* @param {NodeJS.ProcessEnv} dockerEnv
|
|
88
|
+
* @returns {Promise<{ code: number, output: string }>}
|
|
89
|
+
*/
|
|
90
|
+
function _spawnDockerQuiet(args, dockerEnv) {
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
const proc = spawn('docker', args, {
|
|
93
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
94
|
+
shell: false,
|
|
95
|
+
env: dockerEnv
|
|
96
|
+
});
|
|
97
|
+
const chunks = [];
|
|
98
|
+
const push = (buf) => {
|
|
99
|
+
if (buf && buf.length) {
|
|
100
|
+
chunks.push(buf);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
proc.stdout.on('data', push);
|
|
104
|
+
proc.stderr.on('data', push);
|
|
105
|
+
proc.on('close', (code) => {
|
|
106
|
+
resolve({
|
|
107
|
+
code: code !== null ? code : 1,
|
|
108
|
+
output: Buffer.concat(chunks).toString('utf8')
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
proc.on('error', () => resolve({ code: 1, output: '' }));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { runDockerRunEphemeral };
|
|
@@ -62,8 +62,22 @@ function getEnterpriseKnowledgeRoot() {
|
|
|
62
62
|
return path.join(getMaterializationParent(), WORKSPACE_ENTERPRISE_KNOWLEDGE);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function isExistingDirectory(disk, dirPath) {
|
|
66
|
+
try {
|
|
67
|
+
if (!dirPath || !disk.existsSync(dirPath)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const st = disk.statSync(dirPath);
|
|
71
|
+
return Boolean(st && typeof st.isDirectory === 'function' && st.isDirectory());
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
/**
|
|
66
|
-
* Resolves enterprise-knowledge/<anchorKey
|
|
78
|
+
* Resolves enterprise-knowledge/<anchorKey>.
|
|
79
|
+
* Prefer an existing cwd or materialization folder; when `cwd/enterprise-knowledge/` exists and
|
|
80
|
+
* neither copy exists yet, return the cwd path (download/create target).
|
|
67
81
|
* @param {string} anchorKey - Enterprise Knowledge anchor key
|
|
68
82
|
* @returns {string}
|
|
69
83
|
*/
|
|
@@ -71,19 +85,22 @@ function getEnterpriseKnowledgePath(anchorKey) {
|
|
|
71
85
|
if (!anchorKey || typeof anchorKey !== 'string') {
|
|
72
86
|
throw new Error('Anchor key is required and must be a string');
|
|
73
87
|
}
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
88
|
+
const disk = nodeFs();
|
|
89
|
+
const cwdRoot = getCwdEnterpriseKnowledgeRoot();
|
|
90
|
+
const matApp = path.join(getMaterializationParent(), WORKSPACE_ENTERPRISE_KNOWLEDGE, anchorKey);
|
|
91
|
+
if (cwdRoot) {
|
|
92
|
+
const cwdApp = path.join(cwdRoot, anchorKey);
|
|
93
|
+
if (isExistingDirectory(disk, cwdApp)) {
|
|
94
|
+
return cwdApp;
|
|
82
95
|
}
|
|
83
|
-
} catch {
|
|
84
|
-
// ignore
|
|
85
96
|
}
|
|
86
|
-
|
|
97
|
+
if (isExistingDirectory(disk, matApp)) {
|
|
98
|
+
return matApp;
|
|
99
|
+
}
|
|
100
|
+
if (cwdRoot) {
|
|
101
|
+
return path.join(cwdRoot, anchorKey);
|
|
102
|
+
}
|
|
103
|
+
return matApp;
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
function isEnterpriseKnowledgeDirectory(disk, fullPath) {
|