openyida 2026.9.22-beta.1 → 2026.9.22-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/auth/dws-sync.js +21 -9
- package/lib/core/agent-capabilities.js +3 -0
- package/lib/core/copy.js +1 -1
- package/lib/core/utils.js +19 -2
- package/package.json +1 -1
package/lib/auth/dws-sync.js
CHANGED
|
@@ -10,12 +10,19 @@ const FAILURE_STATUSES = new Set([
|
|
|
10
10
|
'dws_identity_unmapped', 'dws_upstream_unavailable', 'dws_policy_rejected', 'dws_session_expired',
|
|
11
11
|
]);
|
|
12
12
|
|
|
13
|
-
function failure(reason) {
|
|
13
|
+
function failure(reason, extra = {}) {
|
|
14
14
|
return new CliError(`DWS sync failed: ${reason}`, {
|
|
15
|
-
code: 'DWS_SYNC_FAILED', details: { reason },
|
|
15
|
+
code: 'DWS_SYNC_FAILED', details: { reason, ...extra },
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function rateLimitFailure(response, headers = {}) {
|
|
20
|
+
const data = response && (response.data || response.content || response);
|
|
21
|
+
if (!data || !['dws_rate_limited', 'dws_rate_limit_unavailable'].includes(data.status)) { return null; }
|
|
22
|
+
const delay = Number(data.retry_after ?? headers['retry-after']);
|
|
23
|
+
return failure(data.status, { retry_after: Number.isInteger(delay) && delay > 0 && delay <= 60 ? delay : 1 });
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
function readHandoff(input = process.stdin, timeoutMs = 10000) {
|
|
20
27
|
return new Promise((resolve, reject) => {
|
|
21
28
|
if (input.isTTY) { reject(failure('STDIN_REQUIRED')); return; }
|
|
@@ -46,7 +53,7 @@ function readHandoff(input = process.stdin, timeoutMs = 10000) {
|
|
|
46
53
|
|| Object.keys(data).some((key) => !['accessToken', 'corpId', 'environment'].includes(key))
|
|
47
54
|
|| typeof data.accessToken !== 'string' || !data.accessToken || data.accessToken.length > 8192
|
|
48
55
|
|| (/\s/.test(data.accessToken) || [...data.accessToken].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127))
|
|
49
|
-
|| typeof data.corpId !== 'string' || !data.corpId || data.corpId.length > 256
|
|
56
|
+
|| (data.corpId !== undefined && (typeof data.corpId !== 'string' || !data.corpId.trim() || data.corpId.length > 256))
|
|
50
57
|
|| !['pre', 'prod'].includes(data.environment)) {
|
|
51
58
|
finish(failure('INVALID_HANDOFF')); return;
|
|
52
59
|
}
|
|
@@ -73,17 +80,20 @@ async function syncDws(options = {}) {
|
|
|
73
80
|
if (target.protocol !== 'https:' || target.username || target.password || target.search || target.hash
|
|
74
81
|
|| target.pathname !== '/') { throw failure('TRUSTED_HTTPS_ORIGIN_REQUIRED'); }
|
|
75
82
|
const handoff = await readHandoff(options.input || process.stdin, options.stdinTimeoutMs);
|
|
76
|
-
if (options.corpId && options.corpId !== handoff.corpId) { throw failure('ORG_CONFLICT'); }
|
|
83
|
+
if (options.corpId && handoff.corpId && options.corpId !== handoff.corpId) { throw failure('ORG_CONFLICT'); }
|
|
84
|
+
const expectedCorp = options.corpId || handoff.corpId;
|
|
77
85
|
let response;
|
|
78
86
|
try {
|
|
79
87
|
response = await requestJson('POST', `${target.origin}/openapi/cli/v1/auth/dws/token`, {
|
|
80
|
-
corpId:
|
|
88
|
+
...(expectedCorp ? { corpId: expectedCorp } : {}), environment: handoff.environment,
|
|
81
89
|
}, { Authorization: `Bearer ${handoff.accessToken}` }, { timeoutMs: 30000, maxResponseBytes: 65536 });
|
|
82
|
-
} catch {
|
|
83
|
-
throw failure('EXCHANGE_REQUEST_FAILED');
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw rateLimitFailure(error && error.payload, (error && error.headers) || {}) || failure('EXCHANGE_REQUEST_FAILED');
|
|
84
92
|
} finally {
|
|
85
93
|
delete handoff.accessToken;
|
|
86
94
|
}
|
|
95
|
+
const limited = rateLimitFailure(response);
|
|
96
|
+
if (limited) { throw limited; }
|
|
87
97
|
const data = response && (response.data || response.content || response);
|
|
88
98
|
if (!data || response.success === false || response.ok === false || data.status !== 'ok') {
|
|
89
99
|
throw failure(data && FAILURE_STATUSES.has(data.status) ? data.status : 'EXCHANGE_REJECTED');
|
|
@@ -92,7 +102,9 @@ async function syncDws(options = {}) {
|
|
|
92
102
|
const now = Date.now() / 1000;
|
|
93
103
|
if (typeof data.access_token !== 'string' || !data.access_token
|
|
94
104
|
|| typeof data.refresh_token !== 'string' || !data.refresh_token
|
|
95
|
-
|| data.client_id !== DINGTALK_OAUTH_CLIENT_ID
|
|
105
|
+
|| data.client_id !== DINGTALK_OAUTH_CLIENT_ID
|
|
106
|
+
|| typeof data.corp_id !== 'string' || !data.corp_id.trim()
|
|
107
|
+
|| (expectedCorp && data.corp_id !== expectedCorp)
|
|
96
108
|
|| typeof data.user_id !== 'string' || !data.user_id
|
|
97
109
|
|| data.credential_source !== 'dws' || data.environment !== handoff.environment
|
|
98
110
|
|| typeof data.session_id !== 'string' || !/^[a-f0-9-]{36}$/.test(data.session_id)
|
|
@@ -129,7 +141,7 @@ async function syncDws(options = {}) {
|
|
|
129
141
|
|
|
130
142
|
async function run(args, options = {}) {
|
|
131
143
|
if (args.length === 2 && args.includes('--capabilities') && args.includes('--json')) {
|
|
132
|
-
const capabilities = { protocol_version: 1, private_stdin: true, independent_profile: true };
|
|
144
|
+
const capabilities = { protocol_version: 1, private_stdin: true, independent_profile: true, server_identity: true };
|
|
133
145
|
console.log(JSON.stringify(capabilities));
|
|
134
146
|
return capabilities;
|
|
135
147
|
}
|
|
@@ -96,7 +96,9 @@ function compactSkillsDiagnostics(skillsDiagnostics) {
|
|
|
96
96
|
reason: candidate.reason,
|
|
97
97
|
exists: candidate.exists,
|
|
98
98
|
writable: candidate.writable,
|
|
99
|
+
readable: candidate.readable,
|
|
99
100
|
usable: candidate.usable,
|
|
101
|
+
installable: candidate.installable,
|
|
100
102
|
workspace_only: candidate.workspace_only,
|
|
101
103
|
}
|
|
102
104
|
: null;
|
|
@@ -105,6 +107,7 @@ function compactSkillsDiagnostics(skillsDiagnostics) {
|
|
|
105
107
|
schema_version: skillsDiagnostics.schema_version,
|
|
106
108
|
active_tool: skillsDiagnostics.active_tool,
|
|
107
109
|
selected: compactCandidate(skillsDiagnostics.selected),
|
|
110
|
+
install_target: compactCandidate(skillsDiagnostics.install_target),
|
|
108
111
|
candidates: (skillsDiagnostics.candidates || []).map(compactCandidate),
|
|
109
112
|
diagnostics: skillsDiagnostics.diagnostics || {},
|
|
110
113
|
};
|
package/lib/core/copy.js
CHANGED
|
@@ -390,7 +390,7 @@ function run(args = process.argv.slice(3)) {
|
|
|
390
390
|
// 若宿主用户级目录不可写,按 capabilities 诊断降级到工作区可发现目录。
|
|
391
391
|
const activeResult = envResults.find((r) => r.isActive);
|
|
392
392
|
const skillsDiagnostics = buildSkillsDiagnostics({ ...runtimeOptions, projectResolution });
|
|
393
|
-
const selectedSkills = skillsDiagnostics.
|
|
393
|
+
const selectedSkills = skillsDiagnostics.install_target;
|
|
394
394
|
|
|
395
395
|
if (selectedSkills && selectedSkills.path) {
|
|
396
396
|
assertCopyDestinationSafe(packageYidaSkillsDir, selectedSkills.path);
|
package/lib/core/utils.js
CHANGED
|
@@ -670,6 +670,14 @@ function buildSkillsCandidate(destPath, source, scope, reason) {
|
|
|
670
670
|
const parentDir = path.dirname(destPath);
|
|
671
671
|
const nearestWritableBase = nearestExistingAncestor(parentDir);
|
|
672
672
|
const writable = canWritePath(destPath);
|
|
673
|
+
const entryPath = path.join(destPath, 'SKILL.md');
|
|
674
|
+
let readable = false;
|
|
675
|
+
try {
|
|
676
|
+
readable = fs.statSync(entryPath).isFile();
|
|
677
|
+
fs.accessSync(entryPath, fs.constants.R_OK);
|
|
678
|
+
} catch {
|
|
679
|
+
readable = false;
|
|
680
|
+
}
|
|
673
681
|
return {
|
|
674
682
|
path: destPath,
|
|
675
683
|
source,
|
|
@@ -680,7 +688,9 @@ function buildSkillsCandidate(destPath, source, scope, reason) {
|
|
|
680
688
|
parent_exists: fs.existsSync(parentDir),
|
|
681
689
|
nearest_existing_parent: nearestWritableBase,
|
|
682
690
|
writable,
|
|
683
|
-
|
|
691
|
+
readable,
|
|
692
|
+
usable: readable,
|
|
693
|
+
installable: scope !== 'package' && writable,
|
|
684
694
|
workspace_only: scope === 'workspace',
|
|
685
695
|
};
|
|
686
696
|
}
|
|
@@ -787,7 +797,13 @@ function buildSkillsDiagnostics(options = {}) {
|
|
|
787
797
|
);
|
|
788
798
|
}
|
|
789
799
|
|
|
790
|
-
|
|
800
|
+
// Bundled documentation is a read fallback, never an installation destination.
|
|
801
|
+
addCandidate(
|
|
802
|
+
options.bundledSkillsPath || path.resolve(__dirname, '../../yida-skills'),
|
|
803
|
+
'package:yida-skills', 'package', 'bundled_skills_read_fallback'
|
|
804
|
+
);
|
|
805
|
+
const selected = candidates.find((candidate) => candidate.usable) || null;
|
|
806
|
+
const installTarget = candidates.find((candidate) => candidate.installable) || null;
|
|
791
807
|
const diagnostics = {
|
|
792
808
|
config_content_error: configSkillPaths.error,
|
|
793
809
|
fallback: selected && selected.workspace_only
|
|
@@ -808,6 +824,7 @@ function buildSkillsDiagnostics(options = {}) {
|
|
|
808
824
|
}
|
|
809
825
|
: null,
|
|
810
826
|
selected,
|
|
827
|
+
install_target: installTarget,
|
|
811
828
|
candidates,
|
|
812
829
|
diagnostics,
|
|
813
830
|
};
|