@yugoo-design/companion 0.1.0-beta.1 → 0.1.0-beta.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Yugoo Design Companion — beta
2
2
 
3
- Package: `@yugoo-design/companion@0.1.0-beta.1`.
3
+ Package: `@yugoo-design/companion@0.1.0-beta.2`.
4
4
 
5
5
  Yugoo Design (语构设计系统) is an interactive design plugin for native Agent Hosts.
6
6
  It combines domain methods, Design Projects, versioned Design Files and shared
@@ -16,26 +16,44 @@ selected beta to the exact version and integrity before acquisition; keep the
16
16
  previous installed version available during updates. Do not substitute `latest`.
17
17
 
18
18
  ```sh
19
- npm install --ignore-scripts --save-exact @yugoo-design/companion@0.1.0-beta.1 --registry=https://registry.npmjs.org
19
+ npm install --ignore-scripts --save-exact @yugoo-design/companion@0.1.0-beta.2 --registry=https://registry.npmjs.org
20
20
  npx --no-install design-system --help
21
21
  ```
22
22
 
23
- Have the Agent in your intended host follow the installed CLI's parameters and
24
- that host's setup requirements:
23
+ Ask the Agent in your current host to install or update Yugoo Design as one
24
+ product. It should identify the host from its actual execution context or your
25
+ explicit choice, not from which CLIs happen to be installed. If identity is
26
+ unclear, ask only for the missing host choice. Follow this acquired version's
27
+ CLI parameters and host requirements; a newer source guide does not add commands
28
+ to an already published package. Users do not install DSH and Companion separately:
25
29
 
26
30
  | Host | Native installation route |
27
31
  | --- | --- |
28
- | Codex | `install --host codex`: matching runtime receipt and saved-plan route. |
32
+ | Codex | `install --host codex`: run the existing plan, runtime check and apply transaction, retaining each receipt. |
29
33
  | Claude Code | `install --host claude-code`: native user-scope registration. |
30
34
  | Qoder CLI | `install --host qoder`: native user-scope registration and independent readback. |
31
- | QwenWork | `prepare-desktop --target qwenwork`: prepare the expert suite and its MCP configuration; import through the host. |
32
- | WorkBuddy | `prepare-desktop --target workbuddy`: prepare the stable marketplace and plugin; import or update through the host. |
33
- | DSH | `install --host dsh`: use the bound aggregate, official CLI/Loader and an explicitly selected existing profile. |
35
+ | QwenWork | `install --host qwenwork`: prepare the expert suite and its MCP configuration; import and enable through the host. |
36
+ | WorkBuddy | `install --host workbuddy`: prepare the stable marketplace and plugin; import or update and enable through the host. |
37
+ | DSH | `install --host dsh`: run the existing plan/apply transaction with the bound aggregate, official CLI/Loader and an explicitly selected existing profile. |
38
+
39
+ Supply the required host inputs shown by `design-system --help`. Without a host,
40
+ the entry returns `host-selection-required`; missing inputs return
41
+ `inputs-required`, both with exit code 2 and no installation attempted. The
42
+ Agent should obtain supported native facts itself and ask only for missing
43
+ choices. Explicit saved-plan and `prepare-desktop` routes remain available.
44
+
45
+ The Agent completes executable steps and reports only the required human
46
+ authorization, import, enable, restart or new-session action. Report acquisition,
47
+ registration/import, activation and verified use separately; do not report an
48
+ untested phase as complete. DSH uses its native integration, not a second
49
+ Companion runtime inside the host.
34
50
 
35
51
  Desktop preparation alone does not register or activate a plugin. Native suite
36
52
  versions are separate from npm versions. Preserve unrelated registrations,
37
53
  models, credentials and Design Projects; do not overwrite an owned previous
38
54
  installation path or automatically repeat an uncertain operation.
55
+ Same-version retries and updates must follow the owning adapter's receipt rules;
56
+ this entry does not introduce a universal replacement or rollback mechanism.
39
57
 
40
58
  Start the host normally, then invoke Yugoo Design in the intended conversation.
41
59
  Confirm the actual loaded skill and tools, open a project, edit and save a Panel,
@@ -6,4 +6,9 @@ try {
6
6
  console.log(JSON.stringify(result))
7
7
  if(process.argv[2]==='check-runtime'&&!['load-verified','disabled','not-applicable'].includes(result.status))process.exitCode=1
8
8
  if(result.status==='failed-partial-state-retained')process.exitCode=1
9
- } catch(error) { console.error(error.message);process.exitCode=1 }
9
+ if(['runtime-not-ready','unsupported-host'].includes(result.status))process.exitCode=1
10
+ if(['host-selection-required','inputs-required'].includes(result.status))process.exitCode=2
11
+ } catch(error) {
12
+ console.error(error.installation ? JSON.stringify({error:error.message,installation:error.installation}) : error.message)
13
+ process.exitCode=1
14
+ }
package/bin/installer.mjs CHANGED
@@ -12,7 +12,8 @@ import { createClaudeNativeAdapter, planClaudeInstallation, applyClaudeInstallat
12
12
  import { installQoderPlugin } from './npm-installer-qoder.mjs'
13
13
  import { checkRuntime, inspectRuntime } from './npm-runtime-check.mjs'
14
14
  import { runDesktopDshInstaller } from './npm-installer-dsh-desktop.mjs'
15
- import { desktopPreparationMain } from './npm-desktop-preparation.mjs'
15
+ import { desktopPreparationMain, desktopPreparationReuseMain } from './npm-desktop-preparation.mjs'
16
+ import { agentInstallation, AGENT_INSTALL_HOSTS, DESKTOP_INSTALL_OPTIONS } from './npm-agent-installation.mjs'
16
17
  import { createDshInstallerAdapter, planDshInstallation, applyDshInstallation } from './npm-installer-dsh.mjs'
17
18
 
18
19
  export const INSTALL_HOSTS = ['dsh','codex','claude-code','qoder','codex-desktop','qwenwork']
@@ -786,18 +787,19 @@ export async function nativePluginDirectory({npmRoot,host}) {
786
787
  }
787
788
  }
