@dashai/cli 0.2.2 → 0.2.4
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/bin.js +271 -103
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -92,6 +92,94 @@ function isPlausibleConfig(v) {
|
|
|
92
92
|
if (typeof u.email !== "string") return false;
|
|
93
93
|
return true;
|
|
94
94
|
}
|
|
95
|
+
|
|
96
|
+
// src/lib/api-client.ts
|
|
97
|
+
var ApiError = class extends Error {
|
|
98
|
+
status;
|
|
99
|
+
code;
|
|
100
|
+
context;
|
|
101
|
+
retriable;
|
|
102
|
+
constructor(status, envelope, fallbackMessage) {
|
|
103
|
+
const code2 = envelope?.code ?? codeFromStatus(status);
|
|
104
|
+
const message = envelope?.message ?? fallbackMessage;
|
|
105
|
+
super(message);
|
|
106
|
+
this.name = "ApiError";
|
|
107
|
+
this.status = status;
|
|
108
|
+
this.code = code2;
|
|
109
|
+
this.context = envelope?.context;
|
|
110
|
+
this.retriable = envelope?.retriable ?? status >= 500;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
async function apiRequest(method, path, body, options = {}) {
|
|
114
|
+
const baseUrl = (options.baseUrl ?? resolveApiUrl()).replace(/\/+$/, "");
|
|
115
|
+
const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
116
|
+
const headers = {
|
|
117
|
+
Accept: "application/json",
|
|
118
|
+
...options.headers ?? {}
|
|
119
|
+
};
|
|
120
|
+
if (body !== void 0) {
|
|
121
|
+
headers["Content-Type"] = "application/json";
|
|
122
|
+
}
|
|
123
|
+
const authMode = options.auth ?? "auto";
|
|
124
|
+
if (authMode === "auto") {
|
|
125
|
+
const saved = readSavedToken();
|
|
126
|
+
if (saved) headers["Authorization"] = `Bearer ${saved}`;
|
|
127
|
+
} else if (typeof authMode === "object" && authMode.token) {
|
|
128
|
+
headers["Authorization"] = `Bearer ${authMode.token}`;
|
|
129
|
+
}
|
|
130
|
+
let res;
|
|
131
|
+
try {
|
|
132
|
+
res = await fetch(url, {
|
|
133
|
+
method,
|
|
134
|
+
headers,
|
|
135
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
136
|
+
});
|
|
137
|
+
} catch (err) {
|
|
138
|
+
throw new ApiError(0, { code: "NETWORK_ERROR", message: err.message }, "Network error");
|
|
139
|
+
}
|
|
140
|
+
if (res.status === 204) {
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
144
|
+
let parsed = null;
|
|
145
|
+
if (contentType.includes("application/json")) {
|
|
146
|
+
try {
|
|
147
|
+
parsed = await res.json();
|
|
148
|
+
} catch {
|
|
149
|
+
parsed = null;
|
|
150
|
+
}
|
|
151
|
+
} else if (res.status >= 400) {
|
|
152
|
+
try {
|
|
153
|
+
parsed = { message: await res.text() };
|
|
154
|
+
} catch {
|
|
155
|
+
parsed = null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
throw new ApiError(res.status, asEnvelope(parsed), `HTTP ${res.status}`);
|
|
160
|
+
}
|
|
161
|
+
return parsed;
|
|
162
|
+
}
|
|
163
|
+
function asEnvelope(v) {
|
|
164
|
+
if (!v || typeof v !== "object") return null;
|
|
165
|
+
const env = v;
|
|
166
|
+
const out2 = {};
|
|
167
|
+
if (typeof env.code === "string") out2.code = env.code;
|
|
168
|
+
if (typeof env.message === "string") out2.message = env.message;
|
|
169
|
+
if (typeof env.retriable === "boolean") out2.retriable = env.retriable;
|
|
170
|
+
if (env.context && typeof env.context === "object") {
|
|
171
|
+
out2.context = env.context;
|
|
172
|
+
}
|
|
173
|
+
return out2;
|
|
174
|
+
}
|
|
175
|
+
function codeFromStatus(status) {
|
|
176
|
+
if (status === 401) return "UNAUTHENTICATED";
|
|
177
|
+
if (status === 403) return "FORBIDDEN";
|
|
178
|
+
if (status === 404) return "NOT_FOUND";
|
|
179
|
+
if (status === 0) return "NETWORK_ERROR";
|
|
180
|
+
if (status >= 500) return "INTERNAL_ERROR";
|
|
181
|
+
return "HTTP_ERROR";
|
|
182
|
+
}
|
|
95
183
|
function success(msg) {
|
|
96
184
|
process.stderr.write(`${pc.green("\u2713")} ${msg}
|
|
97
185
|
`);
|
|
@@ -125,6 +213,7 @@ function dim(s) {
|
|
|
125
213
|
}
|
|
126
214
|
|
|
127
215
|
// src/commands/dev.ts
|
|
216
|
+
var DEFAULT_NEXT_PORT = 3e3;
|
|
128
217
|
var PREFIX_SDK = `\x1B[36m[sdk]\x1B[0m `;
|
|
129
218
|
var PREFIX_NEXT = `\x1B[35m[next]\x1B[0m `;
|
|
130
219
|
async function devCommand(options) {
|
|
@@ -157,17 +246,60 @@ async function devCommand(options) {
|
|
|
157
246
|
return 1;
|
|
158
247
|
}
|
|
159
248
|
const apiUrl = options.apiUrl ?? process.env.DASHWISE_API_URL ?? savedConfig?.apiUrl ?? "http://localhost:3000";
|
|
160
|
-
const
|
|
249
|
+
const port = options.port ?? DEFAULT_NEXT_PORT;
|
|
250
|
+
const explicitInstallationId = options.installationId ?? process.env.DASHWISE_INSTALLATION_ID;
|
|
251
|
+
const reuseDevSessionId = process.env.DASHWISE_DEV_SESSION_ID;
|
|
252
|
+
let installationIdEnv;
|
|
253
|
+
let devSessionIdEnv;
|
|
254
|
+
let revokeDevSession;
|
|
255
|
+
if (explicitInstallationId && explicitInstallationId !== "local") {
|
|
256
|
+
installationIdEnv = explicitInstallationId;
|
|
257
|
+
} else if (reuseDevSessionId) {
|
|
258
|
+
devSessionIdEnv = reuseDevSessionId;
|
|
259
|
+
} else {
|
|
260
|
+
let workspaceContext;
|
|
261
|
+
try {
|
|
262
|
+
workspaceContext = await resolveTargetWorkspace(apiUrl, apiToken, options.workspace);
|
|
263
|
+
} catch (err) {
|
|
264
|
+
fail(err.message);
|
|
265
|
+
return 1;
|
|
266
|
+
}
|
|
267
|
+
const callbackOrigins = computeCallbackOrigins(port, options.callbackOrigin ?? []);
|
|
268
|
+
try {
|
|
269
|
+
const session = await registerDevSession(apiUrl, apiToken, {
|
|
270
|
+
target_workspace_id: workspaceContext.id,
|
|
271
|
+
callback_origins: callbackOrigins
|
|
272
|
+
});
|
|
273
|
+
devSessionIdEnv = session.id;
|
|
274
|
+
revokeDevSession = () => revokeDevSessionBestEffort(apiUrl, apiToken, session.id);
|
|
275
|
+
info(` ${dim("dev session")} ${session.id}`);
|
|
276
|
+
info(` ${dim("workspace")} ${workspaceContext.name} (id=${workspaceContext.id}, slug=${workspaceContext.slug})`);
|
|
277
|
+
info(` ${dim("callbacks")} ${callbackOrigins.join(", ")}`);
|
|
278
|
+
info(` ${dim("expires")} ${session.expires_at}`);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
if (err instanceof ApiError) {
|
|
281
|
+
fail(`Failed to register dev session: ${err.code} \u2014 ${err.message}`);
|
|
282
|
+
} else {
|
|
283
|
+
fail(`Failed to register dev session: ${err.message}`);
|
|
284
|
+
}
|
|
285
|
+
return 1;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
161
288
|
const childEnv = {
|
|
162
289
|
...process.env,
|
|
163
290
|
DASHWISE_API_URL: apiUrl,
|
|
164
291
|
DASHWISE_API_TOKEN: apiToken,
|
|
165
|
-
DASHWISE_INSTALLATION_ID:
|
|
292
|
+
DASHWISE_INSTALLATION_ID: installationIdEnv ?? "",
|
|
293
|
+
...devSessionIdEnv !== void 0 ? { DASHWISE_DEV_SESSION_ID: devSessionIdEnv } : {}
|
|
166
294
|
};
|
|
167
295
|
info(`Dev loop starting \u2014 Ctrl-C to stop.`);
|
|
168
|
-
info(` ${dim("api")}
|
|
169
|
-
|
|
170
|
-
|
|
296
|
+
info(` ${dim("api")} ${apiUrl}`);
|
|
297
|
+
if (installationIdEnv !== void 0) {
|
|
298
|
+
info(` ${dim("installation")} ${installationIdEnv} (prod-mode SSO)`);
|
|
299
|
+
} else {
|
|
300
|
+
info(` ${dim("mode")} dev (DASHWISE_DEV_SESSION_ID injected)`);
|
|
301
|
+
}
|
|
302
|
+
info(` ${dim("token")} ${maskedToken(apiToken)} ${dim(`(from ${apiToken === savedConfig?.token ? "~/.config/dashwise/auth.json" : "$DASHWISE_API_TOKEN"})`)}`);
|
|
171
303
|
info("");
|
|
172
304
|
const children = [];
|
|
173
305
|
if (!options.nextOnly) {
|
|
@@ -175,7 +307,7 @@ async function devCommand(options) {
|
|
|
175
307
|
children.push({ name: "sdk", child: sdkChild, prefix: PREFIX_SDK });
|
|
176
308
|
}
|
|
177
309
|
if (!options.sdkOnly) {
|
|
178
|
-
const nextChild = spawnNextDev(projectRoot, childEnv,
|
|
310
|
+
const nextChild = spawnNextDev(projectRoot, childEnv, port);
|
|
179
311
|
children.push({ name: "next", child: nextChild, prefix: PREFIX_NEXT });
|
|
180
312
|
}
|
|
181
313
|
for (const { child, prefix } of children) {
|
|
@@ -193,6 +325,10 @@ Received ${sig}, stopping children\u2026`);
|
|
|
193
325
|
child.kill(sig);
|
|
194
326
|
}
|
|
195
327
|
}
|
|
328
|
+
if (revokeDevSession) {
|
|
329
|
+
revokeDevSession().catch(() => {
|
|
330
|
+
});
|
|
331
|
+
}
|
|
196
332
|
};
|
|
197
333
|
process.on("SIGINT", () => forwardSignal("SIGINT"));
|
|
198
334
|
process.on("SIGTERM", () => forwardSignal("SIGTERM"));
|
|
@@ -286,96 +422,60 @@ function maskedToken(token) {
|
|
|
286
422
|
const tail = token.slice(-4);
|
|
287
423
|
return `${prefix}\u2026${tail}`;
|
|
288
424
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
this.retriable = envelope?.retriable ?? status >= 500;
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
async function apiRequest(method, path, body, options = {}) {
|
|
308
|
-
const baseUrl = (options.baseUrl ?? resolveApiUrl()).replace(/\/+$/, "");
|
|
309
|
-
const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
310
|
-
const headers = {
|
|
311
|
-
Accept: "application/json",
|
|
312
|
-
...options.headers ?? {}
|
|
313
|
-
};
|
|
314
|
-
if (body !== void 0) {
|
|
315
|
-
headers["Content-Type"] = "application/json";
|
|
316
|
-
}
|
|
317
|
-
const authMode = options.auth ?? "auto";
|
|
318
|
-
if (authMode === "auto") {
|
|
319
|
-
const saved = readSavedToken();
|
|
320
|
-
if (saved) headers["Authorization"] = `Bearer ${saved}`;
|
|
321
|
-
} else if (typeof authMode === "object" && authMode.token) {
|
|
322
|
-
headers["Authorization"] = `Bearer ${authMode.token}`;
|
|
323
|
-
}
|
|
324
|
-
let res;
|
|
325
|
-
try {
|
|
326
|
-
res = await fetch(url, {
|
|
327
|
-
method,
|
|
328
|
-
headers,
|
|
329
|
-
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
330
|
-
});
|
|
331
|
-
} catch (err) {
|
|
332
|
-
throw new ApiError(0, { code: "NETWORK_ERROR", message: err.message }, "Network error");
|
|
333
|
-
}
|
|
334
|
-
if (res.status === 204) {
|
|
335
|
-
return void 0;
|
|
425
|
+
function computeCallbackOrigins(port, extra) {
|
|
426
|
+
const def = `http://localhost:${port}`;
|
|
427
|
+
return Array.from(/* @__PURE__ */ new Set([def, ...extra]));
|
|
428
|
+
}
|
|
429
|
+
async function resolveTargetWorkspace(apiUrl, apiToken, flag) {
|
|
430
|
+
const workspaces = await apiRequest(
|
|
431
|
+
"GET",
|
|
432
|
+
"/api/workspaces",
|
|
433
|
+
void 0,
|
|
434
|
+
{ baseUrl: apiUrl, auth: { token: apiToken } }
|
|
435
|
+
);
|
|
436
|
+
if (workspaces.length === 0) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
"No workspaces found for your account. Create a workspace at the DashWise dashboard first."
|
|
439
|
+
);
|
|
336
440
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
try {
|
|
341
|
-
parsed = await res.json();
|
|
342
|
-
} catch {
|
|
343
|
-
parsed = null;
|
|
344
|
-
}
|
|
345
|
-
} else if (res.status >= 400) {
|
|
346
|
-
try {
|
|
347
|
-
parsed = { message: await res.text() };
|
|
348
|
-
} catch {
|
|
349
|
-
parsed = null;
|
|
441
|
+
if (flag === void 0) {
|
|
442
|
+
if (workspaces.length === 1) {
|
|
443
|
+
return workspaces[0];
|
|
350
444
|
}
|
|
445
|
+
const list = workspaces.map((w) => ` - ${w.slug} (id=${w.id}, name="${w.name}")`).join("\n");
|
|
446
|
+
throw new Error(
|
|
447
|
+
`You have ${workspaces.length} workspaces. Re-run with --workspace <slug-or-id> to pick one:
|
|
448
|
+
${list}`
|
|
449
|
+
);
|
|
351
450
|
}
|
|
352
|
-
|
|
353
|
-
|
|
451
|
+
const asNumber = Number.parseInt(flag, 10);
|
|
452
|
+
const isNumeric = !Number.isNaN(asNumber) && String(asNumber) === flag;
|
|
453
|
+
const match = isNumeric ? workspaces.find((w) => w.id === asNumber) : workspaces.find((w) => w.slug === flag);
|
|
454
|
+
if (!match) {
|
|
455
|
+
const list = workspaces.map((w) => ` - ${w.slug} (id=${w.id})`).join("\n");
|
|
456
|
+
throw new Error(
|
|
457
|
+
`No workspace matching --workspace=${flag}. Available:
|
|
458
|
+
${list}`
|
|
459
|
+
);
|
|
354
460
|
}
|
|
355
|
-
return
|
|
461
|
+
return match;
|
|
356
462
|
}
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
if (env.context && typeof env.context === "object") {
|
|
365
|
-
out2.context = env.context;
|
|
366
|
-
}
|
|
367
|
-
return out2;
|
|
463
|
+
async function registerDevSession(apiUrl, apiToken, body) {
|
|
464
|
+
return apiRequest(
|
|
465
|
+
"POST",
|
|
466
|
+
"/api/auth/dev/sessions",
|
|
467
|
+
body,
|
|
468
|
+
{ baseUrl: apiUrl, auth: { token: apiToken } }
|
|
469
|
+
);
|
|
368
470
|
}
|
|
369
|
-
function
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
471
|
+
async function revokeDevSessionBestEffort(apiUrl, apiToken, id) {
|
|
472
|
+
await apiRequest(
|
|
473
|
+
"DELETE",
|
|
474
|
+
`/api/auth/dev/sessions/${encodeURIComponent(id)}`,
|
|
475
|
+
void 0,
|
|
476
|
+
{ baseUrl: apiUrl, auth: { token: apiToken } }
|
|
477
|
+
);
|
|
376
478
|
}
|
|
377
|
-
|
|
378
|
-
// src/commands/login.ts
|
|
379
479
|
var DEFAULT_TOKEN_NAME = "dashwise-cli";
|
|
380
480
|
async function loginCommand(options) {
|
|
381
481
|
const apiUrl = (options.apiUrl ?? resolveApiUrl()).replace(/\/+$/, "");
|
|
@@ -910,6 +1010,24 @@ function envLocalExample() {
|
|
|
910
1010
|
""
|
|
911
1011
|
].join("\n");
|
|
912
1012
|
}
|
|
1013
|
+
function envLocal() {
|
|
1014
|
+
return [
|
|
1015
|
+
"# Local dev config \u2014 auto-generated by `dashwise module init`.",
|
|
1016
|
+
"# Gitignored; safe to edit. `dashwise dev` (the unified loop) auto-",
|
|
1017
|
+
"# injects DASHWISE_API_TOKEN from ~/.config/dashwise/auth.json AND",
|
|
1018
|
+
"# DASHWISE_DEV_SESSION_ID from the dev session it registers \u2014 so",
|
|
1019
|
+
"# you typically don't need to set either here. Change DASHWISE_API_URL",
|
|
1020
|
+
"# to point at a non-default backend (e.g. https://api.dashwise.net).",
|
|
1021
|
+
"#",
|
|
1022
|
+
"# DASHWISE_INSTALLATION_ID is only needed AFTER you publish + install",
|
|
1023
|
+
"# the module in a workspace; until then dev mode (DASHWISE_DEV_SESSION_ID,",
|
|
1024
|
+
"# auto-set by `dashwise dev`) is the auth path.",
|
|
1025
|
+
"DASHWISE_API_URL=http://localhost:3000",
|
|
1026
|
+
"DASHWISE_API_TOKEN=",
|
|
1027
|
+
"DASHWISE_INSTALLATION_ID=",
|
|
1028
|
+
""
|
|
1029
|
+
].join("\n");
|
|
1030
|
+
}
|
|
913
1031
|
function appLayoutTsx(opts) {
|
|
914
1032
|
return `export const metadata = {
|
|
915
1033
|
title: ${JSON.stringify(opts.name)},
|
|
@@ -1344,11 +1462,23 @@ function Row({ label, value }: { label: string; value: string }) {
|
|
|
1344
1462
|
function appApiAuthSignInTs() {
|
|
1345
1463
|
return `// /api/auth/sign-in \u2014 kick off the SSO flow.
|
|
1346
1464
|
//
|
|
1347
|
-
// Reads \`?return_to=\` from the caller (defaults to '/')
|
|
1348
|
-
//
|
|
1349
|
-
//
|
|
1350
|
-
//
|
|
1351
|
-
//
|
|
1465
|
+
// Reads \`?return_to=\` from the caller (defaults to '/'). Branches
|
|
1466
|
+
// on env:
|
|
1467
|
+
//
|
|
1468
|
+
// - DASHWISE_DEV_SESSION_ID set (set automatically by
|
|
1469
|
+
// \`dashwise dev\`): hits /api/auth/dev/sso/start with the dev
|
|
1470
|
+
// session id. No installation_id required \u2014 the module isn't
|
|
1471
|
+
// installed in a workspace yet.
|
|
1472
|
+
//
|
|
1473
|
+
// - DASHWISE_INSTALLATION_ID set (production / installed-module
|
|
1474
|
+
// path): hits /api/auth/sso/start with the installation id.
|
|
1475
|
+
//
|
|
1476
|
+
// In both cases the backend mints an exchange code + 302s back to
|
|
1477
|
+
// \`/api/auth/callback?code=...\` where we set the session cookie.
|
|
1478
|
+
//
|
|
1479
|
+
// Unauthenticated browser hits to either backend endpoint 302 to
|
|
1480
|
+
// the DashWise FE \`/login?return_to=...\` automatically \u2014 you don't
|
|
1481
|
+
// need to handle the "user not logged in" case here.
|
|
1352
1482
|
import { NextResponse } from 'next/server';
|
|
1353
1483
|
|
|
1354
1484
|
export function GET(request: Request) {
|
|
@@ -1356,19 +1486,43 @@ export function GET(request: Request) {
|
|
|
1356
1486
|
const returnTo = url.searchParams.get('return_to') ?? '/';
|
|
1357
1487
|
const apiBaseUrl = process.env.DASHWISE_API_URL;
|
|
1358
1488
|
const installationId = process.env.DASHWISE_INSTALLATION_ID;
|
|
1359
|
-
|
|
1489
|
+
const devSessionId = process.env.DASHWISE_DEV_SESSION_ID;
|
|
1490
|
+
|
|
1491
|
+
if (!apiBaseUrl) {
|
|
1360
1492
|
return new NextResponse(
|
|
1361
|
-
'DASHWISE_API_URL
|
|
1493
|
+
'DASHWISE_API_URL must be set in .env.local',
|
|
1362
1494
|
{ status: 500 },
|
|
1363
1495
|
);
|
|
1364
1496
|
}
|
|
1497
|
+
if (!installationId && !devSessionId) {
|
|
1498
|
+
return new NextResponse(
|
|
1499
|
+
'Either DASHWISE_INSTALLATION_ID (installed module) or DASHWISE_DEV_SESSION_ID ' +
|
|
1500
|
+
'(local dev via \`dashwise dev\`) must be set. Run \`dashwise dev\` to register ' +
|
|
1501
|
+
'a dev session, or set DASHWISE_INSTALLATION_ID after publishing your module.',
|
|
1502
|
+
{ status: 500 },
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1365
1506
|
// Absolute URL for return_to so the backend knows which host to
|
|
1366
1507
|
// bounce back to (the callback path is fixed at /api/auth/callback
|
|
1367
1508
|
// by SDK convention).
|
|
1368
1509
|
const moduleOrigin = url.origin;
|
|
1369
|
-
const
|
|
1370
|
-
|
|
1371
|
-
ssoStart
|
|
1510
|
+
const absoluteReturnTo = \`\${moduleOrigin}\${returnTo}\`;
|
|
1511
|
+
|
|
1512
|
+
let ssoStart;
|
|
1513
|
+
if (devSessionId) {
|
|
1514
|
+
// Dev mode wins when both are set \u2014 \`dashwise dev\` injects
|
|
1515
|
+
// DASHWISE_DEV_SESSION_ID into the child env, which overrides
|
|
1516
|
+
// whatever was in .env.local. The dev session id keys back to
|
|
1517
|
+
// the dev's workspace + callback origin allowlist.
|
|
1518
|
+
ssoStart = new URL('/api/auth/dev/sso/start', apiBaseUrl);
|
|
1519
|
+
ssoStart.searchParams.set('return_to', absoluteReturnTo);
|
|
1520
|
+
ssoStart.searchParams.set('dev_session_id', devSessionId);
|
|
1521
|
+
} else {
|
|
1522
|
+
ssoStart = new URL('/api/auth/sso/start', apiBaseUrl);
|
|
1523
|
+
ssoStart.searchParams.set('return_to', absoluteReturnTo);
|
|
1524
|
+
ssoStart.searchParams.set('client_id', installationId);
|
|
1525
|
+
}
|
|
1372
1526
|
return NextResponse.redirect(ssoStart);
|
|
1373
1527
|
}
|
|
1374
1528
|
`;
|
|
@@ -2375,6 +2529,7 @@ async function moduleInitCommand(slugArg, options) {
|
|
|
2375
2529
|
["next-env.d.ts", nextEnvDts()],
|
|
2376
2530
|
[".gitignore", gitignore()],
|
|
2377
2531
|
[".env.local.example", envLocalExample()],
|
|
2532
|
+
[".env.local", envLocal()],
|
|
2378
2533
|
["README.md", readme(scaffoldOpts)]
|
|
2379
2534
|
];
|
|
2380
2535
|
if (kind === "custom") {
|
|
@@ -4100,18 +4255,28 @@ workspace.command("use <slug>").description("Set the default workspace for subse
|
|
|
4100
4255
|
process.exitCode = await workspaceUseCommand(slug);
|
|
4101
4256
|
});
|
|
4102
4257
|
program.command("dev").description(
|
|
4103
|
-
"One-command local dev loop: runs the manifest watcher + Next.js dev server together
|
|
4258
|
+
"One-command local dev loop: runs the manifest watcher + Next.js dev server together. Registers a dev-mode SSO session with the backend so workspace members can sign into your locally-running module via DashWise SSO (no installation_id required). Auth + dev-session env auto-injected into both child processes from your saved CLI config. Run `dashwise login` first."
|
|
4104
4259
|
).option("--dir <path>", "Project root containing `module.json` (default: current directory).").option(
|
|
4105
4260
|
"--installation-id <id>",
|
|
4106
|
-
|
|
4261
|
+
"Use prod-mode SSO with this installation id instead of registering a dev session. The installation must already exist in the target workspace."
|
|
4262
|
+
).option(
|
|
4263
|
+
"--workspace <slug-or-id>",
|
|
4264
|
+
"Workspace to scope the dev session to (slug or numeric id). Required if your account belongs to more than one workspace; auto-picked if you have just one."
|
|
4265
|
+
).option(
|
|
4266
|
+
"--callback-origin <url>",
|
|
4267
|
+
"Additional callback origin to allowlist on the dev session (repeatable). The dev server URL (http://localhost:<port>) is always allowlisted; pass this for custom-domain local dev (e.g. https://myapp.local).",
|
|
4268
|
+
collectRepeated,
|
|
4269
|
+
[]
|
|
4107
4270
|
).option(
|
|
4108
4271
|
"--api-url <url>",
|
|
4109
4272
|
"Override the `DASHWISE_API_URL` env var passed to children (default: saved CLI config)."
|
|
4110
|
-
).option("--port <number>", "Port for `next dev` (default:
|
|
4273
|
+
).option("--port <number>", "Port for `next dev` (default: 3000).", parseIntOption).option("--sdk-only", "Only run the manifest watcher; skip `next dev`.").option("--next-only", "Only run `next dev`; skip the manifest watcher.").action(
|
|
4111
4274
|
async (cmdOpts) => {
|
|
4112
4275
|
process.exitCode = await devCommand({
|
|
4113
4276
|
...cmdOpts.dir !== void 0 ? { dir: cmdOpts.dir } : {},
|
|
4114
4277
|
...cmdOpts.installationId !== void 0 ? { installationId: cmdOpts.installationId } : {},
|
|
4278
|
+
...cmdOpts.workspace !== void 0 ? { workspace: cmdOpts.workspace } : {},
|
|
4279
|
+
...cmdOpts.callbackOrigin !== void 0 && cmdOpts.callbackOrigin.length > 0 ? { callbackOrigin: cmdOpts.callbackOrigin } : {},
|
|
4115
4280
|
...cmdOpts.apiUrl !== void 0 ? { apiUrl: cmdOpts.apiUrl } : {},
|
|
4116
4281
|
...cmdOpts.port !== void 0 ? { port: cmdOpts.port } : {},
|
|
4117
4282
|
...cmdOpts.sdkOnly !== void 0 ? { sdkOnly: cmdOpts.sdkOnly } : {},
|
|
@@ -4330,7 +4495,7 @@ moduleCmd.command("action-log").description(
|
|
|
4330
4495
|
}
|
|
4331
4496
|
})();
|
|
4332
4497
|
function getVersion() {
|
|
4333
|
-
return "0.
|
|
4498
|
+
return "0.2.4";
|
|
4334
4499
|
}
|
|
4335
4500
|
function parseIntOption(value) {
|
|
4336
4501
|
const n = Number.parseInt(value, 10);
|
|
@@ -4339,5 +4504,8 @@ function parseIntOption(value) {
|
|
|
4339
4504
|
}
|
|
4340
4505
|
return n;
|
|
4341
4506
|
}
|
|
4507
|
+
function collectRepeated(value, previous) {
|
|
4508
|
+
return [...previous, value];
|
|
4509
|
+
}
|
|
4342
4510
|
//# sourceMappingURL=bin.js.map
|
|
4343
4511
|
//# sourceMappingURL=bin.js.map
|