dsh-d365-kit 0.34.1 → 0.34.4
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/CHANGELOG.md +319 -308
- package/CHANGELOG.zh-CN.md +318 -307
- package/INSTALL.md +94 -94
- package/INSTALL.zh-CN.md +93 -93
- package/LICENSE +21 -21
- package/README.md +336 -327
- package/README.zh-CN.md +336 -327
- package/client.js +1818 -1818
- package/cordis.patch.yml +44 -44
- package/docs/en/architecture.md +172 -172
- package/docs/en/checks.md +184 -184
- package/docs/en/installation.md +151 -151
- package/docs/en/permissions.md +161 -161
- package/docs/en/scenarios.md +202 -202
- package/docs/en/tools.md +665 -665
- package/docs/en/troubleshooting.md +131 -131
- package/docs/zh/architecture.md +172 -172
- package/docs/zh/checks.md +184 -184
- package/docs/zh/installation.md +151 -151
- package/docs/zh/permissions.md +161 -161
- package/docs/zh/scenarios.md +202 -202
- package/docs/zh/tools.md +665 -665
- package/docs/zh/troubleshooting.md +131 -131
- package/examples/prompts.en.md +206 -206
- package/examples/prompts.zh-CN.md +206 -206
- package/examples/waivers.example.json +20 -20
- package/icon.svg +6 -6
- package/index.js +139 -139
- package/lib/auth.js +181 -181
- package/lib/client.js +496 -496
- package/lib/code-check.js +549 -549
- package/lib/config.js +323 -323
- package/lib/contract-history.js +49 -49
- package/lib/contract.js +583 -583
- package/lib/csharp-check.js +493 -493
- package/lib/data-migrate.js +1318 -1318
- package/lib/data-policy.js +78 -78
- package/lib/error-codes.js +80 -80
- package/lib/gitlab.js +161 -161
- package/lib/health.js +205 -205
- package/lib/migrate.js +732 -732
- package/lib/preflight.js +116 -116
- package/lib/refs.js +191 -191
- package/lib/resources.js +320 -320
- package/lib/retention-defaults.js +17 -17
- package/lib/retention.js +384 -384
- package/lib/runtime.js +232 -232
- package/lib/settings-http.js +512 -512
- package/lib/settings-store.js +371 -371
- package/lib/snapshot.js +149 -149
- package/lib/tool-code-check.js +133 -133
- package/lib/tool-configure.js +169 -169
- package/lib/tool-contract.js +376 -376
- package/lib/tool-data-sync.js +559 -559
- package/lib/tool-data-write.js +203 -203
- package/lib/tool-data.js +282 -282
- package/lib/tool-diff.js +484 -484
- package/lib/tool-environments.js +106 -106
- package/lib/tool-flows.js +540 -540
- package/lib/tool-health.js +127 -127
- package/lib/tool-layers.js +497 -497
- package/lib/tool-metadata.js +417 -417
- package/lib/tool-migrate.js +102 -102
- package/lib/tool-refs.js +206 -206
- package/lib/tool-reports.js +299 -299
- package/lib/tool-sync.js +430 -430
- package/lib/tool-write.js +563 -563
- package/lib/waivers.js +83 -83
- package/lib/write-actions.js +1374 -1374
- package/lib/write-policy.js +199 -199
- package/lib/write-store.js +167 -167
- package/locale/en.json +68 -68
- package/locale/zh.json +68 -68
- package/package.json +2 -2
- package/skills/d365-troubleshooting/SKILL.md +115 -115
package/lib/write-store.js
CHANGED
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pending write plans and the change ledger.
|
|
3
|
-
*
|
|
4
|
-
* A plan is inert data. It becomes a change only when a person presses the
|
|
5
|
-
* confirm button in the settings page, which then calls the Host's audited
|
|
6
|
-
* execute route. The agent can create plans and read them; it cannot run one.
|
|
7
|
-
*/
|
|
8
|
-
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
9
|
-
import { join } from 'node:path'
|
|
10
|
-
import { dataRoot } from './snapshot.js'
|
|
11
|
-
|
|
12
|
-
const PLAN_ID = /^[0-9A-Za-z-]{6,64}$/
|
|
13
|
-
|
|
14
|
-
export function plansDir(config) {
|
|
15
|
-
return join(dataRoot(config), 'pending-writes')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function changesDir(config) {
|
|
19
|
-
return join(dataRoot(config), 'changes')
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function planPath(config, id) {
|
|
23
|
-
if (!PLAN_ID.test(String(id ?? ''))) return undefined
|
|
24
|
-
return join(plansDir(config), `${id}.json`)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Persist a plan and return it with its id. */
|
|
28
|
-
export async function createPlan(config, plan) {
|
|
29
|
-
const id = plan.id ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
|
30
|
-
const record = { ...plan, id, createdAt: new Date().toISOString(), status: 'pending' }
|
|
31
|
-
const dir = plansDir(config)
|
|
32
|
-
await mkdir(dir, { recursive: true })
|
|
33
|
-
await writeFile(join(dir, `${id}.json`), `${JSON.stringify(record, null, 2)}\n`, 'utf8')
|
|
34
|
-
return record
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function readPlan(config, id) {
|
|
38
|
-
const path = planPath(config, id)
|
|
39
|
-
if (!path) return undefined
|
|
40
|
-
try {
|
|
41
|
-
return JSON.parse(await readFile(path, 'utf8'))
|
|
42
|
-
} catch {
|
|
43
|
-
return undefined
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function deletePlan(config, id) {
|
|
48
|
-
const path = planPath(config, id)
|
|
49
|
-
if (!path) return false
|
|
50
|
-
try {
|
|
51
|
-
await rm(path)
|
|
52
|
-
return true
|
|
53
|
-
} catch {
|
|
54
|
-
return false
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Move a plan through its lifecycle: pending → running → done | failed.
|
|
60
|
-
*
|
|
61
|
-
* The file is the only guard against executing a plan twice. Marking it
|
|
62
|
-
* "running" before the first write means a crash mid-plan leaves a record that
|
|
63
|
-
* refuses a silent second run, instead of replaying the operations that already
|
|
64
|
-
* landed.
|
|
65
|
-
*/
|
|
66
|
-
export async function setPlanStatus(config, id, status, extra = {}) {
|
|
67
|
-
const plan = await readPlan(config, id)
|
|
68
|
-
if (!plan) return undefined
|
|
69
|
-
const path = planPath(config, id)
|
|
70
|
-
if (!path) return undefined
|
|
71
|
-
const next = { ...plan, ...extra, status, updatedAt: new Date().toISOString() }
|
|
72
|
-
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
|
|
73
|
-
return next
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Every still-readable plan, newest first. */
|
|
77
|
-
export async function listPlans(config) {
|
|
78
|
-
const dir = plansDir(config)
|
|
79
|
-
let names = []
|
|
80
|
-
try {
|
|
81
|
-
names = await readdir(dir)
|
|
82
|
-
} catch {
|
|
83
|
-
return []
|
|
84
|
-
}
|
|
85
|
-
const plans = []
|
|
86
|
-
for (const name of names.filter((name) => name.endsWith('.json'))) {
|
|
87
|
-
const plan = await readPlan(config, name.replace(/\.json$/, ''))
|
|
88
|
-
if (plan) plans.push(plan)
|
|
89
|
-
}
|
|
90
|
-
return plans.sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)))
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Append one executed change to the ledger. This file is the only record of
|
|
95
|
-
* "what did the agent change and when", so it is written before the HTTP calls
|
|
96
|
-
* settle and never rewritten.
|
|
97
|
-
*/
|
|
98
|
-
export async function recordChange(config, record) {
|
|
99
|
-
const dir = changesDir(config)
|
|
100
|
-
await mkdir(dir, { recursive: true })
|
|
101
|
-
const stamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
102
|
-
const name = `${stamp}-${record.planId ?? 'plan'}.json`
|
|
103
|
-
const path = join(dir, name)
|
|
104
|
-
await writeFile(path, `${JSON.stringify({ ...record, recordedAt: new Date().toISOString() }, null, 2)}\n`, 'utf8')
|
|
105
|
-
return path
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** Recent ledger entries, newest first. */
|
|
109
|
-
export async function listChanges(config, limit = 20) {
|
|
110
|
-
const dir = changesDir(config)
|
|
111
|
-
let names = []
|
|
112
|
-
try {
|
|
113
|
-
names = await readdir(dir)
|
|
114
|
-
} catch {
|
|
115
|
-
return []
|
|
116
|
-
}
|
|
117
|
-
const files = names.filter((name) => name.endsWith('.json')).sort().reverse().slice(0, limit)
|
|
118
|
-
const entries = []
|
|
119
|
-
for (const name of files) {
|
|
120
|
-
try {
|
|
121
|
-
entries.push(JSON.parse(await readFile(join(dir, name), 'utf8')))
|
|
122
|
-
} catch {
|
|
123
|
-
// A half-written ledger entry is skipped rather than failing the page.
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return entries
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Record how one publish attempt ended. One file per plan: a re-publish
|
|
131
|
-
* overwrites the previous outcome, which is what "current state" means here.
|
|
132
|
-
*/
|
|
133
|
-
export async function writePublishReport(config, planId, record) {
|
|
134
|
-
const dir = changesDir(config)
|
|
135
|
-
await mkdir(dir, { recursive: true })
|
|
136
|
-
const path = join(dir, `publish-${planId}.json`)
|
|
137
|
-
await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, 'utf8')
|
|
138
|
-
return path
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* How the publish for each executed plan ended.
|
|
143
|
-
*
|
|
144
|
-
* The publish runs detached, so the ledger can only say "running"; the sidecar
|
|
145
|
-
* written when the request settles is the only place that knows the outcome.
|
|
146
|
-
* Keyed by planId so the plans panel can show it next to the change.
|
|
147
|
-
*/
|
|
148
|
-
export async function listPublishReports(config) {
|
|
149
|
-
const dir = changesDir(config)
|
|
150
|
-
let names = []
|
|
151
|
-
try {
|
|
152
|
-
names = await readdir(dir)
|
|
153
|
-
} catch {
|
|
154
|
-
return {}
|
|
155
|
-
}
|
|
156
|
-
const reports = {}
|
|
157
|
-
for (const name of names.filter((name) => name.startsWith('publish-') && name.endsWith('.json'))) {
|
|
158
|
-
try {
|
|
159
|
-
const record = JSON.parse(await readFile(join(dir, name), 'utf8'))
|
|
160
|
-
if (record?.planId) reports[record.planId] = record
|
|
161
|
-
} catch {
|
|
162
|
-
// Skip an unreadable sidecar; the UI falls back to the ledger state.
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return reports
|
|
166
|
-
}
|
|
167
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Pending write plans and the change ledger.
|
|
3
|
+
*
|
|
4
|
+
* A plan is inert data. It becomes a change only when a person presses the
|
|
5
|
+
* confirm button in the settings page, which then calls the Host's audited
|
|
6
|
+
* execute route. The agent can create plans and read them; it cannot run one.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { dataRoot } from './snapshot.js'
|
|
11
|
+
|
|
12
|
+
const PLAN_ID = /^[0-9A-Za-z-]{6,64}$/
|
|
13
|
+
|
|
14
|
+
export function plansDir(config) {
|
|
15
|
+
return join(dataRoot(config), 'pending-writes')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function changesDir(config) {
|
|
19
|
+
return join(dataRoot(config), 'changes')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function planPath(config, id) {
|
|
23
|
+
if (!PLAN_ID.test(String(id ?? ''))) return undefined
|
|
24
|
+
return join(plansDir(config), `${id}.json`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Persist a plan and return it with its id. */
|
|
28
|
+
export async function createPlan(config, plan) {
|
|
29
|
+
const id = plan.id ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
|
30
|
+
const record = { ...plan, id, createdAt: new Date().toISOString(), status: 'pending' }
|
|
31
|
+
const dir = plansDir(config)
|
|
32
|
+
await mkdir(dir, { recursive: true })
|
|
33
|
+
await writeFile(join(dir, `${id}.json`), `${JSON.stringify(record, null, 2)}\n`, 'utf8')
|
|
34
|
+
return record
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function readPlan(config, id) {
|
|
38
|
+
const path = planPath(config, id)
|
|
39
|
+
if (!path) return undefined
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(await readFile(path, 'utf8'))
|
|
42
|
+
} catch {
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function deletePlan(config, id) {
|
|
48
|
+
const path = planPath(config, id)
|
|
49
|
+
if (!path) return false
|
|
50
|
+
try {
|
|
51
|
+
await rm(path)
|
|
52
|
+
return true
|
|
53
|
+
} catch {
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Move a plan through its lifecycle: pending → running → done | failed.
|
|
60
|
+
*
|
|
61
|
+
* The file is the only guard against executing a plan twice. Marking it
|
|
62
|
+
* "running" before the first write means a crash mid-plan leaves a record that
|
|
63
|
+
* refuses a silent second run, instead of replaying the operations that already
|
|
64
|
+
* landed.
|
|
65
|
+
*/
|
|
66
|
+
export async function setPlanStatus(config, id, status, extra = {}) {
|
|
67
|
+
const plan = await readPlan(config, id)
|
|
68
|
+
if (!plan) return undefined
|
|
69
|
+
const path = planPath(config, id)
|
|
70
|
+
if (!path) return undefined
|
|
71
|
+
const next = { ...plan, ...extra, status, updatedAt: new Date().toISOString() }
|
|
72
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
|
|
73
|
+
return next
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Every still-readable plan, newest first. */
|
|
77
|
+
export async function listPlans(config) {
|
|
78
|
+
const dir = plansDir(config)
|
|
79
|
+
let names = []
|
|
80
|
+
try {
|
|
81
|
+
names = await readdir(dir)
|
|
82
|
+
} catch {
|
|
83
|
+
return []
|
|
84
|
+
}
|
|
85
|
+
const plans = []
|
|
86
|
+
for (const name of names.filter((name) => name.endsWith('.json'))) {
|
|
87
|
+
const plan = await readPlan(config, name.replace(/\.json$/, ''))
|
|
88
|
+
if (plan) plans.push(plan)
|
|
89
|
+
}
|
|
90
|
+
return plans.sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Append one executed change to the ledger. This file is the only record of
|
|
95
|
+
* "what did the agent change and when", so it is written before the HTTP calls
|
|
96
|
+
* settle and never rewritten.
|
|
97
|
+
*/
|
|
98
|
+
export async function recordChange(config, record) {
|
|
99
|
+
const dir = changesDir(config)
|
|
100
|
+
await mkdir(dir, { recursive: true })
|
|
101
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
102
|
+
const name = `${stamp}-${record.planId ?? 'plan'}.json`
|
|
103
|
+
const path = join(dir, name)
|
|
104
|
+
await writeFile(path, `${JSON.stringify({ ...record, recordedAt: new Date().toISOString() }, null, 2)}\n`, 'utf8')
|
|
105
|
+
return path
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Recent ledger entries, newest first. */
|
|
109
|
+
export async function listChanges(config, limit = 20) {
|
|
110
|
+
const dir = changesDir(config)
|
|
111
|
+
let names = []
|
|
112
|
+
try {
|
|
113
|
+
names = await readdir(dir)
|
|
114
|
+
} catch {
|
|
115
|
+
return []
|
|
116
|
+
}
|
|
117
|
+
const files = names.filter((name) => name.endsWith('.json')).sort().reverse().slice(0, limit)
|
|
118
|
+
const entries = []
|
|
119
|
+
for (const name of files) {
|
|
120
|
+
try {
|
|
121
|
+
entries.push(JSON.parse(await readFile(join(dir, name), 'utf8')))
|
|
122
|
+
} catch {
|
|
123
|
+
// A half-written ledger entry is skipped rather than failing the page.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return entries
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Record how one publish attempt ended. One file per plan: a re-publish
|
|
131
|
+
* overwrites the previous outcome, which is what "current state" means here.
|
|
132
|
+
*/
|
|
133
|
+
export async function writePublishReport(config, planId, record) {
|
|
134
|
+
const dir = changesDir(config)
|
|
135
|
+
await mkdir(dir, { recursive: true })
|
|
136
|
+
const path = join(dir, `publish-${planId}.json`)
|
|
137
|
+
await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, 'utf8')
|
|
138
|
+
return path
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* How the publish for each executed plan ended.
|
|
143
|
+
*
|
|
144
|
+
* The publish runs detached, so the ledger can only say "running"; the sidecar
|
|
145
|
+
* written when the request settles is the only place that knows the outcome.
|
|
146
|
+
* Keyed by planId so the plans panel can show it next to the change.
|
|
147
|
+
*/
|
|
148
|
+
export async function listPublishReports(config) {
|
|
149
|
+
const dir = changesDir(config)
|
|
150
|
+
let names = []
|
|
151
|
+
try {
|
|
152
|
+
names = await readdir(dir)
|
|
153
|
+
} catch {
|
|
154
|
+
return {}
|
|
155
|
+
}
|
|
156
|
+
const reports = {}
|
|
157
|
+
for (const name of names.filter((name) => name.startsWith('publish-') && name.endsWith('.json'))) {
|
|
158
|
+
try {
|
|
159
|
+
const record = JSON.parse(await readFile(join(dir, name), 'utf8'))
|
|
160
|
+
if (record?.planId) reports[record.planId] = record
|
|
161
|
+
} catch {
|
|
162
|
+
// Skip an unreadable sidecar; the UI falls back to the ledger state.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return reports
|
|
166
|
+
}
|
|
167
|
+
|
package/locale/en.json
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"title": "D365 Toolkit",
|
|
4
|
-
"description": "Developer and deployment toolkit for Dynamics 365 CE: metadata, layers, migration risk, code checks, contract drift and environment health."
|
|
5
|
-
},
|
|
6
|
-
"tools": {
|
|
7
|
-
"d365_envs": {
|
|
8
|
-
"title": "Environments",
|
|
9
|
-
"description": "List configured environments, credential status and connectivity"
|
|
10
|
-
},
|
|
11
|
-
"d365_metadata": {
|
|
12
|
-
"title": "Metadata",
|
|
13
|
-
"description": "Query entities, attributes, forms, views, plugins and solutions by resource"
|
|
14
|
-
},
|
|
15
|
-
"d365_layers": {
|
|
16
|
-
"title": "Solution layers",
|
|
17
|
-
"description": "See which solutions cover a component and which layer is active"
|
|
18
|
-
},
|
|
19
|
-
"d365_sync": {
|
|
20
|
-
"title": "Sync snapshot",
|
|
21
|
-
"description": "Pull environment metadata into a local snapshot and record completeness"
|
|
22
|
-
},
|
|
23
|
-
"d365_diff": {
|
|
24
|
-
"title": "Diff snapshots",
|
|
25
|
-
"description": "Compare two snapshots: additions, deletions and field-level edits"
|
|
26
|
-
},
|
|
27
|
-
"d365_configure": {
|
|
28
|
-
"title": "Configure environments",
|
|
29
|
-
"description": "List, save, remove or test environment definitions"
|
|
30
|
-
},
|
|
31
|
-
"d365_write": {
|
|
32
|
-
"title": "Metadata changes",
|
|
33
|
-
"description": "Plan option values, attributes, table names and view columns, then apply after approval"
|
|
34
|
-
},
|
|
35
|
-
"d365_migrate": {
|
|
36
|
-
"title": "Migration risk",
|
|
37
|
-
"description": "Assess importing a solution elsewhere: shadowing, missing components, lost option values"
|
|
38
|
-
},
|
|
39
|
-
"d365_code_check": {
|
|
40
|
-
"title": "Code change check",
|
|
41
|
-
"description": "Read a GitLab MR and check whether script and plugin changes will run in the target environment"
|
|
42
|
-
},
|
|
43
|
-
"d365_refs": {
|
|
44
|
-
"title": "References",
|
|
45
|
-
"description": "Which forms, views and flows use a field, which forms load a script, which steps a plugin has"
|
|
46
|
-
},
|
|
47
|
-
"d365_data": {
|
|
48
|
-
"title": "Query data",
|
|
49
|
-
"description": "Count, group or sample business records (requires environment permission)"
|
|
50
|
-
},
|
|
51
|
-
"d365_data_write": {
|
|
52
|
-
"title": "Write data",
|
|
53
|
-
"description": "Create or update one business record after approval (requires environment permission)"
|
|
54
|
-
},
|
|
55
|
-
"d365_contract": {
|
|
56
|
-
"title": "Contract drift",
|
|
57
|
-
"description": "Check plugin registrations, form scripts and sitemaps across environments, with waivers"
|
|
58
|
-
},
|
|
59
|
-
"d365_health": {
|
|
60
|
-
"title": "Environment health",
|
|
61
|
-
"description": "Suspended and failed jobs, running imports and publishes, recently installed solutions"
|
|
62
|
-
},
|
|
63
|
-
"d365_reports": {
|
|
64
|
-
"title": "Local data",
|
|
65
|
-
"description": "Inspect local artifact usage, clean up by retention policy or compact snapshots"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "D365 Toolkit",
|
|
4
|
+
"description": "Developer and deployment toolkit for Dynamics 365 CE: metadata, layers, migration risk, code checks, contract drift and environment health."
|
|
5
|
+
},
|
|
6
|
+
"tools": {
|
|
7
|
+
"d365_envs": {
|
|
8
|
+
"title": "Environments",
|
|
9
|
+
"description": "List configured environments, credential status and connectivity"
|
|
10
|
+
},
|
|
11
|
+
"d365_metadata": {
|
|
12
|
+
"title": "Metadata",
|
|
13
|
+
"description": "Query entities, attributes, forms, views, plugins and solutions by resource"
|
|
14
|
+
},
|
|
15
|
+
"d365_layers": {
|
|
16
|
+
"title": "Solution layers",
|
|
17
|
+
"description": "See which solutions cover a component and which layer is active"
|
|
18
|
+
},
|
|
19
|
+
"d365_sync": {
|
|
20
|
+
"title": "Sync snapshot",
|
|
21
|
+
"description": "Pull environment metadata into a local snapshot and record completeness"
|
|
22
|
+
},
|
|
23
|
+
"d365_diff": {
|
|
24
|
+
"title": "Diff snapshots",
|
|
25
|
+
"description": "Compare two snapshots: additions, deletions and field-level edits"
|
|
26
|
+
},
|
|
27
|
+
"d365_configure": {
|
|
28
|
+
"title": "Configure environments",
|
|
29
|
+
"description": "List, save, remove or test environment definitions"
|
|
30
|
+
},
|
|
31
|
+
"d365_write": {
|
|
32
|
+
"title": "Metadata changes",
|
|
33
|
+
"description": "Plan option values, attributes, table names and view columns, then apply after approval"
|
|
34
|
+
},
|
|
35
|
+
"d365_migrate": {
|
|
36
|
+
"title": "Migration risk",
|
|
37
|
+
"description": "Assess importing a solution elsewhere: shadowing, missing components, lost option values"
|
|
38
|
+
},
|
|
39
|
+
"d365_code_check": {
|
|
40
|
+
"title": "Code change check",
|
|
41
|
+
"description": "Read a GitLab MR and check whether script and plugin changes will run in the target environment"
|
|
42
|
+
},
|
|
43
|
+
"d365_refs": {
|
|
44
|
+
"title": "References",
|
|
45
|
+
"description": "Which forms, views and flows use a field, which forms load a script, which steps a plugin has"
|
|
46
|
+
},
|
|
47
|
+
"d365_data": {
|
|
48
|
+
"title": "Query data",
|
|
49
|
+
"description": "Count, group or sample business records (requires environment permission)"
|
|
50
|
+
},
|
|
51
|
+
"d365_data_write": {
|
|
52
|
+
"title": "Write data",
|
|
53
|
+
"description": "Create or update one business record after approval (requires environment permission)"
|
|
54
|
+
},
|
|
55
|
+
"d365_contract": {
|
|
56
|
+
"title": "Contract drift",
|
|
57
|
+
"description": "Check plugin registrations, form scripts and sitemaps across environments, with waivers"
|
|
58
|
+
},
|
|
59
|
+
"d365_health": {
|
|
60
|
+
"title": "Environment health",
|
|
61
|
+
"description": "Suspended and failed jobs, running imports and publishes, recently installed solutions"
|
|
62
|
+
},
|
|
63
|
+
"d365_reports": {
|
|
64
|
+
"title": "Local data",
|
|
65
|
+
"description": "Inspect local artifact usage, clean up by retention policy or compact snapshots"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/locale/zh.json
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"title": "D365 工具箱",
|
|
4
|
-
"description": "Dynamics 365 CE 的开发与部署助手:元数据、解决方案层、迁移风险、代码改动检查、契约漂移、环境健康。"
|
|
5
|
-
},
|
|
6
|
-
"tools": {
|
|
7
|
-
"d365_envs": {
|
|
8
|
-
"title": "环境清单",
|
|
9
|
-
"description": "列出已配置环境、凭据状态与连通性"
|
|
10
|
-
},
|
|
11
|
-
"d365_metadata": {
|
|
12
|
-
"title": "元数据查询",
|
|
13
|
-
"description": "按类别查询实体、字段、表单、视图、插件、解决方案等"
|
|
14
|
-
},
|
|
15
|
-
"d365_layers": {
|
|
16
|
-
"title": "解决方案层",
|
|
17
|
-
"description": "查看组件被哪些解决方案覆盖、当前生效的是哪一层"
|
|
18
|
-
},
|
|
19
|
-
"d365_sync": {
|
|
20
|
-
"title": "同步快照",
|
|
21
|
-
"description": "把环境元数据拉成本地快照并记录采集完整度"
|
|
22
|
-
},
|
|
23
|
-
"d365_diff": {
|
|
24
|
-
"title": "快照对比",
|
|
25
|
-
"description": "比较两份快照的新增、删除与字段级修改"
|
|
26
|
-
},
|
|
27
|
-
"d365_configure": {
|
|
28
|
-
"title": "环境配置",
|
|
29
|
-
"description": "读写环境清单:列出、保存、删除或测试连接"
|
|
30
|
-
},
|
|
31
|
-
"d365_write": {
|
|
32
|
-
"title": "元数据变更",
|
|
33
|
-
"description": "准备选项值、字段、表名与视图列的修改计划,确认后执行"
|
|
34
|
-
},
|
|
35
|
-
"d365_migrate": {
|
|
36
|
-
"title": "迁移评估",
|
|
37
|
-
"description": "评估解决方案导入其它环境的风险:遮蔽、缺失、选项值丢失"
|
|
38
|
-
},
|
|
39
|
-
"d365_code_check": {
|
|
40
|
-
"title": "代码改动检查",
|
|
41
|
-
"description": "读 GitLab MR,检查脚本与插件改动发到目标环境能否运行"
|
|
42
|
-
},
|
|
43
|
-
"d365_refs": {
|
|
44
|
-
"title": "引用反查",
|
|
45
|
-
"description": "字段被哪些表单/视图/流程引用,Web 资源被谁加载,程序集有哪些步骤"
|
|
46
|
-
},
|
|
47
|
-
"d365_data": {
|
|
48
|
-
"title": "业务数据查询",
|
|
49
|
-
"description": "统计、分组或抽样读取业务记录(需环境授权)"
|
|
50
|
-
},
|
|
51
|
-
"d365_data_write": {
|
|
52
|
-
"title": "业务数据写入",
|
|
53
|
-
"description": "新建或修改一条业务记录,确认后执行(需环境授权)"
|
|
54
|
-
},
|
|
55
|
-
"d365_contract": {
|
|
56
|
-
"title": "契约漂移",
|
|
57
|
-
"description": "核对插件注册、表单脚本与站点地图在上游下游是否一致,支持豁免"
|
|
58
|
-
},
|
|
59
|
-
"d365_health": {
|
|
60
|
-
"title": "环境健康",
|
|
61
|
-
"description": "挂起与失败的后台作业、正在运行的导入发布、最近安装的解决方案"
|
|
62
|
-
},
|
|
63
|
-
"d365_reports": {
|
|
64
|
-
"title": "本机数据",
|
|
65
|
-
"description": "查看本机产物占用,按保留策略清理或压缩快照"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "D365 工具箱",
|
|
4
|
+
"description": "Dynamics 365 CE 的开发与部署助手:元数据、解决方案层、迁移风险、代码改动检查、契约漂移、环境健康。"
|
|
5
|
+
},
|
|
6
|
+
"tools": {
|
|
7
|
+
"d365_envs": {
|
|
8
|
+
"title": "环境清单",
|
|
9
|
+
"description": "列出已配置环境、凭据状态与连通性"
|
|
10
|
+
},
|
|
11
|
+
"d365_metadata": {
|
|
12
|
+
"title": "元数据查询",
|
|
13
|
+
"description": "按类别查询实体、字段、表单、视图、插件、解决方案等"
|
|
14
|
+
},
|
|
15
|
+
"d365_layers": {
|
|
16
|
+
"title": "解决方案层",
|
|
17
|
+
"description": "查看组件被哪些解决方案覆盖、当前生效的是哪一层"
|
|
18
|
+
},
|
|
19
|
+
"d365_sync": {
|
|
20
|
+
"title": "同步快照",
|
|
21
|
+
"description": "把环境元数据拉成本地快照并记录采集完整度"
|
|
22
|
+
},
|
|
23
|
+
"d365_diff": {
|
|
24
|
+
"title": "快照对比",
|
|
25
|
+
"description": "比较两份快照的新增、删除与字段级修改"
|
|
26
|
+
},
|
|
27
|
+
"d365_configure": {
|
|
28
|
+
"title": "环境配置",
|
|
29
|
+
"description": "读写环境清单:列出、保存、删除或测试连接"
|
|
30
|
+
},
|
|
31
|
+
"d365_write": {
|
|
32
|
+
"title": "元数据变更",
|
|
33
|
+
"description": "准备选项值、字段、表名与视图列的修改计划,确认后执行"
|
|
34
|
+
},
|
|
35
|
+
"d365_migrate": {
|
|
36
|
+
"title": "迁移评估",
|
|
37
|
+
"description": "评估解决方案导入其它环境的风险:遮蔽、缺失、选项值丢失"
|
|
38
|
+
},
|
|
39
|
+
"d365_code_check": {
|
|
40
|
+
"title": "代码改动检查",
|
|
41
|
+
"description": "读 GitLab MR,检查脚本与插件改动发到目标环境能否运行"
|
|
42
|
+
},
|
|
43
|
+
"d365_refs": {
|
|
44
|
+
"title": "引用反查",
|
|
45
|
+
"description": "字段被哪些表单/视图/流程引用,Web 资源被谁加载,程序集有哪些步骤"
|
|
46
|
+
},
|
|
47
|
+
"d365_data": {
|
|
48
|
+
"title": "业务数据查询",
|
|
49
|
+
"description": "统计、分组或抽样读取业务记录(需环境授权)"
|
|
50
|
+
},
|
|
51
|
+
"d365_data_write": {
|
|
52
|
+
"title": "业务数据写入",
|
|
53
|
+
"description": "新建或修改一条业务记录,确认后执行(需环境授权)"
|
|
54
|
+
},
|
|
55
|
+
"d365_contract": {
|
|
56
|
+
"title": "契约漂移",
|
|
57
|
+
"description": "核对插件注册、表单脚本与站点地图在上游下游是否一致,支持豁免"
|
|
58
|
+
},
|
|
59
|
+
"d365_health": {
|
|
60
|
+
"title": "环境健康",
|
|
61
|
+
"description": "挂起与失败的后台作业、正在运行的导入发布、最近安装的解决方案"
|
|
62
|
+
},
|
|
63
|
+
"d365_reports": {
|
|
64
|
+
"title": "本机数据",
|
|
65
|
+
"description": "查看本机产物占用,按保留策略清理或压缩快照"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-d365-kit",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dynamics 365 CE developer and deployment toolkit for DSH: metadata, layers, snapshots, migration risk, code checks, contract drift, environment health, plug-in traces, message invocation and business-record sync.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,4 +58,4 @@
|
|
|
58
58
|
"inject": []
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
}
|
|
61
|
+
}
|