@skrr-ai/cli 0.1.109 → 0.1.110

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 (63) hide show
  1. package/dist/base-command.d.ts +15 -0
  2. package/dist/base-command.js +64 -17
  3. package/dist/commands/agents/triggers/create.js +1 -1
  4. package/dist/commands/agents/triggers/update.js +1 -1
  5. package/dist/commands/agents/update.d.ts +11 -0
  6. package/dist/commands/agents/update.js +58 -2
  7. package/dist/commands/daemon/install.js +17 -1
  8. package/dist/commands/daemon/login.js +14 -0
  9. package/dist/commands/harnesses/prune.d.ts +26 -0
  10. package/dist/commands/harnesses/prune.js +172 -0
  11. package/dist/commands/login.js +11 -0
  12. package/dist/commands/spaces/workflows/start.js +3 -1
  13. package/dist/commands/spaces/workflows/validate.js +12 -2
  14. package/dist/commands/tasks/activity.js +11 -1
  15. package/dist/commands/tasks/list.js +6 -2
  16. package/dist/commands/tasks/rearm-recurrence.js +22 -3
  17. package/dist/commands/tasks/show.d.ts +8 -0
  18. package/dist/commands/tasks/show.js +64 -32
  19. package/dist/commands/tasks/unschedule.js +5 -1
  20. package/dist/commands/tasks/update.js +11 -1
  21. package/dist/commands/triggers/audit.js +12 -3
  22. package/dist/commands/triggers/create.js +1 -1
  23. package/dist/commands/triggers/fire-detail.d.ts +7 -0
  24. package/dist/commands/triggers/fire-detail.js +30 -12
  25. package/dist/commands/triggers/fire.js +13 -3
  26. package/dist/commands/triggers/parse-intent.js +1 -1
  27. package/dist/commands/triggers/update.js +1 -1
  28. package/dist/commands/workspaces/grants.js +1 -4
  29. package/dist/commands/workspaces/members.js +6 -6
  30. package/dist/lib/commitment-product.js +5 -2
  31. package/dist/lib/commitments.d.ts +2 -1
  32. package/dist/lib/commitments.js +40 -40
  33. package/dist/lib/daemon-machine-identity-copy.d.ts +14 -0
  34. package/dist/lib/daemon-machine-identity-copy.js +27 -0
  35. package/dist/lib/daemon-machine-identity.d.ts +27 -0
  36. package/dist/lib/daemon-machine-identity.js +61 -0
  37. package/dist/lib/daemonHandoff.d.ts +19 -1
  38. package/dist/lib/daemonHandoff.js +59 -0
  39. package/dist/lib/harnesses.d.ts +71 -0
  40. package/dist/lib/harnesses.js +70 -0
  41. package/dist/lib/list-output.d.ts +1 -1
  42. package/dist/lib/list-output.js +2 -0
  43. package/dist/lib/profile-machine-identity-guard.js +11 -1
  44. package/dist/lib/task-economics.d.ts +10 -0
  45. package/dist/lib/task-economics.js +24 -2
  46. package/dist/lib/triggers.d.ts +11 -0
  47. package/dist/lib/triggers.js +43 -1
  48. package/dist/lib/workflow-run-view.d.ts +4 -0
  49. package/dist/lib/workflow-run-view.js +5 -0
  50. package/dist/lib/workspaces.d.ts +24 -7
  51. package/dist/lib/workspaces.js +14 -2
  52. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  53. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +5 -3
  54. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +60 -0
  55. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +130 -6
  56. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  57. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  58. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +60 -0
  59. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +128 -6
  60. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  61. package/dist/node_modules/@skrr-ai/data-provider/index.js +4003 -3968
  62. package/oclif.manifest.json +99 -9
  63. package/package.json +10 -1
@@ -27,6 +27,7 @@ exports.assertKindFlagsApply = assertKindFlagsApply;
27
27
  exports.assertModeRequiredFlags = assertModeRequiredFlags;
28
28
  exports.describeScheduledFire = describeScheduledFire;
29
29
  exports.fireWasAccepted = fireWasAccepted;
30
+ exports.describeStrictFireRefusal = describeStrictFireRefusal;
30
31
  exports.describeFireDetail = describeFireDetail;
31
32
  exports.formatFireInZone = formatFireInZone;
32
33
  exports.nextFireLines = nextFireLines;
@@ -50,6 +51,7 @@ exports.renderTriggerSummary = renderTriggerSummary;
50
51
  exports.renderLegacyRuns = renderLegacyRuns;
51
52
  exports.renderFireAudit = renderFireAudit;
52
53
  const data_provider_1 = require("@skrr-ai/data-provider");
54
+ const node_fs_1 = require("node:fs");
53
55
  const format_1 = require("./format");
54
56
  exports.TRIGGER_KINDS = [
55
57
  'schedule',
@@ -390,12 +392,28 @@ function parseJsonObject(raw, flagName) {
390
392
  }
391
393
  return parsed;
392
394
  }
