@sequenceholdings/studio-cli 0.1.9

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.
Files changed (74) hide show
  1. package/README.md +258 -0
  2. package/dist/artifact/delegate.d.ts +25 -0
  3. package/dist/artifact/delegate.js +263 -0
  4. package/dist/atlas-client.d.ts +44 -0
  5. package/dist/atlas-client.js +173 -0
  6. package/dist/auth-cmds/commands.d.ts +15 -0
  7. package/dist/auth-cmds/commands.js +249 -0
  8. package/dist/auth.d.ts +26 -0
  9. package/dist/auth.js +171 -0
  10. package/dist/bin.d.ts +2 -0
  11. package/dist/bin.js +8 -0
  12. package/dist/cli-errors.d.ts +5 -0
  13. package/dist/cli-errors.js +78 -0
  14. package/dist/config.d.ts +44 -0
  15. package/dist/config.js +103 -0
  16. package/dist/env-flags.d.ts +8 -0
  17. package/dist/env-flags.js +47 -0
  18. package/dist/functions/bundle.d.ts +30 -0
  19. package/dist/functions/bundle.js +137 -0
  20. package/dist/functions/commands.d.ts +86 -0
  21. package/dist/functions/commands.js +999 -0
  22. package/dist/functions/egress-preview.d.ts +32 -0
  23. package/dist/functions/egress-preview.js +54 -0
  24. package/dist/functions/lockfile-origin.d.ts +16 -0
  25. package/dist/functions/lockfile-origin.js +45 -0
  26. package/dist/functions/manifest.d.ts +89 -0
  27. package/dist/functions/manifest.js +586 -0
  28. package/dist/functions/secret-reconcile.d.ts +79 -0
  29. package/dist/functions/secret-reconcile.js +86 -0
  30. package/dist/main.d.ts +14 -0
  31. package/dist/main.js +129 -0
  32. package/dist/orm/delegate.d.ts +8 -0
  33. package/dist/orm/delegate.js +61 -0
  34. package/dist/pat-hints.d.ts +17 -0
  35. package/dist/pat-hints.js +28 -0
  36. package/dist/preview.d.ts +89 -0
  37. package/dist/preview.js +291 -0
  38. package/dist/process/agent-loader.d.ts +24 -0
  39. package/dist/process/agent-loader.js +57 -0
  40. package/dist/process/build.d.ts +14 -0
  41. package/dist/process/build.js +368 -0
  42. package/dist/process/codegen.d.ts +18 -0
  43. package/dist/process/codegen.js +270 -0
  44. package/dist/process/commands.d.ts +47 -0
  45. package/dist/process/commands.js +786 -0
  46. package/dist/process/discover.d.ts +32 -0
  47. package/dist/process/discover.js +131 -0
  48. package/dist/process/lint.d.ts +39 -0
  49. package/dist/process/lint.js +485 -0
  50. package/dist/process/local-bundle.d.ts +17 -0
  51. package/dist/process/local-bundle.js +65 -0
  52. package/dist/process/plan-diff.d.ts +82 -0
  53. package/dist/process/plan-diff.js +333 -0
  54. package/dist/process/resolve-process-pin.d.ts +11 -0
  55. package/dist/process/resolve-process-pin.js +63 -0
  56. package/dist/process/simulate.d.ts +50 -0
  57. package/dist/process/simulate.js +328 -0
  58. package/dist/prompt.d.ts +35 -0
  59. package/dist/prompt.js +65 -0
  60. package/dist/repos/commands.d.ts +49 -0
  61. package/dist/repos/commands.js +548 -0
  62. package/dist/repos/git-clone.d.ts +10 -0
  63. package/dist/repos/git-clone.js +49 -0
  64. package/dist/secrets/commands.d.ts +24 -0
  65. package/dist/secrets/commands.js +704 -0
  66. package/dist/templates/process/example-process/process.ts +43 -0
  67. package/dist/templates/process/package.json +23 -0
  68. package/dist/templates/process/pnpm-workspace.yaml +21 -0
  69. package/dist/templates/process/tsconfig.json +17 -0
  70. package/package.json +78 -0
  71. package/templates/process/example-process/process.ts +43 -0
  72. package/templates/process/package.json +23 -0
  73. package/templates/process/pnpm-workspace.yaml +21 -0
  74. package/templates/process/tsconfig.json +17 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * In-process orchestrator walker with stubbed runners. Lets process
