@pellux/goodvibes-daemon 1.28.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/CHANGELOG.md +383 -0
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/bin/goodvibes-daemon +100 -0
- package/bin/launcher-support.js +226 -0
- package/package.json +96 -0
- package/scripts/check-bun.sh +20 -0
- package/scripts/postinstall.js +244 -0
- package/src/cli/command-catalog.ts +828 -0
- package/src/cli/completion.ts +299 -0
- package/src/cli/help.ts +167 -0
- package/src/cli/index.ts +21 -0
- package/src/cli/parser.ts +55 -0
- package/src/cli/surface-catalog.ts +26 -0
- package/src/cli/types.ts +63 -0
- package/src/cluster/daemon-ws-call.ts +235 -0
- package/src/cluster/raw-reply-route.ts +111 -0
- package/src/config/checkpoint-settings.ts +113 -0
- package/src/config/run-daemon-config-migration.ts +47 -0
- package/src/config/secret-config.ts +175 -0
- package/src/config/secrets.ts +71 -0
- package/src/config/surface.ts +24 -0
- package/src/core/pairing-banner.ts +82 -0
- package/src/daemon/cli.ts +878 -0
- package/src/daemon/config-command.ts +281 -0
- package/src/daemon/handlers/context.ts +29 -0
- package/src/daemon/handlers/contracts.ts +43 -0
- package/src/daemon/handlers/credentials.ts +139 -0
- package/src/daemon/handlers/drafts/draft-store.ts +427 -0
- package/src/daemon/handlers/drafts/index.ts +17 -0
- package/src/daemon/handlers/drafts/register.ts +331 -0
- package/src/daemon/handlers/errors.ts +18 -0
- package/src/daemon/handlers/inbox/aggregator.ts +375 -0
- package/src/daemon/handlers/inbox/cursor-store.ts +512 -0
- package/src/daemon/handlers/inbox/index.ts +221 -0
- package/src/daemon/handlers/inbox/mapping.ts +192 -0
- package/src/daemon/handlers/inbox/poller.ts +239 -0
- package/src/daemon/handlers/inbox/provider-adapter.ts +171 -0
- package/src/daemon/handlers/inbox/providers/discord.ts +276 -0
- package/src/daemon/handlers/inbox/providers/email.ts +176 -0
- package/src/daemon/handlers/inbox/providers/imap-client.ts +300 -0
- package/src/daemon/handlers/inbox/providers/route-util.ts +24 -0
- package/src/daemon/handlers/inbox/providers/slack.ts +287 -0
- package/src/daemon/handlers/index.ts +117 -0
- package/src/daemon/handlers/register.ts +180 -0
- package/src/daemon/handlers/remote/backends/cloud-terminal.ts +143 -0
- package/src/daemon/handlers/remote/backends/docker.ts +79 -0
- package/src/daemon/handlers/remote/backends/index.ts +40 -0
- package/src/daemon/handlers/remote/backends/local-process.ts +113 -0
- package/src/daemon/handlers/remote/backends/process-runner.ts +127 -0
- package/src/daemon/handlers/remote/backends/ssh.ts +126 -0
- package/src/daemon/handlers/remote/backends/types.ts +97 -0
- package/src/daemon/handlers/remote/dispatcher.ts +181 -0
- package/src/daemon/handlers/remote/index.ts +120 -0
- package/src/daemon/handlers/remote/peer-registry.ts +357 -0
- package/src/daemon/handlers/remote/service.ts +191 -0
- package/src/daemon/handlers/routing/inbox-bridge.ts +71 -0
- package/src/daemon/handlers/routing/index.ts +261 -0
- package/src/daemon/handlers/routing/route-store.ts +319 -0
- package/src/daemon/handlers/routing/routing-resolver.ts +75 -0
- package/src/daemon/handlers/sqlite-store.ts +303 -0
- package/src/daemon/handlers/triage/index.ts +57 -0
- package/src/daemon/handlers/triage/integration.ts +213 -0
- package/src/daemon/handlers/triage/pipeline.ts +274 -0
- package/src/daemon/handlers/triage/scorer.ts +287 -0
- package/src/daemon/handlers/triage/tagger/discord.ts +187 -0
- package/src/daemon/handlers/triage/tagger/imap.ts +384 -0
- package/src/daemon/handlers/triage/tagger/index.ts +184 -0
- package/src/daemon/handlers/triage/tagger/shared.ts +70 -0
- package/src/daemon/handlers/triage/tagger/slack.ts +69 -0
- package/src/daemon/handlers/triage/types.ts +50 -0
- package/src/daemon/lifecycle.ts +41 -0
- package/src/daemon/local-daemon-state.ts +233 -0
- package/src/daemon/pair-command.ts +301 -0
- package/src/daemon/provision-wake-model.ts +81 -0
- package/src/daemon/send/channels.ts +200 -0
- package/src/daemon/send/command.ts +333 -0
- package/src/daemon/send/composition.ts +100 -0
- package/src/daemon/send/failure-text.ts +93 -0
- package/src/daemon/send/inert-text.ts +225 -0
- package/src/daemon/send/stdin.ts +24 -0
- package/src/daemon/service-commands.ts +530 -0
- package/src/daemon/sessions-command.ts +209 -0
- package/src/daemon/status-command.ts +481 -0
- package/src/daemon/webui-command.ts +339 -0
- package/src/runtime/boot-tasks.ts +110 -0
- package/src/runtime/cluster-composition.ts +124 -0
- package/src/runtime/cluster-group-composition.ts +284 -0
- package/src/runtime/conversation-rewind-port.ts +171 -0
- package/src/runtime/credential-composition.ts +54 -0
- package/src/runtime/daemon-handler-composition.ts +76 -0
- package/src/runtime/device-posture-composition.ts +115 -0
- package/src/runtime/disposal-wiring.ts +101 -0
- package/src/runtime/fleet-needs-input-push.ts +61 -0
- package/src/runtime/fleet-services.ts +41 -0
- package/src/runtime/hosted-session-composition.ts +128 -0
- package/src/runtime/index.ts +100 -0
- package/src/runtime/knowledge-services.ts +101 -0
- package/src/runtime/legacy-daemon-migration.ts +605 -0
- package/src/runtime/legacy-daemon-reconcile.ts +448 -0
- package/src/runtime/mail-composition.ts +65 -0
- package/src/runtime/notification-dispatch.ts +86 -0
- package/src/runtime/plugin-composition.ts +111 -0
- package/src/runtime/runtime-services-types.ts +268 -0
- package/src/runtime/services.ts +756 -0
- package/src/runtime/trigger-services.ts +62 -0
- package/src/runtime/trust/checkpoint-eligibility.ts +138 -0
- package/src/runtime/trust/trust-gated-approvals.ts +169 -0
- package/src/runtime/update-check.ts +61 -0
- package/src/runtime/workspace-checkpointing.ts +116 -0
- package/src/testing/daemon-fixture.ts +276 -0
- package/src/testing/hosted-session-failures.ts +92 -0
- package/src/version.ts +26 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
SecretDeleteOptions,
|
|
3
|
+
SecretRecord,
|
|
4
|
+
SecretScope,
|
|
5
|
+
SecretSource,
|
|
6
|
+
SecretStorageMedium,
|
|
7
|
+
SecretStorageMode,
|
|
8
|
+
SecretStorageReview,
|
|
9
|
+
SecretWriteOptions,
|
|
10
|
+
} from '@pellux/goodvibes-sdk/platform/config';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
SecretsManager as SdkSecretsManager,
|
|
14
|
+
type SecretsManagerOptions as SdkSecretsManagerOptions,
|
|
15
|
+
} from '@pellux/goodvibes-sdk/platform/config';
|
|
16
|
+
import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
|
|
17
|
+
import { GOODVIBES_DAEMON_SURFACE_ROOT } from './surface.ts';
|
|
18
|
+
|
|
19
|
+
export type SecretsManagerOptions = Omit<SdkSecretsManagerOptions, 'surfaceRoot'>;
|
|
20
|
+
|
|
21
|
+
const RAW_SECRET_LITERAL_PREFIX = '__GOODVIBES_LITERAL_V1__';
|
|
22
|
+
|
|
23
|
+
function isGoodVibesSecretRefInput(value: string): boolean {
|
|
24
|
+
const normalized = value.trim();
|
|
25
|
+
return normalized.startsWith('goodvibes://secrets/') && isSecretRefInput(normalized);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function shouldStoreAsLiteral(value: string): boolean {
|
|
29
|
+
return value.startsWith(RAW_SECRET_LITERAL_PREFIX)
|
|
30
|
+
|| (isSecretRefInput(value) && !isGoodVibesSecretRefInput(value));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function encodeLiteralSecret(value: string): string {
|
|
34
|
+
return `${RAW_SECRET_LITERAL_PREFIX}${Buffer.from(JSON.stringify({ value }), 'utf-8').toString('base64url')}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function decodeLiteralSecret(value: string): string | null {
|
|
38
|
+
if (!value.startsWith(RAW_SECRET_LITERAL_PREFIX)) return null;
|
|
39
|
+
try {
|
|
40
|
+
const decoded = Buffer.from(value.slice(RAW_SECRET_LITERAL_PREFIX.length), 'base64url').toString('utf-8');
|
|
41
|
+
const parsed = JSON.parse(decoded) as unknown;
|
|
42
|
+
if (!parsed || typeof parsed !== 'object' || typeof (parsed as { value?: unknown }).value !== 'string') return null;
|
|
43
|
+
return (parsed as { value: string }).value;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class SecretsManager extends SdkSecretsManager {
|
|
50
|
+
constructor(options: SecretsManagerOptions) {
|
|
51
|
+
super({
|
|
52
|
+
...options,
|
|
53
|
+
surfaceRoot: GOODVIBES_DAEMON_SURFACE_ROOT,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override async get(key: string): Promise<string | null> {
|
|
58
|
+
const envValue = process.env[key];
|
|
59
|
+
if (envValue !== undefined && shouldStoreAsLiteral(envValue)) {
|
|
60
|
+
return decodeLiteralSecret(envValue) ?? envValue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const value = await super.get(key);
|
|
64
|
+
if (value === null) return null;
|
|
65
|
+
return decodeLiteralSecret(value) ?? value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override async set(key: string, value: string, options?: Parameters<SdkSecretsManager['set']>[2]): Promise<void> {
|
|
69
|
+
await super.set(key, shouldStoreAsLiteral(value) ? encodeLiteralSecret(value) : value, options);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* surface.ts — the daemon's single surface-root identifier.
|
|
3
|
+
*
|
|
4
|
+
* Every piece of the daemon's own on-disk state (sessions, recovery snapshots,
|
|
5
|
+
* checkpoints, the transcript journal, watchers, triggers, control-plane stores)
|
|
6
|
+
* lives under `.goodvibes/<surface root>/`. The segment used to be spelled as a
|
|
7
|
+
* bare string literal at each call site, which is how a writer and a reader
|
|
8
|
+
* ended up disagreeing about where the last-session pointer lives.
|
|
9
|
+
*
|
|
10
|
+
* ── Why the daemon's surface root is still `tui` ────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* Because that is where the running daemon's state already is. Every store
|
|
13
|
+
* this daemon has written on every installed machine — sessions, approvals,
|
|
14
|
+
* watchers, devices, channel policies, the code index — sits under
|
|
15
|
+
* `.goodvibes/tui/`. Renaming the segment here would not move that state; it
|
|
16
|
+
* would make the daemon stop finding it, silently, on machines that have been
|
|
17
|
+
* running for months.
|
|
18
|
+
*
|
|
19
|
+
* The rename is a migration, not a constant edit: it needs a copy-forward pass
|
|
20
|
+
* with a receipt, the same shape the SDK already performs for the unscoped →
|
|
21
|
+
* scoped move, plus the live-install rehearsal that goes with it. Until then
|
|
22
|
+
* this constant carries the reason so nobody "tidies" it.
|
|
23
|
+
*/
|
|
24
|
+
export const GOODVIBES_DAEMON_SURFACE_ROOT = 'tui';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pairing-banner.ts — the pairing block, in one place.
|
|
3
|
+
*
|
|
4
|
+
* A daemon prints this once, as it finishes starting. That was the ONLY way to
|
|
5
|
+
* see it: scroll it off the screen, start the daemon as a service where nothing
|
|
6
|
+
* reads stdout, or come back to the box tomorrow, and there was no way to get
|
|
7
|
+
* the link back short of restarting the daemon. `goodvibes-daemon pair` prints
|
|
8
|
+
* it again — and prints exactly THIS, because both callers render from here.
|
|
9
|
+
*
|
|
10
|
+
* It reprints the daemon's existing shared token rather than minting a new one.
|
|
11
|
+
* Minting is a different act with a different consequence (a per-device token,
|
|
12
|
+
* one more record in the token store, a link the boot banner's QR no longer
|
|
13
|
+
* matches), and `pair` is not the command for it — `pairing.handoff.create` is.
|
|
14
|
+
*/
|
|
15
|
+
import {
|
|
16
|
+
buildPairingHandoffLink,
|
|
17
|
+
describeOriginPosture,
|
|
18
|
+
formatPairingOffers,
|
|
19
|
+
formatPostureCapabilities,
|
|
20
|
+
generateQrMatrix,
|
|
21
|
+
pairingPostureNotice,
|
|
22
|
+
renderQrToString,
|
|
23
|
+
type PairingHandoffOfferKind,
|
|
24
|
+
} from '@pellux/goodvibes-sdk/platform/pairing';
|
|
25
|
+
|
|
26
|
+
export interface PairingBannerInput {
|
|
27
|
+
/** The daemon version, stated so a scanned link is attributable to a build. */
|
|
28
|
+
readonly version: string;
|
|
29
|
+
/** The web-app origin the deep link opens. */
|
|
30
|
+
readonly origin: string;
|
|
31
|
+
/** The token the link carries — the daemon's existing shared companion token. */
|
|
32
|
+
readonly token: string;
|
|
33
|
+
readonly offers: readonly PairingHandoffOfferKind[];
|
|
34
|
+
/** False to print the link and the offers without the QR block. */
|
|
35
|
+
readonly includeQr?: boolean | undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PairingBanner {
|
|
39
|
+
readonly lines: readonly string[];
|
|
40
|
+
/** The exact `<origin>/#pair=<token>` URL the QR encodes. */
|
|
41
|
+
readonly deepLink: string;
|
|
42
|
+
/** The capability list, already worded, for a caller rendering its own shape. */
|
|
43
|
+
readonly capabilities: readonly string[];
|
|
44
|
+
/** The one honest posture line, when the posture carries one. */
|
|
45
|
+
readonly notice: string | undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Render the block. Pure: it takes an origin and a token and returns lines, so
|
|
50
|
+
* the boot path and the `pair` command cannot drift, and a test can assert the
|
|
51
|
+
* exact text without a daemon, a socket or a token file.
|
|
52
|
+
*/
|
|
53
|
+
export function renderPairingBanner(input: PairingBannerInput): PairingBanner {
|
|
54
|
+
const deepLink = buildPairingHandoffLink({
|
|
55
|
+
webOrigin: input.origin,
|
|
56
|
+
token: input.token,
|
|
57
|
+
offers: input.offers,
|
|
58
|
+
});
|
|
59
|
+
// The banner renders the SAME SDK posture the pairing verb carries: the
|
|
60
|
+
// labeled capability list, and the one honest LAN line only when the posture
|
|
61
|
+
// holds it.
|
|
62
|
+
const posture = describeOriginPosture(input.origin);
|
|
63
|
+
const capabilities = formatPostureCapabilities(posture);
|
|
64
|
+
const notice = pairingPostureNotice(posture) ?? undefined;
|
|
65
|
+
const qr = input.includeQr === false ? [] : [renderQrToString(generateQrMatrix(deepLink))];
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
deepLink,
|
|
69
|
+
capabilities,
|
|
70
|
+
notice,
|
|
71
|
+
lines: [
|
|
72
|
+
`GoodVibes daemon ${input.version} — scan to pair a device (opens the web app signed in):`,
|
|
73
|
+
'',
|
|
74
|
+
` ${input.origin}`,
|
|
75
|
+
'',
|
|
76
|
+
...(input.offers.length > 0 ? ['Offers (each declinable in the web app):', ...formatPairingOffers(input.offers), ''] : []),
|
|
77
|
+
...(capabilities.length > 0 ? ['This device will get:', ...capabilities, ''] : []),
|
|
78
|
+
...(notice ? [notice, ''] : []),
|
|
79
|
+
...qr,
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|