395
+ /**
396
+ * A JSON-array flag value: inline JSON, `@path` (a file), or a bare path to an existing file. A
397
+ * path used to reach `JSON.parse` and fail with "Unexpected token '/'" (OSK-15402).
398
+ */
393
399
  function parseJsonArray(raw, flagName) {
394
400
  if (raw === undefined)
395
401
  return undefined;
402
+ let text = raw;
403
+ const trimmed = raw.trim();
404
+ if (trimmed.startsWith('@') || (!/^[[{]/.test(trimmed) && (0, node_fs_1.existsSync)(trimmed))) {
405
+ const path = trimmed.startsWith('@') ? trimmed.slice(1) : trimmed;
406
+ try {
407
+ text = (0, node_fs_1.readFileSync)(path, 'utf8');
408
+ }
409
+ catch (err) {
410
+ const msg = err instanceof Error ? err.message : String(err);
411
+ throw new Error(`Cannot read ${flagName} file ${path}: ${msg}`);
412
+ }
413
+ }
396
414
  let parsed;
397
415
  try {
398
- parsed = JSON.parse(raw);
416
+ parsed = JSON.parse(text);
399
417
  }
400
418
  catch (err) {
401
419
  const msg = err instanceof Error ? err.message : String(err);
@@ -443,6 +461,13 @@ function buildScheduleConfigFromFlags(flags) {
443
461
  endAt: flags['end-at'],
444
462
  staleCatchupMs: flags['stale-catchup-ms'],
445
463
  });
464
+ // The range is the server's (`TRIGGER_STALE_CATCHUP_MS_RANGE`), checked here too so the refusal
465
+ // names the flag the person typed and happens before anything is sent (OSK-16474).
466
+ if (flags['stale-catchup-ms'] !== undefined) {
467
+ const refusal = (0, data_provider_1.describeStaleCatchupMsRefusal)(flags['stale-catchup-ms'], '--stale-catchup-ms');
468
+ if (refusal)
469
+ throw new Error(refusal);
470
+ }
446
471
  return schedule;
447
472
  }
448
473
  function assertScheduleTimingShape(schedule, options = {}) {
@@ -840,6 +865,23 @@ function fireWasAccepted(result) {
840
865
  typeof result.originalOutcome === 'string' &&
841
866
  accepted.includes(result.originalOutcome));
842
867
  }
868
+ /**
869
+ * Why `triggers fire --strict` failed. A strict failure used to print exactly what the exit-0 run
870
+ * printed and then exit 1, so nothing said what the flag had judged (OSK-16474). It names the
871
+ * outcome it refused, the reason, the server's sentence when there is one, and what --strict
872
+ * accepts.
873
+ */
874
+ function describeStrictFireRefusal(result) {
875
+ const text = (value) => (typeof value === 'string' && value.trim() ? value.trim() : '');
876
+ const outcome = text(result.outcome) || 'unknown';
877
+ const original = outcome === 'duplicate' ? text(result.originalOutcome) : '';
878
+ const reason = text(result.reason);
879
+ const sentence = text(result.detail) || text(result.errorMessage);
880
+ return (`--strict: the fire was not accepted (outcome ${outcome}` +
881
+ `${original ? `, a repeat of ${original}` : ''}${reason ? `, reason ${reason}` : ''})` +
882
+ `${sentence ? `: ${sentence}` : '.'} ` +
883
+ `--strict accepts only ${data_provider_1.ACCEPTED_AGENT_TRIGGER_FIRE_OUTCOMES.join(', ')}, or a repeat of one.`);
884
+ }
843
885
  /**
844
886
  * The lines a fire or replay owes beyond `outcome=… reason=…` (OSK-15494): the sentence the
845
887
  * server wrote ("Replayed fire … resolved a step that was not waiting when that fire ran:
@@ -21,6 +21,10 @@ export interface RunViewNode {
21
21
  waitKind?: unknown;
22
22
  signalable?: unknown;
23
23
  waitingFor?: unknown;
24
+ stoppedBecause?: {
25
+ code?: unknown;
26
+ message?: unknown;
27
+ } | null;
24
28
  loopSummary?: {
25
29
  total?: unknown;
26
30
  completed?: unknown;
@@ -53,6 +53,11 @@ function describeNodeDetail(node, attention = '') {
53
53
  parts.push(attention);
54
54
  if (wait)
55
55
  parts.push(wait.text);
56
+ // A cancelled node says why it stopped (the run's deadline, a person's stop), not the
57
+ // problem it had been parked on before (OSK-16473).
58
+ const stopped = node.status === 'cancelled' ? node.stoppedBecause?.message : undefined;
59
+ if (typeof stopped === 'string' && stopped)
60
+ parts.push(`stopped: ${stopped}`);
56
61
  const loop = describeLoopProgress(node);
57
62
  if (loop)
58
63
  parts.push(loop);
@@ -35,6 +35,8 @@ export interface WorkspaceStewardshipReport extends WorkspaceGrantReport {
35
35
  membership: {
36
36
  status: 'active' | 'absent' | 'unavailable';
37
37
  role?: string;
38
+ /** `false` on `absent`: a removed member and an id that never existed read alike. */
39
+ canDistinguishRemoved?: boolean;
38
40
  };
39
41
  commitments: StewardshipCommitmentMetadata[] | null;
40
42
  approverSeats: StewardshipCommitmentMetadata[] | null;
@@ -45,17 +47,31 @@ export interface WorkspaceStewardshipReport extends WorkspaceGrantReport {
45
47
  }
46
48
  export declare function getWorkspaceStewardship(workspaceId: string, memberUserId: string): Promise<WorkspaceStewardshipReport>;
47
49
  /** One current member of a workspace, as `GET /api/workspaces/:id/members` returns it. */
50
+ /**
51
+ * One current member as `skrr workspaces members` shows it, in its table and in
52
+ * `--json` alike (OSK-16472). Only these fields: the endpoint's raw row also carries
53
+ * the membership `_id`, monthly usage counters the web billing tab reads, and the
54
+ * user's avatar, none of which a members listing promises.
55
+ */
48
56
  export interface WorkspaceMemberRow {
49
57
  userId: string;
50
- role: string;
51
- joinedAt?: string;
58
+ name: string | null;
59
+ email: string | null;
60
+ role: string | null;
61
+ joinedAt: string | null;
62
+ }
63
+ interface RawWorkspaceMember {
64
+ userId?: unknown;
65
+ role?: unknown;
66
+ joinedAt?: unknown;
52
67
  user?: {
53
- _id?: string;
54
- name?: string;
55
- username?: string;
56
- email?: string;
57
- };
68
+ name?: unknown;
69
+ username?: unknown;
70
+ email?: unknown;
71
+ } | null;
58
72
  }
73
+ /** The ONE projection of a raw members row; nothing else of it reaches output. */
74
+ export declare function toWorkspaceMemberRow(raw: RawWorkspaceMember): WorkspaceMemberRow;
59
75
  export declare function listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMemberRow[]>;
60
76
  /** A user id as the stewardship route takes it: a 24-hex ObjectId. */
61
77
  export declare const USER_ID_PATTERN: RegExp;
@@ -226,3 +242,4 @@ export declare function getWorkspace(workspaceId: string): Promise<WorkspaceRow>
226
242
  * session history to the wrong team.
227
243
  */
228
244
  export declare function matchWorkspace(rows: WorkspaceRow[], ref: string): WorkspaceRow[];
245
+ export {};
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MemberReferenceError = exports.USER_ID_PATTERN = void 0;
4
4
  exports.getWorkspaceStewardship = getWorkspaceStewardship;