3
+ * authors verify their graph topology + routing locally without
4
+ * deploying to Trigger.dev.
5
+ *
6
+ * Mirrors the production orchestrator's `verifyAndAdvance` loop but:
7
+ * - Skips DB persistence
8
+ * - Stubs agent runner with the first outgoing edge + synthetic payload
9
+ * - Stubs human runner identically (or accepts caller-supplied decisions)
10
+ * - Stubs automation runner identically — native handlers live in the
11
+ * Atlas image and can't execute here
12
+ */
13
+ import { DEFAULT_MAX_FANOUT_WIDTH, } from '@sequenceholdings/lattice/define';
14
+ import { extractOutputState, mergeRunState } from '@sequenceholdings/lattice';
15
+ const DEFAULT_MAX_ITERATIONS = 1000;
16
+ const DEFAULT_MAX_REVISITS = 100;
17
+ export async function simulateProcess(opts) {
18
+ const proc = opts.process;
19
+ const startId = opts.startNodeId ?? proc.start_node_id;
20
+ const startNode = proc.nodes.find((n) => n.id === startId);
21
+ if (!startNode) {
22
+ return {
23
+ steps: [],
24
+ status: 'failed',
25
+ error: `start_node_id "${startId}" not found in process "${proc.id}"`,
26
+ };
27
+ }
28
+ const maxIterations = opts.maxIterations ?? DEFAULT_MAX_ITERATIONS;
29
+ const maxRevisits = opts.maxRevisits ?? DEFAULT_MAX_REVISITS;
30
+ const visits = new Map();
31
+ const steps = [];
32
+ const upstream = {};
33
+ let runState = opts.state ?? {};
34
+ const processesById = opts.processesById ??
35
+ new Map([[proc.id, proc]]);
36
+ let current = startNode;
37
+ let iteration = 0;
38
+ try {
39
+ for (;;) {
40
+ iteration += 1;
41
+ if (iteration > maxIterations) {
42
+ throw new Error(`iteration cap (${maxIterations}) exceeded`);
43
+ }
44
+ const visitsForNode = (visits.get(current.id) ?? 0) + 1;
45
+ visits.set(current.id, visitsForNode);
46
+ if (visitsForNode > maxRevisits) {
47
+ throw new Error(`revisit cap (${maxRevisits}) exceeded for node "${current.id}"`);
48
+ }
49
+ const ctx = {
50
+ entity: opts.entity ? { type: opts.entity.type, id: opts.entity.id } : null,
51
+ upstream: upstream,
52
+ state: runState,
53
+ run: {
54
+ id: 'sim',
55
+ process_id: proc.id,
56
+ version: 'simulate',
57
+ started_at: new Date().toISOString(),
58
+ started_by: 'simulator',
59
+ metadata: opts.metadata ?? {},
60
+ },
61
+ };
62
+ const input = await runInputMapperIfAny(current, ctx);
63
+ const output = await dispatchInProcess({
64
+ node: current,
65
+ input,
66
+ ctx,
67
+ humanDecisions: opts.humanDecisions ?? {},
68
+ iteration,
69
+ processesById,
70
+ record: (step) => steps.push(step),
71
+ });
72
+ const statePatch = extractOutputState(output);
73
+ if (statePatch)
74
+ runState = mergeRunState(runState, statePatch);
75
+ const outgoing = current.outgoing_edges;
76
+ let nextEdgeId;
77
+ let toNodeId;
78
+ if (outgoing.length === 0) {
79
+ steps.push({
80
+ iteration,
81
+ node_id: current.id,
82
+ kind: current.kind,
83
+ input,
84
+ output,
85
+ });
86
+ upstream[current.id] = output;
87
+ break;
88
+ }
89
+ else if (outgoing.length === 1) {
90
+ nextEdgeId = output.edge_id ?? outgoing[0].id;
91
+ toNodeId = outgoing[0].to;
92
+ }
93
+ else {
94
+ if (!output.edge_id) {
95
+ throw new Error(`node "${current.id}" has ${outgoing.length} outgoing edges but output did not include edge_id`);
96
+ }
97
+ const edge = outgoing.find((e) => e.id === output.edge_id);
98
+ if (!edge) {
99
+ throw new Error(`node "${current.id}" returned edge_id "${output.edge_id}" not in outgoing_edges [${outgoing.map((e) => e.id).join(', ')}]`);
100
+ }
101
+ nextEdgeId = edge.id;
102
+ toNodeId = edge.to;
103
+ }
104
+ steps.push({
105
+ iteration,
106
+ node_id: current.id,
107
+ kind: current.kind,
108
+ input,
109
+ output,
110
+ next_edge_id: nextEdgeId,
111
+ to_node_id: toNodeId,
112
+ });
113
+ upstream[current.id] = output;
114
+ if (!toNodeId)
115
+ break;
116
+ const nextNode = proc.nodes.find((n) => n.id === toNodeId);
117
+ if (!nextNode) {
118
+ throw new Error(`target node "${toNodeId}" not found in process`);
119
+ }
120
+ current = nextNode;
121
+ }
122
+ return { steps, status: 'completed' };
123
+ }
124
+ catch (err) {
125
+ return {
126
+ steps,
127
+ status: 'failed',
128
+ error: err instanceof Error ? err.message : String(err),
129
+ };
130
+ }
131
+ }
132
+ async function runInputMapperIfAny(node, ctx) {
133
+ const mapper = node.input;
134
+ if (typeof mapper !== 'function')
135
+ return ctx;
136
+ return Promise.resolve(mapper(ctx));
137
+ }
138
+ async function dispatchInProcess(args) {
139
+ switch (args.node.kind) {
140
+ case 'automation': {
141
+ // Native automation handlers run server-side from the Atlas image and
142
+ // can't execute in the simulator. Stub like an agent: take the first
143
+ // declared edge with a synthetic payload so topology + routing still walk.
144
+ const auto = args.node;
145
+ return {
146
+ edge_id: auto.outgoing_edges[0]?.id,
147
+ payload: { _simulated: true, function: auto.registered_function_id },
148
+ };
149
+ }
150
+ case 'agent': {
151
+ const agent = args.node;
152
+ return {
153
+ edge_id: agent.outgoing_edges[0]?.id,
154
+ payload: { _simulated: true, agent_id: agent.agent.id },
155
+ };
156
+ }
157
+ case 'human': {
158
+ const human = args.node;
159
+ const supplied = args.humanDecisions[human.id];
160
+ if (supplied)
161
+ return supplied;
162
+ return {
163
+ edge_id: human.outgoing_edges[0]?.id,
164
+ payload: { _simulated: true, decision: 'approved' },
165
+ };
166
+ }
167
+ case 'parallel': {
168
+ return simulateParallel(args, args.node);
169
+ }
170
+ case 'subprocess': {
171
+ return simulateSubprocess(args, args.node);
172
+ }
173
+ case 'delay': {
174
+ // No real wait in simulation — a delay just continues to its single
175
+ // outgoing edge (auto-taken by the router).
176
+ const delay = args.node;
177
+ return { edge_id: delay.outgoing_edges[0]?.id };
178
+ }
179
+ case 'managed_function': {
180
+ // Simulation cannot call live managed functions — route to success with a stub payload.
181
+ return {
182
+ edge_id: 'success',
183
+ payload: { _simulated: true, kind: 'managed_function' },
184
+ };
185
+ }
186
+ default: {
187
+ const _exhaustive = args.node;
188
+ void _exhaustive;
189
+ throw new Error(`unknown node kind`);
190
+ }
191
+ }
192
+ }
193
+ /**
194
+ * Resolve a parallel block's branches, run each sub-node in-process (recursing
195
+ * for nested parallels), record each as a branch sub-step, then run the
196
+ * author's `join` reducer to produce the parallel node's single output —
197
+ * mirroring runtime semantics (all branches complete, then join; no early
198
+ * exit). Dynamic fan-out branch ids are positional (`"0"`, `"1"`, …), matching
199
+ * the runtime/idempotency convention.
200
+ */
201
+ async function simulateParallel(args, node) {
202
+ const branches = [];
203
+ if (node.branches) {
204
+ node.branches.forEach((b, i) => branches.push({ branchId: b.branch_id, index: i, item: null, sub: b.node }));
205
+ }
206
+ else {
207
+ const items = node.fan_out(args.ctx) ?? [];
208
+ const cap = node.max_fanout ?? DEFAULT_MAX_FANOUT_WIDTH;
209
+ if (items.length > cap) {
210
+ throw new Error(`parallel node "${node.id}" fan_out produced ${items.length} items; ` +
211
+ `exceeds max_fanout=${cap}`);
212
+ }
213
+ items.forEach((item, i) => branches.push({ branchId: String(i), index: i, item, sub: node.branch }));
214
+ }
215
+ const results = [];
216
+ for (const br of branches) {
217
+ const branchCtx = {
218
+ ...args.ctx,
219
+ branch: { id: br.branchId, index: br.index, item: br.item },
220
+ };
221
+ const subInput = await runInputMapperIfAny(br.sub, branchCtx);
222
+ const subOutput = await dispatchInProcess({
223
+ node: br.sub,
224
+ input: subInput,
225
+ ctx: branchCtx,
226
+ humanDecisions: args.humanDecisions,
227
+ iteration: args.iteration,
228
+ processesById: args.processesById,
229
+ record: args.record,
230
+ });
231
+ args.record({
232
+ iteration: args.iteration,
233
+ node_id: br.sub.id,
234
+ kind: br.sub.kind,
235
+ input: subInput,
236
+ output: subOutput,
237
+ branch_id: br.branchId,
238
+ parent_node_id: node.id,
239
+ });
240
+ results.push({
241
+ branch_id: br.branchId,
242
+ node_id: br.sub.id,
243
+ index: br.index,
244
+ status: 'completed',
245
+ output: subOutput,
246
+ item: br.item,
247
+ });
248
+ }
249
+ const joinOutput = node.join(results, args.ctx);
250
+ return foldBranchState(joinOutput, results);
251
+ }
252
+ /**
253
+ * Mirror the runtime parallel runner's `mergeBranchState`: fold each branch's
254
+ * `output.state` onto the join output (branch order, the join's own state last)
255
+ * so branch state patches reach run state in simulation exactly as production
256
+ * folds them. The simulator runs every branch fresh, so there is no cached
257
+ * branch to skip.
258
+ */
259
+ function foldBranchState(joinOutput, branches) {
260
+ const merged = {};
261
+ const fold = (s) => {
262
+ if (!s)
263
+ return;
264
+ for (const [k, v] of Object.entries(s))
265
+ merged[k] = v;
266
+ };
267
+ for (const b of branches) {
268
+ if (b.status === 'failed')
269
+ continue;
270
+ fold(b.output.state);
271
+ }
272
+ fold(joinOutput.state);
273
+ if (Object.keys(merged).length === 0)
274
+ return joinOutput;
275
+ return { ...joinOutput, state: merged };
276
+ }
277
+ async function simulateSubprocess(args, node) {
278
+ const child = args.processesById.get(node.process);
279
+ if (!child) {
280
+ throw new Error(`subprocess node "${node.id}" references process "${node.process}" which is not loaded — ` +
281
+ `include it in the same bundle roots`);
282
+ }
283
+ const childMetadata = node.input &&
284
+ typeof args.input === 'object' &&
285
+ args.input !== null &&
286
+ !Array.isArray(args.input)
287
+ ? args.input
288
+ : {};
289
+ const childResult = await simulateProcess({
290
+ process: child,
291
+ processesById: args.processesById,
292
+ entity: args.ctx.entity
293
+ ? { type: String(args.ctx.entity.type ?? 'unknown'), id: String(args.ctx.entity.id ?? 'sim') }
294
+ : undefined,
295
+ metadata: childMetadata,
296
+ humanDecisions: args.humanDecisions,
297
+ });
298
+ const childFailed = childResult.status === 'failed';
299
+ if (childFailed && node.on_child_error === 'fail') {
300
+ throw new Error(childResult.error ?? `child process "${node.process}" simulation failed`);
301
+ }
302
+ const childOutput = childResult.steps.at(-1)?.output;
303
+ return node.output({
304
+ run_id: 'sim-child',
305
+ process_id: node.process,
306
+ version: node.version ?? 'simulate',
307
+ status: childFailed ? 'failed' : 'completed',
308
+ output: childOutput,
309
+ ...(childResult.error ? { error: childResult.error } : {}),
310
+ }, args.ctx);
311
+ }
312
+ export function formatSimulateResult(result) {
313
+ const lines = [];
314
+ for (const step of result.steps) {
315
+ const isBranch = step.parent_node_id !== undefined;
316
+ const indent = isBranch ? ' ↳ ' : '';
317
+ const label = isBranch ? `[${step.branch_id}] ${step.node_id}` : step.node_id;
318
+ const routing = isBranch
319
+ ? '' // branch sub-steps route internally; only the parallel join routes
320
+ : step.next_edge_id
321
+ ? ` → ${step.next_edge_id} → ${step.to_node_id}`
322
+ : ' (terminal)';
323
+ lines.push(`${String(step.iteration).padStart(3)} ${indent}${step.kind.padEnd(10)} ${label}${routing}`);
324
+ }
325
+ lines.push('');
326
+ lines.push(`status: ${result.status}${result.error ? ' — ' + result.error : ''}`);
327
+ return lines.join('\n');
328
+ }
@@ -0,0 +1,35 @@
1
+ export declare const LOG = "[seq-studio]";
2
+ export type PromptFn = (question: string) => Promise<string>;
3
+ /** Visible TTY prompt (echo on). */
4
+ export declare function promptVisible(question: string, promptFn?: PromptFn): Promise<string>;
5
+ /**
6
+ * Print a preview block and require exact `yes` before proceeding.
7
+ * Pass `confirmed: true` (--yes) for non-interactive automation.
8
+ */
9
+ export declare function confirmYes({ preview, prompt, confirmed, promptFn, }: {
10
+ preview: string[];
11
+ prompt?: string;
12
+ confirmed?: boolean;
13
+ promptFn?: PromptFn;
14
+ }): Promise<boolean>;
15
+ export interface PromptChoice {
16
+ /** Single-character selector, e.g. `y`. */
17
+ key: string;
18
+ /** Human-readable line shown under the preview. */
19
+ label: string;
20
+ /** Token returned when this choice is selected. */
21
+ value: string;
22
+ }
23
+ /**
24
+ * Print a preview block plus a keyed multi-choice menu and return the chosen
25
+ * option's `value` (or `null` when aborted / unrecognized). The multi-way
26
+ * analogue of `confirmYes`, used by `secrets pin` (pin+redeploy / pin-only /
27
+ * cancel). Pass `auto` (resolved from flags) to skip the prompt for CI.
28
+ */
29
+ export declare function confirmChoice({ preview, choices, prompt, auto, promptFn, }: {
30
+ preview: string[];
31
+ choices: PromptChoice[];
32
+ prompt?: string;
33
+ auto?: string;
34
+ promptFn?: PromptFn;
35
+ }): Promise<string | null>;
package/dist/prompt.js ADDED
@@ -0,0 +1,65 @@
1
+ import { createInterface } from 'node:readline';
2
+ export const LOG = '[seq-studio]';
3
+ /** Visible TTY prompt (echo on). */
4
+ export async function promptVisible(question, promptFn) {
5
+ if (promptFn)
6
+ return promptFn(question);
7
+ if (!process.stdin.isTTY) {
8
+ throw new Error('stdin is not a TTY — re-run with --yes to confirm non-interactively');
9
+ }
10
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
11
+ const answer = await new Promise((resolvePrompt) => {
12
+ rl.question(question, (value) => resolvePrompt(value));
13
+ });
14
+ rl.close();
15
+ return answer.trim();
16
+ }
17
+ /**
18
+ * Print a preview block and require exact `yes` before proceeding.
19
+ * Pass `confirmed: true` (--yes) for non-interactive automation.
20
+ */
21
+ export async function confirmYes({ preview, prompt = 'Type "yes" to confirm: ', confirmed = false, promptFn, }) {
22
+ for (const line of preview) {
23
+ console.log(line);
24
+ }
25
+ if (confirmed)
26
+ return true;
27
+ if (!process.stdin.isTTY && !promptFn) {
28
+ console.error(`${LOG} re-run with --yes to confirm non-interactively`);
29
+ return false;
30
+ }
31
+ const answer = await promptVisible(prompt, promptFn);
32
+ if (answer !== 'yes') {
33
+ console.error(`${LOG} aborted — answer was not "yes"`);
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ /**
39
+ * Print a preview block plus a keyed multi-choice menu and return the chosen
40
+ * option's `value` (or `null` when aborted / unrecognized). The multi-way
41
+ * analogue of `confirmYes`, used by `secrets pin` (pin+redeploy / pin-only /
42
+ * cancel). Pass `auto` (resolved from flags) to skip the prompt for CI.
43
+ */
44
+ export async function confirmChoice({ preview, choices, prompt, auto, promptFn, }) {
45
+ for (const line of preview) {
46
+ console.log(line);
47
+ }
48
+ for (const choice of choices) {
49
+ console.log(` [${choice.key}] ${choice.label}`);
50
+ }
51
+ if (auto !== undefined)
52
+ return auto;
53
+ if (!process.stdin.isTTY && !promptFn) {
54
+ console.error(`${LOG} not a TTY — re-run with --yes (add --no-redeploy to skip redeploy) to choose non-interactively`);
55
+ return null;
56
+ }
57
+ const keyPrompt = prompt ?? `Choose [${choices.map((c) => c.key).join('/')}]: `;
58
+ const answer = (await promptVisible(keyPrompt, promptFn)).trim().toLowerCase();
59
+ const match = choices.find((c) => c.key.toLowerCase() === answer);
60
+ if (!match) {
61
+ console.error(`${LOG} aborted — unrecognized choice`);
62
+ return null;
63
+ }
64
+ return match.value;
65
+ }
@@ -0,0 +1,49 @@
1
+ import type { ParsedArgs } from '../process/commands.js';
2
+ import { type CommandContext } from '../functions/commands.js';
3
+ import { type RunGitClone } from './git-clone.js';
4
+ import { type ResolvedEnv } from '../config.js';
5
+ export declare function parseRepoPath(input: string | undefined): {
6
+ namespace: string;
7
+ name: string;
8
+ };
9
+ /**
10
+ * A value-taking flag given without a value parses as `true` (parseArgs
11
+ * semantics) — silently ignoring it would make the command do something other
12
+ * than what was asked (e.g. `list --namespace --mine` printing the FULL repo
13
+ * list, or `pull --ref --out x` fetching the default branch). Error instead.
14
+ */
15
+ export declare function stringFlag(flags: ParsedArgs['flags'], key: string): string | undefined;
16
+ /** Smart-HTTP clone base (aligned with PLA-83: `…/repos/<id>/git`). */
17
+ export declare function cloneUrl(ctx: CommandContext, repoId: string): string;
18
+ /**
19
+ * `--force` may recursively delete `destDir`. Refuse cwd / home / filesystem
20
+ * roots / ancestors of cwd so a typo like `--out . --force` cannot wipe the
21
+ * working tree.
22
+ */
23
+ export declare function assertSafeForceDest(destDir: string): void;
24
+ export declare function reposNamespacesCommand(args: ParsedArgs): Promise<number>;
25
+ export declare function reposListCommand(args: ParsedArgs): Promise<number>;
26
+ export declare function reposShowCommand(args: ParsedArgs): Promise<number>;
27
+ export declare function reposCreateCommand(args: ParsedArgs): Promise<number>;
28
+ export declare function reposPullCommand(args: ParsedArgs): Promise<number>;
29
+ /**
30
+ * Preferred clone verb.
31
+ *
32
+ * With `ATLAS_GIT_PAT`:
33
+ * - `--url <clone-url>` or `--id <repo-uuid>` → smart-HTTP clone with no seqapi
34
+ * - `<ns>/<name>` → needs Auth0 to resolve the UUID, then smart-HTTP clone
35
+ * Without a PAT: JSON materialize (needs Auth0) + PAT setup hints.
36
+ *
37
+ * `gitClone` is injectable for tests.
38
+ */
39
+ export declare function reposCloneCommand(args: ParsedArgs, deps?: {
40
+ gitClone?: RunGitClone;
41
+ }): Promise<number>;
42
+ /**
43
+ * Validate clone URL shape and that the origin matches the resolved Atlas env
44
+ * so askpass never sends ATLAS_GIT_PAT to an attacker-controlled host.
45
+ */
46
+ export declare function normalizeCloneUrl(raw: string, env: ResolvedEnv): string;
47
+ export declare function reposDeleteCommand(args: ParsedArgs): Promise<number>;
48
+ export declare const REPOS_USAGE = "usage:\n seq-studio repos list [-e env] [--namespace <slug>] [--mine] repos visible on the environment\n seq-studio repos namespaces [create <slug>] [-e env] list or create namespaces\n seq-studio repos show <ns>/<name> [-e env] repo detail (branches, clone URL)\n seq-studio repos create <ns>/<name> [-e env] [--default-branch b] create an empty repo\n seq-studio repos clone <ns>/<name> [-e env] [--ref r] [--out dir] [--force]\n seq-studio repos clone --url <https://\u2026/repos/<id>/git> [-e env] [--ref r] [--out dir]\n seq-studio repos clone --id <uuid> [-e env] [--ref r] [--out dir]\n smart-HTTP when ATLAS_GIT_PAT is set;\n otherwise JSON materialize (<ns>/<name>)\n seq-studio repos pull <ns>/<name> [-e env] [--ref r] [--out dir] [--force]\n materialize the tree at a ref (JSON API)\n seq-studio repos delete <ns>/<name> [-e env] [--yes] delete a repo (confirm prompt)\n\n Flags: -e/--env <local|staging|production|banksouth>\n\n clone prefers real git clone (PAT via askpass \u2014 never written into the remote\n URL). Without ATLAS_GIT_PAT, <ns>/<name> falls back to the JSON API and prints\n how to get a PAT (Atlas UI /settings/tokens \u2014 no seqapi required).\n No seqapi + PAT: use --url from Repositories \u2192 Clone (or --id <uuid>).\n --ref accepts a branch, tag, or commit SHA (SHA \u2192 clone then checkout).\n\n Authenticate JSON API calls with: seqapi login\n Authenticate git clone/push with: ATLAS_GIT_PAT (from Atlas Settings \u2192 Tokens)\n";
49
+ export declare function runReposCommand(sub: string | undefined, args: ParsedArgs): Promise<number>;