openyida 2026.7.18 → 2026.7.21
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 +37 -2
- package/bin/yida.js +113 -18
- package/docs/capabilities.md +202 -0
- package/lib/agent-center/agent-center.js +2 -3
- package/lib/app/app-list.js +3 -4
- package/lib/app/build-page.js +13 -6
- package/lib/app/check-page.js +16 -6
- package/lib/app/compile.js +17 -7
- package/lib/app/create-app.js +14 -2
- package/lib/app/create-form/api-path.js +25 -0
- package/lib/app/create-form/args.js +13 -0
- package/lib/app/create-form/commands.js +4 -0
- package/lib/app/create-form/definition-reader.js +23 -23
- package/lib/app/create-form/field-normalizers.js +26 -7
- package/lib/app/create-form/schema-patch.js +4 -1
- package/lib/app/create-form.js +155 -68
- package/lib/app/create-page.js +47 -10
- package/lib/app/generate-page.js +20 -1
- package/lib/app/get-form-config.js +2 -1
- package/lib/app/get-schema.js +67 -15
- package/lib/app/import-app.js +94 -42
- package/lib/app/list-forms.js +4 -8
- package/lib/app/page-ir.js +140 -0
- package/lib/app/publish.js +289 -556
- package/lib/app/schema-field-resolution.js +370 -0
- package/lib/app/services/app-reader.js +85 -0
- package/lib/app/services/app-service.js +235 -0
- package/lib/app/services/canvas-page-compiler.js +91 -0
- package/lib/app/services/canvas-page-schema-builder.js +149 -0
- package/lib/app/services/field-bindings.js +68 -0
- package/lib/app/services/form-compiler.js +1922 -0
- package/lib/app/services/form-mode-service.js +182 -0
- package/lib/app/services/form-schema-patcher.js +333 -0
- package/lib/app/services/form-schema-reader.js +59 -0
- package/lib/app/services/form-service.js +379 -0
- package/lib/app/services/form-validation.js +562 -0
- package/lib/app/services/native-page-compiler.js +92 -0
- package/lib/app/services/native-page-schema-builder.js +428 -0
- package/lib/app/services/page-resource-service.js +185 -0
- package/lib/app/update-app.js +22 -9
- package/lib/app/update-form-config.js +4 -2
- package/lib/asset/asset-cmd.js +19 -6
- package/lib/auth/oauth-loopback.js +376 -15
- package/lib/auth/token-auth.js +34 -3
- package/lib/auth/token-store.js +22 -4
- package/lib/basic-info/basic-info.js +2 -1
- package/lib/bridge/bridge.js +43 -18
- package/lib/core/agent-capabilities.js +5 -3
- package/lib/core/command-errors.js +38 -0
- package/lib/core/command-manifest.js +59 -1
- package/lib/core/copy.js +12 -0
- package/lib/core/doctor.js +15 -4
- package/lib/core/env-cmd.js +1 -1
- package/lib/core/env.js +7 -6
- package/lib/core/i18n.js +66 -8
- package/lib/core/legacy-schema-guard.js +408 -0
- package/lib/core/locales/en.js +38 -46
- package/lib/core/locales/zh.js +30 -46
- package/lib/core/sample.js +12 -2
- package/lib/core/utils.js +334 -66
- package/lib/core/yida-client.js +108 -5
- package/lib/corp-efficiency/corp-efficiency.js +2 -2
- package/lib/corp-manager/corp-manager.js +3 -3
- package/lib/formula/evaluate.js +11 -68
- package/lib/formula/field-refs.js +83 -0
- package/lib/i18n-management/i18n-management.js +2 -3
- package/lib/process/configure-process.js +44 -1992
- package/lib/process/create-process.js +54 -87
- package/lib/process/preview-process.js +20 -8
- package/lib/process/services/process-compiler.js +2056 -0
- package/lib/process/services/process-reader.js +232 -0
- package/lib/process/services/process-resource-service.js +571 -0
- package/lib/process/services/process-service.js +122 -0
- package/lib/process/services/process-stage-checkpoint.js +246 -0
- package/lib/report/append.js +7 -26
- package/lib/report/http.js +31 -5
- package/lib/report/index.js +10 -2
- package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
- package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
- package/lib/samples/yida-canvas-custom-page/dashboard-starter.canvas.jsx +5 -2
- package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
- package/lib/samples/yida-canvas-custom-page/portal-shell-home.canvas.jsx +2 -2
- package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +206 -10
- package/lib/samples/yida-custom-page/data-management.oyd.jsx +14 -3
- package/lib/samples/yida-custom-page/design-tokens.js +2 -2
- package/lib/samples/yida-custom-page/detail-profile.oyd.jsx +1 -1
- package/lib/samples/yida-custom-page/portal-shell-home.oyd.jsx +7 -2
- package/lib/samples/yida-custom-page/split-pane-detail.oyd.jsx +2 -1
- package/lib/schema/adapters/app-adapter.js +210 -0
- package/lib/schema/adapters/form-adapter.js +1362 -0
- package/lib/schema/adapters/keys.js +19 -0
- package/lib/schema/adapters/page-adapter.js +566 -0
- package/lib/schema/adapters/process-adapter.js +519 -0
- package/lib/schema/applier.js +1878 -0
- package/lib/schema/apply-store.js +1083 -0
- package/lib/schema/command.js +645 -0
- package/lib/schema/dependency-graph.js +83 -0
- package/lib/schema/errors.js +481 -0
- package/lib/schema/hash.js +9 -0
- package/lib/schema/manifest-limits.js +177 -0
- package/lib/schema/manifest-loader.js +309 -0
- package/lib/schema/manifest-schema-v1.json +193 -0
- package/lib/schema/normalize-manifest.js +281 -0
- package/lib/schema/page-canvas-foundation.js +447 -0
- package/lib/schema/page-data-source-builder.js +561 -0
- package/lib/schema/page-foundation.js +556 -0
- package/lib/schema/page-source-loader.js +314 -0
- package/lib/schema/planner.js +691 -0
- package/lib/schema/remote-dispatch-boundary.js +39 -0
- package/lib/schema/remote-missing.js +21 -0
- package/lib/schema/remote-reader.js +189 -0
- package/lib/schema/resource-registry.js +157 -0
- package/lib/schema/server-revision.js +71 -0
- package/lib/schema/sort.js +28 -0
- package/lib/schema/state-store.js +589 -0
- package/package.json +10 -8
- package/scripts/check-release-risks.js +254 -0
- package/scripts/generate-command-docs.js +6 -1
- package/scripts/i18n-baseline.json +11 -11
- package/scripts/postinstall.js +8 -12
- package/scripts/validate-ci.sh +15 -11
- package/scripts/validate-i18n.js +30 -9
- package/scripts/validate-package-size.js +5 -2
- package/scripts/validate-skills.js +4 -0
- package/scripts/validate-structure.js +15 -0
- package/yida-skills/SKILL.md +117 -30
- package/yida-skills/references/schema-as-code-phase1.md +99 -0
- package/yida-skills/references/setup-and-env.md +59 -59
- package/yida-skills/references/task-retrospective.md +6 -6
- package/yida-skills/skills/yida-app/SKILL.md +78 -17
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +15 -0
- package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
- package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
- package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
- package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
- package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
- package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
- package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
- package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
- package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
- package/yida-skills/skills/yida-integration/SKILL.md +7 -1
- package/yida-skills/skills/yida-login/SKILL.md +50 -99
- package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
- package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
- package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
- package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
- package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
- package/yida-skills/skills/yida-report/SKILL.md +9 -235
- package/yida-skills/skills/yida-report/references/schema-builder-details.md +239 -0
- package/yida-skills/skills/yida-skill-evaluator/SKILL.md +170 -0
- package/yida-skills/skills-index.json +238 -54
- package/lib/core/locales/ar.js +0 -834
- package/lib/core/locales/de.js +0 -834
- package/lib/core/locales/es.js +0 -834
- package/lib/core/locales/fr.js +0 -834
- package/lib/core/locales/hi.js +0 -834
- package/lib/core/locales/ja.js +0 -1227
- package/lib/core/locales/ko.js +0 -836
- package/lib/core/locales/pt.js +0 -834
- package/lib/core/locales/vi.js +0 -834
- package/lib/core/locales/zh-HK.js +0 -1263
- package/project/pages/src/demo-birthday-game.oyd.jsx +0 -832
- package/project/pages/src/demo-chip-insight.oyd.jsx +0 -983
- package/project/pages/src/demo-compat-smoke.oyd.jsx +0 -58
- package/project/pages/src/demo-crm-batch-entry.oyd.jsx +0 -805
- package/project/pages/src/demo-crm-dashboard.oyd.jsx +0 -677
- package/project/pages/src/demo-future-vision-2026.oyd.jsx +0 -1102
- package/project/pages/src/demo-ppt.oyd.jsx +0 -1192
- package/project/pages/src/demo-salary-calculator.oyd.jsx +0 -904
- package/project/pages/src/openyida-knowledge-doc.oyd.jsx +0 -1714
- package/yida-skills/skills/large-file-write/SKILL.md +0 -93
- package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
- package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
- package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
- package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
- package/yida-skills/skills/sls-log-workbench/sls-query.js +0 -295
|
@@ -4,6 +4,7 @@ const { loadAuthData, triggerLogin, resolveBaseUrl, httpPost, requestWithAutoLog
|
|
|
4
4
|
const { t } = require('../core/i18n');
|
|
5
5
|
const { buildYidaTitleI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
|
|
6
6
|
const { banner, step, label, success, fail, warn, info, error, result, usage } = require('../core/chalk');
|
|
7
|
+
const { throwCommandError, throwUsage } = require('../core/command-errors');
|
|
7
8
|
|
|
8
9
|
function parseArgs() {
|
|
9
10
|
const args = process.argv.slice(2);
|
|
@@ -24,7 +25,7 @@ function parseArgs() {
|
|
|
24
25
|
info(t('update_form_config.params_label'));
|
|
25
26
|
label(t('update_form_config.param_is_render_nav'), '');
|
|
26
27
|
label(t('update_form_config.param_title'), '');
|
|
27
|
-
|
|
28
|
+
throwUsage(t('update_form_config.usage'), t('update_form_config.example'));
|
|
28
29
|
}
|
|
29
30
|
return {
|
|
30
31
|
appType: args[0],
|
|
@@ -218,10 +219,11 @@ async function main() {
|
|
|
218
219
|
}
|
|
219
220
|
} else {
|
|
220
221
|
fail(t('common.request_failed_label'));
|
|
221
|
-
|
|
222
|
+
throwCommandError(t('common.request_failed_label'));
|
|
222
223
|
}
|
|
223
224
|
}
|
|
224
225
|
|
|
225
226
|
main().catch((err) => {
|
|
226
227
|
error(t('common.exception', err.message));
|
|
228
|
+
process.exitCode = err && err.exitCode ? err.exitCode : 1;
|
|
227
229
|
});
|
package/lib/asset/asset-cmd.js
CHANGED
|
@@ -24,6 +24,7 @@ const {
|
|
|
24
24
|
detectImageGenerator,
|
|
25
25
|
getMaterialSourcingGuidance,
|
|
26
26
|
} = require('./ai-image');
|
|
27
|
+
const { throwCommandError, throwStatus } = require('../core/command-errors');
|
|
27
28
|
|
|
28
29
|
function printHelp() {
|
|
29
30
|
console.log(`
|
|
@@ -110,8 +111,9 @@ function runStatus(options) {
|
|
|
110
111
|
|
|
111
112
|
async function runVerifyUrl(options) {
|
|
112
113
|
if (!options.urls.length) {
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
throwCommandError('错误:请至少提供一个图片 URL。用法:openyida asset verify-url <url> [<url> ...]', {
|
|
115
|
+
code: 'ASSET_VERIFY_URL_INVALID_ARGUMENTS',
|
|
116
|
+
});
|
|
115
117
|
}
|
|
116
118
|
const results = await verifyImageUrls(options.urls, {
|
|
117
119
|
minBytes: options.minBytes,
|
|
@@ -132,7 +134,12 @@ async function runVerifyUrl(options) {
|
|
|
132
134
|
' reason=' + r.reason
|
|
133
135
|
);
|
|
134
136
|
});
|
|
135
|
-
if (!allOk) {
|
|
137
|
+
if (!allOk) {
|
|
138
|
+
throwCommandError('图片 URL 校验失败', {
|
|
139
|
+
code: 'ASSET_VERIFY_URL_FAILED',
|
|
140
|
+
details: { results },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
136
143
|
}
|
|
137
144
|
|
|
138
145
|
async function runResolve(options) {
|
|
@@ -164,7 +171,12 @@ async function runResolve(options) {
|
|
|
164
171
|
result.materialGaps.forEach((g) => console.log(' - ' + g));
|
|
165
172
|
}
|
|
166
173
|
// 非 final 时以非零退出码提示调用方(生成链路可据此拦截「最终版」)
|
|
167
|
-
if (result.materialStatus !== 'final') {
|
|
174
|
+
if (result.materialStatus !== 'final') {
|
|
175
|
+
throwStatus('素材未达到 final 状态', 2, {
|
|
176
|
+
code: 'ASSET_MATERIAL_NOT_FINAL',
|
|
177
|
+
details: result,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
168
180
|
}
|
|
169
181
|
|
|
170
182
|
function runGenerate(options) {
|
|
@@ -208,9 +220,10 @@ async function run(args) {
|
|
|
208
220
|
runGenerate(options);
|
|
209
221
|
break;
|
|
210
222
|
default:
|
|
211
|
-
console.error('未知子命令: ' + options.subCommand);
|
|
212
223
|
printHelp();
|
|
213
|
-
|
|
224
|
+
throwCommandError('未知子命令: ' + options.subCommand, {
|
|
225
|
+
code: 'ASSET_UNKNOWN_SUBCOMMAND',
|
|
226
|
+
});
|
|
214
227
|
}
|
|
215
228
|
}
|
|
216
229
|
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const http = require('http');
|
|
5
|
-
const
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { spawn, spawnSync } = require('child_process');
|
|
6
9
|
|
|
7
10
|
const CALLBACK_PATH = '/oauth/callback';
|
|
8
11
|
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -11,30 +14,336 @@ function randomToken(bytes = 24) {
|
|
|
11
14
|
return crypto.randomBytes(bytes).toString('base64url');
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
const CHROMIUM_MAC_BUNDLES = {
|
|
18
|
+
'com.google.chrome': 'com.google.Chrome',
|
|
19
|
+
'com.microsoft.edgemac': 'com.microsoft.edgemac',
|
|
20
|
+
'com.brave.browser': 'com.brave.Browser',
|
|
21
|
+
'com.operasoftware.opera': 'com.operasoftware.Opera',
|
|
22
|
+
'com.vivaldi.vivaldi': 'com.vivaldi.Vivaldi',
|
|
23
|
+
'org.chromium.chromium': 'org.chromium.Chromium',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Only these browser families have a known reliable new-window launch path.
|
|
27
|
+
// Anything else (unknown/unsupported default browser) must fall back.
|
|
28
|
+
const SUPPORTED_BROWSER_FAMILIES = new Set(['chromium', 'firefox', 'safari']);
|
|
29
|
+
|
|
30
|
+
// Chromium accepts `--new-window`; Firefox uses the single-dash `-new-window`.
|
|
31
|
+
function newWindowFlag(family) {
|
|
32
|
+
return family === 'firefox' ? '-new-window' : '--new-window';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function appleScriptSafe(value) {
|
|
36
|
+
return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function classifyMacBundleId(bundleId) {
|
|
40
|
+
if (!bundleId) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const id = String(bundleId).toLowerCase();
|
|
44
|
+
if (id === 'com.apple.safari') {
|
|
45
|
+
return { family: 'safari' };
|
|
46
|
+
}
|
|
47
|
+
if (id === 'org.mozilla.firefox') {
|
|
48
|
+
return { family: 'firefox', bundleId: 'org.mozilla.firefox' };
|
|
49
|
+
}
|
|
50
|
+
if (CHROMIUM_MAC_BUNDLES[id]) {
|
|
51
|
+
return { family: 'chromium', bundleId: CHROMIUM_MAC_BUNDLES[id] };
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function classifyWindowsProgId(progId) {
|
|
57
|
+
if (!progId) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const id = String(progId).toLowerCase();
|
|
61
|
+
if (id.includes('firefox')) {
|
|
62
|
+
return 'firefox';
|
|
63
|
+
}
|
|
64
|
+
if (
|
|
65
|
+
id.startsWith('chromehtml') ||
|
|
66
|
+
id.startsWith('msedge') ||
|
|
67
|
+
id.startsWith('bravehtml') ||
|
|
68
|
+
id.startsWith('opera') ||
|
|
69
|
+
id.includes('vivaldi') ||
|
|
70
|
+
id.includes('chromium')
|
|
71
|
+
) {
|
|
72
|
+
return 'chromium';
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function classifyLinuxDesktop(name) {
|
|
78
|
+
if (!name) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const id = String(name).toLowerCase();
|
|
82
|
+
if (id.includes('firefox')) {
|
|
83
|
+
return 'firefox';
|
|
84
|
+
}
|
|
85
|
+
if (
|
|
86
|
+
id.includes('chrome') ||
|
|
87
|
+
id.includes('chromium') ||
|
|
88
|
+
id.includes('brave') ||
|
|
89
|
+
id.includes('edge') ||
|
|
90
|
+
id.includes('opera') ||
|
|
91
|
+
id.includes('vivaldi')
|
|
92
|
+
) {
|
|
93
|
+
return 'chromium';
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parseMacDefaultBrowser(defaultsOutput) {
|
|
99
|
+
if (!defaultsOutput) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
// The secure plist is an array of dicts; each `}` closes one handler entry.
|
|
103
|
+
const blocks = String(defaultsOutput).split('}');
|
|
104
|
+
for (const block of blocks) {
|
|
105
|
+
if (/LSHandlerURLScheme\s*=\s*https\b/i.test(block)) {
|
|
106
|
+
const match = block.match(/LSHandlerRole(?:All|Viewer)\s*=\s*"?([\w.-]+)"?/i);
|
|
107
|
+
if (match) {
|
|
108
|
+
return match[1].toLowerCase();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parseWindowsProgId(regOutput) {
|
|
116
|
+
if (!regOutput) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const match = String(regOutput).match(/ProgId\s+REG_\w+\s+(\S+)/i);
|
|
120
|
+
return match ? match[1] : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function parseWindowsCommandExec(regOutput) {
|
|
124
|
+
if (!regOutput) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const match = String(regOutput).match(/REG_\w+\s+(.+)$/m);
|
|
128
|
+
if (!match) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const value = match[1].trim();
|
|
132
|
+
if (value.startsWith('"')) {
|
|
133
|
+
const end = value.indexOf('"', 1);
|
|
134
|
+
if (end > 1) {
|
|
135
|
+
return value.slice(1, end);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return value.split(/\s+/)[0] || null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parseLinuxExec(desktopContent) {
|
|
142
|
+
if (!desktopContent) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const match = String(desktopContent).match(/^Exec=(.+)$/m);
|
|
146
|
+
if (!match) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
// Drop desktop-entry field codes (%u %U %f %F ...) then take the executable.
|
|
150
|
+
const withoutCodes = match[1].replace(/%[a-zA-Z]/g, '').trim();
|
|
151
|
+
const first = withoutCodes.match(/^"([^"]+)"|^(\S+)/);
|
|
152
|
+
if (!first) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return first[1] || first[2] || null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function runCapture(command, args) {
|
|
159
|
+
try {
|
|
160
|
+
const result = spawnSync(command, args, {
|
|
161
|
+
encoding: 'utf8',
|
|
162
|
+
timeout: 2500,
|
|
163
|
+
windowsHide: true,
|
|
164
|
+
});
|
|
165
|
+
if (result && typeof result.stdout === 'string' && result.stdout.length > 0) {
|
|
166
|
+
return result.stdout;
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Detect the system default browser so we can open a real new window.
|
|
175
|
+
// Returns { family, bundleId? , execPath? } or null when detection fails or
|
|
176
|
+
// the default browser is not a supported family (unknown ProgId/desktop/bundle).
|
|
177
|
+
// `deps` allows injecting command runner / file reader for deterministic tests.
|
|
178
|
+
function detectDefaultBrowser(platform = process.platform, deps = {}) {
|
|
179
|
+
const run = deps.run || runCapture;
|
|
180
|
+
const readFile = deps.readFile || ((filePath) => fs.readFileSync(filePath, 'utf8'));
|
|
181
|
+
const homedir = deps.homedir || os.homedir;
|
|
182
|
+
try {
|
|
183
|
+
if (platform === 'darwin') {
|
|
184
|
+
const output = run('defaults', [
|
|
185
|
+
'read',
|
|
186
|
+
'com.apple.LaunchServices/com.apple.launchservices.secure',
|
|
187
|
+
]);
|
|
188
|
+
return classifyMacBundleId(parseMacDefaultBrowser(output));
|
|
189
|
+
}
|
|
190
|
+
if (platform === 'win32') {
|
|
191
|
+
const progOutput = run('reg', [
|
|
192
|
+
'query',
|
|
193
|
+
'HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\https\\UserChoice',
|
|
194
|
+
'/v',
|
|
195
|
+
'ProgId',
|
|
196
|
+
]);
|
|
197
|
+
const progId = parseWindowsProgId(progOutput);
|
|
198
|
+
const family = classifyWindowsProgId(progId);
|
|
199
|
+
// Unknown ProgId (e.g. IE.HTTP or any browser we don't map) -> fall back.
|
|
200
|
+
if (!family) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const commandOutput =
|
|
204
|
+
run('reg', ['query', `HKCU\\Software\\Classes\\${progId}\\shell\\open\\command`, '/ve']) ||
|
|
205
|
+
run('reg', ['query', `HKEY_CLASSES_ROOT\\${progId}\\shell\\open\\command`, '/ve']);
|
|
206
|
+
const execPath = parseWindowsCommandExec(commandOutput);
|
|
207
|
+
if (!execPath) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
return { family, execPath };
|
|
211
|
+
}
|
|
212
|
+
// linux and other unix-like desktops
|
|
213
|
+
const desktop = run('xdg-settings', ['get', 'default-web-browser']);
|
|
214
|
+
const name = desktop ? desktop.trim() : '';
|
|
215
|
+
if (!name) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
const family = classifyLinuxDesktop(name);
|
|
219
|
+
// Unknown .desktop (e.g. some non-browser or unmapped browser) -> fall back.
|
|
220
|
+
if (!family) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const candidates = [
|
|
224
|
+
path.join(homedir(), '.local/share/applications', name),
|
|
225
|
+
path.join('/usr/share/applications', name),
|
|
226
|
+
path.join('/usr/local/share/applications', name),
|
|
227
|
+
];
|
|
228
|
+
for (const candidate of candidates) {
|
|
229
|
+
try {
|
|
230
|
+
const content = readFile(candidate);
|
|
231
|
+
const execPath = parseLinuxExec(content);
|
|
232
|
+
if (execPath) {
|
|
233
|
+
return { family, execPath };
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
// try the next candidate location
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
17
242
|
}
|
|
243
|
+
}
|
|
18
244
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
245
|
+
function resolveBrowserLauncher(url, platform = process.platform, openMode = 'legacy', browser = null) {
|
|
246
|
+
if (openMode === 'newWindow' && browser && SUPPORTED_BROWSER_FAMILIES.has(browser.family)) {
|
|
247
|
+
if (platform === 'darwin') {
|
|
248
|
+
if (browser.family === 'safari') {
|
|
249
|
+
// Safari has no CLI new-window flag; AppleScript opens a new window.
|
|
250
|
+
const safeUrl = appleScriptSafe(url);
|
|
251
|
+
return {
|
|
252
|
+
command: 'osascript',
|
|
253
|
+
args: [
|
|
254
|
+
'-e',
|
|
255
|
+
`tell application "Safari" to make new document with properties {URL:"${safeUrl}"}`,
|
|
256
|
+
'-e',
|
|
257
|
+
'tell application "Safari" to activate',
|
|
258
|
+
],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (browser.bundleId) {
|
|
262
|
+
// `open -b <bundle> -n --args --new-window <url>` forces a new window
|
|
263
|
+
// in the specific Chromium/Firefox app instead of a new tab.
|
|
264
|
+
return {
|
|
265
|
+
command: 'open',
|
|
266
|
+
args: ['-b', browser.bundleId, '-n', '--args', newWindowFlag(browser.family), url],
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
} else if (browser.execPath) {
|
|
270
|
+
return {
|
|
271
|
+
command: browser.execPath,
|
|
272
|
+
args: [newWindowFlag(browser.family), url],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
26
275
|
}
|
|
27
276
|
|
|
277
|
+
if (platform === 'darwin') {
|
|
278
|
+
return { command: 'open', args: [url] };
|
|
279
|
+
}
|
|
280
|
+
if (platform === 'win32') {
|
|
281
|
+
// rundll32 hands the URL to the default browser as a single argument.
|
|
282
|
+
// Going through `cmd /c start` would let cmd.exe treat the `&` in the
|
|
283
|
+
// OAuth URL as command separators and truncate the query string after
|
|
284
|
+
// redirect_uri, dropping client_id/state and breaking login.
|
|
285
|
+
return { command: 'rundll32', args: ['url.dll,FileProtocolHandler', url] };
|
|
286
|
+
}
|
|
287
|
+
return { command: 'xdg-open', args: [url] };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function launchBrowserLauncher(launcher, onError) {
|
|
28
291
|
try {
|
|
29
|
-
const child = spawn(command, args, {
|
|
292
|
+
const child = spawn(launcher.command, launcher.args, {
|
|
30
293
|
detached: true,
|
|
31
294
|
stdio: 'ignore',
|
|
32
295
|
});
|
|
33
296
|
child.unref();
|
|
297
|
+
if (onError) {
|
|
298
|
+
child.once('error', onError);
|
|
299
|
+
}
|
|
34
300
|
return true;
|
|
35
301
|
} catch {
|
|
302
|
+
if (onError) {
|
|
303
|
+
onError();
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function openBrowser(url) {
|
|
310
|
+
if (process.env.OPENYIDA_NO_BROWSER === '1') {
|
|
36
311
|
return false;
|
|
37
312
|
}
|
|
313
|
+
|
|
314
|
+
const platform = process.platform;
|
|
315
|
+
const legacyLauncher = resolveBrowserLauncher(url, platform, 'legacy');
|
|
316
|
+
const runLegacy = () => launchBrowserLauncher(legacyLauncher);
|
|
317
|
+
|
|
318
|
+
let browser = null;
|
|
319
|
+
try {
|
|
320
|
+
browser = detectDefaultBrowser(platform);
|
|
321
|
+
} catch {
|
|
322
|
+
browser = null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Default browser detected and supported → open a real new window in that
|
|
326
|
+
// browser, falling back to the plain default-browser open if launch errors.
|
|
327
|
+
if (browser && SUPPORTED_BROWSER_FAMILIES.has(browser.family)) {
|
|
328
|
+
const newWindowLauncher = resolveBrowserLauncher(url, platform, 'newWindow', browser);
|
|
329
|
+
let fallbackUsed = false;
|
|
330
|
+
const runFallback = () => {
|
|
331
|
+
if (fallbackUsed) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
fallbackUsed = true;
|
|
335
|
+
return runLegacy();
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const launched = launchBrowserLauncher(newWindowLauncher, runFallback);
|
|
339
|
+
if (!launched) {
|
|
340
|
+
return runFallback();
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Detection failed → fall back to the system default browser (new tab).
|
|
346
|
+
return runLegacy();
|
|
38
347
|
}
|
|
39
348
|
|
|
40
349
|
function buildDingtalkOAuthUrl(options) {
|
|
@@ -59,8 +368,45 @@ function buildDingtalkOAuthUrl(options) {
|
|
|
59
368
|
return url.toString();
|
|
60
369
|
}
|
|
61
370
|
|
|
62
|
-
function responseHtml(title, body) {
|
|
63
|
-
|
|
371
|
+
function responseHtml(title, body, options = {}) {
|
|
372
|
+
const {
|
|
373
|
+
autoClose = false,
|
|
374
|
+
autoCloseDelayMs = 1200,
|
|
375
|
+
staticHint = 'You can close this page and return to the terminal.',
|
|
376
|
+
} = options;
|
|
377
|
+
|
|
378
|
+
const closeScript = autoClose ? `
|
|
379
|
+
<script>
|
|
380
|
+
(function() {
|
|
381
|
+
const delay = Math.max(0, ${Number(autoCloseDelayMs)});
|
|
382
|
+
function forceClose() {
|
|
383
|
+
try {
|
|
384
|
+
if (window.opener && !window.opener.closed && window.opener.focus) {
|
|
385
|
+
window.opener.focus();
|
|
386
|
+
}
|
|
387
|
+
} catch (err) {}
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
window.close();
|
|
391
|
+
} catch (err) {}
|
|
392
|
+
|
|
393
|
+
if (!window.closed) {
|
|
394
|
+
try {
|
|
395
|
+
window.open('', '_self').close();
|
|
396
|
+
} catch (err) {}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
setTimeout(forceClose, delay);
|
|
401
|
+
})();
|
|
402
|
+
</script>
|
|
403
|
+
` : '';
|
|
404
|
+
|
|
405
|
+
const closeHint = autoClose
|
|
406
|
+
? ''
|
|
407
|
+
: `<p>${staticHint}</p>`;
|
|
408
|
+
|
|
409
|
+
return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title></head><body><h3>${title}</h3><p>${body}</p>${closeHint}${closeScript}</body></html>`;
|
|
64
410
|
}
|
|
65
411
|
|
|
66
412
|
function runDingtalkLoopback(options = {}) {
|
|
@@ -115,7 +461,13 @@ function runDingtalkLoopback(options = {}) {
|
|
|
115
461
|
|
|
116
462
|
res.statusCode = 200;
|
|
117
463
|
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
118
|
-
res.end(responseHtml(
|
|
464
|
+
res.end(responseHtml(
|
|
465
|
+
'OpenYida login completed',
|
|
466
|
+
'Login callback received. Returning to terminal.',
|
|
467
|
+
{
|
|
468
|
+
autoClose: true,
|
|
469
|
+
}
|
|
470
|
+
));
|
|
119
471
|
finish(null, {
|
|
120
472
|
code,
|
|
121
473
|
authCode,
|
|
@@ -152,5 +504,14 @@ function runDingtalkLoopback(options = {}) {
|
|
|
152
504
|
module.exports = {
|
|
153
505
|
runDingtalkLoopback,
|
|
154
506
|
buildDingtalkOAuthUrl,
|
|
507
|
+
resolveBrowserLauncher,
|
|
508
|
+
detectDefaultBrowser,
|
|
509
|
+
classifyMacBundleId,
|
|
510
|
+
classifyWindowsProgId,
|
|
511
|
+
classifyLinuxDesktop,
|
|
512
|
+
parseMacDefaultBrowser,
|
|
513
|
+
parseWindowsProgId,
|
|
514
|
+
parseWindowsCommandExec,
|
|
515
|
+
parseLinuxExec,
|
|
155
516
|
CALLBACK_PATH,
|
|
156
517
|
};
|
package/lib/auth/token-auth.js
CHANGED
|
@@ -13,8 +13,10 @@ const {
|
|
|
13
13
|
const { runDingtalkLoopback } = require('./oauth-loopback');
|
|
14
14
|
const {
|
|
15
15
|
clearTokenSession,
|
|
16
|
+
isHostInjectedTokenMode,
|
|
16
17
|
loadTokenSession,
|
|
17
18
|
maskToken,
|
|
19
|
+
normalizeTokenSession,
|
|
18
20
|
saveTokenSession,
|
|
19
21
|
} = require('./token-store');
|
|
20
22
|
|
|
@@ -35,6 +37,7 @@ function appendPath(baseUrl, path) {
|
|
|
35
37
|
|
|
36
38
|
function getArgEndpoint(options = {}) {
|
|
37
39
|
return options.endpoint ||
|
|
40
|
+
(options.env && options.env.OPENYIDA_ENDPOINT) ||
|
|
38
41
|
null;
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -49,7 +52,8 @@ function resolveTokenBaseUrl(options = {}) {
|
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
const { config } = getCurrentEnvConfig(options.projectRoot);
|
|
52
|
-
const
|
|
55
|
+
const env = options.env || process.env;
|
|
56
|
+
const baseUrl = normalizeBaseUrl(env.OPENYIDA_ENDPOINT || config.baseUrl, null);
|
|
53
57
|
if (!baseUrl) {
|
|
54
58
|
throw new Error('missing base_url: pass --endpoint or set OPENYIDA_ENDPOINT');
|
|
55
59
|
}
|
|
@@ -299,18 +303,29 @@ async function tokenLogin(options = {}) {
|
|
|
299
303
|
function tokenStatus(options = {}) {
|
|
300
304
|
const session = loadTokenSession(options);
|
|
301
305
|
if (!session || (!session.access_token && !session.refresh_token)) {
|
|
302
|
-
|
|
306
|
+
const env = options.env || process.env;
|
|
307
|
+
const hostInjected = isHostInjectedTokenMode(env);
|
|
308
|
+
const status = {
|
|
303
309
|
ok: false,
|
|
304
310
|
auth_mode: 'token',
|
|
311
|
+
auth_source: hostInjected ? 'env' : undefined,
|
|
305
312
|
status: 'not_logged_in',
|
|
306
313
|
can_auto_use: false,
|
|
307
|
-
|
|
314
|
+
failure_reason: hostInjected ? 'env_token_missing' : undefined,
|
|
315
|
+
message: hostInjected
|
|
316
|
+
? 'host-injected token is missing. Ask the host to inject OPENYIDA_ACCESS_TOKEN or OPENYIDA_REFRESH_TOKEN.'
|
|
317
|
+
: undefined,
|
|
308
318
|
};
|
|
319
|
+
if (!hostInjected) {
|
|
320
|
+
status.token_file = require('./token-store').getTokenFilePath(options);
|
|
321
|
+
}
|
|
322
|
+
return status;
|
|
309
323
|
}
|
|
310
324
|
if (!session.access_token && session.refresh_token) {
|
|
311
325
|
return {
|
|
312
326
|
ok: true,
|
|
313
327
|
auth_mode: 'token',
|
|
328
|
+
auth_source: session.auth_source,
|
|
314
329
|
status: 'refresh_required',
|
|
315
330
|
can_auto_use: true,
|
|
316
331
|
refresh_token: maskToken(session.refresh_token),
|
|
@@ -324,6 +339,7 @@ function tokenStatus(options = {}) {
|
|
|
324
339
|
// whether this access token must be refreshed.
|
|
325
340
|
ok: true,
|
|
326
341
|
auth_mode: 'token',
|
|
342
|
+
auth_source: session.auth_source,
|
|
327
343
|
status: 'ok',
|
|
328
344
|
can_auto_use: true,
|
|
329
345
|
token_type: session.token_type,
|
|
@@ -343,6 +359,7 @@ async function tokenRefresh(options = {}) {
|
|
|
343
359
|
return {
|
|
344
360
|
ok: false,
|
|
345
361
|
auth_mode: 'token',
|
|
362
|
+
auth_source: session && session.auth_source,
|
|
346
363
|
status: 'missing_refresh_token',
|
|
347
364
|
can_auto_use: false,
|
|
348
365
|
};
|
|
@@ -380,6 +397,20 @@ async function tokenRefresh(options = {}) {
|
|
|
380
397
|
message: normalized.message || 'auth service did not return access_token',
|
|
381
398
|
};
|
|
382
399
|
}
|
|
400
|
+
const env = options.env || process.env;
|
|
401
|
+
if (isHostInjectedTokenMode(env) && session.auth_source === 'env') {
|
|
402
|
+
env.OPENYIDA_ACCESS_TOKEN = normalized.access_token;
|
|
403
|
+
if (normalized.refresh_token) {
|
|
404
|
+
env.OPENYIDA_REFRESH_TOKEN = normalized.refresh_token;
|
|
405
|
+
}
|
|
406
|
+
if (normalized.expires_at) {
|
|
407
|
+
env.OPENYIDA_ACCESS_TOKEN_EXPIRES_AT = String(normalized.expires_at);
|
|
408
|
+
}
|
|
409
|
+
return normalizeTokenSession({
|
|
410
|
+
...normalized,
|
|
411
|
+
auth_source: 'env',
|
|
412
|
+
});
|
|
413
|
+
}
|
|
383
414
|
return saveTokenSession(normalized, options);
|
|
384
415
|
}
|
|
385
416
|
|
package/lib/auth/token-store.js
CHANGED
|
@@ -119,17 +119,27 @@ function saveTokenSession(session, options = {}) {
|
|
|
119
119
|
return normalized;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
function isHostInjectedTokenMode(env = process.env) {
|
|
123
|
+
const authEnabled = String(env.YIDA_AUTH_ENABLED || '').trim().toLowerCase();
|
|
124
|
+
return ['1', 'true', 'yes', 'on'].includes(authEnabled);
|
|
125
|
+
}
|
|
126
|
+
|
|
122
127
|
function loadEnvTokenSession(env = process.env) {
|
|
128
|
+
const accessToken = String(env.OPENYIDA_ACCESS_TOKEN || '').trim();
|
|
123
129
|
const refreshToken = String(env.OPENYIDA_REFRESH_TOKEN || '').trim();
|
|
124
|
-
if (!refreshToken) {
|
|
130
|
+
if (!accessToken && !refreshToken) {
|
|
125
131
|
return null;
|
|
126
132
|
}
|
|
127
133
|
return normalizeTokenSession({
|
|
134
|
+
access_token: accessToken,
|
|
128
135
|
refresh_token: refreshToken,
|
|
129
136
|
base_url: env.OPENYIDA_ENDPOINT,
|
|
130
137
|
client_id: env.OPENYIDA_TOKEN_CLIENT_ID,
|
|
131
138
|
corp_id: env.OPENYIDA_TOKEN_CORP_ID,
|
|
132
139
|
user_id: env.OPENYIDA_TOKEN_USER_ID,
|
|
140
|
+
token_type: env.OPENYIDA_TOKEN_TYPE,
|
|
141
|
+
expires_at: env.OPENYIDA_ACCESS_TOKEN_EXPIRES_AT,
|
|
142
|
+
expires_in: env.OPENYIDA_ACCESS_TOKEN_EXPIRES_IN,
|
|
133
143
|
auth_source: 'env',
|
|
134
144
|
});
|
|
135
145
|
}
|
|
@@ -147,24 +157,31 @@ function loadLocalTokenSession(options = {}) {
|
|
|
147
157
|
}
|
|
148
158
|
|
|
149
159
|
function loadTokenSession(options = {}) {
|
|
160
|
+
const env = options.env || process.env;
|
|
161
|
+
const envSession = loadEnvTokenSession(env);
|
|
162
|
+
if (isHostInjectedTokenMode(env)) {
|
|
163
|
+
return envSession;
|
|
164
|
+
}
|
|
165
|
+
|
|
150
166
|
const localSession = loadLocalTokenSession(options);
|
|
151
|
-
const envSession = loadEnvTokenSession(options.env || process.env);
|
|
152
167
|
if (!localSession && !envSession) {
|
|
153
168
|
return null;
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
const localAccessToken = localSession && localSession.access_token;
|
|
157
172
|
const localRefreshToken = localSession && localSession.refresh_token;
|
|
173
|
+
const envAccessToken = envSession && envSession.access_token;
|
|
158
174
|
const envRefreshToken = envSession && envSession.refresh_token;
|
|
159
175
|
const usesLocal = Boolean(localAccessToken || localRefreshToken);
|
|
160
176
|
const usesEnv = Boolean(
|
|
161
|
-
!localRefreshToken &&
|
|
177
|
+
(!localAccessToken && !localRefreshToken && envAccessToken) ||
|
|
178
|
+
(!localRefreshToken && envRefreshToken)
|
|
162
179
|
);
|
|
163
180
|
|
|
164
181
|
return normalizeTokenSession({
|
|
165
182
|
...(envSession || {}),
|
|
166
183
|
...(localSession || {}),
|
|
167
|
-
access_token: localAccessToken,
|
|
184
|
+
access_token: localAccessToken || (!localSession && envAccessToken),
|
|
168
185
|
refresh_token: localRefreshToken || envRefreshToken,
|
|
169
186
|
auth_source: usesLocal && usesEnv ? 'mixed' : (usesLocal ? 'local' : 'env'),
|
|
170
187
|
});
|
|
@@ -210,6 +227,7 @@ module.exports = {
|
|
|
210
227
|
normalizeTokenSession,
|
|
211
228
|
loadEnvTokenSession,
|
|
212
229
|
loadLocalTokenSession,
|
|
230
|
+
isHostInjectedTokenMode,
|
|
213
231
|
isAccessTokenUsable,
|
|
214
232
|
maskToken,
|
|
215
233
|
resolveEnvName,
|