camstack 1.2.190 → 1.2.191
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.js +247 -201
- package/dist/{launcher-ILGFB3AQ.js → launcher-AZI5I6X5.js} +2716 -1793
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,7 @@ async function runServe(args) {
|
|
|
37
37
|
...typeof values.data === "string" ? { data: values.data } : {}
|
|
38
38
|
};
|
|
39
39
|
Object.assign(process.env, buildServeEnv(opts));
|
|
40
|
-
await import("./launcher-
|
|
40
|
+
await import("./launcher-AZI5I6X5.js");
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// src/commands/agent.ts
|
|
@@ -82,7 +82,7 @@ async function runAgent(args) {
|
|
|
82
82
|
...typeof values.port === "string" ? { port: values.port } : {}
|
|
83
83
|
};
|
|
84
84
|
Object.assign(process.env, buildAgentEnv(opts));
|
|
85
|
-
await import("./launcher-
|
|
85
|
+
await import("./launcher-AZI5I6X5.js");
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// src/commands/setup.ts
|
|
@@ -14968,6 +14968,7 @@ var WELL_KNOWN_TABS = [
|
|
|
14968
14968
|
];
|
|
14969
14969
|
var WELL_KNOWN_TAB_MAP = Object.fromEntries(WELL_KNOWN_TABS.map((t) => [t.id, t]));
|
|
14970
14970
|
var SECRET_RETRY_MAX_MS = 5 * 6e4;
|
|
14971
|
+
var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
|
|
14971
14972
|
var PEER_BYTES_CHUNK_BYTES = 256 * 1024;
|
|
14972
14973
|
var PeerBytesTicketSchema = external_exports.object({
|
|
14973
14974
|
/** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
|
|
@@ -18452,6 +18453,150 @@ var ConvertResultSchema = external_exports.object({
|
|
|
18452
18453
|
entry: ModelCatalogEntrySchema,
|
|
18453
18454
|
artifacts: external_exports.array(ConvertArtifactSchema).readonly()
|
|
18454
18455
|
});
|
|
18456
|
+
var MethodAccessSchema = external_exports.enum([
|
|
18457
|
+
"view",
|
|
18458
|
+
"create",
|
|
18459
|
+
"delete"
|
|
18460
|
+
]);
|
|
18461
|
+
var AllowedProviderSchema = external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]);
|
|
18462
|
+
var AllowedDevicesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]));
|
|
18463
|
+
var CapScopeSchema = external_exports.enum(["device", "system"]);
|
|
18464
|
+
var DeviceSelectorSchema = external_exports.discriminatedUnion("kind", [
|
|
18465
|
+
external_exports.object({ kind: external_exports.literal("all") }),
|
|
18466
|
+
external_exports.object({
|
|
18467
|
+
kind: external_exports.literal("ids"),
|
|
18468
|
+
ids: external_exports.array(external_exports.number().int()).min(1)
|
|
18469
|
+
}),
|
|
18470
|
+
external_exports.object({
|
|
18471
|
+
kind: external_exports.literal("types"),
|
|
18472
|
+
types: external_exports.array(external_exports.enum(DeviceType)).min(1)
|
|
18473
|
+
}),
|
|
18474
|
+
external_exports.object({
|
|
18475
|
+
kind: external_exports.literal("locations"),
|
|
18476
|
+
locations: external_exports.array(external_exports.string().min(1)).min(1)
|
|
18477
|
+
})
|
|
18478
|
+
]);
|
|
18479
|
+
var DeviceTokenScopeSchema = external_exports.object({
|
|
18480
|
+
type: external_exports.literal("device"),
|
|
18481
|
+
/** The device SET this grant covers — resolved against the live fleet. */
|
|
18482
|
+
selector: DeviceSelectorSchema,
|
|
18483
|
+
access: external_exports.array(MethodAccessSchema).min(1),
|
|
18484
|
+
/**
|
|
18485
|
+
* Whether a grant on a PARENT device transparently covers its accessory
|
|
18486
|
+
* CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
|
|
18487
|
+
* Direction is parent → children ONLY.
|
|
18488
|
+
*
|
|
18489
|
+
* Absent → the matcher DERIVES it from the access flavour: `view`
|
|
18490
|
+
* inherits (a camera viewer sees the camera's accessories), `create` /
|
|
18491
|
+
* `delete` do NOT (actuating/removing a child is an explicit act the
|
|
18492
|
+
* operator must grant on the child, not inherit from the parent). Set it
|
|
18493
|
+
* explicitly to override that default per grant.
|
|
18494
|
+
*/
|
|
18495
|
+
includeLinked: external_exports.boolean().optional()
|
|
18496
|
+
});
|
|
18497
|
+
function migrateLegacyTokenScope(raw) {
|
|
18498
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
18499
|
+
if (Reflect.get(raw, "type") !== "device") return raw;
|
|
18500
|
+
if (Reflect.get(raw, "selector") !== void 0) return raw;
|
|
18501
|
+
const targets = Reflect.get(raw, "targets");
|
|
18502
|
+
if (!Array.isArray(targets)) return raw;
|
|
18503
|
+
return {
|
|
18504
|
+
type: "device",
|
|
18505
|
+
selector: {
|
|
18506
|
+
kind: "ids",
|
|
18507
|
+
ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
|
|
18508
|
+
},
|
|
18509
|
+
access: Reflect.get(raw, "access")
|
|
18510
|
+
};
|
|
18511
|
+
}
|
|
18512
|
+
var TokenScopeSchema = external_exports.preprocess(migrateLegacyTokenScope, external_exports.discriminatedUnion("type", [
|
|
18513
|
+
external_exports.object({
|
|
18514
|
+
type: external_exports.literal("category"),
|
|
18515
|
+
target: CapScopeSchema,
|
|
18516
|
+
access: external_exports.array(MethodAccessSchema).min(1)
|
|
18517
|
+
}),
|
|
18518
|
+
external_exports.object({
|
|
18519
|
+
type: external_exports.literal("capability"),
|
|
18520
|
+
target: external_exports.string(),
|
|
18521
|
+
access: external_exports.array(MethodAccessSchema).min(1)
|
|
18522
|
+
}),
|
|
18523
|
+
external_exports.object({
|
|
18524
|
+
type: external_exports.literal("addon"),
|
|
18525
|
+
target: external_exports.string(),
|
|
18526
|
+
access: external_exports.array(MethodAccessSchema).min(1)
|
|
18527
|
+
}),
|
|
18528
|
+
DeviceTokenScopeSchema
|
|
18529
|
+
]));
|
|
18530
|
+
var UserRecordSchema = external_exports.object({
|
|
18531
|
+
id: external_exports.string(),
|
|
18532
|
+
username: external_exports.string(),
|
|
18533
|
+
passwordHash: external_exports.string(),
|
|
18534
|
+
/**
|
|
18535
|
+
* Admin bypass. When true, the middleware skips the scope-access
|
|
18536
|
+
* check entirely. There is no other axis of privilege; the legacy
|
|
18537
|
+
* role enum collapsed onto this boolean in v2.
|
|
18538
|
+
*/
|
|
18539
|
+
isAdmin: external_exports.boolean().default(false),
|
|
18540
|
+
allowedProviders: AllowedProviderSchema,
|
|
18541
|
+
allowedDevices: AllowedDevicesSchema,
|
|
18542
|
+
/**
|
|
18543
|
+
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
18544
|
+
* ignored. Non-admins without scopes are locked out of every
|
|
18545
|
+
* protected call.
|
|
18546
|
+
*/
|
|
18547
|
+
scopes: external_exports.array(TokenScopeSchema).default([]),
|
|
18548
|
+
createdAt: external_exports.number(),
|
|
18549
|
+
updatedAt: external_exports.number()
|
|
18550
|
+
});
|
|
18551
|
+
var ApiKeyRecordSchema = external_exports.object({
|
|
18552
|
+
id: external_exports.string(),
|
|
18553
|
+
label: external_exports.string(),
|
|
18554
|
+
isAdmin: external_exports.boolean().default(false),
|
|
18555
|
+
allowedProviders: AllowedProviderSchema,
|
|
18556
|
+
allowedDevices: AllowedDevicesSchema,
|
|
18557
|
+
tokenHash: external_exports.string(),
|
|
18558
|
+
tokenPrefix: external_exports.string(),
|
|
18559
|
+
createdAt: external_exports.number(),
|
|
18560
|
+
lastUsedAt: external_exports.number().optional()
|
|
18561
|
+
});
|
|
18562
|
+
var ScopedTokenSchema = external_exports.object({
|
|
18563
|
+
id: external_exports.string(),
|
|
18564
|
+
userId: external_exports.string(),
|
|
18565
|
+
name: external_exports.string(),
|
|
18566
|
+
tokenHash: external_exports.string(),
|
|
18567
|
+
tokenPrefix: external_exports.string(),
|
|
18568
|
+
scopes: external_exports.array(TokenScopeSchema),
|
|
18569
|
+
expiresAt: external_exports.number().nullish(),
|
|
18570
|
+
lastUsedAt: external_exports.number().nullish(),
|
|
18571
|
+
createdAt: external_exports.number()
|
|
18572
|
+
});
|
|
18573
|
+
var ShareViewGrantSchema = external_exports.object({
|
|
18574
|
+
kind: external_exports.enum(SHARE_VIEW_KINDS),
|
|
18575
|
+
deviceIds: external_exports.array(external_exports.number().int().nonnegative()).min(1).max(64),
|
|
18576
|
+
/** Talk-back, opt-in at mint time; absent means off. */
|
|
18577
|
+
talk: external_exports.boolean().optional()
|
|
18578
|
+
});
|
|
18579
|
+
var ShareViewScopeSchema = external_exports.object({
|
|
18580
|
+
type: external_exports.literal("share-view"),
|
|
18581
|
+
grant: ShareViewGrantSchema
|
|
18582
|
+
});
|
|
18583
|
+
var AddonRoutesScopeSchema = external_exports.object({
|
|
18584
|
+
type: external_exports.literal("addon-routes"),
|
|
18585
|
+
access: external_exports.array(MethodAccessSchema).min(1)
|
|
18586
|
+
});
|
|
18587
|
+
var CredentialScopeSchema = external_exports.union([
|
|
18588
|
+
external_exports.literal("*"),
|
|
18589
|
+
ShareViewScopeSchema,
|
|
18590
|
+
AddonRoutesScopeSchema,
|
|
18591
|
+
TokenScopeSchema
|
|
18592
|
+
]);
|
|
18593
|
+
var CREDENTIAL_TOKEN_KIND = "credential";
|
|
18594
|
+
var CredentialTokenClaimsSchema = external_exports.object({
|
|
18595
|
+
kind: external_exports.literal(CREDENTIAL_TOKEN_KIND),
|
|
18596
|
+
sub: external_exports.string().min(1),
|
|
18597
|
+
jti: external_exports.string().min(16),
|
|
18598
|
+
scopes: external_exports.array(CredentialScopeSchema).min(1)
|
|
18599
|
+
});
|
|
18455
18600
|
var AppFlowPurposeSchema = external_exports.enum(["new-session", "step-up"]);
|
|
18456
18601
|
var AppPageLangSchema = external_exports.enum(["en", "it"]);
|
|
18457
18602
|
var APP_FLOW_REFUSAL_MESSAGES = {
|
|
@@ -50264,123 +50409,6 @@ var notificationRulesCapability = {
|
|
|
50264
50409
|
getAlarmSensors: method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" })
|
|
50265
50410
|
}
|
|
50266
50411
|
};
|
|
50267
|
-
var MethodAccessSchema = external_exports.enum([
|
|
50268
|
-
"view",
|
|
50269
|
-
"create",
|
|
50270
|
-
"delete"
|
|
50271
|
-
]);
|
|
50272
|
-
var AllowedProviderSchema = external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]);
|
|
50273
|
-
var AllowedDevicesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]));
|
|
50274
|
-
var CapScopeSchema = external_exports.enum(["device", "system"]);
|
|
50275
|
-
var DeviceSelectorSchema = external_exports.discriminatedUnion("kind", [
|
|
50276
|
-
external_exports.object({ kind: external_exports.literal("all") }),
|
|
50277
|
-
external_exports.object({
|
|
50278
|
-
kind: external_exports.literal("ids"),
|
|
50279
|
-
ids: external_exports.array(external_exports.number().int()).min(1)
|
|
50280
|
-
}),
|
|
50281
|
-
external_exports.object({
|
|
50282
|
-
kind: external_exports.literal("types"),
|
|
50283
|
-
types: external_exports.array(external_exports.enum(DeviceType)).min(1)
|
|
50284
|
-
}),
|
|
50285
|
-
external_exports.object({
|
|
50286
|
-
kind: external_exports.literal("locations"),
|
|
50287
|
-
locations: external_exports.array(external_exports.string().min(1)).min(1)
|
|
50288
|
-
})
|
|
50289
|
-
]);
|
|
50290
|
-
var DeviceTokenScopeSchema = external_exports.object({
|
|
50291
|
-
type: external_exports.literal("device"),
|
|
50292
|
-
/** The device SET this grant covers — resolved against the live fleet. */
|
|
50293
|
-
selector: DeviceSelectorSchema,
|
|
50294
|
-
access: external_exports.array(MethodAccessSchema).min(1),
|
|
50295
|
-
/**
|
|
50296
|
-
* Whether a grant on a PARENT device transparently covers its accessory
|
|
50297
|
-
* CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
|
|
50298
|
-
* Direction is parent → children ONLY.
|
|
50299
|
-
*
|
|
50300
|
-
* Absent → the matcher DERIVES it from the access flavour: `view`
|
|
50301
|
-
* inherits (a camera viewer sees the camera's accessories), `create` /
|
|
50302
|
-
* `delete` do NOT (actuating/removing a child is an explicit act the
|
|
50303
|
-
* operator must grant on the child, not inherit from the parent). Set it
|
|
50304
|
-
* explicitly to override that default per grant.
|
|
50305
|
-
*/
|
|
50306
|
-
includeLinked: external_exports.boolean().optional()
|
|
50307
|
-
});
|
|
50308
|
-
function migrateLegacyTokenScope(raw) {
|
|
50309
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
50310
|
-
if (Reflect.get(raw, "type") !== "device") return raw;
|
|
50311
|
-
if (Reflect.get(raw, "selector") !== void 0) return raw;
|
|
50312
|
-
const targets = Reflect.get(raw, "targets");
|
|
50313
|
-
if (!Array.isArray(targets)) return raw;
|
|
50314
|
-
return {
|
|
50315
|
-
type: "device",
|
|
50316
|
-
selector: {
|
|
50317
|
-
kind: "ids",
|
|
50318
|
-
ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
|
|
50319
|
-
},
|
|
50320
|
-
access: Reflect.get(raw, "access")
|
|
50321
|
-
};
|
|
50322
|
-
}
|
|
50323
|
-
var TokenScopeSchema = external_exports.preprocess(migrateLegacyTokenScope, external_exports.discriminatedUnion("type", [
|
|
50324
|
-
external_exports.object({
|
|
50325
|
-
type: external_exports.literal("category"),
|
|
50326
|
-
target: CapScopeSchema,
|
|
50327
|
-
access: external_exports.array(MethodAccessSchema).min(1)
|
|
50328
|
-
}),
|
|
50329
|
-
external_exports.object({
|
|
50330
|
-
type: external_exports.literal("capability"),
|
|
50331
|
-
target: external_exports.string(),
|
|
50332
|
-
access: external_exports.array(MethodAccessSchema).min(1)
|
|
50333
|
-
}),
|
|
50334
|
-
external_exports.object({
|
|
50335
|
-
type: external_exports.literal("addon"),
|
|
50336
|
-
target: external_exports.string(),
|
|
50337
|
-
access: external_exports.array(MethodAccessSchema).min(1)
|
|
50338
|
-
}),
|
|
50339
|
-
DeviceTokenScopeSchema
|
|
50340
|
-
]));
|
|
50341
|
-
var UserRecordSchema = external_exports.object({
|
|
50342
|
-
id: external_exports.string(),
|
|
50343
|
-
username: external_exports.string(),
|
|
50344
|
-
passwordHash: external_exports.string(),
|
|
50345
|
-
/**
|
|
50346
|
-
* Admin bypass. When true, the middleware skips the scope-access
|
|
50347
|
-
* check entirely. There is no other axis of privilege; the legacy
|
|
50348
|
-
* role enum collapsed onto this boolean in v2.
|
|
50349
|
-
*/
|
|
50350
|
-
isAdmin: external_exports.boolean().default(false),
|
|
50351
|
-
allowedProviders: AllowedProviderSchema,
|
|
50352
|
-
allowedDevices: AllowedDevicesSchema,
|
|
50353
|
-
/**
|
|
50354
|
-
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
50355
|
-
* ignored. Non-admins without scopes are locked out of every
|
|
50356
|
-
* protected call.
|
|
50357
|
-
*/
|
|
50358
|
-
scopes: external_exports.array(TokenScopeSchema).default([]),
|
|
50359
|
-
createdAt: external_exports.number(),
|
|
50360
|
-
updatedAt: external_exports.number()
|
|
50361
|
-
});
|
|
50362
|
-
var ApiKeyRecordSchema = external_exports.object({
|
|
50363
|
-
id: external_exports.string(),
|
|
50364
|
-
label: external_exports.string(),
|
|
50365
|
-
isAdmin: external_exports.boolean().default(false),
|
|
50366
|
-
allowedProviders: AllowedProviderSchema,
|
|
50367
|
-
allowedDevices: AllowedDevicesSchema,
|
|
50368
|
-
tokenHash: external_exports.string(),
|
|
50369
|
-
tokenPrefix: external_exports.string(),
|
|
50370
|
-
createdAt: external_exports.number(),
|
|
50371
|
-
lastUsedAt: external_exports.number().optional()
|
|
50372
|
-
});
|
|
50373
|
-
var ScopedTokenSchema = external_exports.object({
|
|
50374
|
-
id: external_exports.string(),
|
|
50375
|
-
userId: external_exports.string(),
|
|
50376
|
-
name: external_exports.string(),
|
|
50377
|
-
tokenHash: external_exports.string(),
|
|
50378
|
-
tokenPrefix: external_exports.string(),
|
|
50379
|
-
scopes: external_exports.array(TokenScopeSchema),
|
|
50380
|
-
expiresAt: external_exports.number().nullish(),
|
|
50381
|
-
lastUsedAt: external_exports.number().nullish(),
|
|
50382
|
-
createdAt: external_exports.number()
|
|
50383
|
-
});
|
|
50384
50412
|
var OauthIntegrationDescriptorSchema = external_exports.object({
|
|
50385
50413
|
/** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
|
|
50386
50414
|
integrationId: external_exports.string(),
|
|
@@ -85953,6 +85981,7 @@ import * as clack from "@clack/prompts";
|
|
|
85953
85981
|
import {
|
|
85954
85982
|
clearSession,
|
|
85955
85983
|
defaultSession,
|
|
85984
|
+
jwtClaims,
|
|
85956
85985
|
listSessions,
|
|
85957
85986
|
loadSession,
|
|
85958
85987
|
saveSession
|
|
@@ -85968,20 +85997,11 @@ import {
|
|
|
85968
85997
|
validateHubAddress
|
|
85969
85998
|
} from "@camstack/develop";
|
|
85970
85999
|
import {
|
|
85971
|
-
discoverHubs
|
|
85972
|
-
normalizeHubUrl
|
|
86000
|
+
discoverHubs
|
|
85973
86001
|
} from "@camstack/develop/hub-discovery";
|
|
85974
|
-
var ADMIN_TOKEN_SUFFIX = ".admin-token";
|
|
85975
86002
|
function validateManualHubUrl(value) {
|
|
85976
86003
|
return validateHubAddress(value);
|
|
85977
86004
|
}
|
|
85978
|
-
function hubUrlFromAdminTokenFile(fileName) {
|
|
85979
|
-
if (!fileName.endsWith(ADMIN_TOKEN_SUFFIX)) return null;
|
|
85980
|
-
const slug = fileName.slice(0, -ADMIN_TOKEN_SUFFIX.length);
|
|
85981
|
-
const match = /^(.+?)_(\d{1,5})$/.exec(slug);
|
|
85982
|
-
const candidate = match ? `https://${match[1]}:${match[2]}` : `https://${slug}`;
|
|
85983
|
-
return normalizeHubUrl(candidate)?.url ?? null;
|
|
85984
|
-
}
|
|
85985
86005
|
function knownHubCandidates(deps) {
|
|
85986
86006
|
const dir = deps.sessionDir ?? defaultSessionDir();
|
|
85987
86007
|
const env = deps.env ?? process.env;
|
|
@@ -86004,9 +86024,6 @@ function knownHubCandidates(deps) {
|
|
|
86004
86024
|
}
|
|
86005
86025
|
} catch {
|
|
86006
86026
|
}
|
|
86007
|
-
} else {
|
|
86008
|
-
const url2 = hubUrlFromAdminTokenFile(file2);
|
|
86009
|
-
if (url2 !== null) out.push({ url: url2, source: "saved admin token" });
|
|
86010
86027
|
}
|
|
86011
86028
|
}
|
|
86012
86029
|
const fromEnv = resolveHubUrl({ env, session: null });
|
|
@@ -86134,30 +86151,32 @@ function isLoginPayload(value) {
|
|
|
86134
86151
|
const u = v.user;
|
|
86135
86152
|
return typeof u.id === "string" && typeof u.username === "string";
|
|
86136
86153
|
}
|
|
86137
|
-
function
|
|
86138
|
-
if (
|
|
86139
|
-
|
|
86140
|
-
|
|
86141
|
-
|
|
86142
|
-
|
|
86143
|
-
|
|
86144
|
-
if (prior.token.startsWith(SCOPED_TOKEN_PREFIX) && prior.tokenId) {
|
|
86145
|
-
await callTrpcMutation(
|
|
86146
|
-
`${server}/trpc/userManagement.revokeScopedToken?batch=1`,
|
|
86147
|
-
`Bearer ${authJwt}`,
|
|
86148
|
-
{ id: prior.tokenId },
|
|
86149
|
-
isUnknown
|
|
86150
|
-
);
|
|
86151
|
-
return;
|
|
86154
|
+
function revocationFor(session) {
|
|
86155
|
+
if (session.tokenId) {
|
|
86156
|
+
return {
|
|
86157
|
+
procedure: "userManagement.revokeScopedToken",
|
|
86158
|
+
input: { id: session.tokenId },
|
|
86159
|
+
bearer: "fresh"
|
|
86160
|
+
};
|
|
86152
86161
|
}
|
|
86153
|
-
if (
|
|
86154
|
-
|
|
86155
|
-
`${server}/trpc/auth.logout?batch=1`,
|
|
86156
|
-
`Bearer ${prior.token}`,
|
|
86157
|
-
{},
|
|
86158
|
-
isLogoutPayload
|
|
86159
|
-
);
|
|
86162
|
+
if (jwtClaims(session.token) !== null) {
|
|
86163
|
+
return { procedure: "auth.logout", input: {}, bearer: "self" };
|
|
86160
86164
|
}
|
|
86165
|
+
return null;
|
|
86166
|
+
}
|
|
86167
|
+
async function performRevocation(server, session, freshJwt) {
|
|
86168
|
+
const r = revocationFor(session);
|
|
86169
|
+
if (!r) return;
|
|
86170
|
+
const bearer = r.bearer === "fresh" && freshJwt ? freshJwt : session.token;
|
|
86171
|
+
await callTrpcMutation(
|
|
86172
|
+
`${server}/trpc/${r.procedure}?batch=1`,
|
|
86173
|
+
`Bearer ${bearer}`,
|
|
86174
|
+
r.input,
|
|
86175
|
+
isUnknown
|
|
86176
|
+
);
|
|
86177
|
+
}
|
|
86178
|
+
async function revokePriorSession(server, prior, authJwt) {
|
|
86179
|
+
await performRevocation(server, prior, authJwt);
|
|
86161
86180
|
}
|
|
86162
86181
|
async function completeSecondFactor(server, login) {
|
|
86163
86182
|
const factors = login.secondFactors ?? [];
|
|
@@ -86174,7 +86193,7 @@ async function completeSecondFactor(server, login) {
|
|
|
86174
86193
|
return await callTrpcMutation(
|
|
86175
86194
|
`${server}/trpc/auth.loginVerifyTotp?batch=1`,
|
|
86176
86195
|
void 0,
|
|
86177
|
-
{ challengeToken: login.token, code },
|
|
86196
|
+
{ challengeToken: login.token, code, refreshable: true },
|
|
86178
86197
|
isLoginPayload
|
|
86179
86198
|
);
|
|
86180
86199
|
} catch (err) {
|
|
@@ -86267,6 +86286,8 @@ async function loginCommand(opts) {
|
|
|
86267
86286
|
let displayName;
|
|
86268
86287
|
let userId;
|
|
86269
86288
|
let isAdmin = false;
|
|
86289
|
+
let refreshToken;
|
|
86290
|
+
let expiresAt;
|
|
86270
86291
|
while (true) {
|
|
86271
86292
|
const authSpinner = clack.spinner();
|
|
86272
86293
|
authSpinner.start(
|
|
@@ -86276,7 +86297,7 @@ async function loginCommand(opts) {
|
|
|
86276
86297
|
const first = await callTrpcMutation(
|
|
86277
86298
|
`${server}/trpc/auth.login?batch=1`,
|
|
86278
86299
|
void 0,
|
|
86279
|
-
{ username, password: password2 },
|
|
86300
|
+
{ username, password: password2, refreshable: true },
|
|
86280
86301
|
isLoginPayload
|
|
86281
86302
|
);
|
|
86282
86303
|
const owesSecondFactor = (first.secondFactors ?? []).length > 0;
|
|
@@ -86286,6 +86307,8 @@ async function loginCommand(opts) {
|
|
|
86286
86307
|
displayName = login.user.username;
|
|
86287
86308
|
userId = login.user.id;
|
|
86288
86309
|
isAdmin = login.user.isAdmin === true;
|
|
86310
|
+
refreshToken = login.refreshToken;
|
|
86311
|
+
expiresAt = login.expiresAt;
|
|
86289
86312
|
if (!owesSecondFactor) authSpinner.stop(`Authenticated as ${displayName}`);
|
|
86290
86313
|
break;
|
|
86291
86314
|
} catch (err) {
|
|
@@ -86317,14 +86340,14 @@ async function loginCommand(opts) {
|
|
|
86317
86340
|
token: jwt2,
|
|
86318
86341
|
scopes: [],
|
|
86319
86342
|
createdAt: Date.now(),
|
|
86320
|
-
...isAdmin ? { isAdmin: true } : {}
|
|
86343
|
+
...isAdmin ? { isAdmin: true } : {},
|
|
86344
|
+
...refreshToken ? { refreshToken } : {},
|
|
86345
|
+
...expiresAt !== void 0 ? { expiresAt } : {}
|
|
86321
86346
|
};
|
|
86322
86347
|
const file2 = saveSession(session);
|
|
86323
86348
|
const role = isAdmin ? "administrator" : "user";
|
|
86324
|
-
clack.outro(
|
|
86325
|
-
|
|
86326
|
-
Cached at ${file2}`
|
|
86327
|
-
);
|
|
86349
|
+
clack.outro(`\u2713 Logged in as ${displayName} (${role}) \u2192 ${server}
|
|
86350
|
+
Cached at ${file2}`);
|
|
86328
86351
|
}
|
|
86329
86352
|
async function logoutCommand(opts) {
|
|
86330
86353
|
const session = loadSession(opts.server);
|
|
@@ -86336,21 +86359,7 @@ async function logoutCommand(opts) {
|
|
|
86336
86359
|
const spinner3 = clack.spinner();
|
|
86337
86360
|
spinner3.start(`Revoking token on ${session.server}`);
|
|
86338
86361
|
try {
|
|
86339
|
-
|
|
86340
|
-
await callTrpcMutation(
|
|
86341
|
-
`${session.server}/trpc/userManagement.revokeScopedToken?batch=1`,
|
|
86342
|
-
`Bearer ${session.token}`,
|
|
86343
|
-
{ id: session.tokenId },
|
|
86344
|
-
isUnknown
|
|
86345
|
-
);
|
|
86346
|
-
} else if (!session.token.startsWith(SCOPED_TOKEN_PREFIX) && !session.token.startsWith(ADMIN_TOKEN_PREFIX)) {
|
|
86347
|
-
await callTrpcMutation(
|
|
86348
|
-
`${session.server}/trpc/auth.logout?batch=1`,
|
|
86349
|
-
`Bearer ${session.token}`,
|
|
86350
|
-
{},
|
|
86351
|
-
isLogoutPayload
|
|
86352
|
-
);
|
|
86353
|
-
}
|
|
86362
|
+
await performRevocation(session.server, session, void 0);
|
|
86354
86363
|
spinner3.stop("Session revoked server-side.");
|
|
86355
86364
|
} catch (err) {
|
|
86356
86365
|
spinner3.stop(
|
|
@@ -86396,7 +86405,15 @@ async function whoamiCommand(opts) {
|
|
|
86396
86405
|
if (session.tokenId) console.log(` tokenId: ${session.tokenId}`);
|
|
86397
86406
|
const scopeLine = session.scopes.length > 0 ? session.scopes.map((s) => `${s.type}:${s.target}`).join(", ") : "(login JWT)";
|
|
86398
86407
|
console.log(` scopes: ${scopeLine}`);
|
|
86399
|
-
|
|
86408
|
+
const claims = jwtClaims(session.token);
|
|
86409
|
+
const admin = session.isAdmin === true || claims?.["isAdmin"] === true;
|
|
86410
|
+
console.log(` admin: ${admin ? "yes" : "no"}`);
|
|
86411
|
+
const expiresAt = session.expiresAt ?? (typeof claims?.["exp"] === "number" ? claims["exp"] * 1e3 : void 0);
|
|
86412
|
+
if (expiresAt !== void 0) {
|
|
86413
|
+
console.log(
|
|
86414
|
+
` expires: ${new Date(expiresAt).toISOString()}${session.refreshToken ? " (renewed automatically)" : ""}`
|
|
86415
|
+
);
|
|
86416
|
+
}
|
|
86400
86417
|
console.log(` createdAt: ${new Date(session.createdAt).toISOString()}`);
|
|
86401
86418
|
console.log("");
|
|
86402
86419
|
console.log(`[camstack] Pinging ${session.server}/api/auth/whoami\u2026`);
|
|
@@ -86441,7 +86458,7 @@ import {
|
|
|
86441
86458
|
MissingCredentialsError,
|
|
86442
86459
|
normalizeHubAddress as normalizeHubAddress3,
|
|
86443
86460
|
touchSession as touchSession3,
|
|
86444
|
-
|
|
86461
|
+
resolveFreshCredentials as resolveFreshCredentials3
|
|
86445
86462
|
} from "@camstack/develop";
|
|
86446
86463
|
|
|
86447
86464
|
// src/commands/discover.ts
|
|
@@ -86688,7 +86705,20 @@ import * as fs7 from "fs";
|
|
|
86688
86705
|
import * as path7 from "path";
|
|
86689
86706
|
import { parseArgs as parseArgs6 } from "util";
|
|
86690
86707
|
import * as clack3 from "@clack/prompts";
|
|
86691
|
-
import { loadSession as loadSession2,
|
|
86708
|
+
import { loadSession as loadSession2, resolveFreshCredentials, touchSession } from "@camstack/develop";
|
|
86709
|
+
|
|
86710
|
+
// src/http/hub-transport.ts
|
|
86711
|
+
var cliHubTransport = async (req) => {
|
|
86712
|
+
const res = await hubRequest(req.url, {
|
|
86713
|
+
method: req.method,
|
|
86714
|
+
headers: req.headers,
|
|
86715
|
+
...req.body !== void 0 ? { body: req.body } : {},
|
|
86716
|
+
timeoutMs: req.timeoutMs
|
|
86717
|
+
});
|
|
86718
|
+
return { status: res.status, text: await res.text() };
|
|
86719
|
+
};
|
|
86720
|
+
|
|
86721
|
+
// src/commands/dev.ts
|
|
86692
86722
|
var DEFAULT_DEBOUNCE_MS = 500;
|
|
86693
86723
|
var DEFAULT_WATCH_SUBDIR = "src";
|
|
86694
86724
|
function resolveAddonDir(arg) {
|
|
@@ -86720,12 +86750,12 @@ function watchRecursive(target, onEvent) {
|
|
|
86720
86750
|
return { close: () => w.close(), recursive: false };
|
|
86721
86751
|
}
|
|
86722
86752
|
}
|
|
86723
|
-
async function pushOnce(addonPath,
|
|
86753
|
+
async function pushOnce(addonPath, getOpts, label) {
|
|
86724
86754
|
const start = Date.now();
|
|
86725
86755
|
console.log(`
|
|
86726
86756
|
[camstack dev] \u2192 ${label}\u2026`);
|
|
86727
86757
|
try {
|
|
86728
|
-
await deployAddon(addonPath,
|
|
86758
|
+
await deployAddon(addonPath, await getOpts());
|
|
86729
86759
|
const took = ((Date.now() - start) / 1e3).toFixed(1);
|
|
86730
86760
|
console.log(`[camstack dev] \u2713 Synced in ${took}s`);
|
|
86731
86761
|
} catch (err) {
|
|
@@ -86754,12 +86784,14 @@ async function runDev(args) {
|
|
|
86754
86784
|
});
|
|
86755
86785
|
const addonArg = positionals[0] ?? ".";
|
|
86756
86786
|
const addonDir = resolveAddonDir(addonArg);
|
|
86757
|
-
const
|
|
86787
|
+
const credentialInputs = {
|
|
86758
86788
|
...typeof values.server === "string" ? { server: values.server } : {},
|
|
86759
86789
|
...typeof values.token === "string" ? { token: values.token } : {},
|
|
86760
86790
|
...typeof values["token-file"] === "string" ? { tokenFile: values["token-file"] } : {},
|
|
86761
|
-
loadSession: loadSession2
|
|
86762
|
-
|
|
86791
|
+
loadSession: loadSession2,
|
|
86792
|
+
transport: cliHubTransport
|
|
86793
|
+
};
|
|
86794
|
+
const creds = await resolveFreshCredentials(credentialInputs);
|
|
86763
86795
|
if (creds.warning) console.error(`[camstack dev] Warning: ${creds.warning}`);
|
|
86764
86796
|
if (creds.source === "session" && creds.server) touchSession(creds.server);
|
|
86765
86797
|
const serverUrl = creds.server;
|
|
@@ -86778,6 +86810,10 @@ async function runDev(args) {
|
|
|
86778
86810
|
buildMode: values["no-build"] === true ? "skip" : "auto",
|
|
86779
86811
|
...typeof values["build-script"] === "string" ? { buildScript: values["build-script"] } : {}
|
|
86780
86812
|
};
|
|
86813
|
+
const freshOpts = async () => ({
|
|
86814
|
+
...deployOpts,
|
|
86815
|
+
token: (await resolveFreshCredentials(credentialInputs)).token
|
|
86816
|
+
});
|
|
86781
86817
|
const watchOverride = typeof values["watch-path"] === "string" ? values["watch-path"] : void 0;
|
|
86782
86818
|
const watchTarget = watchOverride ? path7.resolve(addonDir, watchOverride) : fs7.existsSync(path7.join(addonDir, DEFAULT_WATCH_SUBDIR)) ? path7.join(addonDir, DEFAULT_WATCH_SUBDIR) : addonDir;
|
|
86783
86819
|
if (!fs7.existsSync(watchTarget)) {
|
|
@@ -86791,7 +86827,7 @@ async function runDev(args) {
|
|
|
86791
86827
|
);
|
|
86792
86828
|
clack3.log.info(`Debounce: ${debounceMs}ms`);
|
|
86793
86829
|
if (values["no-initial-push"] !== true) {
|
|
86794
|
-
await pushOnce(addonDir,
|
|
86830
|
+
await pushOnce(addonDir, freshOpts, "Initial sync");
|
|
86795
86831
|
} else {
|
|
86796
86832
|
console.log("[camstack dev] Skipping initial push (--no-initial-push). Waiting for changes\u2026");
|
|
86797
86833
|
}
|
|
@@ -86807,7 +86843,7 @@ async function runDev(args) {
|
|
|
86807
86843
|
}
|
|
86808
86844
|
pushing = true;
|
|
86809
86845
|
try {
|
|
86810
|
-
await pushOnce(addonDir,
|
|
86846
|
+
await pushOnce(addonDir, freshOpts, "File change detected \u2014 rebuilding");
|
|
86811
86847
|
} finally {
|
|
86812
86848
|
pushing = false;
|
|
86813
86849
|
if (pending) {
|
|
@@ -86843,7 +86879,7 @@ import * as clack4 from "@clack/prompts";
|
|
|
86843
86879
|
import {
|
|
86844
86880
|
loadSession as loadSession3,
|
|
86845
86881
|
touchSession as touchSession2,
|
|
86846
|
-
|
|
86882
|
+
resolveFreshCredentials as resolveFreshCredentials2,
|
|
86847
86883
|
withAuthHint as withAuthHint3
|
|
86848
86884
|
} from "@camstack/develop";
|
|
86849
86885
|
var POLL_INTERVAL_MS2 = 2e3;
|
|
@@ -86907,12 +86943,14 @@ async function runWatch(args) {
|
|
|
86907
86943
|
allowPositionals: true
|
|
86908
86944
|
});
|
|
86909
86945
|
const addonArg = positionals[0] ?? ".";
|
|
86910
|
-
const
|
|
86946
|
+
const credentialInputs = {
|
|
86911
86947
|
...typeof values.server === "string" ? { server: values.server } : {},
|
|
86912
86948
|
...typeof values.token === "string" ? { token: values.token } : {},
|
|
86913
86949
|
...typeof values["token-file"] === "string" ? { tokenFile: values["token-file"] } : {},
|
|
86914
|
-
loadSession: loadSession3
|
|
86915
|
-
|
|
86950
|
+
loadSession: loadSession3,
|
|
86951
|
+
transport: cliHubTransport
|
|
86952
|
+
};
|
|
86953
|
+
const creds = await resolveFreshCredentials2(credentialInputs);
|
|
86916
86954
|
if (creds.warning) console.error(`[camstack watch] Warning: ${creds.warning}`);
|
|
86917
86955
|
if (creds.source === "session" && creds.server) touchSession2(creds.server);
|
|
86918
86956
|
const serverUrl = creds.server;
|
|
@@ -86951,7 +86989,14 @@ async function runWatch(args) {
|
|
|
86951
86989
|
process.on("SIGTERM", shutdown);
|
|
86952
86990
|
while (!stop) {
|
|
86953
86991
|
try {
|
|
86954
|
-
const
|
|
86992
|
+
const fresh = await resolveFreshCredentials2(credentialInputs);
|
|
86993
|
+
const entries2 = await fetchLogs(
|
|
86994
|
+
serverUrl,
|
|
86995
|
+
fresh.token,
|
|
86996
|
+
addonId,
|
|
86997
|
+
LOG_BATCH_LIMIT,
|
|
86998
|
+
creds.source
|
|
86999
|
+
);
|
|
86955
87000
|
const sorted = [...entries2].toSorted((a, b) => {
|
|
86956
87001
|
const ta = typeof a.timestamp === "number" ? a.timestamp : new Date(a.timestamp).getTime();
|
|
86957
87002
|
const tb = typeof b.timestamp === "number" ? b.timestamp : new Date(b.timestamp).getTime();
|
|
@@ -87423,7 +87468,7 @@ function buildCommands() {
|
|
|
87423
87468
|
" -p, --password <pwd> Password (skips prompt \u2014 prefer letting the CLI prompt)",
|
|
87424
87469
|
` --token-name <name> Ignored (kept for scripts); login uses your hub session JWT`,
|
|
87425
87470
|
" --admin Ignored (kept for muscle memory); admin follows your account role",
|
|
87426
|
-
" --admin-days <n> Ignored (kept for muscle memory);
|
|
87471
|
+
" --admin-days <n> Ignored (kept for muscle memory); one login, one token format"
|
|
87427
87472
|
].join("\n")
|
|
87428
87473
|
},
|
|
87429
87474
|
{
|
|
@@ -87434,7 +87479,7 @@ function buildCommands() {
|
|
|
87434
87479
|
"Usage: camstack discover [options]",
|
|
87435
87480
|
"",
|
|
87436
87481
|
"Lists the hubs `camstack login` would offer: those announcing _camstack._tcp over",
|
|
87437
|
-
" mDNS plus saved sessions,
|
|
87482
|
+
" mDNS plus saved sessions, $CAMSTACK_HUB_URL and scratchpad/.hub-url,",
|
|
87438
87483
|
" reachable ones only, each with its source. Agents never announce this service.",
|
|
87439
87484
|
"",
|
|
87440
87485
|
"Options:",
|
|
@@ -87711,7 +87756,7 @@ async function runWhoami(args) {
|
|
|
87711
87756
|
const server = stringOpt(parsed.values, "server");
|
|
87712
87757
|
await whoamiCommand(server ? { server: normalizeHubAddress3(server) } : {});
|
|
87713
87758
|
}
|
|
87714
|
-
function resolveClosureDeployOpts(values) {
|
|
87759
|
+
async function resolveClosureDeployOpts(values) {
|
|
87715
87760
|
const nodeId = stringOpt(values, "node");
|
|
87716
87761
|
const cluster = boolOpt(values, "cluster") || boolOpt(values, "all");
|
|
87717
87762
|
if (cluster && nodeId) {
|
|
@@ -87719,7 +87764,7 @@ function resolveClosureDeployOpts(values) {
|
|
|
87719
87764
|
process.exit(1);
|
|
87720
87765
|
}
|
|
87721
87766
|
const version2 = stringOpt(values, "version");
|
|
87722
|
-
const creds = resolveCommandCredentials(values);
|
|
87767
|
+
const creds = await resolveCommandCredentials(values);
|
|
87723
87768
|
return {
|
|
87724
87769
|
serverUrl: creds.server ?? DEFAULT_SERVER_URL,
|
|
87725
87770
|
token: creds.token,
|
|
@@ -87757,7 +87802,7 @@ async function runDeploy(args) {
|
|
|
87757
87802
|
console.error("[camstack] Error: --closure does not take addon --build / --no-build flags.");
|
|
87758
87803
|
process.exit(1);
|
|
87759
87804
|
}
|
|
87760
|
-
await deployServer(resolveClosureDeployOpts(parsed.values));
|
|
87805
|
+
await deployServer(await resolveClosureDeployOpts(parsed.values));
|
|
87761
87806
|
return;
|
|
87762
87807
|
}
|
|
87763
87808
|
if (stringOpt(parsed.values, "version")) {
|
|
@@ -87765,7 +87810,7 @@ async function runDeploy(args) {
|
|
|
87765
87810
|
process.exit(1);
|
|
87766
87811
|
}
|
|
87767
87812
|
const addonPath = parsed.positionals[0] ?? ".";
|
|
87768
|
-
const creds = resolveCommandCredentials(parsed.values);
|
|
87813
|
+
const creds = await resolveCommandCredentials(parsed.values);
|
|
87769
87814
|
const server = creds.server ?? DEFAULT_SERVER_URL;
|
|
87770
87815
|
const token = creds.token;
|
|
87771
87816
|
const nodeId = stringOpt(parsed.values, "node");
|
|
@@ -87817,19 +87862,20 @@ async function runDeployServer(args) {
|
|
|
87817
87862
|
return;
|
|
87818
87863
|
}
|
|
87819
87864
|
console.log("[camstack] note: `deploy-server` is an alias of `deploy --closure`");
|
|
87820
|
-
await deployServer(resolveClosureDeployOpts(parsed.values));
|
|
87865
|
+
await deployServer(await resolveClosureDeployOpts(parsed.values));
|
|
87821
87866
|
}
|
|
87822
87867
|
var DEFAULT_SERVER_URL = "https://localhost:4443";
|
|
87823
|
-
function resolveCommandCredentials(values) {
|
|
87868
|
+
async function resolveCommandCredentials(values) {
|
|
87824
87869
|
const server = stringOpt(values, "server");
|
|
87825
87870
|
const token = stringOpt(values, "token");
|
|
87826
87871
|
const tokenFile = stringOpt(values, "token-file");
|
|
87827
87872
|
try {
|
|
87828
|
-
const creds =
|
|
87873
|
+
const creds = await resolveFreshCredentials3({
|
|
87829
87874
|
...server ? { server } : {},
|
|
87830
87875
|
...token ? { token } : {},
|
|
87831
87876
|
...tokenFile ? { tokenFile } : {},
|
|
87832
|
-
loadSession: loadSession4
|
|
87877
|
+
loadSession: loadSession4,
|
|
87878
|
+
transport: cliHubTransport
|
|
87833
87879
|
});
|
|
87834
87880
|
if (creds.warning) console.error(`[camstack] Warning: ${creds.warning}`);
|
|
87835
87881
|
if (creds.source === "session" && creds.server) touchSession3(creds.server);
|