@ductape/cli 0.2.22 → 0.3.0
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 +7 -0
- package/README.md +7 -0
- package/dist/commands/generate.js +1 -1
- package/dist/commands/migrate-codebase.d.ts +176 -0
- package/dist/commands/migrate-codebase.js +753 -0
- package/dist/commands/migration-artifact.d.ts +32 -0
- package/dist/commands/migration-artifact.js +48 -0
- package/dist/commands/migration-assurance.d.ts +10 -0
- package/dist/commands/migration-assurance.js +120 -0
- package/dist/commands/migration-database.d.ts +21 -0
- package/dist/commands/migration-database.js +162 -0
- package/dist/commands/migration-e2e.d.ts +68 -0
- package/dist/commands/migration-e2e.js +189 -0
- package/dist/commands/migration-environments.d.ts +20 -0
- package/dist/commands/migration-environments.js +58 -0
- package/dist/commands/migration-generated.d.ts +68 -0
- package/dist/commands/migration-generated.js +217 -0
- package/dist/commands/migration-portfolio.d.ts +88 -0
- package/dist/commands/migration-portfolio.js +223 -0
- package/dist/commands/migration-products.d.ts +23 -0
- package/dist/commands/migration-products.js +88 -0
- package/dist/commands/migration-review.d.ts +248 -0
- package/dist/commands/migration-review.js +624 -0
- package/dist/commands/migration-secrets.d.ts +24 -0
- package/dist/commands/migration-secrets.js +93 -0
- package/dist/commands/migration-slice.d.ts +132 -0
- package/dist/commands/migration-slice.js +266 -0
- package/dist/commands/migration-verification.d.ts +12 -0
- package/dist/commands/migration-verification.js +194 -0
- package/dist/commands/products.d.ts +1 -0
- package/dist/commands/products.js +33 -1
- package/dist/commands/secrets.d.ts +8 -0
- package/dist/commands/secrets.js +44 -0
- package/dist/index.js +354 -2
- package/dist/lib/integrations.js +6 -1
- package/dist/lib/migration-artifact.d.ts +13 -0
- package/dist/lib/migration-artifact.js +103 -0
- package/package.json +8 -1
- package/API_PARITY.md +0 -58
- package/DB_MIGRATE_PLAN.md +0 -223
- package/ductape.example.ts +0 -18
- package/src/commands/apply.ts +0 -153
- package/src/commands/apps-import.ts +0 -43
- package/src/commands/apps.ts +0 -92
- package/src/commands/cloud.ts +0 -68
- package/src/commands/completion.ts +0 -119
- package/src/commands/db-migrate.ts +0 -209
- package/src/commands/db-schema.ts +0 -429
- package/src/commands/db.ts +0 -63
- package/src/commands/generate.ts +0 -74
- package/src/commands/graph.ts +0 -51
- package/src/commands/init.ts +0 -39
- package/src/commands/install.ts +0 -76
- package/src/commands/link.ts +0 -84
- package/src/commands/login.ts +0 -145
- package/src/commands/logout.ts +0 -7
- package/src/commands/platform.ts +0 -123
- package/src/commands/products.ts +0 -228
- package/src/commands/profiles.ts +0 -23
- package/src/commands/resources.ts +0 -213
- package/src/commands/secrets.ts +0 -46
- package/src/commands/unlink.ts +0 -15
- package/src/commands/whoami.ts +0 -27
- package/src/commands/workspaces.ts +0 -98
- package/src/index.ts +0 -523
- package/src/lib/api-client.ts +0 -88
- package/src/lib/app-import.ts +0 -82
- package/src/lib/apply-loaders.ts +0 -30
- package/src/lib/config.ts +0 -230
- package/src/lib/context-store.ts +0 -77
- package/src/lib/db-methods.ts +0 -56
- package/src/lib/db-types.ts +0 -104
- package/src/lib/encryption.ts +0 -12
- package/src/lib/forms/enums.ts +0 -52
- package/src/lib/forms/index.ts +0 -8
- package/src/lib/forms/prompt.ts +0 -209
- package/src/lib/forms/schemas.ts +0 -288
- package/src/lib/forms/types.ts +0 -27
- package/src/lib/http.ts +0 -76
- package/src/lib/integrations.ts +0 -105
- package/src/lib/interactive-opts.ts +0 -13
- package/src/lib/migration-files.ts +0 -58
- package/src/lib/output.ts +0 -39
- package/src/lib/platform-api.ts +0 -209
- package/src/lib/proxy/context.ts +0 -94
- package/src/lib/proxy/db-proxy.ts +0 -40
- package/src/lib/proxy/graph-proxy.ts +0 -64
- package/src/lib/proxy/sdk-proxy.ts +0 -58
- package/src/lib/read-body.ts +0 -57
- package/src/lib/resources.ts +0 -160
- package/src/lib/schema-loader.ts +0 -152
- package/src/lib/snippets.ts +0 -165
- package/src/lib/templates.ts +0 -157
- package/src/lib/workspace-context.ts +0 -43
- package/src/lib/workspaces.ts +0 -283
- package/test/apply-loaders.test.ts +0 -40
- package/test/encryption.test.ts +0 -20
- package/test/forms.test.ts +0 -25
- package/test/http.test.ts +0 -15
- package/test/platform-api.test.ts +0 -42
- package/test/resources.test.ts +0 -23
- package/test/workspace-resolve.test.ts +0 -29
- package/tsconfig.json +0 -15
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildCrudParams,
|
|
3
|
-
listResourceTypes,
|
|
4
|
-
proxyMethodForVerb,
|
|
5
|
-
resolveResourceType,
|
|
6
|
-
type CrudVerb,
|
|
7
|
-
} from '../lib/resources.js';
|
|
8
|
-
import { resolveResourceSchema } from '../lib/forms/index.js';
|
|
9
|
-
import { isInteractive, promptTag } from '../lib/forms/prompt.js';
|
|
10
|
-
import { toInteractiveOpts, type CommandInteractiveFlags } from '../lib/interactive-opts.js';
|
|
11
|
-
import { bodyRequiredHint, resolveBody } from '../lib/read-body.js';
|
|
12
|
-
import { requireSession, getSdkProxy } from '../lib/proxy/context.js';
|
|
13
|
-
import { printJson } from '../lib/output.js';
|
|
14
|
-
|
|
15
|
-
const CRUD_VERBS: CrudVerb[] = ['create', 'list', 'get', 'update', 'delete', 'connect'];
|
|
16
|
-
|
|
17
|
-
export function runResourcesList(json: boolean): void {
|
|
18
|
-
printJson({ resource_types: listResourceTypes() }, json);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function runEventTopicCrud(
|
|
22
|
-
verb: string,
|
|
23
|
-
opts: { tag?: string; file?: string; json?: boolean } & CommandInteractiveFlags,
|
|
24
|
-
extraArgs: string[],
|
|
25
|
-
): Promise<void> {
|
|
26
|
-
const crud = verb.toLowerCase() as CrudVerb;
|
|
27
|
-
if (!CRUD_VERBS.includes(crud)) {
|
|
28
|
-
throw new Error(`Unknown verb "${verb}". Use: ${CRUD_VERBS.join(', ')}`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const session = requireSession();
|
|
32
|
-
const productTag = session.project.product_tag;
|
|
33
|
-
const interactive = toInteractiveOpts(opts);
|
|
34
|
-
let tag = opts.tag ?? extraArgs[0];
|
|
35
|
-
if (!tag && ['get', 'update', 'delete', 'list'].includes(crud) && isInteractive(interactive)) {
|
|
36
|
-
tag = await promptTag(crud === 'list' ? 'Broker tag' : 'Topic tag (broker:topic)');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const method = crud === 'get' ? 'topics.fetch' : `topics.${crud}`;
|
|
40
|
-
const needsBody = crud === 'create' || crud === 'update';
|
|
41
|
-
const body = needsBody
|
|
42
|
-
? await resolveBody({
|
|
43
|
-
...interactive,
|
|
44
|
-
file: opts.file,
|
|
45
|
-
schema: undefined,
|
|
46
|
-
patch: crud === 'update',
|
|
47
|
-
requireBody: crud === 'create',
|
|
48
|
-
})
|
|
49
|
-
: undefined;
|
|
50
|
-
|
|
51
|
-
if (crud === 'create' && (!body || Object.keys(body).length === 0)) {
|
|
52
|
-
throw new Error(`create requires a body. ${bodyRequiredHint('messageBrokers:topics:create')}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let params: unknown[];
|
|
56
|
-
if (crud === 'create') {
|
|
57
|
-
params = [productTag, body ?? {}];
|
|
58
|
-
} else if (crud === 'list') {
|
|
59
|
-
if (!tag) throw new Error('--tag <broker-tag> is required for list');
|
|
60
|
-
params = [productTag, tag];
|
|
61
|
-
} else if (crud === 'get' || crud === 'delete') {
|
|
62
|
-
if (!tag) throw new Error('--tag <broker:topic> is required');
|
|
63
|
-
params = [productTag, tag];
|
|
64
|
-
} else if (crud === 'update') {
|
|
65
|
-
if (!tag) throw new Error('--tag <broker:topic> is required');
|
|
66
|
-
params = [productTag, tag, body ?? {}];
|
|
67
|
-
} else {
|
|
68
|
-
throw new Error(`Unsupported verb "${crud}" for events topics`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const proxy = getSdkProxy(session);
|
|
72
|
-
const result = await proxy.execute('messageBrokers', method, params);
|
|
73
|
-
if (crud === 'create') {
|
|
74
|
-
const fullTag = String((body as { tag?: unknown } | undefined)?.tag ?? '');
|
|
75
|
-
if (!fullTag.includes(':')) {
|
|
76
|
-
throw new Error('Topic create response could not be verified because body.tag is invalid');
|
|
77
|
-
}
|
|
78
|
-
const verified = await proxy.execute(
|
|
79
|
-
'messageBrokers',
|
|
80
|
-
'topics.fetch',
|
|
81
|
-
[productTag, fullTag],
|
|
82
|
-
);
|
|
83
|
-
if (!verified) {
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Topic creation returned without an error, but "${fullTag}" was not found during verification`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
printJson({ created: true, topic: verified }, Boolean(opts.json));
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (crud === 'get' && !result) {
|
|
92
|
-
throw new Error(`Topic "${tag}" was not found`);
|
|
93
|
-
}
|
|
94
|
-
printJson(result, Boolean(opts.json));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export async function runNotificationMessageCrud(
|
|
98
|
-
verb: string,
|
|
99
|
-
opts: { tag?: string; notification?: string; file?: string; json?: boolean },
|
|
100
|
-
): Promise<void> {
|
|
101
|
-
const crud = verb.toLowerCase();
|
|
102
|
-
if (!['create', 'list', 'get', 'update'].includes(crud)) {
|
|
103
|
-
throw new Error('Notification message verb must be: create | list | get | update');
|
|
104
|
-
}
|
|
105
|
-
const session = requireSession();
|
|
106
|
-
const proxy = getSdkProxy(session);
|
|
107
|
-
const product = session.project.product_tag;
|
|
108
|
-
const body = ['create', 'update'].includes(crud)
|
|
109
|
-
? await resolveBody({ file: opts.file, patch: crud === 'update', requireBody: crud === 'create', interactive: false })
|
|
110
|
-
: undefined;
|
|
111
|
-
const method = `messages.${crud === 'get' ? 'fetch' : crud}`;
|
|
112
|
-
let params: unknown[];
|
|
113
|
-
if (crud === 'create') params = [product, body ?? {}];
|
|
114
|
-
else if (crud === 'list') {
|
|
115
|
-
if (!opts.notification) throw new Error('--notification <notification-tag> is required');
|
|
116
|
-
params = [product, opts.notification];
|
|
117
|
-
} else if (crud === 'get') {
|
|
118
|
-
if (!opts.tag) throw new Error('--tag <notification:message> is required');
|
|
119
|
-
params = [product, opts.tag];
|
|
120
|
-
} else {
|
|
121
|
-
if (!opts.tag) throw new Error('--tag <notification:message> is required');
|
|
122
|
-
params = [product, opts.tag, body ?? {}];
|
|
123
|
-
}
|
|
124
|
-
try {
|
|
125
|
-
printJson(await proxy.execute('notifications', method, params), Boolean(opts.json));
|
|
126
|
-
} catch (error) {
|
|
127
|
-
if (crud !== 'list' || !opts.notification) throw error;
|
|
128
|
-
|
|
129
|
-
// Compatibility fallback for platform versions whose message lookup uses a stale product
|
|
130
|
-
// snapshot while the component inventory already contains the notification.
|
|
131
|
-
const listed = await proxy.execute<unknown>(
|
|
132
|
-
'notifications',
|
|
133
|
-
'list',
|
|
134
|
-
buildCrudParams('notifications', 'list', product, {}),
|
|
135
|
-
);
|
|
136
|
-
const notifications = Array.isArray(listed)
|
|
137
|
-
? listed
|
|
138
|
-
: Array.isArray((listed as { data?: unknown[] } | null)?.data)
|
|
139
|
-
? (listed as { data: unknown[] }).data
|
|
140
|
-
: [];
|
|
141
|
-
const notification = notifications.find(
|
|
142
|
-
(item) => (item as { tag?: string }).tag === opts.notification,
|
|
143
|
-
) as { messages?: unknown[] } | undefined;
|
|
144
|
-
if (!notification) throw error;
|
|
145
|
-
printJson(notification.messages ?? [], Boolean(opts.json));
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export async function runResourceCrud(
|
|
150
|
-
typeName: string,
|
|
151
|
-
verb: string,
|
|
152
|
-
opts: { tag?: string; file?: string; json?: boolean } & CommandInteractiveFlags,
|
|
153
|
-
extraArgs: string[],
|
|
154
|
-
): Promise<void> {
|
|
155
|
-
const crud = verb.toLowerCase() as CrudVerb;
|
|
156
|
-
if (!CRUD_VERBS.includes(crud)) {
|
|
157
|
-
throw new Error(`Unknown verb "${verb}". Use: ${CRUD_VERBS.join(', ')}`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const session = requireSession();
|
|
161
|
-
const module = resolveResourceType(typeName);
|
|
162
|
-
|
|
163
|
-
if (module === 'product' || module === 'app') {
|
|
164
|
-
if (crud === 'list') {
|
|
165
|
-
throw new Error(
|
|
166
|
-
`Use \`ductape ${module}s list\` — listing uses the workspace REST API (not sdk-proxy).`,
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
if (crud === 'delete') {
|
|
170
|
-
throw new Error(`Use \`ductape ${module}s delete --id <id>\`.`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const method = proxyMethodForVerb(crud);
|
|
175
|
-
const interactive = toInteractiveOpts(opts);
|
|
176
|
-
let tag = opts.tag ?? extraArgs[0];
|
|
177
|
-
if (!tag && ['get', 'update', 'delete'].includes(crud) && isInteractive(interactive)) {
|
|
178
|
-
tag = await promptTag('Component tag');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const needsBody = crud === 'create' || crud === 'update' || crud === 'connect';
|
|
182
|
-
const schema = needsBody ? resolveResourceSchema(module, crud) : undefined;
|
|
183
|
-
const body = needsBody
|
|
184
|
-
? await resolveBody({
|
|
185
|
-
...interactive,
|
|
186
|
-
file: opts.file,
|
|
187
|
-
schema,
|
|
188
|
-
patch: crud === 'update',
|
|
189
|
-
requireBody: crud === 'create',
|
|
190
|
-
})
|
|
191
|
-
: undefined;
|
|
192
|
-
|
|
193
|
-
if (crud === 'create' && (!body || Object.keys(body).length === 0)) {
|
|
194
|
-
throw new Error(`create requires a body. ${bodyRequiredHint(`${module}:create`)}`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
let payload = body;
|
|
198
|
-
if (crud === 'connect' && tag) {
|
|
199
|
-
payload = {
|
|
200
|
-
...(payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : {}),
|
|
201
|
-
tag,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const params = buildCrudParams(module, method, session.project.product_tag, {
|
|
206
|
-
tag,
|
|
207
|
-
body: payload,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const proxy = getSdkProxy(session);
|
|
211
|
-
const result = await proxy.execute(module, method, params);
|
|
212
|
-
printJson(result, Boolean(opts.json));
|
|
213
|
-
}
|
package/src/commands/secrets.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { isInteractive, promptKey } from '../lib/forms/prompt.js';
|
|
2
|
-
import { toInteractiveOpts, type CommandInteractiveFlags } from '../lib/interactive-opts.js';
|
|
3
|
-
import { bodyRequiredHint, resolveBody } from '../lib/read-body.js';
|
|
4
|
-
import { buildCrudParams, proxyMethodForVerb, type CrudVerb } from '../lib/resources.js';
|
|
5
|
-
import { getSdkProxy, requireSession } from '../lib/proxy/context.js';
|
|
6
|
-
import { printJson } from '../lib/output.js';
|
|
7
|
-
|
|
8
|
-
const VERBS: CrudVerb[] = ['create', 'list', 'get', 'update', 'delete'];
|
|
9
|
-
|
|
10
|
-
export async function runSecrets(
|
|
11
|
-
verb: string,
|
|
12
|
-
opts: { key?: string; file?: string; json?: boolean } & CommandInteractiveFlags,
|
|
13
|
-
): Promise<void> {
|
|
14
|
-
const crud = verb.toLowerCase() as CrudVerb;
|
|
15
|
-
if (!VERBS.includes(crud)) {
|
|
16
|
-
throw new Error(`Unknown secrets verb "${verb}". Use: ${VERBS.join(', ')}`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const session = requireSession();
|
|
20
|
-
const method = proxyMethodForVerb(crud);
|
|
21
|
-
const interactive = toInteractiveOpts(opts);
|
|
22
|
-
let key = opts.key;
|
|
23
|
-
if (!key && ['fetch', 'delete', 'update'].includes(method) && isInteractive(interactive)) {
|
|
24
|
-
key = await promptKey();
|
|
25
|
-
}
|
|
26
|
-
const body =
|
|
27
|
-
crud === 'create' || crud === 'update'
|
|
28
|
-
? await resolveBody({
|
|
29
|
-
...interactive,
|
|
30
|
-
file: opts.file,
|
|
31
|
-
schemaKey: `secrets:${crud}`,
|
|
32
|
-
patch: crud === 'update',
|
|
33
|
-
requireBody: crud === 'create',
|
|
34
|
-
})
|
|
35
|
-
: undefined;
|
|
36
|
-
if (crud === 'create' && (!body || Object.keys(body).length === 0)) {
|
|
37
|
-
throw new Error(`create requires a body. ${bodyRequiredHint('secrets:create')}`);
|
|
38
|
-
}
|
|
39
|
-
const params = buildCrudParams('secrets', method, session.project.product_tag, {
|
|
40
|
-
tag: key,
|
|
41
|
-
body,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const result = await getSdkProxy(session).execute('secrets', method, params);
|
|
45
|
-
printJson(result, Boolean(opts.json));
|
|
46
|
-
}
|
package/src/commands/unlink.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { findProjectConfig, PROJECT_CONFIG_PATH } from '../lib/config.js';
|
|
4
|
-
import { success } from '../lib/output.js';
|
|
5
|
-
|
|
6
|
-
export function runUnlink(opts: { dir?: string }): void {
|
|
7
|
-
const dir = path.resolve(opts.dir ?? process.cwd());
|
|
8
|
-
const found = findProjectConfig(dir);
|
|
9
|
-
if (!found) {
|
|
10
|
-
throw new Error('No linked project in this directory');
|
|
11
|
-
}
|
|
12
|
-
const configPath = path.join(found.dir, PROJECT_CONFIG_PATH);
|
|
13
|
-
fs.unlinkSync(configPath);
|
|
14
|
-
success(`Removed ${configPath}`);
|
|
15
|
-
}
|
package/src/commands/whoami.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { getActiveProfileName, getApiUrl, loadCredentials, loadGlobalConfig, findProjectConfig } from '../lib/config.js';
|
|
2
|
-
import { getActiveWorkspace } from '../lib/workspaces.js';
|
|
3
|
-
import { printJson } from '../lib/output.js';
|
|
4
|
-
|
|
5
|
-
export function runWhoami(json: boolean): void {
|
|
6
|
-
const creds = loadCredentials();
|
|
7
|
-
const project = findProjectConfig();
|
|
8
|
-
const active = getActiveWorkspace();
|
|
9
|
-
const profile = project?.config
|
|
10
|
-
? getActiveProfileName(project.config)
|
|
11
|
-
: loadGlobalConfig().default_profile;
|
|
12
|
-
|
|
13
|
-
printJson(
|
|
14
|
-
{
|
|
15
|
-
logged_in: Boolean(creds),
|
|
16
|
-
email: creds?.email,
|
|
17
|
-
user_id: creds?.user_id,
|
|
18
|
-
profile,
|
|
19
|
-
api_url: getApiUrl(profile),
|
|
20
|
-
active_workspace: active
|
|
21
|
-
? { id: active.id, name: active.name }
|
|
22
|
-
: null,
|
|
23
|
-
project: project?.config ?? null,
|
|
24
|
-
},
|
|
25
|
-
json,
|
|
26
|
-
);
|
|
27
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
applyActiveWorkspace,
|
|
3
|
-
fetchWorkspaces,
|
|
4
|
-
getActiveWorkspace,
|
|
5
|
-
printWorkspaceTable,
|
|
6
|
-
promptWorkspaceSelection,
|
|
7
|
-
selectAndApplyWorkspace,
|
|
8
|
-
workspaceId,
|
|
9
|
-
workspaceLabel,
|
|
10
|
-
} from '../lib/workspaces.js';
|
|
11
|
-
import { printJson, success } from '../lib/output.js';
|
|
12
|
-
|
|
13
|
-
export async function runWorkspacesList(opts: {
|
|
14
|
-
profile?: string;
|
|
15
|
-
json?: boolean;
|
|
16
|
-
refresh?: boolean;
|
|
17
|
-
}): Promise<void> {
|
|
18
|
-
const workspaces = await fetchWorkspaces(opts.profile);
|
|
19
|
-
const active = getActiveWorkspace();
|
|
20
|
-
|
|
21
|
-
if (opts.json) {
|
|
22
|
-
printJson(
|
|
23
|
-
{
|
|
24
|
-
active_workspace_id: active?.id ?? null,
|
|
25
|
-
active_workspace_name: active?.name ?? null,
|
|
26
|
-
workspaces,
|
|
27
|
-
},
|
|
28
|
-
true,
|
|
29
|
-
);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (active) {
|
|
34
|
-
console.log(`Active workspace: ${active.name ?? active.id}`);
|
|
35
|
-
console.log(` ${active.id}\n`);
|
|
36
|
-
}
|
|
37
|
-
printWorkspaceTable(workspaces, active?.id);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function runWorkspacesCurrent(json: boolean): void {
|
|
41
|
-
const active = getActiveWorkspace();
|
|
42
|
-
if (!active) {
|
|
43
|
-
if (json) {
|
|
44
|
-
printJson({ active: null }, true);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
console.log('No active workspace. Run: ductape login or ductape workspaces use');
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (json) {
|
|
51
|
-
printJson({ active_workspace_id: active.id, active_workspace_name: active.name }, true);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
console.log(`Active workspace: ${active.name ?? active.id}`);
|
|
55
|
-
console.log(` ${active.id}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function runWorkspacesUse(
|
|
59
|
-
selector: string | undefined,
|
|
60
|
-
opts: { profile?: string; dir?: string; json?: boolean },
|
|
61
|
-
): Promise<void> {
|
|
62
|
-
const workspaces = await fetchWorkspaces(opts.profile);
|
|
63
|
-
|
|
64
|
-
let chosen;
|
|
65
|
-
if (!selector) {
|
|
66
|
-
chosen = await promptWorkspaceSelection(workspaces, {
|
|
67
|
-
message: 'Switch workspace:',
|
|
68
|
-
});
|
|
69
|
-
await applyActiveWorkspace(chosen, { syncProject: true, dir: opts.dir });
|
|
70
|
-
} else {
|
|
71
|
-
chosen = await selectAndApplyWorkspace({
|
|
72
|
-
profile: opts.profile,
|
|
73
|
-
selector,
|
|
74
|
-
skipPrompt: true,
|
|
75
|
-
syncProject: true,
|
|
76
|
-
dir: opts.dir,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const id = workspaceId(chosen);
|
|
81
|
-
const name = workspaceLabel(chosen);
|
|
82
|
-
if (opts.json) {
|
|
83
|
-
printJson({ active_workspace_id: id, active_workspace_name: name }, true);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
success(`Switched to workspace: ${name} (${id})`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Refresh workspace list from API and update login cache */
|
|
90
|
-
export async function runWorkspacesRefresh(opts: { profile?: string; json?: boolean }): Promise<void> {
|
|
91
|
-
const workspaces = await fetchWorkspaces(opts.profile);
|
|
92
|
-
if (opts.json) {
|
|
93
|
-
printJson({ workspaces, count: workspaces.length }, true);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
success(`Refreshed ${workspaces.length} workspace(s) from API`);
|
|
97
|
-
await runWorkspacesList({ profile: opts.profile, json: false });
|
|
98
|
-
}
|