@samyx/preview-stacks-client 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@
25
25
  * polling a job without a terminal-state list (`queued` is not running and not finished either), or
26
26
  * treating `readiness.state === 'watching'` as an error rather than "ask again".
27
27
  */
28
- import type { CancelStack, DeliveryRow, DeploymentRow, Health, HostVar, Job, Kind, Logs, Me, NotifierRow, Readiness, Role, Runtime, ShareLink, ShareView, SpecMeta, SsoConfig, SsoConfigResponse, SwarmInfo, User } from './types.ts';
28
+ import type { CancelStack, DeliveryRow, DeploymentRow, Health, HostSettings, HostVar, Job, Kind, Logs, Me, NotifierRow, Readiness, Role, Runtime, SettingKey, SettingWritten, ShareLink, ShareView, SpecMeta, SsoConfig, SsoConfigResponse, SwarmInfo, User } from './types.ts';
29
29
  export * from './types.ts';
30
30
  /** A non-2xx answer. `body` is the parsed JSON, which is where the server's own message lives. */
31
31
  export declare class PstackError extends Error {
@@ -193,9 +193,13 @@ export declare function createClient(opts: ClientOptions): {
193
193
  users: {
194
194
  list: () => Promise<User[]>;
195
195
  /**
196
- * An ABSENT `role` means VIEWER — the least privilege, not the most. This route used to create
197
- * an administrator every time; a script that relied on that must now say `role: 'admin'` and
198
- * mean it. A role outside the four is a 400 rather than a silently powerless account.
196
+ * An ABSENT `role` means the host's `default_role` setting `viewer` on a host where nobody
197
+ * has changed it, which is what this used to mean unconditionally. It is still the least
198
+ * privilege by default and never admin by omission, but it is now an OPERATOR'S choice
199
+ * (`settings.set('default_role', …)`), so a script that wants a specific role must name it
200
+ * rather than rely on the host's. This route used to create an administrator every time; a
201
+ * script that relied on THAT must say `role: 'admin'` and mean it. A role outside the four is
202
+ * a 400 rather than a silently powerless account.
199
203
  */
200
204
  create: (body: {
201
205
  username: string;
@@ -330,6 +334,27 @@ export declare function createClient(opts: ClientOptions): {
330
334
  deleted: string;
331
335
  }>;
332
336
  };
337
+ /**
338
+ * The two host settings that change at RUNTIME: the running-job cap and the role an account
339
+ * created with no role named gets.
340
+ *
341
+ * Reading is viewer's; writing is per KEY, not per route — `max_jobs` is maintainer's and
342
+ * `default_role` is admin's, and each row says which in `minRole`. The read also says where
343
+ * each value came from (`source`), which is the only way to tell a value you set from one your
344
+ * environment is supplying.
345
+ */
346
+ settings: {
347
+ get: () => Promise<HostSettings>;
348
+ /**
349
+ * Store one. Refused (400) for an unknown key or a value the server will not take —
350
+ * `max_jobs` is an integer ≥ 1, `default_role` one of the four roles.
351
+ *
352
+ * `max_jobs` takes effect on the NEXT dispatch, with no restart: raising it starts a job that
353
+ * was waiting for a slot, and LOWERING IT CANCELS NOTHING — the jobs already running run to
354
+ * completion. The `note` in the response says so; print it rather than paraphrasing.
355
+ */
356
+ set: (key: SettingKey, value: number | Role) => Promise<SettingWritten>;
357
+ };
333
358
  /**
334
359
  * Poll a job until it reaches a terminal state.
335
360
  *
package/dist/index.js CHANGED
@@ -132,6 +132,10 @@ function createClient(opts) {
132
132
  put: (name, value, secret = false) => put(`/api/host-vars/${enc(name)}`, { value, secret }),
133
133
  remove: (name) => del(`/api/host-vars/${enc(name)}`)
134
134
  },
135
+ settings: {
136
+ get: () => get("/api/settings"),
137
+ set: (key, value) => put(`/api/settings/${enc(key)}`, { value })
138
+ },
135
139
  async waitForJob(jobId, o = {}) {
136
140
  const interval = o.intervalMs ?? 2000;
137
141
  const deadline = Date.now() + (o.timeoutMs ?? 30 * 60000);
package/dist/types.d.ts CHANGED
@@ -360,6 +360,50 @@ export type Me = {
360
360
  expiresAt?: number | null;
361
361
  };
362
362
  };
363
+ /**
364
+ * The two settings that can be changed at RUNTIME, without restarting the container. The server
365
+ * refuses any other key rather than storing it, so this union is the whole surface.
366
+ */
367
+ export type SettingKey = 'max_jobs' | 'default_role';
368
+ /**
369
+ * One setting, as read and as returned by a write.
370
+ *
371
+ * `source` says where the value came from: `db` (someone set it through the API), `env`
372
+ * (`PSTACK_MAX_JOBS`), or `default` (what the binary ships with). Precedence is **database >
373
+ * environment > built-in default** — the environment variable is the DEFAULT, not the authority, so
374
+ * a stored value survives the next restart and a host that never sets one behaves exactly as it did
375
+ * before this existed.
376
+ *
377
+ * `minRole` is the least role that may WRITE this key, served from the server's own permission
378
+ * table: `max_jobs` is maintainer's, `default_role` is admin's. Reading is viewer's. Treat a name
379
+ * this build does not recognise as the most privileged, never the least.
380
+ */
381
+ export type SettingRow = {
382
+ key: SettingKey;
383
+ /** A number for `max_jobs`; a role name for `default_role`. */
384
+ value: number | string;
385
+ source: 'db' | 'env' | 'default';
386
+ minRole: string;
387
+ };
388
+ export type HostSettings = {
389
+ settings: SettingRow[];
390
+ /** `null` when `PSTACK_MAX_JOBS` is unset — never `0`, which the server reads as unset. */
391
+ env: {
392
+ PSTACK_MAX_JOBS: number | null;
393
+ };
394
+ precedence: string;
395
+ };
396
+ /**
397
+ * What a write answers: the fresh row (`source` is now `db`), and for `max_jobs` a `note`.
398
+ *
399
+ * The new cap is in force immediately — no restart — but LOWERING IT CANCELS NOTHING. Jobs already
400
+ * running run to completion; the cap applies to the next job that starts. A script that drops the
401
+ * cap to 1 has not stopped the four jobs in flight.
402
+ */
403
+ export type SettingWritten = SettingRow & {
404
+ stored: true;
405
+ note?: string;
406
+ };
363
407
  export type SsoClaimMap = {
364
408
  subject: string;
365
409
  username: string;
@@ -406,9 +450,15 @@ export type SsoConfig = {
406
450
  */
407
451
  requiredGroups: string[];
408
452
  /**
409
- * The role an account this provider MINTS is created at. Deliberately not narrowed to `Role`: the
410
- * server stores this string without validating it, and one it does not recognise ranks below
411
- * viewer. Whatever it says is the floor every person who signs in through this provider gets.
453
+ * The role an account this provider MINTS is created at, and **empty means INHERIT** the host's
454
+ * `default_role` setting, resolved when the account is provisioned rather than frozen when the
455
+ * provider was saved. Inherit falls back to `viewer` when the host default is unset, and never to
456
+ * admin by omission.
457
+ *
458
+ * Deliberately not narrowed to `Role`: the server stores this string without validating it, and
459
+ * one it does not recognise ranks below viewer. A provider stored before 0.33.0 carries a literal
460
+ * `"viewer"` (the server used to fill an empty value in), and keeps it until it is saved again
461
+ * with `''`.
412
462
  */
413
463
  defaultRole: string;
414
464
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samyx/preview-stacks-client",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Typed API client for a pstack control plane: deployments, jobs, readiness, containers, logs, notifiers.",
5
5
  "repository": {
6
6
  "type": "git",