5
+ exports.toWorkspaceMemberRow = toWorkspaceMemberRow;
5
6
  exports.listWorkspaceMembers = listWorkspaceMembers;
6
7
  exports.resolveMemberUserId = resolveMemberUserId;
7
8
  exports.listWorkspaceGrants = listWorkspaceGrants;
@@ -18,9 +19,20 @@ const triggers_1 = require("./triggers");
18
19
  async function getWorkspaceStewardship(workspaceId, memberUserId) {
19
20
  return (0, api_fetch_1.apiFetch)(`/api/workspaces/${encodeURIComponent(workspaceId)}/stewardship/members/${encodeURIComponent(memberUserId)}`);
20
21
  }
22
+ const text = (value) => (typeof value === 'string' && value ? value : null);
23
+ /** The ONE projection of a raw members row; nothing else of it reaches output. */
24
+ function toWorkspaceMemberRow(raw) {
25
+ return {
26
+ userId: String(raw.userId ?? ''),
27
+ name: text(raw.user?.name) ?? text(raw.user?.username),
28
+ email: text(raw.user?.email),
29
+ role: text(raw.role),
30
+ joinedAt: text(raw.joinedAt),
31
+ };
32
+ }
21
33
  async function listWorkspaceMembers(workspaceId) {
22
34
  const response = await (0, api_fetch_1.apiFetch)(`/api/workspaces/${encodeURIComponent(workspaceId)}/members`);
23
- return Array.isArray(response?.data) ? response.data : [];
35
+ return Array.isArray(response?.data) ? response.data.map(toWorkspaceMemberRow) : [];
24
36
  }
25
37
  /** A user id as the stewardship route takes it: a 24-hex ObjectId. */
26
38
  exports.USER_ID_PATTERN = /^[a-f0-9]{24}$/i;
@@ -51,7 +63,7 @@ async function resolveMemberUserId(workspaceId, reference, list = listWorkspaceM
51
63
  throw new MemberReferenceError(`"${reference}" is not a user id or an email. A user id is 24 hexadecimal characters. ${listHint}`);
52
64
  }
53
65
  const email = value.toLowerCase();
54
- const matches = (await list(workspaceId)).filter((member) => member.user?.email?.toLowerCase() === email);
66
+ const matches = (await list(workspaceId)).filter((member) => member.email?.toLowerCase() === email);
55
67
  if (matches.length === 1)
56
68
  return String(matches[0].userId);
