sharetribe-cli 1.15.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/.eslintrc.json +29 -0
- package/.prettierrc +9 -0
- package/build.js +58 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +7 -0
- package/package.json +58 -0
- package/src/commands/assets/index.ts +338 -0
- package/src/commands/events/index.ts +289 -0
- package/src/commands/help.ts +19 -0
- package/src/commands/listing-approval.ts +121 -0
- package/src/commands/login.ts +43 -0
- package/src/commands/logout.ts +17 -0
- package/src/commands/notifications/index.ts +221 -0
- package/src/commands/process/aliases.ts +82 -0
- package/src/commands/process/combined.ts +62 -0
- package/src/commands/process/create.ts +35 -0
- package/src/commands/process/index.ts +309 -0
- package/src/commands/process/list.ts +75 -0
- package/src/commands/process/pull.ts +81 -0
- package/src/commands/process/push.ts +67 -0
- package/src/commands/search/index.ts +254 -0
- package/src/commands/stripe/index.ts +114 -0
- package/src/commands/version.ts +40 -0
- package/src/index.ts +131 -0
- package/src/types/index.ts +21 -0
- package/src/util/command-router.ts +41 -0
- package/src/util/help-formatter.ts +266 -0
- package/src/util/output.ts +83 -0
- package/test/help-comparison.test.ts +255 -0
- package/test/process-builder.test.ts +14 -0
- package/test/process-integration.test.ts +189 -0
- package/test/strict-comparison.test.ts +722 -0
- package/tsconfig.json +50 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search command - manage search schemas
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import {
|
|
7
|
+
listSearchSchemas as sdkListSearchSchemas,
|
|
8
|
+
setSearchSchema as sdkSetSearchSchema,
|
|
9
|
+
unsetSearchSchema as sdkUnsetSearchSchema,
|
|
10
|
+
} from 'sharetribe-flex-build-sdk';
|
|
11
|
+
import { printTable, printError } from '../../util/output.js';
|
|
12
|
+
|
|
13
|
+
interface SetSchemaOptions {
|
|
14
|
+
key: string;
|
|
15
|
+
scope: string;
|
|
16
|
+
type: string;
|
|
17
|
+
doc?: string;
|
|
18
|
+
default?: string;
|
|
19
|
+
schemaFor?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface UnsetSchemaOptions {
|
|
23
|
+
key: string;
|
|
24
|
+
scope: string;
|
|
25
|
+
schemaFor?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Scope label mapping
|
|
30
|
+
*/
|
|
31
|
+
const SCOPE_LABELS: Record<string, string> = {
|
|
32
|
+
metadata: 'Metadata',
|
|
33
|
+
private: 'Private data',
|
|
34
|
+
protected: 'Protected data',
|
|
35
|
+
public: 'Public data',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sets a search schema field
|
|
40
|
+
*/
|
|
41
|
+
async function setSearchSchema(marketplace: string, opts: SetSchemaOptions): Promise<void> {
|
|
42
|
+
try {
|
|
43
|
+
await sdkSetSearchSchema(undefined, marketplace, {
|
|
44
|
+
key: opts.key,
|
|
45
|
+
scope: opts.scope,
|
|
46
|
+
type: opts.type,
|
|
47
|
+
doc: opts.doc,
|
|
48
|
+
defaultValue: opts.default,
|
|
49
|
+
schemaFor: opts.schemaFor,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const schemaFor = opts.schemaFor || 'listing';
|
|
53
|
+
const scopeLabel = SCOPE_LABELS[opts.scope] || opts.scope;
|
|
54
|
+
console.log(`${scopeLabel} schema, ${opts.key} is successfully set for ${schemaFor}.`);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
57
|
+
printError(error.message as string);
|
|
58
|
+
} else {
|
|
59
|
+
printError('Failed to set search schema');
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Unsets a search schema field
|
|
67
|
+
*/
|
|
68
|
+
async function unsetSearchSchema(marketplace: string, opts: UnsetSchemaOptions): Promise<void> {
|
|
69
|
+
try {
|
|
70
|
+
await sdkUnsetSearchSchema(undefined, marketplace, {
|
|
71
|
+
key: opts.key,
|
|
72
|
+
scope: opts.scope,
|
|
73
|
+
schemaFor: opts.schemaFor,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const schemaFor = opts.schemaFor || 'listing';
|
|
77
|
+
const scopeLabel = SCOPE_LABELS[opts.scope] || opts.scope;
|
|
78
|
+
console.log(`${scopeLabel} schema, ${opts.key} is successfully unset for ${schemaFor}.`);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
81
|
+
printError(error.message as string);
|
|
82
|
+
} else {
|
|
83
|
+
printError('Failed to unset search schema');
|
|
84
|
+
}
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Converts default value to display string
|
|
91
|
+
*/
|
|
92
|
+
function getDefaultValueLabel(value: unknown): string {
|
|
93
|
+
if (value === undefined || value === null) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
return value.join(', ');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return String(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Lists all search schemas
|
|
106
|
+
*/
|
|
107
|
+
async function listSearchSchemas(marketplace: string): Promise<void> {
|
|
108
|
+
try {
|
|
109
|
+
const schemas = await sdkListSearchSchemas(undefined, marketplace);
|
|
110
|
+
|
|
111
|
+
if (schemas.length === 0) {
|
|
112
|
+
console.log('No search schemas found.');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Map and sort the data (by schema-for, scope, key)
|
|
117
|
+
const rows = schemas
|
|
118
|
+
.map((s) => ({
|
|
119
|
+
'Schema for': s.schemaFor,
|
|
120
|
+
'Scope': s.scope,
|
|
121
|
+
'Key': s.key,
|
|
122
|
+
'Type': s.type,
|
|
123
|
+
'Default value': getDefaultValueLabel(s.defaultValue),
|
|
124
|
+
'Doc': s.doc || '',
|
|
125
|
+
}))
|
|
126
|
+
.sort((a, b) => {
|
|
127
|
+
// Sort by schema-for, then scope, then key
|
|
128
|
+
if (a['Schema for'] !== b['Schema for']) {
|
|
129
|
+
return a['Schema for'].localeCompare(b['Schema for']);
|
|
130
|
+
}
|
|
131
|
+
if (a['Scope'] !== b['Scope']) {
|
|
132
|
+
return a['Scope'].localeCompare(b['Scope']);
|
|
133
|
+
}
|
|
134
|
+
return a['Key'].localeCompare(b['Key']);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Print table using flex-cli compatible formatting
|
|
138
|
+
const headers = ['Schema for', 'Scope', 'Key', 'Type', 'Default value', 'Doc'];
|
|
139
|
+
|
|
140
|
+
// Calculate column widths
|
|
141
|
+
// flex-cli uses keywords (e.g., :version) which when stringified include the ':' prefix
|
|
142
|
+
// To match flex-cli widths, we add 1 to header length to simulate the ':' prefix
|
|
143
|
+
const widths: Record<string, number> = {};
|
|
144
|
+
for (const h of headers) {
|
|
145
|
+
widths[h] = h.length + 1;
|
|
146
|
+
}
|
|
147
|
+
for (const row of rows) {
|
|
148
|
+
for (const h of headers) {
|
|
149
|
+
const value = row[h] || '';
|
|
150
|
+
widths[h] = Math.max(widths[h], value.length);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Print empty line before table
|
|
155
|
+
console.log('');
|
|
156
|
+
|
|
157
|
+
// Print header
|
|
158
|
+
// flex-cli search format: each column padded to max_width, with 2 space separator between columns
|
|
159
|
+
// Last column: padding with trailing space
|
|
160
|
+
const headerParts = headers.map((h, i) => {
|
|
161
|
+
const width = widths[h] || 0;
|
|
162
|
+
const padded = h.padEnd(width);
|
|
163
|
+
return i === headers.length - 1 ? padded + ' ' : padded + ' ';
|
|
164
|
+
});
|
|
165
|
+
console.log(headerParts.join(''));
|
|
166
|
+
|
|
167
|
+
// Print rows
|
|
168
|
+
for (const row of rows) {
|
|
169
|
+
const rowParts = headers.map((h, i) => {
|
|
170
|
+
const value = row[h] || '';
|
|
171
|
+
const width = widths[h] || 0;
|
|
172
|
+
const padded = value.padEnd(width);
|
|
173
|
+
return i === headers.length - 1 ? padded + ' ' : padded + ' ';
|
|
174
|
+
});
|
|
175
|
+
console.log(rowParts.join(''));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Print empty line after table
|
|
179
|
+
console.log('');
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
182
|
+
printError(error.message as string);
|
|
183
|
+
} else {
|
|
184
|
+
printError('Failed to list search schemas');
|
|
185
|
+
}
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Registers search commands
|
|
192
|
+
*/
|
|
193
|
+
export function registerSearchCommands(program: Command): void {
|
|
194
|
+
const searchCmd = program
|
|
195
|
+
.command('search')
|
|
196
|
+
.description('list all search schemas')
|
|
197
|
+
.option('-m, --marketplace <MARKETPLACE_ID>', 'marketplace identifier')
|
|
198
|
+
.action(async (options) => {
|
|
199
|
+
const marketplace = options.marketplace || program.opts().marketplace;
|
|
200
|
+
if (!marketplace) {
|
|
201
|
+
console.error('Error: --marketplace is required');
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
await listSearchSchemas(marketplace);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// search set
|
|
208
|
+
searchCmd
|
|
209
|
+
.command('set')
|
|
210
|
+
.description('set search schema')
|
|
211
|
+
.requiredOption('--key <KEY>', 'schema key')
|
|
212
|
+
.requiredOption('--scope <SCOPE>', 'schema scope')
|
|
213
|
+
.requiredOption('--type <TYPE>', 'value type (enum, multi-enum, boolean, long, or text)')
|
|
214
|
+
.option('--doc <DOC>', 'description of the schema')
|
|
215
|
+
.option('--default <DEFAULT>', 'default value for search if value is not set')
|
|
216
|
+
.option('--schema-for <SCHEMA_FOR>', 'subject of the schema (listing, userProfile, or transaction)')
|
|
217
|
+
.option('-m, --marketplace <MARKETPLACE_ID>', 'marketplace identifier')
|
|
218
|
+
.action(async (opts) => {
|
|
219
|
+
const marketplace = opts.marketplace || program.opts().marketplace;
|
|
220
|
+
if (!marketplace) {
|
|
221
|
+
console.error('Error: --marketplace is required');
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
await setSearchSchema(marketplace, {
|
|
225
|
+
key: opts.key,
|
|
226
|
+
scope: opts.scope,
|
|
227
|
+
type: opts.type,
|
|
228
|
+
doc: opts.doc,
|
|
229
|
+
default: opts.default,
|
|
230
|
+
schemaFor: opts.schemaFor,
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// search unset
|
|
235
|
+
searchCmd
|
|
236
|
+
.command('unset')
|
|
237
|
+
.description('unset search schema')
|
|
238
|
+
.requiredOption('--key <KEY>', 'schema key')
|
|
239
|
+
.requiredOption('--scope <SCOPE>', 'schema scope')
|
|
240
|
+
.option('--schema-for <SCHEMA_FOR>', 'subject of the schema (listing, userProfile, or transaction)')
|
|
241
|
+
.option('-m, --marketplace <MARKETPLACE_ID>', 'marketplace identifier')
|
|
242
|
+
.action(async (opts) => {
|
|
243
|
+
const marketplace = opts.marketplace || program.opts().marketplace;
|
|
244
|
+
if (!marketplace) {
|
|
245
|
+
console.error('Error: --marketplace is required');
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
await unsetSearchSchema(marketplace, {
|
|
249
|
+
key: opts.key,
|
|
250
|
+
scope: opts.scope,
|
|
251
|
+
schemaFor: opts.schemaFor,
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe command - manage Stripe integration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { updateStripeVersion as sdkUpdateStripeVersion, SUPPORTED_STRIPE_VERSIONS } from 'sharetribe-flex-build-sdk';
|
|
7
|
+
import { printError } from '../../util/output.js';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Prompts for version selection
|
|
13
|
+
*/
|
|
14
|
+
async function promptForVersion(): Promise<string> {
|
|
15
|
+
const answers = await inquirer.prompt([
|
|
16
|
+
{
|
|
17
|
+
type: 'list',
|
|
18
|
+
name: 'version',
|
|
19
|
+
message: 'Select Stripe API version:',
|
|
20
|
+
choices: [...SUPPORTED_STRIPE_VERSIONS],
|
|
21
|
+
},
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
return answers.version;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Prompts for confirmation
|
|
29
|
+
*/
|
|
30
|
+
async function promptForConfirmation(): Promise<boolean> {
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log('WARNING: Changing Stripe API version may affect your integration.');
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log('After updating the Stripe API version, you may need to:');
|
|
35
|
+
console.log('- Handle new Capabilities requirements');
|
|
36
|
+
console.log('- Update identity verification settings');
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log('See Stripe documentation for details:');
|
|
39
|
+
console.log('https://stripe.com/docs/connect/capabilities-overview');
|
|
40
|
+
console.log('https://stripe.com/docs/connect/identity-verification');
|
|
41
|
+
console.log('');
|
|
42
|
+
|
|
43
|
+
const answers = await inquirer.prompt([
|
|
44
|
+
{
|
|
45
|
+
type: 'confirm',
|
|
46
|
+
name: 'confirmed',
|
|
47
|
+
message: 'Do you want to continue?',
|
|
48
|
+
default: false,
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
return answers.confirmed;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Updates Stripe API version
|
|
57
|
+
*/
|
|
58
|
+
async function updateStripeVersion(
|
|
59
|
+
marketplace: string,
|
|
60
|
+
version?: string,
|
|
61
|
+
force?: boolean
|
|
62
|
+
): Promise<void> {
|
|
63
|
+
try {
|
|
64
|
+
// Get version if not provided
|
|
65
|
+
let selectedVersion = version;
|
|
66
|
+
if (!selectedVersion) {
|
|
67
|
+
selectedVersion = await promptForVersion();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get confirmation unless --force flag is used
|
|
71
|
+
if (!force) {
|
|
72
|
+
const confirmed = await promptForConfirmation();
|
|
73
|
+
if (!confirmed) {
|
|
74
|
+
console.log('Cancelled.');
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Update via API (SDK validates version)
|
|
80
|
+
await sdkUpdateStripeVersion(undefined, marketplace, selectedVersion);
|
|
81
|
+
|
|
82
|
+
console.log(`Stripe API version successfully changed to ${selectedVersion}`);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
85
|
+
printError(error.message as string);
|
|
86
|
+
} else {
|
|
87
|
+
printError('Failed to update Stripe API version');
|
|
88
|
+
}
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Registers stripe commands
|
|
95
|
+
*/
|
|
96
|
+
export function registerStripeCommands(program: Command): void {
|
|
97
|
+
const stripeCmd = program.command('stripe').description('manage Stripe integration');
|
|
98
|
+
|
|
99
|
+
// stripe update-version
|
|
100
|
+
stripeCmd
|
|
101
|
+
.command('update-version')
|
|
102
|
+
.description('update Stripe API version in use')
|
|
103
|
+
.option('--version <VERSION>', 'Stripe API version to update to')
|
|
104
|
+
.option('-f, --force', 'skip confirmation prompt and force update')
|
|
105
|
+
.option('-m, --marketplace <MARKETPLACE_ID>', 'marketplace identifier')
|
|
106
|
+
.action(async (opts) => {
|
|
107
|
+
const marketplace = opts.marketplace || program.opts().marketplace;
|
|
108
|
+
if (!marketplace) {
|
|
109
|
+
console.error('Error: --marketplace is required');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
await updateStripeVersion(marketplace, opts.version, opts.force);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version command - displays the CLI version
|
|
3
|
+
*
|
|
4
|
+
* Must match flex-cli output format exactly
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Finds package.json by traversing up from current file
|
|
13
|
+
*/
|
|
14
|
+
function findPackageJson(): string {
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
let currentDir = dirname(__filename);
|
|
17
|
+
|
|
18
|
+
// Traverse up to find package.json
|
|
19
|
+
while (currentDir !== '/') {
|
|
20
|
+
try {
|
|
21
|
+
const pkgPath = join(currentDir, 'package.json');
|
|
22
|
+
const content = readFileSync(pkgPath, 'utf-8');
|
|
23
|
+
return content;
|
|
24
|
+
} catch {
|
|
25
|
+
currentDir = dirname(currentDir);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
throw new Error('Could not find package.json');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Displays the version of the CLI
|
|
34
|
+
*
|
|
35
|
+
* Output must match flex-cli exactly
|
|
36
|
+
*/
|
|
37
|
+
export function version(): void {
|
|
38
|
+
const packageJson = JSON.parse(findPackageJson());
|
|
39
|
+
console.log(packageJson.version);
|
|
40
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sharetribe CLI - Unofficial 100% compatible implementation
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for the CLI application
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { version } from './commands/version.js';
|
|
12
|
+
import { login } from './commands/login.js';
|
|
13
|
+
import { logout } from './commands/logout.js';
|
|
14
|
+
import { registerProcessCommands } from './commands/process/index.js';
|
|
15
|
+
import { registerSearchCommands } from './commands/search/index.js';
|
|
16
|
+
import { registerAssetsCommands } from './commands/assets/index.js';
|
|
17
|
+
import { registerNotificationsCommands } from './commands/notifications/index.js';
|
|
18
|
+
import { registerListingApprovalCommand } from './commands/listing-approval.js';
|
|
19
|
+
import { registerEventsCommand } from './commands/events/index.js';
|
|
20
|
+
import { registerStripeCommands } from './commands/stripe/index.js';
|
|
21
|
+
import { configureHelp } from './util/help-formatter.js';
|
|
22
|
+
import { routeProcessCommand } from './util/command-router.js';
|
|
23
|
+
|
|
24
|
+
// Get package.json for version info
|
|
25
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
+
const __dirname = dirname(__filename);
|
|
27
|
+
const packageJson = JSON.parse(
|
|
28
|
+
readFileSync(join(__dirname, '../package.json'), 'utf-8')
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Route argv to handle process subcommands
|
|
32
|
+
const routedArgv = routeProcessCommand(process.argv);
|
|
33
|
+
|
|
34
|
+
const program = new Command();
|
|
35
|
+
|
|
36
|
+
// Configure custom help formatter to match flex-cli
|
|
37
|
+
configureHelp(program);
|
|
38
|
+
|
|
39
|
+
// Configure output to add trailing newline (flex-cli behavior)
|
|
40
|
+
program.configureOutput({
|
|
41
|
+
writeOut: (str) => process.stdout.write(str + '\n'),
|
|
42
|
+
writeErr: (str) => process.stderr.write(str + '\n'),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Configure the main program
|
|
46
|
+
program
|
|
47
|
+
.name('sharetribe-cli')
|
|
48
|
+
.description('CLI to interact with Sharetribe Flex')
|
|
49
|
+
.version(packageJson.version, '-V', 'output the version number')
|
|
50
|
+
.option('-m, --marketplace <MARKETPLACE_ID>', 'marketplace identifier');
|
|
51
|
+
|
|
52
|
+
// Register commands
|
|
53
|
+
|
|
54
|
+
// version command
|
|
55
|
+
program
|
|
56
|
+
.command('version')
|
|
57
|
+
.description('show version')
|
|
58
|
+
.action(() => {
|
|
59
|
+
version();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// login command
|
|
63
|
+
program
|
|
64
|
+
.command('login')
|
|
65
|
+
.description('log in with API key')
|
|
66
|
+
.action(async () => {
|
|
67
|
+
await login();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// logout command
|
|
71
|
+
program
|
|
72
|
+
.command('logout')
|
|
73
|
+
.description('logout')
|
|
74
|
+
.action(async () => {
|
|
75
|
+
await logout();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Register process commands
|
|
79
|
+
registerProcessCommands(program);
|
|
80
|
+
|
|
81
|
+
// Register search commands
|
|
82
|
+
registerSearchCommands(program);
|
|
83
|
+
|
|
84
|
+
// Register assets commands
|
|
85
|
+
registerAssetsCommands(program);
|
|
86
|
+
|
|
87
|
+
// Register notifications commands
|
|
88
|
+
registerNotificationsCommands(program);
|
|
89
|
+
|
|
90
|
+
// Register listing-approval command
|
|
91
|
+
registerListingApprovalCommand(program);
|
|
92
|
+
|
|
93
|
+
// Register events command
|
|
94
|
+
registerEventsCommand(program);
|
|
95
|
+
|
|
96
|
+
// Register stripe commands
|
|
97
|
+
registerStripeCommands(program);
|
|
98
|
+
|
|
99
|
+
// Register custom help command (to support "help process list" syntax)
|
|
100
|
+
program
|
|
101
|
+
.command('help [command...]')
|
|
102
|
+
.description('display help for Flex CLI')
|
|
103
|
+
.action((commandPath: string[]) => {
|
|
104
|
+
if (!commandPath || commandPath.length === 0) {
|
|
105
|
+
program.outputHelp();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Navigate to the nested command
|
|
110
|
+
let targetCmd: Command = program;
|
|
111
|
+
for (const cmdName of commandPath) {
|
|
112
|
+
const subCmd = targetCmd.commands.find(c => c.name() === cmdName);
|
|
113
|
+
if (!subCmd) {
|
|
114
|
+
console.error(`Unknown command: ${commandPath.join(' ')}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
targetCmd = subCmd;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Show help for the target command
|
|
121
|
+
targetCmd.outputHelp();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// If no command specified, show help and exit with status 0
|
|
125
|
+
if (!routedArgv.slice(2).length) {
|
|
126
|
+
program.outputHelp();
|
|
127
|
+
// Don't call process.exit() - let Commander handle it naturally with exitOverride
|
|
128
|
+
} else {
|
|
129
|
+
// Parse command line arguments with routed argv
|
|
130
|
+
program.parse(routedArgv);
|
|
131
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for sharetribe-cli
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface CommandContext {
|
|
6
|
+
apiClient?: unknown; // Will be defined when API client is implemented
|
|
7
|
+
marketplace?: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CommandOptions {
|
|
12
|
+
marketplace?: string;
|
|
13
|
+
help?: boolean;
|
|
14
|
+
version?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ProcessDefinition {
|
|
18
|
+
// Will be expanded as we implement process handling
|
|
19
|
+
name: string;
|
|
20
|
+
version?: number;
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom command router to handle Commander.js limitations
|
|
3
|
+
*
|
|
4
|
+
* Commander.js cannot handle parent and child commands with the same option names.
|
|
5
|
+
* This router intercepts argv and routes to the correct command handler.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Routes process subcommands to avoid Commander parent/child option conflicts
|
|
10
|
+
*
|
|
11
|
+
* This is necessary because Commander validates parent command options before
|
|
12
|
+
* subcommand actions run, causing conflicts when both use --path.
|
|
13
|
+
*/
|
|
14
|
+
export function routeProcessCommand(argv: string[]): string[] {
|
|
15
|
+
// Check if this is a process subcommand
|
|
16
|
+
const processIndex = argv.findIndex(arg => arg === 'process');
|
|
17
|
+
if (processIndex === -1) {
|
|
18
|
+
return argv;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check for subcommands
|
|
22
|
+
const nextArg = argv[processIndex + 1];
|
|
23
|
+
const subcommands = ['list', 'create', 'push', 'pull', 'create-alias', 'update-alias', 'delete-alias', 'deploy'];
|
|
24
|
+
|
|
25
|
+
if (nextArg && subcommands.includes(nextArg)) {
|
|
26
|
+
// This is a subcommand - remove 'process' from argv and make the subcommand top-level
|
|
27
|
+
// e.g. ['node', 'cli', 'process', 'pull', ...] => ['node', 'cli', 'process-pull', ...]
|
|
28
|
+
const newArgv = [
|
|
29
|
+
...argv.slice(0, processIndex),
|
|
30
|
+
`process-${nextArg}`,
|
|
31
|
+
...argv.slice(processIndex + 2)
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Special handling: If this is an alias command with --version option,
|
|
35
|
+
// we need to filter out the global --version flag from program
|
|
36
|
+
// by ensuring the routed command is properly isolated
|
|
37
|
+
return newArgv;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return argv;
|
|
41
|
+
}
|