788
789
 
789
- export async function installerMain(args,{npmRoot,userRoot=homedir(),adapter=codexAdapter,dshAdapterFactory=createDshInstallerAdapter,claudeAdapterFactory=createClaudeNativeAdapter,qoderInstaller=installQoderPlugin}={}) {
790
+ export async function installerMain(args,{npmRoot,userRoot=homedir(),adapter=codexAdapter,dshAdapterFactory=createDshInstallerAdapter,claudeAdapterFactory=createClaudeNativeAdapter,qoderInstaller=installQoderPlugin,agentDispatch=true}={}) {
790
791
  if(args[0]==='prepare-desktop')return desktopPreparationMain(args.slice(1),{npmRoot})
791
792
  const {values,positionals}=parseArgs({args,allowPositionals:true,options:{host:{type:'string'},'dry-run':{type:'boolean'},
792
793
  help:{type:'boolean',short:'h'},apply:{type:'boolean'},plan:{type:'string'},'receipt-dir':{type:'string'},receipt:{type:'string'},'runtime-receipt':{type:'string'},'codex-executable':{type:'string'},'claude-executable':{type:'string'},'user-root':{type:'string'},'disable-embedding':{type:'boolean'},
793
794
  'qoder-executable':{type:'string'},'qoder-config-dir':{type:'string'},
794
- 'dsh-config':{type:'string'},profile:{type:'string'},'aggregate-selector':{type:'string'},registry:{type:'string'}}})
795
+ 'dsh-config':{type:'string'},profile:{type:'string'},'aggregate-selector':{type:'string'},registry:{type:'string'},
796
+ ...Object.fromEntries(DESKTOP_INSTALL_OPTIONS.map(key=>[key,{type:key==='separate-mcp'?'boolean':'string'}]))}})
795
797
  if(values.help)return {
796
798
  schemaVersion:1,kind:'installer-help',status:'not-applicable',
797
799
  usage:'design-system <command> [options]',
798
800
  commands:{
799
801
  'prepare-desktop':'Prepare a small QwenWork suite or stable WorkBuddy market from this exact acquired npm package. Uses --target, --package-name/--package-version, --suite-version, --node-executable and an external --destination; does not register a host. Updates use the previous preparation receipt.',
800
- install:'Install persistently through the selected native host. Claude accepts a direct install with executable and receipt directory, or --dry-run / --apply. DSH/Codex use their existing saved-plan routes.',
802
+ install:'The current Agent selects --host from trusted context or one user choice. Direct install orchestrates the existing DSH/Codex saved-plan routes, native Claude/Qoder registration, or QwenWork/WorkBuddy import preparation. Missing inputs and pending activation are explicit. Existing --dry-run / --apply remain available.',
801
803
  'plugin-directory':'Inspect the optional Claude/Qoder session-only --plugin-dir entry; this is not persistent installation.',
802
804
  'check-runtime':'Check runtime prerequisites and write an external runtime receipt; does not install.',
803
805
  recover:'Recover an owned Codex or Claude installation transaction using --receipt.',
@@ -805,7 +807,7 @@ export async function installerMain(args,{npmRoot,userRoot=homedir(),adapter=cod
805
807
  },
806
808
  options:{
807
809
  '--help, -h':'Show this JSON usage without reading configuration or registering a host.',
808
- '--host <host>':`Explicit host: ${INSTALL_HOSTS.join(', ')}. Names do not imply verified registration.`,
810
+ '--host <host>':`Current host: ${AGENT_INSTALL_HOSTS.join(', ')}. Omission requests one choice; installed binaries are not active-host identity. Explicit legacy codex-desktop remains plan-only.`,
809
811
  '--dry-run':'Prepare an install plan; requires --receipt-dir and writes that plan.',
810
812
  '--apply':'Apply a saved plan. Codex and Claude require --runtime-receipt; DSH requires explicit pins and external --receipt-dir.',
811
813
  '--plan <file>':'Saved installation plan used by --apply.',
@@ -822,12 +824,27 @@ export async function installerMain(args,{npmRoot,userRoot=homedir(),adapter=cod
822
824
  '--profile <name>':'DSH install only: explicitly selected existing profile; never initialized implicitly.',
823
825
  '--aggregate-selector <name@version>':'DSH install only: optional exact aggregate override matching the product release mapping; required for old or unbound candidates.',
824
826
  '--registry <https-url>':'DSH install only: optional credential-free HTTPS registry override matching the product mapping; required for old or unbound candidates.',
827
+ '--destination <directory>':'QwenWork/WorkBuddy direct install: exclusive external preparation directory, or the same owned directory with --previous-receipt.',
828
+ '--suite-version <version>':'QwenWork/WorkBuddy: exact native suite version, separate from the acquired npm version.',
829
+ '--node-executable <path>':'QwenWork/WorkBuddy: absolute Windows Node path used by the imported suite.',
830
+ '--qwenwork-runtime <file>':'QwenWork: verified native executable/worker/Node paths and byte pins. Agent obtains these through supported native inspection; no credentials.',
831
+ '--separate-mcp':'QwenWork: emit one external connector instead of bundling it when organization policy requires this.',
832
+ '--previous-receipt <file>':'Desktop: verify and reuse this owned preparation for a same-version repeat, or prepare a strictly newer native suite/npm version using its update boundary.',
833
+ '--npm-root-windows, --destination-windows':'Desktop: explicit Windows path mappings required when preparing from another platform; native Windows paths must match actual roots.',
825
834
  },
826
- limitations:['plugin-directory is an optional session-only diagnostic, not installation. Persistent installation uses native host registration. Codex requires an admitted adapter; DSH requires pinned public CLI inputs; unimplemented hosts are plan-only.',
835
+ limitations:['plugin-directory is an optional session-only diagnostic, not installation. Codex requires an admitted adapter; DSH requires pinned public CLI inputs. Desktop preparation needs native import and activation. Exit 2 means host selection or inputs are required; exit 0 does not establish a usable native session.',
827
836
  'DSH failures retain partial state without rollback. Configuration parsing is not actual host runtime or release acceptance.',
828
837
  'Uninstall requires a successful owned receipt. Package installation is not Bundle/Panel/GRA/KAG acceptance.'],
829
838
  }
830
839
  assert.equal(positionals.length,1,'Exactly one installer command required')
840
+ if(agentDispatch&&positionals[0]==='install'&&!values['dry-run']&&!values.apply) {
841
+ const context={npmRoot,userRoot,adapter,dshAdapterFactory,claudeAdapterFactory,qoderInstaller,agentDispatch:false}
842
+ return agentInstallation(values,{npmRoot,admittedCodexAdapter:adapter!==codexAdapter,
843
+ invoke:nextArgs=>installerMain(nextArgs,context),
844
+ desktopPrepare:nextArgs=>desktopPreparationMain(nextArgs,{npmRoot}),
845
+ desktopReuse:nextArgs=>desktopPreparationReuseMain(nextArgs,{npmRoot})})
846
+ }
847
+ for(const key of DESKTOP_INSTALL_OPTIONS)assert.ok(!Object.hasOwn(values,key),`--${key} is only valid for direct desktop install`)
831
848
  if(positionals[0]==='plugin-directory'&&['claude-code','qoder'].includes(values.host)) {
832
849
  assert.deepEqual(Object.keys(values),['host'],'Native directory entry accepts only --host; transaction/runtime options must not be silently ignored')
833
850
  return nativePluginDirectory({npmRoot,host:values.host})
@@ -0,0 +1,141 @@
1
+ /** Agent-facing orchestration of the existing native installers, not a registration authority. */
2
+ import assert from 'node:assert/strict'
3
+ import { randomUUID } from 'node:crypto'
4
+ import { lstat, readFile } from 'node:fs/promises'
5
+ import { join } from 'node:path'
6
+
7
+ export const AGENT_INSTALL_HOSTS = ['dsh', 'codex', 'claude-code', 'qoder', 'qwenwork', 'workbuddy']
8
+ export const DESKTOP_INSTALL_OPTIONS = ['destination', 'node-executable', 'suite-version', 'npm-root-windows',
9
+ 'destination-windows', 'qwenwork-runtime', 'separate-mcp', 'previous-receipt']
10
+ const nativeOptions = {
11
+ dsh: ['host', 'receipt-dir', 'dsh-config', 'profile', 'aggregate-selector', 'registry'],
12
+ codex: ['host', 'receipt-dir', 'codex-executable', 'user-root', 'disable-embedding'],
13
+ 'claude-code': ['host', 'receipt-dir', 'claude-executable', 'user-root', 'disable-embedding'],
14
+ qoder: ['host', 'receipt-dir', 'qoder-executable', 'qoder-config-dir'],
15
+ }
16
+
17
+ function argv(command, values, keys) {
18
+ const args = [command]
19
+ for (const key of keys) if (Object.hasOwn(values, key)) {
20
+ args.push('--' + key)
21
+ if (typeof values[key] !== 'boolean') args.push(values[key])
22
+ else assert.equal(values[key], true, 'False CLI flags are not supported')
23
+ }
24
+ return args
25
+ }
26
+ function pending(host, status, missing, message) {
27
+ return { schemaVersion: 1, kind: 'agent-installation', status, host: host ?? null,
28
+ hostRegistered: false, usable: false, missing,
29
+ installation: { host: host ?? null, acquisition: 'unverified', registration: 'not-performed',
30
+ activation: 'not-performed', usability: 'unverified', nextActions: [{ action: 'supply-inputs', automatic: false, message }] } }
31
+ }
32
+ function activationActions(host, result) {
33
+ const verify = { action: 'verify-native-session', automatic: true,
34
+ message: 'In the intended native conversation, verify the actually loaded Yugoo Design version and tools; then open a project and verify Panel save/reopen and original-conversation receipt. Registration alone is not usability.' }
35
+ if (host === 'qwenwork') return [
36
+ { action: 'import-expert-suite', automatic: false, path: result.nativeImport?.archive,
37
+ message: 'Import this exact expert-suite ZIP through QwenWork. If bundled MCP is disabled by organization policy, use the emitted connector configuration and retain exactly one Yugoo Design connector.' },
38
+ { action: 'activate-native-suite', automatic: false,
39
+ message: 'Enable the imported suite; start a new native conversation if QwenWork requires it. Retain the acquired npm runtime and confirm the actually loaded suite version.' }, verify]
40
+ if (host === 'workbuddy') return [
41
+ { action: 'import-or-update-market', automatic: false, path: result.nativeImport?.market,
42
+ message: 'Import this stable marketplace through WorkBuddy, or refresh the same market and update its existing Yugoo Design plugin. Preparation has not registered or activated it.' },
43
+ { action: 'activate-native-plugin', automatic: false,
44
+ message: 'Enable the plugin and start a new native conversation if WorkBuddy requires it; confirm the actually loaded suite version.' }, verify]
45
+ return [{ action: 'activate-native-session', automatic: false,
46
+ message: host === 'dsh'
47
+ ? 'Use the selected existing DSH profile and the host-supported plugin activation/restart flow; verify runtime loading in a new native session.'
48
+ : host === 'codex'
49
+ ? 'Start a new Codex conversation normally in the intended workspace and invoke the installed yugoo Skill through the native Skill entry. Retain the registered npm runtime.'
50
+ : 'Start a new native host conversation normally in the intended workspace and invoke /yugoo-design:yugoo. Retain the registered npm runtime; no --plugin-dir is needed.' }, verify]
51
+ }
52
+
53
+ /** Project only verified facts from the owning native/preparation receipt. */
54
+ export function installationResult(host, result, packageIdentity) {
55
+ const failed = result.status === 'failed-partial-state-retained'
56
+ const desktop = host === 'qwenwork' || host === 'workbuddy'
57
+ const registered = !desktop && !failed && (result.hostRegistered === true
58
+ || host === 'dsh' && result.status === 'registered-config-parsed' && result.readback?.bundleRegistered === true)
59
+ const identity = result.package ?? packageIdentity
60
+ return { ...result, host, usable: false,
61
+ installation: { host, package: identity ?? null,
62
+ acquisition: identity?.name && identity?.version && (host !== 'dsh' || result.readback?.packageInstalled === true) ? 'available' : 'unverified',
63
+ // This entry consumes an acquired installation; it never verifies a registry download.
64
+ registryAcquisitionVerified: false,
65
+ registration: failed ? 'failed-partial-state-retained' : registered ? 'verified' : 'not-performed',
66
+ activation: failed ? 'not-performed' : 'required', usability: 'unverified',
67
+ nextActions: failed ? [{ action: 'inspect-retained-state', automatic: false, path: result.receiptFile,
68
+ message: 'Inspect the owning receipt and retained DSH state before another operation. No automatic rollback or retry was attempted.' }]
69
+ : activationActions(host, result) } }
70
+ }
71
+
72
+ export async function agentInstallation(values, { npmRoot, invoke, desktopPrepare, desktopReuse,
73
+ admittedCodexAdapter = false, platform = process.platform } = {}) {
74
+ const host = values.host
75
+ // Installed executables, PATH, host profiles and process names cannot identify this conversation.
76
+ if (!host) return { ...pending(null, 'host-selection-required', ['host'],
77
+ 'Select the host of this conversation. If trusted native context identifies it, the current Agent should supply --host; otherwise ask one host-selection question.'), hosts: AGENT_INSTALL_HOSTS }
78
+ if (host === 'codex-desktop') return pending(host, 'unsupported-host', [],
79
+ 'Codex Desktop native registration is not implemented by this entry. Select the actual supported host; do not substitute a CLI or infer usability from a prepared plan.')
80
+ assert.ok(AGENT_INSTALL_HOSTS.includes(host), 'Explicit supported host required')
81
+ const desktop = host === 'qwenwork' || host === 'workbuddy'
82
+ const allowed = desktop ? ['host', ...DESKTOP_INSTALL_OPTIONS] : nativeOptions[host]
83
+ for (const key of Object.keys(values)) assert.ok(allowed.includes(key), `${host} install accepts only ${allowed.map(k => '--' + k).join(', ')}; unsupported option --${key}`)
84
+ const required = desktop ? ['destination', 'node-executable', 'suite-version'] : ['receipt-dir']
85
+ if (host === 'dsh') required.push('dsh-config', 'profile')
86
+ if (host === 'codex' && !admittedCodexAdapter) required.push('codex-executable')
87
+ if (host === 'claude-code') required.push('claude-executable')
88
+ if (host === 'qwenwork') required.push('qwenwork-runtime')
89
+ if (host === 'workbuddy') for (const key of ['qwenwork-runtime', 'separate-mcp'])
90
+ assert.ok(!Object.hasOwn(values, key), `WorkBuddy install does not accept --${key}`)
91
+ if (desktop && platform !== 'win32') required.push('npm-root-windows', 'destination-windows')
92
+ const missing = required.filter(key => typeof values[key] !== 'string' || !values[key])
93
+ if (missing.length) return pending(host, 'inputs-required', missing,
94
+ `The current Agent should obtain verified values for ${missing.map(key => '--' + key).join(', ')}. Ask only for information or host actions it cannot obtain through supported native inspection. No installation was attempted.`)
95
+
96
+ let plan
97
+ try {
98
+ if (desktop) {
99
+ const path = join(npmRoot, 'package.json'), info = await lstat(path)
100
+ assert.ok(info.isFile() && info.size <= 1024 * 1024, 'Expected bounded regular acquired package manifest')
101
+ const manifest = JSON.parse(await readFile(path, 'utf8'))
102
+ const args = argv('prepare-desktop', values, DESKTOP_INSTALL_OPTIONS).slice(1)
103
+ args.push('--target', host, '--package-name', manifest.name, '--package-version', manifest.version)
104
+ const reused = values['previous-receipt'] && await desktopReuse(args)
105
+ return installationResult(host, reused || await desktopPrepare(args))
106
+ }
107
+ if (host === 'claude-code' || host === 'qoder')
108
+ return installationResult(host, await invoke(argv('install', values, allowed)))
109
+ // DSH's exclusive plan files are never overwritten. Each direct operation gets
110
+ // its own receipt subdirectory; Codex keeps the stable directory needed by upgrades.
111
+ const nativeValues = host === 'dsh' ? { ...values, 'receipt-dir': join(values['receipt-dir'], randomUUID()) } : values
112
+ const dryArgs = argv('install', nativeValues, allowed)
113
+ dryArgs.push('--dry-run')
114
+ plan = await invoke(dryArgs)
115
+ assert.ok(plan.planFile, 'The owning installer did not retain an installation plan')
116
+ if (host === 'codex') {
117
+ assert.equal(plan.applySupported, true, 'Codex native installation is not admitted by the owning adapter')
118
+ const runtime = await invoke(argv('check-runtime', values, ['host', 'receipt-dir', 'disable-embedding']))
119
+ if (!['load-verified', 'disabled'].includes(runtime.status)) return {
120
+ ...pending(host, 'runtime-not-ready', [], 'Inspect the runtime receipt. No native apply was attempted; repair the reported prerequisites before re-running install.'),
121
+ runtimeReceipt: runtime, planFile: plan.planFile }
122
+ assert.ok(runtime.receiptFile, 'The owning runtime checker did not retain its receipt')
123
+ const applyArgs = argv('install', values, allowed.filter(key => !['receipt-dir', 'disable-embedding'].includes(key)))
124
+ applyArgs.push('--apply', '--plan', plan.planFile, '--runtime-receipt', runtime.receiptFile)
125
+ return installationResult(host, await invoke(applyArgs), plan.package)
126
+ }
127
+ const applyArgs = argv('install', nativeValues, allowed)
128
+ applyArgs.push('--apply', '--plan', plan.planFile)
129
+ return installationResult(host, await invoke(applyArgs), plan.package)
130
+ } catch (error) {
131
+ const receipt = error.receiptFile ?? error.receiptPath ?? null
132
+ error.installation = { host, acquisition: 'unverified', registration: 'failed-or-unconfirmed',
133
+ activation: 'not-performed', usability: 'unverified', receiptFile: receipt,
134
+ planFile: plan?.planFile ?? null,
135
+ nextActions: [{ action: receipt ? 'inspect-owned-receipt' : 'inspect-failure', automatic: false,
136
+ message: host === 'codex' || host === 'claude-code'
137
+ ? 'Inspect the failure and matching owned receipt; use the existing recover --host route only when that receipt requires recovery. Do not delete profiles, projects or unrelated plugins.'
138
+ : 'Inspect retained state before retrying. This host has no general automatic rollback in this entry; use its supported update/recovery flow.' }] }
139
+ throw error
140
+ }
141
+ }
@@ -201,11 +201,61 @@ export async function prepareDesktopPackage({ npmRoot, packageName, packageVersi
201
201
  }
202
202
  }
203
203
 
204
- export async function desktopPreparationMain(args, { npmRoot } = {}) {
204
+ function preparationArguments(args) {
205
205
  const { values, positionals } = parseArgs({ args, options: { help: { type: 'boolean', short: 'h' }, target: { type: 'string' }, 'package-name': { type: 'string' }, 'package-version': { type: 'string' },
206
206
  destination: { type: 'string' }, 'node-executable': { type: 'string' }, 'suite-version': { type: 'string' }, 'npm-root-windows': { type: 'string' }, 'destination-windows': { type: 'string' },
207
207
  'qwenwork-runtime': { type: 'string' }, 'separate-mcp': { type: 'boolean' }, 'previous-receipt': { type: 'string' } }, allowPositionals: true })
208
208
  assert.equal(positionals.length, 0, 'No positional preparation arguments allowed')
209
+ return values
210
+ }
211
+
212
+ /** Same-version reuse under the owning preparation lease; no artifact or receipt rewrite. */
213
+ export async function desktopPreparationReuseMain(args, { npmRoot } = {}) {
214
+ const values = preparationArguments(args)
215
+ if (!values['previous-receipt']) return null
216
+ const output = resolve(values.destination), root = resolve(npmRoot)
217
+ await safe(root); await safe(output)
218
+ assert.equal(await realpath(root), root)
219
+ assert.ok(output !== root && !confined(root, output) && !confined(output, root), 'Preparation must be outside npm package')
220
+ assert.ok(outsideHostProfile(output), 'Do not prepare inside native host profiles')
221
+ const prior = await priorReceipt(resolve(values['previous-receipt'])), body = prior.body
222
+ const manifestBytes = await file(join(npmRoot, 'package.json')), manifest = JSON.parse(manifestBytes)
223
+ if (manifest.version !== body.package.version) return null // The existing preparer owns new-version updates.
224
+ assert.equal(manifest.name, values['package-name']); assert.equal(manifest.version, values['package-version'])
225
+ assert.equal(manifest['x-design-release-review']?.fixture, false, 'Fixture packages cannot reuse desktop imports')
226
+ assert.equal(body.package.npmRoot, resolve(npmRoot), 'Same-version reuse requires the same retained npm directory')
227
+ assert.equal(body.package.manifestSha256, sha(manifestBytes), 'Same-version acquired manifest changed')
228
+ assert.equal(body.target, values.target); assert.equal(body.destination, resolve(values.destination))
229
+ assert.equal(body.suiteVersion, values['suite-version'], 'Same npm version cannot change the native suite version')
230
+ const windowsNpm = windowsPath(values['npm-root-windows'] ?? (process.platform === 'win32' ? npmRoot : undefined))
231
+ const windowsOutput = windowsPath(values['destination-windows'] ?? (process.platform === 'win32' ? body.destination : undefined))
232
+ assert.ok(outsideHostProfile(windowsOutput), 'Do not prepare inside native host profiles')
233
+ if (process.platform === 'win32') { assert.equal(windowsNpm.toLowerCase(), win32.normalize(root).toLowerCase()); assert.equal(windowsOutput.toLowerCase(), win32.normalize(output).toLowerCase()) }
234
+ assert.equal(body.windows.npmRoot, windowsNpm); assert.equal(body.windows.destination, windowsOutput)
235
+ assert.equal(body.windows.nodeExecutable, windowsPath(values['node-executable']), 'Same-version Node path changed')
236
+ if (body.target === 'qwenwork') {
237
+ assert.equal(body.nativeImport.includeMcp, !values['separate-mcp'], 'Same-version connector policy changed')
238
+ const nativePins = JSON.parse(await file(resolve(values['qwenwork-runtime'])))
239
+ assert.deepEqual(nativePins, JSON.parse(await file(join(dirname(body.nativeImport.suite), 'runtime-pins.json'))), 'Same-version native pins changed')
240
+ if (process.platform === 'win32') for (const [path, expected] of [[nativePins.executable, nativePins.executableSha256], [nativePins.worker, nativePins.workerSha256]])
241
+ assert.equal(await streamHash(path), expected, 'QwenWork native bytes do not match provided pins')
242
+ }
243
+ const lock = join(output, '.preparation.lock'), owner = randomUUID()
244
+ // The inventory excludes this transient file, so bytes alone cannot admit a
245
+ // repeat while another preparation is active or interrupted. Reuse its exact
246
+ // exclusive boundary and recheck the snapshot under our own reservation.
247
+ await writeFile(lock, owner, { flag: 'wx', mode: 0o600 })
248
+ try {
249
+ assert.deepEqual(await priorReceipt(resolve(values['previous-receipt'])), prior, 'Preparation changed while acquiring its reuse lock')
250
+ return { ...body, receiptFile: resolve(values['previous-receipt']), receiptSha256: prior.sha256, idempotent: true }
251
+ } finally {
252
+ assert.equal((await file(lock)).toString(), owner, 'Preparation reuse lock ownership changed; retained for inspection')
253
+ await unlink(lock)
254
+ }
255
+ }
256
+
257
+ export async function desktopPreparationMain(args, { npmRoot } = {}) {
258
+ const values = preparationArguments(args)
209
259
  if (values.help) return { kind: 'desktop-preparation-help', status: 'not-applicable', usage: 'design-system prepare-desktop --target qwenwork|workbuddy --package-name EXACT_SCOPED_NAME --package-version EXACT_NPM_VERSION --suite-version NATIVE_RELEASE_VERSION --destination EXTERNAL_DIRECTORY --node-executable ABSOLUTE_WINDOWS_NODE',
210
260
  options: { '--qwenwork-runtime': 'Explicit JSON with existing QwenWork executable/worker/Node paths and exact executable/worker hashes.', '--separate-mcp': 'QwenWork only: omit the suite connector and emit one external connector JSON for organization policy.', '--previous-receipt': 'Verified successful preparation receipt for an owned new-version update; preserves the same WorkBuddy market.', '--npm-root-windows, --destination-windows': 'Explicit target path mapping required for cross-platform preparation; native Windows paths must match actual roots.' },
211
261
  limits: ['Preparation does not acquire from registry, register a host, grant native admission, verify loaded versions or alter a host profile.', 'Acquire the exact npm release first; retain its durable installation and old version until the native update is verified.'] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yugoo-design/companion",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Yugoo Design Companion beta: interactive Design Projects and Panels in supported native Agent Hosts.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "yugoo-design",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Yugoo Design: interactive Design Panels with Claude Code native agents and background design reviews."
5
5
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yugoo-design",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Yugoo Design: local Design Project Panels for an explicitly bound Codex conversation (development preview).",
5
5
  "author": {
6
6
  "name": "yugoo-design contributors"
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "yugoo-design",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Yugoo Design: interactive Design Panels with Qoder-native Producers, Workers and background reviews."
5
5
  }
@@ -3,20 +3,21 @@
3
3
  "fixture": false,
4
4
  "distribution": "beta",
5
5
  "publishReady": false,
6
- "version": "0.1.0-beta.1",
6
+ "version": "0.1.0-beta.2",
7
7
  "readmeSource": {
8
8
  "path": "packages/companion/README.md",
9
- "sourceSha256": "840a0bec3965a8c66c984a56a2cb4023c1882fa7d19d8c87163ef102214be6b9",
10
- "sectionSha256": "8e52fade20de7dc2f452fe63fa6634d0f8996ef99f3d9e775e011009c0d0a626"
9
+ "sourceSha256": "ff913a08a4c91a7136a2cfc528608ac6e1ab1b12bab9b11000266c0108f381cf",
10
+ "sectionSha256": "35243cc02309bc13cc7e5f9a23c52520f3071ecfadf1bad6bd1a30231c0f71a0"
11
11
  },
12
12
  "assets": {
13
13
  "LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
14
14
  "NOTICE": "6119b21e1fa5d37ec3ab337ad43685970310aa5c97a0e90ecf6603d17091f371",
15
- "README.md": "df9f588f656b0abad96c7eaa7e7db398f55caeca422a185633605453944c9605",
15
+ "README.md": "7a1db4c01cecbfb03294112c32ac00422f6ef6ec672aff13379d7613f635ff27",
16
16
  "THIRD_PARTY_NOTICES.md": "bfcab921c6c15ccc47f9b1a6cc5941675c95962de5ea5b0edc50fbfa2aef136a",
17
- "bin/design-system.mjs": "a0bbc5a1226f7b21a7ed9169a31690e77426727c8fcdee596d23d54f51459c1a",
18
- "bin/installer.mjs": "508e7bdde776ebadca3a008e9e84fb47fd5bc5fde0fc4cacccaea03269a300d0",
19
- "bin/npm-desktop-preparation.mjs": "3c532e036d8f1046840ed279f74ca62341843064d9f9b61859260ecfc0fd2f3e",
17
+ "bin/design-system.mjs": "9bf4f4d733f71cf33a2fe239de9dcfcd887ebdd1e8026b5b29cfd89309ca2444",
18
+ "bin/installer.mjs": "d769c4e66c8029c9fd054ab5e0ce950ea788efb43d7d587862c0aae8b136582e",
19
+ "bin/npm-agent-installation.mjs": "419ec607ea1003baf560c768c6642c4342c057121faa61ccf61bec51439a498c",
20
+ "bin/npm-desktop-preparation.mjs": "d78bc2a529965011cc360d4bc731fc624114cd83bd634be71f34367d6fc424d0",
20
21
  "bin/npm-installer-claude.mjs": "c9336fed52319c924214b4e4c4f1ffc714db0e97bb587214cd7c67ebb6637925",
21
22
  "bin/npm-installer-codex.mjs": "ca18b3cc18d20aae66526797dedcf232322d027a7cf1251fd02943f1f2765d73",
22
23
  "bin/npm-installer-dsh-desktop.mjs": "93f9e7adf4457508e0013f25c4357ae3965ae205d268262d78a6fdd8aa2b9866",
@@ -417,7 +418,7 @@
417
418
  "licenses/third-party/whiteboard-fonts/licenses/JetBrainsMono-OFL.txt": "30f0c136e3c88e422d0791acd97238870f9054a9729bc34cf2ff0d4ed8cac4ad",
418
419
  "licenses/third-party/whiteboard-fonts/licenses/source-han-sans-LICENSE.txt": "fcac737e761ec63dbfbdce11030a1780161920d80315edba9c8beff1c2bac5a2",
419
420
  "licenses/third-party/whiteboard-fonts/manifest.json": "c164438115a40cdead905085e95298f28a4ffa2a3947512b9a98509f33c26972",
420
- "package.json": "8d95243ebeda80d8e581d72e094a3f6547af4367781e909b5afd69b32f74059e",
421
+ "package.json": "4810e2a27f32848524c97af325fef90faacba09634671311304cf915d27d29d9",
421
422
  "payload/desktop-preparation/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
422
423
  "payload/desktop-preparation/NOTICE": "6b4eec391144fcbd96ec7b45a28764ab1932722a96325248377c3e28aac2c62d",
423
424
  "payload/desktop-preparation/THIRD_PARTY_NOTICES.md": "bfcab921c6c15ccc47f9b1a6cc5941675c95962de5ea5b0edc50fbfa2aef136a",
@@ -454,7 +455,7 @@
454
455
  "payload/desktop-preparation/src/workbuddy-relay-service.mjs": "44d60d6956250417507fb76c71c3030a01bc38e565e4572a761b46f255f8e669",
455
456
  "payload/desktop-preparation/src/workbuddy-ui-probes.mjs": "a91d4651248125769254755bdf66a44bbc9e5a793dad0a137c35acbb10a89a88",
456
457
  "payload/desktop-preparation/workbuddy-package.mjs": "56ff591047d83ec742f22b286a9574ab9e05d5850ebed6f002a6e0bebfcd89c9",
457
- "payload/overlays/claude-code/.claude-plugin/plugin.json": "7f397e6ae09e3acdac9a50952efccb986b976fb8e8c1ba2c9d1a7404a08a04da",
458
+ "payload/overlays/claude-code/.claude-plugin/plugin.json": "b15a0cc6401481180bbadb1c04f050f53d7ceb45614a6fff83f95745ff8c6b1a",
458
459
  "payload/overlays/claude-code/.mcp.json": "1108847d27abd953a52cd7c3ea4cd416898d67dcfdb80a92255d889186b12b66",
459
460
  "payload/overlays/claude-code/hooks/hooks.json": "e8bea6fdf13dce4023a2112dd4a31fd1a77e0f4ca2d4c506882ea80ef86b6482",
460
461
  "payload/overlays/claude-code/scripts/claude-connection.mjs": "83a6a2de1b28d1b5dd230b1447abac6f2209a80d6681f08766a53e7b6f63200f",
@@ -468,7 +469,7 @@
468
469
  "payload/overlays/claude-code/skills/design-component/SKILL.md": "85a1f2a016174787226eaa9e9e6675e86057212d322a65014edd71f3e4861b34",
469
470
  "payload/overlays/claude-code/skills/yugoo/SKILL.md": "e1b2284a94c5d113ef0cd2e58fce4f3405a21905fef741296baeae3cd45adb33",
470
471
  "payload/overlays/claude-code/skills/yugoo/native-execution.md": "8d27e523ddaad770e261300f7b74db5cff322d2342a838f87b253361a51404d3",
471
- "payload/overlays/codex/.codex-plugin/plugin.json": "9f3ebf93daee64ff0c66a968a7d2acf19213d68fc4012ced2c3a7ee44460fd2a",
472
+ "payload/overlays/codex/.codex-plugin/plugin.json": "d52986e0e9503926f4a5903b2604f87633a8105af24202c066b710ebefe2d254",
472
473
  "payload/overlays/codex/.mcp.json": "ee6450b4f9a5239ae9c173d07198d37041fad65f9f65ecf4ff88ccf8528a609a",
473
474
  "payload/overlays/codex/README.md": "82f392681e120723fb90be5a8d2eb36c10113811f9eb355fcfc24167614b2373",
474
475
  "payload/overlays/codex/scripts/design.mjs": "c116e3a9f60830923692a9f8785640d9a13a97320771bf9201de0e5089618a74",
@@ -477,7 +478,7 @@
477
478
  "payload/overlays/codex/skills/design-panel/native-execution.md": "8d27e523ddaad770e261300f7b74db5cff322d2342a838f87b253361a51404d3",
478
479
  "payload/overlays/codex/skills/yugoo/SKILL.md": "fdb035c4c9f43a2c1fbd1ceac6a2bd19e9cdec6cd5e068efa804b1c86bec513a",
479
480
  "payload/overlays/qoder/.mcp.json": "7f86b52fffd552312441b9bd99dd39a3b80b2893dc4a85519614d906bdc6884e",
480
- "payload/overlays/qoder/.qoder-plugin/plugin.json": "a4e205b8c3fea0a77549abaa5050e72758f65874dd00dbf6ad84426fe5fd707e",
481
+ "payload/overlays/qoder/.qoder-plugin/plugin.json": "c423856f4dd4e4a1d58e1de72dd4deb9b722d5151b3fa7da95d6c8d522574187",
481
482
  "payload/overlays/qoder/commands/yugoo.md": "4e9d0e4a3da232b44a3d2c8495c7d3dd40bbb9ba2dff4b389ab8ca587522793d",
482
483
  "payload/overlays/qoder/hooks/hooks.json": "6452852583af61a7b4f0ed501a41d02a212a8487e5d920bcb0b2ee7e7e643d7d",
483
484
  "payload/overlays/qoder/scripts/design.mjs": "c116e3a9f60830923692a9f8785640d9a13a97320771bf9201de0e5089618a74",