57
69
  throw new MemberReferenceError(matches.length === 0
@@ -30,7 +30,7 @@ export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, n
30
30
  export { DPOP_HEADER, DPOP_MAX_AGE_SECONDS, DPOP_PROOF_INVALID, DPOP_PROOF_REQUIRED, DPOP_PROOF_TYP, DPOP_PROOF_VERIFY_UNAVAILABLE, accessTokenHash, createDpopKey, isAth, isJwkThumbprint, normalizeDpopHtu, verifyDpopProof, type DpopKey, type DpopProofClaims, type DpopProofRequest, type DpopVerifyFailureReason, type DpopVerifyOptions, type DpopVerifyResult, } from './dpop.js';
31
31
  export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, splitKeyConvergencePolicy, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT, type DecryptOnReadOptions, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
32
32
  export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
33
- export { getMachineUuid, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
33
+ export { getMachineUuid, getMachineUuidForProfile, describeMachineIdentity, type MachineIdentityDescription, type MachineIdentityReason, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
34
34
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
35
35
  export { PAIR_AAD_PREFIX, PAIR_AAD_MAX_LEN, PAIR_BUNDLE_MAX_BASE64_LEN, PAIR_NONCE_LEN, PAIR_PUBKEY_LEN, PAIR_SECRET_LEN, PairBundleError, assemblePairBundleBase64, base64ToUint8Array as pairBase64ToUint8Array, base64UrlToUint8Array as pairBase64UrlToUint8Array, buildPairAad, buildPairUrl, generatePairKeyPair, openPairBundleBase64, parsePairBundle, parsePairPlaintext, parsePairUrl, uint8ArrayToBase64 as pairUint8ArrayToBase64, uint8ArrayToBase64Url as pairUint8ArrayToBase64Url, type AssembleBundleOptions as PairAssembleBundleOptions, type OpenBundleOptions as PairOpenBundleOptions, type PairBundle, type PairBundleErrorCode, type PairKeyPair, type PairPlaintextV1, type PairUrlFields, } from './daemonPair.js';
36
36
  export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, type HarnessTrustTier, type CredentialDeliveryPolicy, type CredentialMode, } from './harnessTrust.js';
@@ -24,9 +24,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.runHeadersHelper = exports.SENTINEL_TRIGGERING_FAILURES = exports.__resetAuthHelperFailureReasonForTest = exports.getLastAuthHelperFailureReason = exports.getDefaultHelperTtlMs = exports.invokeAuthHelper = exports.AuthHelperUnavailableError = exports.AuthHelperUntrustedError = exports.isStaticTokenKind = exports.PERSONAL_ACCESS_TOKEN_PREFIX = exports.CI_TOKEN_PREFIX = exports.classifyTokenKind = exports.invalidateAuthHelperCache = exports.__resetCredentialResolverForTest = exports.CREDENTIAL_PRECEDENCE = exports.CredentialResolver = exports.__setFdReaderForTest = exports.readAuthFromFd = exports.createRefreshScheduler = exports.UNKNOWN_401_BUDGET = exports.__resetUnknown401StreaksForTest = exports.__refreshDiagnostics = exports.installLockExitHandlers = exports.clearReauthState = exports.needsReauthState = exports.persistReauthState = exports.withAuthLock = exports.revokeDaemonRefreshSession = exports.refreshDaemonToken = exports.classifyRefreshResponse = exports.EXIT_NEEDS_REAUTH = exports.reauthCommand = exports.formatReauthMessage = exports.handleMidStreamAuthExpiry = exports.isAuthExpiredSseEvent = exports.decodeJwtPayload = exports.decodeJwtExpiry = exports.TransientAuthFailure = exports.PermanentAuthFailure = exports.emitAuthTelemetry = exports.getAuthBinaryName = exports.getAuthKillSwitches = exports.getAuthConfigDir = exports.getAuthLogger = exports.configureAuthCore = exports.OAUTH_CLI_SCOPE = exports.isDaemonScope = exports.VALID_DAEMON_SCOPE_SET = exports.ALL_DAEMON_SCOPES = exports.SCOPES = void 0;
26
26
  exports.jwkThumbprint = exports.generateDeviceKeyPair = exports.buildDeviceProof = exports.zeroizeKekCaches = exports.WindowsKek = exports.registerZeroizeHook = exports.MacosKek = exports.LinuxKek = exports.KekUnavailableError = exports.UnavailableKek = exports.InMemoryKek = exports.getKekStrategy = exports.__setKekStrategyForTest = exports.credEnvelopeWrap = exports.credEnvelopeUnwrap = exports.credEnvelopeSerialize = exports.credSealBuffer = exports.credOpenBuffer = exports.CRED_KEY_LEN = exports.isCredEnvelopeString = exports.generateDek = exports.CRED_ENVELOPE_MAGIC = exports.credEnvelopeEncrypt = exports.credEnvelopeDeserialize = exports.credEnvelopeDecrypt = exports.buildCredentialAad = exports.isValidRecoveryCode = exports.normalizeRecoveryCode = exports.parseRecoveryCode = exports.formatRecoveryCode = exports.MAX_TTL_DAYS = exports.MIN_TTL_DAYS = exports.parseTtlDays = exports.LoginExchangeError = exports.LoginInitError = exports.loginWithLocalhost = exports.LocalCallbackMalformedError = exports.LocalCallbackDeniedError = exports.LocalCallbackStateMismatchError = exports.LocalCallbackTimeoutError = exports.LocalCallbackBindError = exports.LocalCallbackError = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.startLocalCallback = exports.verifyChallenge = exports.safeEqual = exports.generateState = exports.challenge = exports.generateVerifier = exports.invalidateHeadersHelperCache = void 0;
27
- exports.qualifyMachineUuid = exports.getMachineUuidProfile = exports.setMachineUuidProfile = exports.getHardwareMachineUuid = exports.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = exports.describeDeviceKeySplit = exports.describeDeviceIdentityState = exports.getDevicePublicKey = exports.isDeviceIdentityActive = exports.clearEnrollmentMarker = exports.enrollWithServer = exports.signServerRequest = exports.resetDeviceIdentity = exports.initDeviceIdentity = exports.__resetShutdownGuardForTest = exports.__getCredEnvelopeStateForTest = exports.__setCredEnvelopeStateForTest = exports.__wrappedDekFilePathForTest = exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT = exports.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.splitKeyConvergencePolicy = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDpopProof = exports.normalizeDpopHtu = exports.isJwkThumbprint = exports.isAth = exports.createDpopKey = exports.accessTokenHash = exports.DPOP_PROOF_VERIFY_UNAVAILABLE = exports.DPOP_PROOF_TYP = exports.DPOP_PROOF_REQUIRED = exports.DPOP_PROOF_INVALID = exports.DPOP_MAX_AGE_SECONDS = exports.DPOP_HEADER = exports.verifyDeviceProof = exports.tokenHash = exports.publicJwkFromX = exports.normalizeHtu = void 0;
28
- exports.LOOPBACK_DEFAULT_MAX_BODY_BYTES = exports.LoopbackTargetRefusedError = exports.isLoopbackAddress = exports.isLoopbackHostname = exports.assertLoopbackHttpUrl = exports.loopbackFetch = exports.withLoopbackNoProxy = exports.envHasProxy = exports.LOOPBACK_NO_PROXY_HOSTS = exports.isDaemonChildEnv = exports.DAEMON_CHILD_ENV = exports.SENSITIVE_ENV_PREFIX_EXCEPTIONS = exports.SENSITIVE_ENV_NAME_TOKENS = exports.SENSITIVE_ENV_SUFFIXES = exports.SENSITIVE_ENV_PREFIXES = exports.SENSITIVE_ENV_VARS = exports.sanitizeSpawnEnv = exports.harnessLoadsClaudeSkillsLayout = exports.claudeSkillsLayoutEvidence = exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = exports.tierPermitsConfiguredAutoMode = exports.credentialPolicyForTier = exports.getHarnessTier = exports.HARNESS_TIERS = exports.pairUint8ArrayToBase64Url = exports.pairUint8ArrayToBase64 = exports.parsePairUrl = exports.parsePairPlaintext = exports.parsePairBundle = exports.openPairBundleBase64 = exports.generatePairKeyPair = exports.buildPairUrl = exports.buildPairAad = exports.pairBase64UrlToUint8Array = exports.pairBase64ToUint8Array = exports.assemblePairBundleBase64 = exports.PairBundleError = exports.PAIR_SECRET_LEN = exports.PAIR_PUBKEY_LEN = exports.PAIR_NONCE_LEN = exports.PAIR_BUNDLE_MAX_BASE64_LEN = exports.PAIR_AAD_MAX_LEN = exports.PAIR_AAD_PREFIX = exports.__resetCachedDaemonIdForTest = exports.legacyDaemonUuids = exports.ensureMachineDaemonId = exports.__resetMachineUuidCacheForTest = exports.HOSTED_RUNTIME_MACHINE_SOURCES = exports.hostedRuntimeIdOverride = exports.MACHINE_IDENTITY_FILENAME = void 0;
29
- exports.compareReleaseVersions = exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = exports.resolveChannelManifestUrl = exports.isFirstPartyHarnessChannel = exports.firstPartyHarnessFeedCandidates = exports.firstPartyHarnessFeedBase = exports.firstPartyHarnessChannelPrefix = exports.FIRST_PARTY_HARNESS_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.migrateFirstPartyHarnessHome = exports.linkFirstPartyHarnessAliases = exports.firstPartyHarnessInstalledBinaryPath = exports.firstPartyHarnessHomeDir = exports.firstPartyHarnessBinDir = exports.FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = exports.describeLegacyState = exports.findLegacyLocalState = exports.DEFAULT_BINARY_NAME = exports.LEGACY_NATIVE_ID_PREFIXES = exports.NATIVE_ID_PREFIX = exports.LEGACY_CONFIG_DIR_NAMES = exports.CONFIG_DIR_NAME = void 0;
27
+ exports.setMachineUuidProfile = exports.getHardwareMachineUuid = exports.describeMachineIdentity = exports.getMachineUuidForProfile = exports.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = exports.describeDeviceKeySplit = exports.describeDeviceIdentityState = exports.getDevicePublicKey = exports.isDeviceIdentityActive = exports.clearEnrollmentMarker = exports.enrollWithServer = exports.signServerRequest = exports.resetDeviceIdentity = exports.initDeviceIdentity = exports.__resetShutdownGuardForTest = exports.__getCredEnvelopeStateForTest = exports.__setCredEnvelopeStateForTest = exports.__wrappedDekFilePathForTest = exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT = exports.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.splitKeyConvergencePolicy = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDpopProof = exports.normalizeDpopHtu = exports.isJwkThumbprint = exports.isAth = exports.createDpopKey = exports.accessTokenHash = exports.DPOP_PROOF_VERIFY_UNAVAILABLE = exports.DPOP_PROOF_TYP = exports.DPOP_PROOF_REQUIRED = exports.DPOP_PROOF_INVALID = exports.DPOP_MAX_AGE_SECONDS = exports.DPOP_HEADER = exports.verifyDeviceProof = exports.tokenHash = exports.publicJwkFromX = exports.normalizeHtu = void 0;
28
+ exports.isLoopbackAddress = exports.isLoopbackHostname = exports.assertLoopbackHttpUrl = exports.loopbackFetch = exports.withLoopbackNoProxy = exports.envHasProxy = exports.LOOPBACK_NO_PROXY_HOSTS = exports.isDaemonChildEnv = exports.DAEMON_CHILD_ENV = exports.SENSITIVE_ENV_PREFIX_EXCEPTIONS = exports.SENSITIVE_ENV_NAME_TOKENS = exports.SENSITIVE_ENV_SUFFIXES = exports.SENSITIVE_ENV_PREFIXES = exports.SENSITIVE_ENV_VARS = exports.sanitizeSpawnEnv = exports.harnessLoadsClaudeSkillsLayout = exports.claudeSkillsLayoutEvidence = exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = exports.tierPermitsConfiguredAutoMode = exports.credentialPolicyForTier = exports.getHarnessTier = exports.HARNESS_TIERS = exports.pairUint8ArrayToBase64Url = exports.pairUint8ArrayToBase64 = exports.parsePairUrl = exports.parsePairPlaintext = exports.parsePairBundle = exports.openPairBundleBase64 = exports.generatePairKeyPair = exports.buildPairUrl = exports.buildPairAad = exports.pairBase64UrlToUint8Array = exports.pairBase64ToUint8Array = exports.assemblePairBundleBase64 = exports.PairBundleError = exports.PAIR_SECRET_LEN = exports.PAIR_PUBKEY_LEN = exports.PAIR_NONCE_LEN = exports.PAIR_BUNDLE_MAX_BASE64_LEN = exports.PAIR_AAD_MAX_LEN = exports.PAIR_AAD_PREFIX = exports.__resetCachedDaemonIdForTest = exports.legacyDaemonUuids = exports.ensureMachineDaemonId = exports.__resetMachineUuidCacheForTest = exports.HOSTED_RUNTIME_MACHINE_SOURCES = exports.hostedRuntimeIdOverride = exports.MACHINE_IDENTITY_FILENAME = exports.qualifyMachineUuid = exports.getMachineUuidProfile = void 0;
29
+ exports.compareReleaseVersions = exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = exports.resolveChannelManifestUrl = exports.isFirstPartyHarnessChannel = exports.firstPartyHarnessFeedCandidates = exports.firstPartyHarnessFeedBase = exports.firstPartyHarnessChannelPrefix = exports.FIRST_PARTY_HARNESS_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.migrateFirstPartyHarnessHome = exports.linkFirstPartyHarnessAliases = exports.firstPartyHarnessInstalledBinaryPath = exports.firstPartyHarnessHomeDir = exports.firstPartyHarnessBinDir = exports.FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = exports.describeLegacyState = exports.findLegacyLocalState = exports.DEFAULT_BINARY_NAME = exports.LEGACY_NATIVE_ID_PREFIXES = exports.NATIVE_ID_PREFIX = exports.LEGACY_CONFIG_DIR_NAMES = exports.CONFIG_DIR_NAME = exports.LOOPBACK_DEFAULT_MAX_BODY_BYTES = exports.LoopbackTargetRefusedError = void 0;
30
30
  // Daemon scope vocabulary — single source of truth for non-Mongoose tiers
31
31
  // (daemon, desktop, cli). Schema-bound canonical lives in
32
32
  // `@skrr-ai/data-schemas/common/daemonScopes`; the two are kept byte-equal
@@ -284,6 +284,8 @@ Object.defineProperty(exports, "__getDeviceIdentityStateForTest", { enumerable:
284
284
  // OSK-12063. Also exposed dependency-free as `@skrr-ai/auth-core/machine-uuid`.
285
285
  var machineUuid_js_1 = require("./machineUuid.js");
286
286
  Object.defineProperty(exports, "getMachineUuid", { enumerable: true, get: function () { return machineUuid_js_1.getMachineUuid; } });
287
+ Object.defineProperty(exports, "getMachineUuidForProfile", { enumerable: true, get: function () { return machineUuid_js_1.getMachineUuidForProfile; } });
288
+ Object.defineProperty(exports, "describeMachineIdentity", { enumerable: true, get: function () { return machineUuid_js_1.describeMachineIdentity; } });
287
289
  Object.defineProperty(exports, "getHardwareMachineUuid", { enumerable: true, get: function () { return machineUuid_js_1.getHardwareMachineUuid; } });
288
290
  Object.defineProperty(exports, "setMachineUuidProfile", { enumerable: true, get: function () { return machineUuid_js_1.setMachineUuidProfile; } });
289
291
  Object.defineProperty(exports, "getMachineUuidProfile", { enumerable: true, get: function () { return machineUuid_js_1.getMachineUuidProfile; } });
@@ -56,5 +56,65 @@ export declare function getMachineUuidProfile(): string | null;
56
56
  * profile that already held credentials; see the block above.
57
57
  */
58
58
  export declare function getMachineUuid(): string;
59
+ /**
60
+ * The `machineUuid` a process running as `profile` sends, WITHOUT changing which
61
+ * profile this process runs as.
62
+ *
63
+ * The CLI needs the answer for the profile it is about to hand a credential to,
64
+ * and asks the daemon binary what IT will send (`accept-handoff --print-id`).
65
+ * Two programs that compute one identity can only be known to agree by
66
+ * comparing them, and a daemon that predates per-profile identities does not
67
+ * agree: it sends the hardware value whatever the profile's
68
+ * `machine-identity.json` says, so a credential minted for what it reports
69
+ * registers the profile on the default machine's row (the OSK-13845 failure, now
70
+ * reached through version skew rather than through the rule). The comparison
71
+ * needs this: the expected value for the TARGET profile, which is not
72
+ * necessarily the one this process is running as.
73
+ *
74
+ * Decides and persists the profile's identity if nothing has yet — the same
75
+ * side effect `getMachineUuid()` has, for the same reason.
76
+ */
77
+ export declare function getMachineUuidForProfile(profile: string | null | undefined): string;
78
+ export type MachineIdentityReason =
79
+ /** The default profile, which always uses this computer's own identity. */
80
+ 'default_profile'
81
+ /** A hosted runtime, whose identity the control plane issued. */
82
+ | 'hosted_runtime'
83
+ /** A profile that held no credentials when its identity was first decided. */
84
+ | 'new_profile'
85
+ /** A profile that already held credentials, so it keeps the shared identity. */
86
+ | 'pre_existing_profile'
87
+ /** Recovered from the copy kept outside the profile after product state was removed. */
88
+ | 'restored_profile'
89
+ /** The profile's record could not be read (the identity is still the one in use). */
90
+ | 'unrecorded';
91
+ export interface MachineIdentityDescription {
92
+ /** The profile this process runs as; null for the default profile. */
93
+ profile: string | null;
94
+ /** The value sent to the server as `machineUuid`. */
95
+ machineUuid: string;
96
+ /** This computer's own identity. */
97
+ hardware: string;
98
+ /**
99
+ * A non-default profile that registers on the SAME machine row as the default
100
+ * profile: they share one Daemon row, one device-key pin and one machine page.
101
+ */
102
+ sharesDefaultMachine: boolean;
103
+ reason: MachineIdentityReason;
104
+ /** The files that made a profile "already hold credentials", when recorded. */
105
+ evidence: string[];
106
+ decidedAt: string | null;
107
+ /** One sentence for a person. Written once, here, so no surface words it differently. */
108
+ sentence: string;
109
+ }
110
+ /**
111
+ * Which machine this process registers as, and WHY — the question a person has
112
+ * when two profiles turn up on one machine page and the logs say nothing.
113
+ *
114
+ * A read, not a decision: it reports the identity `getMachineUuid()` settled on
115
+ * (settling it if nothing has, exactly as any caller would) and the record that
116
+ * explains it. It never changes what a profile registers as.
117
+ */
118
+ export declare function describeMachineIdentity(): MachineIdentityDescription;
59
119
  /** @internal test seam — forget the cached value so the next call re-probes. */
60
120
  export declare function __resetMachineUuidCacheForTest(): void;
@@ -10,6 +10,8 @@ exports.qualifyMachineUuid = qualifyMachineUuid;
10
10
  exports.setMachineUuidProfile = setMachineUuidProfile;
11
11
  exports.getMachineUuidProfile = getMachineUuidProfile;
12
12
  exports.getMachineUuid = getMachineUuid;
13
+ exports.getMachineUuidForProfile = getMachineUuidForProfile;
14
+ exports.describeMachineIdentity = describeMachineIdentity;
13
15
  exports.__resetMachineUuidCacheForTest = __resetMachineUuidCacheForTest;
14
16
  /**
15
17
  * machineUuid.ts — the ONE hardware machine identity sent to the server as
@@ -231,7 +233,7 @@ function readPersistedChoice(file, hardware, profile, requireProfile = false) {
231
233
  return null;
232
234
  }
233
235
  }
234
- function persistChoice(dir, file, hardware, machineUuid, reason, profile) {
236
+ function persistChoice(dir, file, hardware, machineUuid, reason, profile, evidence) {
235
237
  try {
236
238
  node_fs_1.default.mkdirSync(dir, { recursive: true, mode: 0o700 });
237
239
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
@@ -241,6 +243,12 @@ function persistChoice(dir, file, hardware, machineUuid, reason, profile) {
241
243
  machineUuid,
242
244
  profile,
243
245
  reason,
246
+ // WHICH file made a profile "already hold credentials". The reason alone
247
+ // says a profile shares the default's machine row; this says what put it
248
+ // there, which is the difference between "an older Skrr set it up" and
249
+ // "its first credential was written before anything chose its identity".
250
+ // Additive: a reader that predates it ignores the field.
251
+ ...(evidence && evidence.length > 0 ? { evidence: [...evidence] } : {}),
244
252
  decidedAt: new Date().toISOString(),
245
253
  };
246
254
  node_fs_1.default.writeFileSync(tmp, `${JSON.stringify(body)}\n`, { mode: 0o600 });
@@ -280,7 +288,7 @@ function resolveProfileMachineUuid(profile, hardware) {
280
288
  persistChoice(dir, file, hardware, chosen, 'restored_profile', profile);
281
289
  }
282
290
  if (!chosen) {
283
- const preExisting = PRE_EXISTING_PROFILE_EVIDENCE.some((name) => {
291
+ const evidence = PRE_EXISTING_PROFILE_EVIDENCE.filter((name) => {
284
292
  try {
285
293
  return node_fs_1.default.existsSync(node_path_1.default.join(dir, name));
286
294
  }
@@ -288,8 +296,9 @@ function resolveProfileMachineUuid(profile, hardware) {
288
296
  return false;
289
297
  }
290
298
  });
299
+ const preExisting = evidence.length > 0;
291
300
  chosen = preExisting ? hardware : qualifyMachineUuid(hardware, profile);
292
- persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile', profile);
301
+ persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile', profile, evidence);
293
302
  }
294
303
  preserveRecoveryChoice(profile, hardware, chosen);
295
304
  profileChoiceCache.set(cacheKey, chosen);
@@ -309,14 +318,129 @@ function resolveProfileMachineUuid(profile, hardware) {
309
318
  * profile that already held credentials; see the block above.
310
319
  */
311
320
  function getMachineUuid() {
321
+ return getMachineUuidForProfile(activeMachineProfile);
322
+ }
323
+ /**
324
+ * The `machineUuid` a process running as `profile` sends, WITHOUT changing which
325
+ * profile this process runs as.
326
+ *
327
+ * The CLI needs the answer for the profile it is about to hand a credential to,
328
+ * and asks the daemon binary what IT will send (`accept-handoff --print-id`).
329
+ * Two programs that compute one identity can only be known to agree by
330
+ * comparing them, and a daemon that predates per-profile identities does not
331
+ * agree: it sends the hardware value whatever the profile's
332
+ * `machine-identity.json` says, so a credential minted for what it reports
333
+ * registers the profile on the default machine's row (the OSK-13845 failure, now
334
+ * reached through version skew rather than through the rule). The comparison
335
+ * needs this: the expected value for the TARGET profile, which is not
336
+ * necessarily the one this process is running as.
337
+ *
338
+ * Decides and persists the profile's identity if nothing has yet — the same
339
+ * side effect `getMachineUuid()` has, for the same reason.
340
+ */
341
+ function getMachineUuidForProfile(profile) {
312
342
  const hardware = getHardwareMachineUuid();
313
343
  // A hosted runtime's identity is ISSUED and fenced by the control plane.
314
344
  if (hostedRuntimeIdOverride('OVERSKY_MACHINE_UUID'))
315
345
  return hardware;
316
- const profile = activeMachineProfile;
317
- if (!profile || profile === DEFAULT_PROFILE_NAME)
346
+ const name = typeof profile === 'string' ? profile.trim() : '';
347
+ if (!name || name === DEFAULT_PROFILE_NAME)
318
348
  return hardware;
319
- return resolveProfileMachineUuid(profile, hardware);
349
+ return resolveProfileMachineUuid(name, hardware);
350
+ }
351
+ function readRecordedChoice(profile) {
352
+ try {
353
+ const file = node_path_1.default.join((0, configRoot_js_1.resolveConfigRoot)(), 'profiles', profile, exports.MACHINE_IDENTITY_FILENAME);
354
+ const parsed = JSON.parse(node_fs_1.default.readFileSync(file, 'utf-8'));
355
+ const reason = parsed.reason === 'new_profile' ||
356
+ parsed.reason === 'pre_existing_profile' ||
357
+ parsed.reason === 'restored_profile'
358
+ ? parsed.reason
359
+ : null;
360
+ return {
361
+ reason,
362
+ evidence: Array.isArray(parsed.evidence)
363
+ ? parsed.evidence.filter((e) => typeof e === 'string')
364
+ : [],
365
+ decidedAt: typeof parsed.decidedAt === 'string' ? parsed.decidedAt : null,
366
+ };
367
+ }
368
+ catch {
369
+ return { reason: null, evidence: [], decidedAt: null };
370
+ }
371
+ }
372
+ /**
373
+ * Which machine this process registers as, and WHY — the question a person has
374
+ * when two profiles turn up on one machine page and the logs say nothing.
375
+ *
376
+ * A read, not a decision: it reports the identity `getMachineUuid()` settled on
377
+ * (settling it if nothing has, exactly as any caller would) and the record that
378
+ * explains it. It never changes what a profile registers as.
379
+ */
380
+ function describeMachineIdentity() {
381
+ const hardware = getHardwareMachineUuid();
382
+ const machineUuid = getMachineUuid();
383
+ const active = activeMachineProfile;
384
+ const profile = active && active !== DEFAULT_PROFILE_NAME ? active : null;
385
+ const base = { profile, machineUuid, hardware };
386
+ if (hostedRuntimeIdOverride('OVERSKY_MACHINE_UUID')) {
387
+ return {
388
+ ...base,
389
+ sharesDefaultMachine: false,
390
+ reason: 'hosted_runtime',
391
+ evidence: [],
392
+ decidedAt: null,
393
+ sentence: 'This runtime is hosted; its machine identity was issued by the control plane.',
394
+ };
395
+ }
396
+ if (!profile) {
397
+ return {
398
+ ...base,
399
+ sharesDefaultMachine: false,
400
+ reason: 'default_profile',
401
+ evidence: [],
402
+ decidedAt: null,
403
+ sentence: "The default profile uses this computer's own machine identity.",
404
+ };
405
+ }
406
+ const recorded = readRecordedChoice(profile);
407
+ const sharesDefaultMachine = machineUuid === hardware;
408
+ const reason = recorded.reason ?? 'unrecorded';
409
+ const decided = recorded.decidedAt ? ` (decided ${recorded.decidedAt})` : '';
410
+ const held = recorded.evidence.length > 0 ? ` (${recorded.evidence.join(', ')})` : '';
411
+ let sentence;
412
+ if (reason === 'new_profile') {
413
+ sentence =
414
+ `Profile '${profile}' has its own machine identity: it held no credentials when that ` +
415
+ `identity was first chosen${decided}.`;
416
+ }
417
+ else if (reason === 'pre_existing_profile') {
418
+ sentence =
419
+ `Profile '${profile}' shares this computer's default machine identity: it already held ` +
420
+ `credentials${held} when its identity was first chosen${decided}, and the server ties ` +
421
+ 'those credentials to that identity, so changing it would sign the profile out.';
422
+ }
423
+ else if (reason === 'restored_profile') {
424
+ sentence = sharesDefaultMachine
425
+ ? `Profile '${profile}' shares this computer's default machine identity, restored from the ` +
426
+ `copy kept outside the profile after its state was removed${decided}.`
427
+ : `Profile '${profile}' has its own machine identity, restored from the copy kept outside ` +
428
+ `the profile after its state was removed${decided}.`;
429
+ }
430
+ else {
431
+ sentence = sharesDefaultMachine
432
+ ? `Profile '${profile}' uses this computer's default machine identity; the record of why ` +
433
+ 'could not be read.'
434
+ : `Profile '${profile}' has its own machine identity; the record of why could not be read.`;
435
+ }
436
+ return {
437
+ ...base,
438
+ sharesDefaultMachine,
439
+ reason,
440
+ evidence: recorded.evidence,
441
+ decidedAt: recorded.decidedAt,
442
+ sentence,
443
+ };
320
444
  }
321
445
  /** @internal test seam — forget the cached value so the next call re-probes. */
322
446
  function __resetMachineUuidCacheForTest() {
@@ -30,7 +30,7 @@ export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, n
30
30
  export { DPOP_HEADER, DPOP_MAX_AGE_SECONDS, DPOP_PROOF_INVALID, DPOP_PROOF_REQUIRED, DPOP_PROOF_TYP, DPOP_PROOF_VERIFY_UNAVAILABLE, accessTokenHash, createDpopKey, isAth, isJwkThumbprint, normalizeDpopHtu, verifyDpopProof, type DpopKey, type DpopProofClaims, type DpopProofRequest, type DpopVerifyFailureReason, type DpopVerifyOptions, type DpopVerifyResult, } from './dpop.js';
31
31
  export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, splitKeyConvergencePolicy, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT, type DecryptOnReadOptions, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
32
32
  export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
33
- export { getMachineUuid, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
33
+ export { getMachineUuid, getMachineUuidForProfile, describeMachineIdentity, type MachineIdentityDescription, type MachineIdentityReason, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
34
34
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
35
35
  export { PAIR_AAD_PREFIX, PAIR_AAD_MAX_LEN, PAIR_BUNDLE_MAX_BASE64_LEN, PAIR_NONCE_LEN, PAIR_PUBKEY_LEN, PAIR_SECRET_LEN, PairBundleError, assemblePairBundleBase64, base64ToUint8Array as pairBase64ToUint8Array, base64UrlToUint8Array as pairBase64UrlToUint8Array, buildPairAad, buildPairUrl, generatePairKeyPair, openPairBundleBase64, parsePairBundle, parsePairPlaintext, parsePairUrl, uint8ArrayToBase64 as pairUint8ArrayToBase64, uint8ArrayToBase64Url as pairUint8ArrayToBase64Url, type AssembleBundleOptions as PairAssembleBundleOptions, type OpenBundleOptions as PairOpenBundleOptions, type PairBundle, type PairBundleErrorCode, type PairKeyPair, type PairPlaintextV1, type PairUrlFields, } from './daemonPair.js';
36
36
  export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, type HarnessTrustTier, type CredentialDeliveryPolicy, type CredentialMode, } from './harnessTrust.js';
@@ -125,7 +125,7 @@ __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest,
125
125
  // Hardware machine identity sent as `machineUuid` (IOPlatformUUID /
126
126
  // machine-id / MachineGuid). ONE implementation for daemon and CLI —
127
127
  // OSK-12063. Also exposed dependency-free as `@skrr-ai/auth-core/machine-uuid`.
128
- export { getMachineUuid, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
128
+ export { getMachineUuid, getMachineUuidForProfile, describeMachineIdentity, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
129
129
  // Stable per-machine UUIDv7 identity persisted at <configDir>/daemon.id.
130
130
  // NOT a `machineUuid` — see machineUuid.ts.
131
131
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
@@ -56,5 +56,65 @@ export declare function getMachineUuidProfile(): string | null;
56
56
  * profile that already held credentials; see the block above.
57
57
  */
58
58
  export declare function getMachineUuid(): string;
59
+ /**
60
+ * The `machineUuid` a process running as `profile` sends, WITHOUT changing which
61
+ * profile this process runs as.
62
+ *
63
+ * The CLI needs the answer for the profile it is about to hand a credential to,
64
+ * and asks the daemon binary what IT will send (`accept-handoff --print-id`).
65
+ * Two programs that compute one identity can only be known to agree by
66
+ * comparing them, and a daemon that predates per-profile identities does not
67
+ * agree: it sends the hardware value whatever the profile's
68
+ * `machine-identity.json` says, so a credential minted for what it reports
69
+ * registers the profile on the default machine's row (the OSK-13845 failure, now
70
+ * reached through version skew rather than through the rule). The comparison
71
+ * needs this: the expected value for the TARGET profile, which is not
72
+ * necessarily the one this process is running as.
73
+ *
74
+ * Decides and persists the profile's identity if nothing has yet — the same
75
+ * side effect `getMachineUuid()` has, for the same reason.
76
+ */
77
+ export declare function getMachineUuidForProfile(profile: string | null | undefined): string;
78
+ export type MachineIdentityReason =
79
+ /** The default profile, which always uses this computer's own identity. */
80
+ 'default_profile'
81
+ /** A hosted runtime, whose identity the control plane issued. */
82
+ | 'hosted_runtime'
83
+ /** A profile that held no credentials when its identity was first decided. */
84
+ | 'new_profile'
85
+ /** A profile that already held credentials, so it keeps the shared identity. */
86
+ | 'pre_existing_profile'
87
+ /** Recovered from the copy kept outside the profile after product state was removed. */
88
+ | 'restored_profile'
89
+ /** The profile's record could not be read (the identity is still the one in use). */
90
+ | 'unrecorded';
91
+ export interface MachineIdentityDescription {
92
+ /** The profile this process runs as; null for the default profile. */
93
+ profile: string | null;
94
+ /** The value sent to the server as `machineUuid`. */
95
+ machineUuid: string;
96
+ /** This computer's own identity. */
97
+ hardware: string;
98
+ /**
99
+ * A non-default profile that registers on the SAME machine row as the default
100
+ * profile: they share one Daemon row, one device-key pin and one machine page.
101
+ */
102
+ sharesDefaultMachine: boolean;
103
+ reason: MachineIdentityReason;
104
+ /** The files that made a profile "already hold credentials", when recorded. */
105
+ evidence: string[];
106
+ decidedAt: string | null;
107
+ /** One sentence for a person. Written once, here, so no surface words it differently. */
108
+ sentence: string;
109
+ }
110
+ /**
111
+ * Which machine this process registers as, and WHY — the question a person has
112
+ * when two profiles turn up on one machine page and the logs say nothing.
113
+ *
114
+ * A read, not a decision: it reports the identity `getMachineUuid()` settled on
115
+ * (settling it if nothing has, exactly as any caller would) and the record that
116
+ * explains it. It never changes what a profile registers as.
117
+ */
118
+ export declare function describeMachineIdentity(): MachineIdentityDescription;
59
119
  /** @internal test seam — forget the cached value so the next call re-probes. */
60
120
  export declare function __resetMachineUuidCacheForTest(): void;