deepline 0.1.0 → 0.1.1
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/dist/cli/index.js +212 -54
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +198 -40
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +3256 -0
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +710 -0
- package/dist/repo/apps/play-runner-workers/src/entry.ts +5070 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +21 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +177 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +52 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +100 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +184 -0
- package/dist/repo/sdk/src/cli/commands/auth.ts +482 -0
- package/dist/repo/sdk/src/cli/commands/billing.ts +188 -0
- package/dist/repo/sdk/src/cli/commands/csv.ts +123 -0
- package/dist/repo/sdk/src/cli/commands/db.ts +119 -0
- package/dist/repo/sdk/src/cli/commands/feedback.ts +40 -0
- package/dist/repo/sdk/src/cli/commands/org.ts +117 -0
- package/dist/repo/sdk/src/cli/commands/play.ts +3200 -0
- package/dist/repo/sdk/src/cli/commands/tools.ts +687 -0
- package/dist/repo/sdk/src/cli/dataset-stats.ts +341 -0
- package/dist/repo/sdk/src/cli/index.ts +138 -0
- package/dist/repo/sdk/src/cli/progress.ts +135 -0
- package/dist/repo/sdk/src/cli/trace.ts +61 -0
- package/dist/repo/sdk/src/cli/utils.ts +145 -0
- package/dist/repo/sdk/src/client.ts +1188 -0
- package/dist/repo/sdk/src/compat.ts +77 -0
- package/dist/repo/sdk/src/config.ts +285 -0
- package/dist/repo/sdk/src/errors.ts +125 -0
- package/dist/repo/sdk/src/http.ts +391 -0
- package/dist/repo/sdk/src/index.ts +139 -0
- package/dist/repo/sdk/src/play.ts +1330 -0
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +133 -0
- package/dist/repo/sdk/src/plays/harness-stub.ts +210 -0
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +326 -0
- package/dist/repo/sdk/src/tool-output.ts +489 -0
- package/dist/repo/sdk/src/types.ts +669 -0
- package/dist/repo/sdk/src/version.ts +2 -0
- package/dist/repo/sdk/src/worker-play-entry.ts +286 -0
- package/dist/repo/shared_libs/observability/node-tracing.ts +129 -0
- package/dist/repo/shared_libs/observability/tracing.ts +98 -0
- package/dist/repo/shared_libs/play-runtime/backend.ts +139 -0
- package/dist/repo/shared_libs/play-runtime/batch-runtime.ts +182 -0
- package/dist/repo/shared_libs/play-runtime/batching-types.ts +91 -0
- package/dist/repo/shared_libs/play-runtime/context.ts +3999 -0
- package/dist/repo/shared_libs/play-runtime/coordinator-headers.ts +78 -0
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +250 -0
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +713 -0
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +10 -0
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +304 -0
- package/dist/repo/shared_libs/play-runtime/db-session.ts +462 -0
- package/dist/repo/shared_libs/play-runtime/dedup-backend.ts +0 -0
- package/dist/repo/shared_libs/play-runtime/default-batch-strategies.ts +124 -0
- package/dist/repo/shared_libs/play-runtime/execution-plan.ts +262 -0
- package/dist/repo/shared_libs/play-runtime/live-events.ts +214 -0
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +50 -0
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +114 -0
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +158 -0
- package/dist/repo/shared_libs/play-runtime/profiles.ts +90 -0
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +172 -0
- package/dist/repo/shared_libs/play-runtime/protocol.ts +121 -0
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +42 -0
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +33 -0
- package/dist/repo/shared_libs/play-runtime/runtime-actions.ts +208 -0
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +1873 -0
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +2 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +201 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +48 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +84 -0
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +174 -0
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +147 -0
- package/dist/repo/shared_libs/play-runtime/suspension.ts +68 -0
- package/dist/repo/shared_libs/play-runtime/tool-batch-executor.ts +146 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +387 -0
- package/dist/repo/shared_libs/play-runtime/tracing.ts +31 -0
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +75 -0
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +140 -0
- package/dist/repo/shared_libs/plays/artifact-transport.ts +14 -0
- package/dist/repo/shared_libs/plays/artifact-types.ts +49 -0
- package/dist/repo/shared_libs/plays/bundling/index.ts +1346 -0
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +186 -0
- package/dist/repo/shared_libs/plays/contracts.ts +51 -0
- package/dist/repo/shared_libs/plays/dataset.ts +308 -0
- package/dist/repo/shared_libs/plays/definition.ts +264 -0
- package/dist/repo/shared_libs/plays/file-refs.ts +11 -0
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +206 -0
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +164 -0
- package/dist/repo/shared_libs/plays/row-identity.ts +302 -0
- package/dist/repo/shared_libs/plays/runtime-validation.ts +415 -0
- package/dist/repo/shared_libs/plays/static-pipeline.ts +560 -0
- package/dist/repo/shared_libs/temporal/constants.ts +39 -0
- package/dist/repo/shared_libs/temporal/preview-config.ts +153 -0
- package/package.json +4 -4
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { buildDatasetStats } from '../dataset-stats.js';
|
|
3
|
+
import { readCsvRows, printJson, clip } from '../utils.js';
|
|
4
|
+
|
|
5
|
+
type CsvShowOptions = {
|
|
6
|
+
format?: 'json' | 'csv' | 'table';
|
|
7
|
+
rows?: string;
|
|
8
|
+
columns?: string;
|
|
9
|
+
summary?: boolean;
|
|
10
|
+
verbose?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function parseRowRange(raw: string | undefined): [number, number] {
|
|
14
|
+
const source = raw?.trim() || '0:19';
|
|
15
|
+
const [startRaw, endRaw] = source.split(':', 2);
|
|
16
|
+
const start = Number.parseInt(startRaw ?? '', 10);
|
|
17
|
+
const end = Number.parseInt(endRaw ?? '', 10);
|
|
18
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start < 0 || end < start) {
|
|
19
|
+
throw new Error(`Invalid --rows value: ${source} (expected start:end, e.g. 0:19).`);
|
|
20
|
+
}
|
|
21
|
+
return [start, end];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function selectRows<T>(rows: T[], start: number, end: number): T[] {
|
|
25
|
+
return rows.slice(start, end + 1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function selectColumns(
|
|
29
|
+
rows: Array<Record<string, string>>,
|
|
30
|
+
rawColumns: string | undefined,
|
|
31
|
+
): Array<Record<string, string>> {
|
|
32
|
+
if (!rawColumns?.trim()) return rows;
|
|
33
|
+
const requested = rawColumns.split(',').map((part) => part.trim()).filter(Boolean);
|
|
34
|
+
if (requested.length === 0) {
|
|
35
|
+
throw new Error('Invalid --columns value: provide at least one column name.');
|
|
36
|
+
}
|
|
37
|
+
const available = new Set(Object.keys(rows[0] ?? {}));
|
|
38
|
+
const missing = requested.filter((column) => !available.has(column));
|
|
39
|
+
if (missing.length > 0) {
|
|
40
|
+
throw new Error(`Unknown --columns value(s): ${missing.join(', ')}.`);
|
|
41
|
+
}
|
|
42
|
+
return rows.map((row) => {
|
|
43
|
+
const next: Record<string, string> = {};
|
|
44
|
+
for (const column of requested) next[column] = row[column] ?? '';
|
|
45
|
+
return next;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function renderTable(rows: Array<Record<string, string>>, totalRows: number, verbose: boolean): string {
|
|
50
|
+
const columns = Object.keys(rows[0] ?? {});
|
|
51
|
+
if (columns.length === 0) return 'No rows.';
|
|
52
|
+
const widths = Object.fromEntries(
|
|
53
|
+
columns.map((column) => [
|
|
54
|
+
column,
|
|
55
|
+
Math.max(
|
|
56
|
+
column.length,
|
|
57
|
+
...rows.map((row) => {
|
|
58
|
+
const value = row[column] ?? '';
|
|
59
|
+
return (verbose ? value : clip(value, 42)).length;
|
|
60
|
+
}),
|
|
61
|
+
),
|
|
62
|
+
]),
|
|
63
|
+
) as Record<string, number>;
|
|
64
|
+
|
|
65
|
+
const header = columns.map((column) => column.padEnd(widths[column]!)).join(' | ');
|
|
66
|
+
const divider = columns.map((column) => '-'.repeat(widths[column]!)).join('-+-');
|
|
67
|
+
const body = rows.map((row) =>
|
|
68
|
+
columns
|
|
69
|
+
.map((column) => {
|
|
70
|
+
const raw = row[column] ?? '';
|
|
71
|
+
const display = verbose ? raw : clip(raw, 42);
|
|
72
|
+
return display.padEnd(widths[column]!);
|
|
73
|
+
})
|
|
74
|
+
.join(' | '),
|
|
75
|
+
);
|
|
76
|
+
return [header, divider, ...body, '', `showing ${rows.length} row(s) of ${totalRows}`].join('\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function handleCsvShow(options: CsvShowOptions & { csv: string }): Promise<void> {
|
|
80
|
+
const csvPath = options.csv;
|
|
81
|
+
const [rowStart, rowEnd] = parseRowRange(options.rows);
|
|
82
|
+
const allRows = readCsvRows(csvPath);
|
|
83
|
+
const selected = selectColumns(selectRows(allRows, rowStart, rowEnd), options.columns);
|
|
84
|
+
const format = options.format ?? 'json';
|
|
85
|
+
|
|
86
|
+
if (options.summary) {
|
|
87
|
+
printJson(buildDatasetStats(selected, allRows.length));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (format === 'json') {
|
|
92
|
+
printJson({
|
|
93
|
+
rows: selected.map((row, index) => ({ __row: rowStart + index, ...row })),
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (format === 'csv') {
|
|
99
|
+
const columns = Object.keys(selected[0] ?? {});
|
|
100
|
+
process.stdout.write(`${columns.join(',')}\n`);
|
|
101
|
+
for (const row of selected) {
|
|
102
|
+
process.stdout.write(`${columns.map((column) => JSON.stringify(row[column] ?? '')).join(',')}\n`);
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
process.stdout.write(`${renderTable(selected, allRows.length, Boolean(options.verbose))}\n`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function registerCsvCommands(program: Command): void {
|
|
111
|
+
const csv = program.command('csv').description('Inspect local CSV files.');
|
|
112
|
+
|
|
113
|
+
csv
|
|
114
|
+
.command('show')
|
|
115
|
+
.description('Display rows from a CSV file in json, csv, or table form.')
|
|
116
|
+
.requiredOption('--csv <path>', 'Input CSV path')
|
|
117
|
+
.option('--format <format>', 'Output format: json, csv, or table', 'json')
|
|
118
|
+
.option('--rows <range>', 'Row range start:end', '0:19')
|
|
119
|
+
.option('--columns <names>', 'Comma-separated column names to include')
|
|
120
|
+
.option('--summary', 'Print a summary payload instead of row output')
|
|
121
|
+
.option('--verbose', 'Do not truncate long values in table output')
|
|
122
|
+
.action(handleCsvShow);
|
|
123
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { DeeplineClient } from '../../client.js';
|
|
3
|
+
import type { CustomerDbQueryResult } from '../../types.js';
|
|
4
|
+
import { argsWantJson } from '../utils.js';
|
|
5
|
+
|
|
6
|
+
function parsePositiveInteger(value: string, flagName: string): number {
|
|
7
|
+
const parsed = Number.parseInt(value, 10);
|
|
8
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
9
|
+
throw new Error(`${flagName} must be a positive integer.`);
|
|
10
|
+
}
|
|
11
|
+
return parsed;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function formatCell(value: unknown): string {
|
|
15
|
+
if (value === null || value === undefined) return '';
|
|
16
|
+
const text = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
17
|
+
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function printTable(result: CustomerDbQueryResult): void {
|
|
21
|
+
const rows = result.rows.filter(
|
|
22
|
+
(row): row is Record<string, unknown> =>
|
|
23
|
+
Boolean(row) && typeof row === 'object' && !Array.isArray(row),
|
|
24
|
+
);
|
|
25
|
+
const responseColumns =
|
|
26
|
+
result.columns.length > 0
|
|
27
|
+
? result.columns.map((column) => column.name)
|
|
28
|
+
: [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
29
|
+
const businessColumns = responseColumns.filter((column) => !column.startsWith('_'));
|
|
30
|
+
const columns = businessColumns.length > 0 ? businessColumns : responseColumns;
|
|
31
|
+
const hiddenColumns = responseColumns.filter((column) => !columns.includes(column));
|
|
32
|
+
|
|
33
|
+
console.log(
|
|
34
|
+
`${result.command} returned ${result.row_count_returned} row(s)` +
|
|
35
|
+
(result.truncated ? ' (truncated)' : ''),
|
|
36
|
+
);
|
|
37
|
+
if (hiddenColumns.length > 0) {
|
|
38
|
+
console.log(
|
|
39
|
+
`Showing ${columns.length}/${responseColumns.length} columns; hidden metadata: ${hiddenColumns.join(', ')}`,
|
|
40
|
+
);
|
|
41
|
+
console.log('Use --json or select metadata columns explicitly when you need run ids/errors/stages.');
|
|
42
|
+
}
|
|
43
|
+
if (rows.length === 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const widths = columns.map((column) =>
|
|
48
|
+
Math.min(
|
|
49
|
+
80,
|
|
50
|
+
Math.max(
|
|
51
|
+
column.length,
|
|
52
|
+
...rows.slice(0, 20).map((row) => formatCell(row[column]).length),
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
);
|
|
56
|
+
console.log(columns.map((column, index) => column.padEnd(widths[index]!)).join(' '));
|
|
57
|
+
console.log(widths.map((width) => '-'.repeat(width)).join(' '));
|
|
58
|
+
for (const row of rows) {
|
|
59
|
+
console.log(
|
|
60
|
+
columns
|
|
61
|
+
.map((column, index) => formatCell(row[column]).padEnd(widths[index]!))
|
|
62
|
+
.join(' '),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function handleDbQuery(args: string[]): Promise<number> {
|
|
68
|
+
const sqlIndex = args.indexOf('--sql');
|
|
69
|
+
const sql = sqlIndex >= 0 ? args[sqlIndex + 1]?.trim() : '';
|
|
70
|
+
if (!sql) {
|
|
71
|
+
console.error('Usage: deepline db query --sql "select * from table limit 20" [--max-rows N] [--json]');
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const maxRowsIndex = args.indexOf('--max-rows');
|
|
76
|
+
const maxRows =
|
|
77
|
+
maxRowsIndex >= 0 && args[maxRowsIndex + 1]
|
|
78
|
+
? parsePositiveInteger(args[maxRowsIndex + 1]!, '--max-rows')
|
|
79
|
+
: undefined;
|
|
80
|
+
const jsonOutput = argsWantJson(args);
|
|
81
|
+
const client = new DeeplineClient();
|
|
82
|
+
const result = await client.queryCustomerDb({ sql, maxRows });
|
|
83
|
+
|
|
84
|
+
if (jsonOutput) {
|
|
85
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
printTable(result);
|
|
90
|
+
console.error(
|
|
91
|
+
`Tool equivalent: deepline tools execute query_customer_db --payload ${JSON.stringify({
|
|
92
|
+
sql,
|
|
93
|
+
...(maxRows ? { max_rows: maxRows } : {}),
|
|
94
|
+
})} --json`,
|
|
95
|
+
);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function registerDbCommands(program: Command): void {
|
|
100
|
+
const db = program
|
|
101
|
+
.command('db')
|
|
102
|
+
.alias('customer-db')
|
|
103
|
+
.description('Query the tenant customer database.');
|
|
104
|
+
|
|
105
|
+
db.command('query')
|
|
106
|
+
.alias('psql')
|
|
107
|
+
.description('Run SQL against the tenant customer database.')
|
|
108
|
+
.requiredOption('--sql <sql>', 'SQL statement')
|
|
109
|
+
.option('--max-rows <n>', 'Maximum returned rows')
|
|
110
|
+
.option('--json', 'Emit raw JSON response')
|
|
111
|
+
.action(async (options) => {
|
|
112
|
+
process.exitCode = await handleDbQuery([
|
|
113
|
+
'--sql',
|
|
114
|
+
options.sql,
|
|
115
|
+
...(options.maxRows ? ['--max-rows', options.maxRows] : []),
|
|
116
|
+
...(options.json ? ['--json'] : []),
|
|
117
|
+
]);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getAuthedHttpClient, collectLocalEnvInfo, printJson, shouldEmitJson } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
async function handleFeedback(
|
|
5
|
+
text: string,
|
|
6
|
+
options: { command?: string; payload?: string; json?: boolean },
|
|
7
|
+
): Promise<void> {
|
|
8
|
+
const { http } = getAuthedHttpClient();
|
|
9
|
+
const response = await http.post('/api/v2/cli/feedback', {
|
|
10
|
+
text,
|
|
11
|
+
environment: collectLocalEnvInfo(),
|
|
12
|
+
...(options.command ? { command: options.command } : {}),
|
|
13
|
+
...(options.payload ? { payload: options.payload } : {}),
|
|
14
|
+
});
|
|
15
|
+
if (shouldEmitJson(options.json)) {
|
|
16
|
+
printJson(response);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
process.stdout.write('Feedback submitted. Thank you.\n');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function registerFeedbackCommands(program: Command): void {
|
|
23
|
+
const feedback = program.command('feedback').description('Submit CLI feedback to Deepline.');
|
|
24
|
+
|
|
25
|
+
feedback
|
|
26
|
+
.argument('<text>', 'Feedback text')
|
|
27
|
+
.option('--command <command>', 'Command that reproduced the issue')
|
|
28
|
+
.option('--payload <payload>', 'JSON or plain-text payload for the repro')
|
|
29
|
+
.option('--json', 'Emit JSON output')
|
|
30
|
+
.action(handleFeedback);
|
|
31
|
+
|
|
32
|
+
program
|
|
33
|
+
.command('provide-feedback')
|
|
34
|
+
.description('Legacy alias for `deepline feedback`.')
|
|
35
|
+
.argument('<text>', 'Feedback text')
|
|
36
|
+
.option('--command <command>', 'Command that reproduced the issue')
|
|
37
|
+
.option('--payload <payload>', 'JSON or plain-text payload for the repro')
|
|
38
|
+
.option('--json', 'Emit JSON output')
|
|
39
|
+
.action(handleFeedback);
|
|
40
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { HttpClient } from '../../http.js';
|
|
3
|
+
import {
|
|
4
|
+
hostEnvFilePath,
|
|
5
|
+
resolveConfig,
|
|
6
|
+
saveHostEnvValues,
|
|
7
|
+
} from '../../config.js';
|
|
8
|
+
import { printJson, shouldEmitJson } from '../utils.js';
|
|
9
|
+
|
|
10
|
+
type Organization = {
|
|
11
|
+
org_id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
role?: string;
|
|
14
|
+
is_current?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
async function fetchOrganizations(http: HttpClient, apiKey: string): Promise<{
|
|
18
|
+
current_org_id: string | null;
|
|
19
|
+
organizations: Organization[];
|
|
20
|
+
}> {
|
|
21
|
+
return http.post('/api/v2/auth/cli/organizations', { api_key: apiKey });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function printOrgList(orgs: Organization[]): void {
|
|
25
|
+
for (const [index, org] of orgs.entries()) {
|
|
26
|
+
const current = org.is_current ? ' (current)' : '';
|
|
27
|
+
const role = org.role ? ` [${org.role}]` : '';
|
|
28
|
+
process.stdout.write(` ${index + 1}. ${org.name}${role}${current}\n`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function handleOrgList(options: { json?: boolean }): Promise<void> {
|
|
33
|
+
const config = resolveConfig();
|
|
34
|
+
const http = new HttpClient(config);
|
|
35
|
+
const payload = await fetchOrganizations(http, config.apiKey);
|
|
36
|
+
if (shouldEmitJson(options.json)) {
|
|
37
|
+
printJson(payload);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
process.stdout.write('Your organizations:\n');
|
|
41
|
+
printOrgList(payload.organizations);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function handleOrgSwitch(
|
|
45
|
+
selection: string | undefined,
|
|
46
|
+
options: { json?: boolean; orgId?: string },
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
const config = resolveConfig();
|
|
49
|
+
const http = new HttpClient(config);
|
|
50
|
+
const payload = await fetchOrganizations(http, config.apiKey);
|
|
51
|
+
if (!selection && !options.orgId) {
|
|
52
|
+
if (shouldEmitJson(options.json)) {
|
|
53
|
+
printJson(payload);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
process.stdout.write('Your organizations:\n');
|
|
57
|
+
printOrgList(payload.organizations);
|
|
58
|
+
process.stdout.write('\nRun: deepline org switch <number>\n');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let target = payload.organizations.find((org) => org.org_id === options.orgId);
|
|
63
|
+
if (!target && selection) {
|
|
64
|
+
const index = Number.parseInt(selection, 10);
|
|
65
|
+
if (Number.isFinite(index) && index >= 1 && index <= payload.organizations.length) {
|
|
66
|
+
target = payload.organizations[index - 1];
|
|
67
|
+
} else {
|
|
68
|
+
target = payload.organizations.find((org) => org.name === selection || org.org_id === selection);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!target) {
|
|
73
|
+
throw new Error('Could not resolve the selected organization.');
|
|
74
|
+
}
|
|
75
|
+
if (target.is_current) {
|
|
76
|
+
if (shouldEmitJson(options.json)) {
|
|
77
|
+
printJson({ ok: true, unchanged: true, organization: target });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
process.stdout.write(`Already on ${target.name}.\n`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const switched = await http.post<{ api_key: string; org_id: string; org_name: string }>(
|
|
85
|
+
'/api/v2/auth/cli/switch',
|
|
86
|
+
{ api_key: config.apiKey, org_id: target.org_id },
|
|
87
|
+
);
|
|
88
|
+
saveHostEnvValues(config.baseUrl, {
|
|
89
|
+
DEEPLINE_API_KEY: switched.api_key,
|
|
90
|
+
DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
|
|
91
|
+
DEEPLINE_ACTIVE_ORG_NAME: switched.org_name,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (shouldEmitJson(options.json)) {
|
|
95
|
+
printJson({ ok: true, host_env_path: hostEnvFilePath(config.baseUrl), ...switched });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
process.stdout.write(`Switched to ${switched.org_name}.\n`);
|
|
99
|
+
process.stdout.write(`Saved host auth in ${hostEnvFilePath(config.baseUrl)}\n`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function registerOrgCommands(program: Command): void {
|
|
103
|
+
const org = program.command('org').description('List and switch organizations.');
|
|
104
|
+
|
|
105
|
+
org
|
|
106
|
+
.command('list')
|
|
107
|
+
.description('List your organizations.')
|
|
108
|
+
.option('--json', 'Emit JSON output')
|
|
109
|
+
.action(handleOrgList);
|
|
110
|
+
|
|
111
|
+
org
|
|
112
|
+
.command('switch [selection]')
|
|
113
|
+
.description('Switch to another organization and save the new API key in the host auth file.')
|
|
114
|
+
.option('--org-id <id>', 'Switch using an explicit organization id')
|
|
115
|
+
.option('--json', 'Emit JSON output')
|
|
116
|
+
.action(handleOrgSwitch);
|
|
117
|
+
}
|