@empirical-gauzza/cli 1.0.15 → 1.0.17
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/constants.js +1 -1
- package/lib/help.js +14 -8
- package/lib/pluginUpdateCheck.js +148 -143
- package/lib/workspaceCommands.js +5 -5
- package/package.json +1 -1
package/lib/constants.js
CHANGED
|
@@ -202,7 +202,7 @@ Invite commands operate on the active team workspace. Create prints a shareable
|
|
|
202
202
|
empirical workspace delete <number|name|id>
|
|
203
203
|
empirical workspace delete <number|name|id> --confirm
|
|
204
204
|
|
|
205
|
-
Deletion dry-runs by default. \`--confirm\` calls the existing soft-delete endpoint; the Default
|
|
205
|
+
Deletion dry-runs by default. \`--confirm\` calls the existing soft-delete endpoint; the Default Workspace cannot be deleted. If the active workspace is deleted, the CLI selects the Default Workspace.
|
|
206
206
|
`,
|
|
207
207
|
|
|
208
208
|
doctor: `# Empirical Doctor
|
package/lib/help.js
CHANGED
|
@@ -19,26 +19,32 @@ function showHelp() {
|
|
|
19
19
|
log(' empirical plugin check [--force]');
|
|
20
20
|
log(' empirical policy show [--refresh]');
|
|
21
21
|
log(' empirical policy set --json <policy>');
|
|
22
|
-
log(' empirical auth login');
|
|
23
|
-
log(' empirical auth logout');
|
|
22
|
+
log(' empirical auth login Interactive browser login');
|
|
23
|
+
log(' empirical auth logout Clear the locally stored session');
|
|
24
24
|
log(' empirical oauth bootstrap headless [--print-access-token]');
|
|
25
|
+
log(' First-time token setup for CI/headless/API-key use');
|
|
25
26
|
log(' empirical oauth bootstrap headless --write-env [--env-output <path>]');
|
|
26
27
|
log(' empirical oauth refresh headless [--print-access-token]');
|
|
28
|
+
log(' Renew an existing headless token before/after it expires');
|
|
27
29
|
log(' empirical oauth refresh headless --write-env [--env-output <path>]');
|
|
28
30
|
log(' empirical oauth bootstrap docker [--print-access-token]');
|
|
31
|
+
log(' Same as bootstrap headless, tuned for container startup');
|
|
29
32
|
log(' empirical oauth refresh docker [--print-access-token]');
|
|
30
33
|
log(' empirical oauth client set [--client-id <id>] [--client-secret <secret>]');
|
|
31
34
|
log(' empirical oauth client show');
|
|
32
35
|
log(' empirical oauth client clear');
|
|
33
|
-
log(' empirical workspace list');
|
|
34
|
-
log(' empirical workspace current');
|
|
35
|
-
log(' empirical workspace select [<number|name>]');
|
|
36
|
-
log(' empirical workspace use <number|name|id>');
|
|
36
|
+
log(' empirical workspace list List workspaces you can select, and which one is active');
|
|
37
|
+
log(' empirical workspace current Show the currently active workspace');
|
|
38
|
+
log(' empirical workspace select [<number|name>] Set the active workspace (prompts if omitted)');
|
|
39
|
+
log(' empirical workspace use <number|name|id> Alias for `workspace select`');
|
|
37
40
|
log(' empirical workspace create <name> [--description <text>]');
|
|
41
|
+
log(' Create a new shared team workspace');
|
|
38
42
|
log(' empirical workspace invite create [--role reader|writer|manager] [--expires-in-days <n>] [--max-uses <n>]');
|
|
39
|
-
log('
|
|
40
|
-
log(' empirical workspace invite
|
|
43
|
+
log(' Create a shareable invite link for the active team workspace');
|
|
44
|
+
log(' empirical workspace invite list List active invites for the active team workspace');
|
|
45
|
+
log(' empirical workspace invite revoke <token> Revoke a previously created invite');
|
|
41
46
|
log(' empirical workspace delete <number|name|id> [--confirm]');
|
|
47
|
+
log(' Soft-delete a team workspace (dry-run without --confirm)');
|
|
42
48
|
log(' empirical oauth bootstrap openclaw [--print-access-token] (legacy alias)');
|
|
43
49
|
log(' empirical oauth refresh openclaw [--print-access-token] (legacy alias)');
|
|
44
50
|
log(' empirical --oauth-bootstrap-headless [--print-access-token]');
|
package/lib/pluginUpdateCheck.js
CHANGED
|
@@ -1,143 +1,148 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { askYesNo, ask, log, warn, uiHeader, parseSelection } = require('./ui');
|
|
4
|
-
const { runResolvedCommand, commandExists, summarizeProcessOutput } = require('./fsUtils');
|
|
5
|
-
const { readDeviceConfig, writeDeviceConfig } = require('./deviceConfig');
|
|
6
|
-
const { installNativePlugin } = require('./installers/nativePlugin');
|
|
7
|
-
|
|
8
|
-
const PLUGIN_METADATA_URL = 'https://raw.githubusercontent.com/mattgauzza/empirical-memory-plugin/main/.claude-plugin/marketplace.json';
|
|
9
|
-
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
10
|
-
const SUPPORTED_TARGETS = ['codex', 'claude', 'copilot'];
|
|
11
|
-
|
|
12
|
-
function parseVersion(value) {
|
|
13
|
-
const match = String(value || '').trim().match(/^v?(\d+)\.(\d+)\.(\d+)/i);
|
|
14
|
-
return match ? match.slice(1).map(Number) : null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function compareVersions(left, right) {
|
|
18
|
-
const a = parseVersion(left);
|
|
19
|
-
const b = parseVersion(right);
|
|
20
|
-
if (!a || !b) return null;
|
|
21
|
-
for (let i = 0; i < 3; i += 1) {
|
|
22
|
-
if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1;
|
|
23
|
-
}
|
|
24
|
-
return 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function extractVersion(output, pluginName = 'empirical-memory') {
|
|
28
|
-
const text = String(output || '');
|
|
29
|
-
const jsonCandidates = [text, ...text.split(/\r?\n/).map((line) => line.trim())];
|
|
30
|
-
for (const candidate of jsonCandidates) {
|
|
31
|
-
try {
|
|
32
|
-
const value = JSON.parse(candidate);
|
|
33
|
-
const rows = Array.isArray(value) ? value : [value, ...(Array.isArray(value?.plugins) ? value.plugins : [])];
|
|
34
|
-
const match = rows.find((row) => row && (row.name === pluginName || row.plugin === pluginName));
|
|
35
|
-
if (match?.version) return String(match.version);
|
|
36
|
-
} catch (_error) {
|
|
37
|
-
// Native CLIs may print a table instead of JSON.
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const line = text.split(/\r?\n/).find((value) => value.toLowerCase().includes(pluginName.toLowerCase()));
|
|
41
|
-
const match = line && line.match(new RegExp(`${pluginName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[^\\d]*(\\d+\\.\\d+\\.\\d+)`, 'i'));
|
|
42
|
-
return match ? match[1] : null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function readInstalledVersions(target, pluginNames) {
|
|
46
|
-
const commands = {
|
|
47
|
-
codex: ['codex', ['plugin', 'list']],
|
|
48
|
-
claude: ['claude', ['plugin', 'list']],
|
|
49
|
-
copilot: ['copilot', ['plugin', 'list']]
|
|
50
|
-
};
|
|
51
|
-
const [command, args] = commands[target];
|
|
52
|
-
if (!commandExists(command)) return null;
|
|
53
|
-
const result = runResolvedCommand(command, args, { stdio: 'pipe' });
|
|
54
|
-
if (!result || result.status !== 0) return null;
|
|
55
|
-
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
56
|
-
return Object.fromEntries(pluginNames.map((name) => [name, extractVersion(output, name)]));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function fetchLatestVersions(fetchImpl = globalThis.fetch) {
|
|
60
|
-
if (typeof fetchImpl !== 'function') return null;
|
|
61
|
-
const response = await fetchImpl(PLUGIN_METADATA_URL, { signal: AbortSignal.timeout(5000) });
|
|
62
|
-
if (!response.ok) throw new Error(`marketplace metadata returned HTTP ${response.status}`);
|
|
63
|
-
const data = await response.json();
|
|
64
|
-
if (Array.isArray(data?.plugins)) {
|
|
65
|
-
return Object.fromEntries(data.plugins.map((plugin) => [plugin.name, plugin.version]).filter(([, version]) => version));
|
|
66
|
-
}
|
|
67
|
-
return data?.version ? { 'empirical-memory': data.version } : {};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function shouldCheck(cache, now = Date.now(), force = false) {
|
|
71
|
-
return force || !cache?.checkedAt || now - Number(cache.checkedAt) >= CHECK_INTERVAL_MS;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function checkNativePluginUpdates({ force = false, interactive = process.stdin.isTTY, fetchImpl } = {}) {
|
|
75
|
-
if (!interactive && !force) return { skipped: true, reason: 'noninteractive' };
|
|
76
|
-
const config = readDeviceConfig();
|
|
77
|
-
const cache = config.pluginUpdates || {};
|
|
78
|
-
if (!shouldCheck(cache, Date.now(), force)) return { skipped: true, reason: 'rate_limited' };
|
|
79
|
-
|
|
80
|
-
let latestVersion;
|
|
81
|
-
try {
|
|
82
|
-
latestVersion = await fetchLatestVersions(fetchImpl);
|
|
83
|
-
} catch (error) {
|
|
84
|
-
config.pluginUpdates = { ...cache, checkedAt: Date.now(), lastError: error.message };
|
|
85
|
-
writeDeviceConfig(config);
|
|
86
|
-
return { skipped: true, reason: 'marketplace_unavailable' };
|
|
87
|
-
}
|
|
88
|
-
const pluginNames = Object.keys(latestVersion);
|
|
89
|
-
const installed = {};
|
|
90
|
-
for (const target of SUPPORTED_TARGETS) installed[target] = readInstalledVersions(target, pluginNames);
|
|
91
|
-
config.pluginUpdates = { checkedAt: Date.now(), latestVersions: latestVersion, installed };
|
|
92
|
-
writeDeviceConfig(config);
|
|
93
|
-
|
|
94
|
-
const outdated = SUPPORTED_TARGETS.flatMap((target) => pluginNames
|
|
95
|
-
.filter((name) => compareVersions(latestVersion[name], installed[target][name]) === 1)
|
|
96
|
-
.map((name) => ({ target, name, installed: installed[target][name], latest: latestVersion[name] })));
|
|
97
|
-
const latestSignature = JSON.stringify(latestVersion);
|
|
98
|
-
if (outdated.length === 0 || cache.offeredVersion === latestSignature) {
|
|
99
|
-
return { latestVersion, installed, outdated };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!interactive) {
|
|
103
|
-
return { latestVersion, installed, outdated, promptSuppressed: true };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { askYesNo, ask, log, warn, uiHeader, parseSelection } = require('./ui');
|
|
4
|
+
const { runResolvedCommand, commandExists, summarizeProcessOutput } = require('./fsUtils');
|
|
5
|
+
const { readDeviceConfig, writeDeviceConfig } = require('./deviceConfig');
|
|
6
|
+
const { installNativePlugin } = require('./installers/nativePlugin');
|
|
7
|
+
|
|
8
|
+
const PLUGIN_METADATA_URL = 'https://raw.githubusercontent.com/mattgauzza/empirical-memory-plugin/main/.claude-plugin/marketplace.json';
|
|
9
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
10
|
+
const SUPPORTED_TARGETS = ['codex', 'claude', 'copilot'];
|
|
11
|
+
|
|
12
|
+
function parseVersion(value) {
|
|
13
|
+
const match = String(value || '').trim().match(/^v?(\d+)\.(\d+)\.(\d+)/i);
|
|
14
|
+
return match ? match.slice(1).map(Number) : null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function compareVersions(left, right) {
|
|
18
|
+
const a = parseVersion(left);
|
|
19
|
+
const b = parseVersion(right);
|
|
20
|
+
if (!a || !b) return null;
|
|
21
|
+
for (let i = 0; i < 3; i += 1) {
|
|
22
|
+
if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1;
|
|
23
|
+
}
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function extractVersion(output, pluginName = 'empirical-memory') {
|
|
28
|
+
const text = String(output || '');
|
|
29
|
+
const jsonCandidates = [text, ...text.split(/\r?\n/).map((line) => line.trim())];
|
|
30
|
+
for (const candidate of jsonCandidates) {
|
|
31
|
+
try {
|
|
32
|
+
const value = JSON.parse(candidate);
|
|
33
|
+
const rows = Array.isArray(value) ? value : [value, ...(Array.isArray(value?.plugins) ? value.plugins : [])];
|
|
34
|
+
const match = rows.find((row) => row && (row.name === pluginName || row.plugin === pluginName));
|
|
35
|
+
if (match?.version) return String(match.version);
|
|
36
|
+
} catch (_error) {
|
|
37
|
+
// Native CLIs may print a table instead of JSON.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const line = text.split(/\r?\n/).find((value) => value.toLowerCase().includes(pluginName.toLowerCase()));
|
|
41
|
+
const match = line && line.match(new RegExp(`${pluginName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[^\\d]*(\\d+\\.\\d+\\.\\d+)`, 'i'));
|
|
42
|
+
return match ? match[1] : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function readInstalledVersions(target, pluginNames) {
|
|
46
|
+
const commands = {
|
|
47
|
+
codex: ['codex', ['plugin', 'list']],
|
|
48
|
+
claude: ['claude', ['plugin', 'list']],
|
|
49
|
+
copilot: ['copilot', ['plugin', 'list']]
|
|
50
|
+
};
|
|
51
|
+
const [command, args] = commands[target];
|
|
52
|
+
if (!commandExists(command)) return null;
|
|
53
|
+
const result = runResolvedCommand(command, args, { stdio: 'pipe' });
|
|
54
|
+
if (!result || result.status !== 0) return null;
|
|
55
|
+
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
56
|
+
return Object.fromEntries(pluginNames.map((name) => [name, extractVersion(output, name)]));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function fetchLatestVersions(fetchImpl = globalThis.fetch) {
|
|
60
|
+
if (typeof fetchImpl !== 'function') return null;
|
|
61
|
+
const response = await fetchImpl(PLUGIN_METADATA_URL, { signal: AbortSignal.timeout(5000) });
|
|
62
|
+
if (!response.ok) throw new Error(`marketplace metadata returned HTTP ${response.status}`);
|
|
63
|
+
const data = await response.json();
|
|
64
|
+
if (Array.isArray(data?.plugins)) {
|
|
65
|
+
return Object.fromEntries(data.plugins.map((plugin) => [plugin.name, plugin.version]).filter(([, version]) => version));
|
|
66
|
+
}
|
|
67
|
+
return data?.version ? { 'empirical-memory': data.version } : {};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function shouldCheck(cache, now = Date.now(), force = false) {
|
|
71
|
+
return force || !cache?.checkedAt || now - Number(cache.checkedAt) >= CHECK_INTERVAL_MS;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function checkNativePluginUpdates({ force = false, interactive = process.stdin.isTTY, fetchImpl } = {}) {
|
|
75
|
+
if (!interactive && !force) return { skipped: true, reason: 'noninteractive' };
|
|
76
|
+
const config = readDeviceConfig();
|
|
77
|
+
const cache = config.pluginUpdates || {};
|
|
78
|
+
if (!shouldCheck(cache, Date.now(), force)) return { skipped: true, reason: 'rate_limited' };
|
|
79
|
+
|
|
80
|
+
let latestVersion;
|
|
81
|
+
try {
|
|
82
|
+
latestVersion = await fetchLatestVersions(fetchImpl);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
config.pluginUpdates = { ...cache, checkedAt: Date.now(), lastError: error.message };
|
|
85
|
+
writeDeviceConfig(config);
|
|
86
|
+
return { skipped: true, reason: 'marketplace_unavailable' };
|
|
87
|
+
}
|
|
88
|
+
const pluginNames = Object.keys(latestVersion);
|
|
89
|
+
const installed = {};
|
|
90
|
+
for (const target of SUPPORTED_TARGETS) installed[target] = readInstalledVersions(target, pluginNames);
|
|
91
|
+
config.pluginUpdates = { checkedAt: Date.now(), latestVersions: latestVersion, installed };
|
|
92
|
+
writeDeviceConfig(config);
|
|
93
|
+
|
|
94
|
+
const outdated = SUPPORTED_TARGETS.flatMap((target) => pluginNames
|
|
95
|
+
.filter((name) => compareVersions(latestVersion[name], installed[target][name]) === 1)
|
|
96
|
+
.map((name) => ({ target, name, installed: installed[target][name], latest: latestVersion[name] })));
|
|
97
|
+
const latestSignature = JSON.stringify(latestVersion);
|
|
98
|
+
if (outdated.length === 0 || cache.offeredVersion === latestSignature) {
|
|
99
|
+
return { latestVersion, installed, outdated };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!interactive) {
|
|
103
|
+
return { latestVersion, installed, outdated, promptSuppressed: true };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// latestVersion is a map of pluginName -> version (e.g. {'empirical-memory': '1.1.2', ...}),
|
|
107
|
+
// not a single scalar -- interpolating it directly stringified to "[object Object]". Prefer
|
|
108
|
+
// the core plugin's version since that's the one most users recognize; the per-target
|
|
109
|
+
// breakdown line right below already lists every outdated plugin/version precisely.
|
|
110
|
+
const headlineVersion = latestVersion['empirical-memory'] || Object.values(latestVersion)[0] || 'update available';
|
|
111
|
+
log(uiHeader(`A newer empirical-memory plugin is available (${headlineVersion}).`));
|
|
112
|
+
log(`Installed versions: ${outdated.map((item) => `${item.target} ${item.name} ${item.installed} -> ${item.latest}`).join(', ')}`);
|
|
113
|
+
const update = await askYesNo('Update the native plugin now?', true);
|
|
114
|
+
if (!update) {
|
|
115
|
+
config.pluginUpdates = { ...config.pluginUpdates, offeredVersion: latestSignature, outcome: 'declined' };
|
|
116
|
+
writeDeviceConfig(config);
|
|
117
|
+
return { latestVersion, installed, outdated, declined: true };
|
|
118
|
+
}
|
|
119
|
+
const selection = (await ask('Which agents should be updated? (all, or numbers 1-' + outdated.length + ') ')).trim().toLowerCase();
|
|
120
|
+
const outdatedTargets = [...new Set(outdated.map((item) => item.target))];
|
|
121
|
+
const selectedTargets = selection === 'all' || !selection
|
|
122
|
+
? outdatedTargets
|
|
123
|
+
: parseSelection(selection, outdatedTargets.length).map((index) => outdatedTargets[index]);
|
|
124
|
+
const results = {};
|
|
125
|
+
for (const target of selectedTargets) {
|
|
126
|
+
try {
|
|
127
|
+
installNativePlugin(target, [...new Set(outdated.filter((item) => item.target === target).map((item) => item.name))]);
|
|
128
|
+
results[target] = 'updated';
|
|
129
|
+
} catch (error) {
|
|
130
|
+
results[target] = `failed: ${summarizeProcessOutput({ stderr: error.message })}`;
|
|
131
|
+
warn(`Could not update ${target}: ${error.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
config.pluginUpdates = { ...config.pluginUpdates, offeredVersion: latestSignature, outcome: results };
|
|
135
|
+
writeDeviceConfig(config);
|
|
136
|
+
return { latestVersion, installed, outdated, results };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = {
|
|
140
|
+
CHECK_INTERVAL_MS,
|
|
141
|
+
SUPPORTED_TARGETS,
|
|
142
|
+
parseVersion,
|
|
143
|
+
compareVersions,
|
|
144
|
+
extractVersion,
|
|
145
|
+
shouldCheck,
|
|
146
|
+
fetchLatestVersions,
|
|
147
|
+
checkNativePluginUpdates
|
|
148
|
+
};
|
package/lib/workspaceCommands.js
CHANGED
|
@@ -174,11 +174,11 @@ async function runWorkspaceCommand(args) {
|
|
|
174
174
|
const selected = payload.defaultOption.selected
|
|
175
175
|
? payload.defaultOption
|
|
176
176
|
: (payload.workspaces || []).find((ws) => ws.selected) || payload.defaultOption;
|
|
177
|
-
log(`Current workspace: ${selected.label || 'Default
|
|
177
|
+
log(`Current workspace: ${selected.label || 'Default Workspace'}`);
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
180
|
const payload = await httpJson(currentUrl, 'GET', null, authHeaders);
|
|
181
|
-
log(`Current workspace: ${payload?.current?.label || 'Default
|
|
181
|
+
log(`Current workspace: ${payload?.current?.label || 'Default Workspace'}`);
|
|
182
182
|
if (payload?.locked) {
|
|
183
183
|
log('This OAuth client is admin-locked to a workspace.');
|
|
184
184
|
}
|
|
@@ -206,7 +206,7 @@ async function runWorkspaceCommand(args) {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
if (!requested) {
|
|
209
|
-
throw new Error('Please provide a workspace selection, e.g. `empirical workspace use 1` or `empirical workspace use "Default
|
|
209
|
+
throw new Error('Please provide a workspace selection, e.g. `empirical workspace use 1` or `empirical workspace use "Default Workspace"`.');
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
const target = resolveWorkspaceTarget(requested, payload, teamPayload);
|
|
@@ -250,7 +250,7 @@ async function runWorkspaceCommand(args) {
|
|
|
250
250
|
if (target?.ambiguous) throw new Error(`Ambiguous workspace: ${requested}. Use its list number or workspace ID.`);
|
|
251
251
|
if (!target) throw new Error(`Workspace not found: ${requested}`);
|
|
252
252
|
if (target.isDefault || target.key === 'default_brain') {
|
|
253
|
-
throw new Error('The Default
|
|
253
|
+
throw new Error('The Default Workspace cannot be deleted.');
|
|
254
254
|
}
|
|
255
255
|
if (!target.workspaceId) throw new Error(`Workspace is not a deletable team brain: ${target.label}`);
|
|
256
256
|
|
|
@@ -265,7 +265,7 @@ async function runWorkspaceCommand(args) {
|
|
|
265
265
|
if (target.selected && clientId) {
|
|
266
266
|
const updateUrl = new URL(`/oauth/clients/${encodedClientId}/workspace-selection`, baseUrl).toString();
|
|
267
267
|
const updated = await httpJson(updateUrl, 'POST', { key: 'default_brain' }, authHeaders);
|
|
268
|
-
log(uiSuccess(`Soft-deleted workspace ${targetDescription}. Active workspace reset to ${updated?.current?.label || 'Default
|
|
268
|
+
log(uiSuccess(`Soft-deleted workspace ${targetDescription}. Active workspace reset to ${updated?.current?.label || 'Default Workspace'}.`));
|
|
269
269
|
clearSelectedWorkspace();
|
|
270
270
|
} else {
|
|
271
271
|
log(uiSuccess(`Soft-deleted workspace ${targetDescription}.`));
|