@cydm/happy-elves 0.1.0-beta.310 → 0.1.0-beta.312

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.
@@ -116,8 +116,15 @@ const booleanFlags = new Set([
116
116
  ]);
117
117
  const knownFlags = new Set([...valueFlags, ...booleanFlags]);
118
118
  const dashPrefixedValueFlags = new Set(["secret"]);
119
+ const repeatableValueFlags = new Set([
120
+ "tag",
121
+ "tags",
122
+ "release-tag",
123
+ "reviewer-tag",
124
+ "dogfood-tag",
125
+ ]);
119
126
  export function parseCliFlags(argv) {
120
- const parsed = parseSharedFlags(argv, { valueFlags, dashPrefixedValueFlags });
127
+ const parsed = parseSharedFlags(argv, { valueFlags, dashPrefixedValueFlags, repeatableValueFlags });
121
128
  const unknown = Object.keys(parsed.flags).filter((flag) => !knownFlags.has(flag));
122
129
  if (unknown.length > 0) {
123
130
  throw new CliError(`Unknown flag: --${unknown[0]}`, "UNKNOWN_FLAG");
@@ -14,7 +14,10 @@ export async function topLevelStatus(flags) {
14
14
  const relayError = snapshotResult.status === "rejected"
15
15
  ? snapshotResult.reason instanceof Error ? snapshotResult.reason.message : String(snapshotResult.reason)
16
16
  : undefined;
17
- const latestSession = snapshot ? [...snapshot.sessions].sort((left, right) => right.updatedAt - left.updatedAt)[0] : undefined;
17
+ const visibleSessions = snapshot
18
+ ? await visibleStatusSessions(client, snapshot.sessions)
19
+ : [];
20
+ const latestSession = [...visibleSessions].sort((left, right) => right.updatedAt - left.updatedAt)[0];
18
21
  const latestOutput = latestSession ? await readStoredTurnOutput(latestSession.id).catch(() => undefined) : undefined;
19
22
  const latestRelayOutput = latestSession && !latestOutput
20
23
  ? lastAssistantOutput(await client.history(latestSession.id, 200).catch(() => []))
@@ -31,8 +34,8 @@ export async function topLevelStatus(flags) {
31
34
  onlineCount: snapshot?.machines.filter((machine) => machine.online).length ?? 0,
32
35
  },
33
36
  sessions: {
34
- count: snapshot?.sessions.length ?? 0,
35
- runningCount: snapshot?.sessions.filter((session) => session.status === "running").length ?? 0,
37
+ count: visibleSessions.length,
38
+ runningCount: visibleSessions.filter((session) => session.status === "running").length,
36
39
  },
37
40
  latestSession: latestSession ? {
38
41
  sessionId: latestSession.id,
@@ -64,4 +67,13 @@ export async function topLevelStatus(flags) {
64
67
  if (!data.relay.ok)
65
68
  process.exitCode = 1;
66
69
  }
70
+ async function visibleStatusSessions(client, sessions) {
71
+ const projected = await Promise.all(sessions.map(async (session) => ({
72
+ session,
73
+ metadata: await client.decodeSessionMetadata(session).catch(() => ({})),
74
+ })));
75
+ return projected
76
+ .filter(({ session, metadata }) => session.status !== "closed" && metadata.hiddenFromWorkspace !== true && metadata.source !== "dogfood")
77
+ .map(({ session }) => session);
78
+ }
67
79
  //# sourceMappingURL=status.js.map
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { normalizeSessionTags, sessionTagsMatch } from "../../../../packages/shared/dist/index.js";
4
- import { CliError, ControllerClient, DEFAULT_COMMAND_TIMEOUT_MS, compactText, ok, parseDurationMs, readConfig, releaseProfilesPath, wantsJson, } from "./lib/index.js";
4
+ import { CliError, ControllerClient, DEFAULT_COMMAND_TIMEOUT_MS, compactText, ok, parseDurationMs, readConfig, releaseProfilesPath, wantsJson, wantsVerbose, } from "./lib/index.js";
5
5
  const defaultReleaseProfile = {
6
6
  name: "happy-elves",
7
7
  releaseSessionTags: ["Happy Elves", "Release Session"],
@@ -61,7 +61,7 @@ export async function handleRelease({ domain, action, positional, flags }) {
61
61
  ackTimeoutMs: parseDurationMs(flags["ack-timeout"], 15_000),
62
62
  inactivityTimeoutMs: parseDurationMs(flags["inactivity-timeout"] ?? flags["runtime-timeout"], DEFAULT_COMMAND_TIMEOUT_MS),
63
63
  });
64
- printReleaseOutput("release.notify", {
64
+ printReleaseOutput("release.notify", await releaseNotifyOutput(client, {
65
65
  profile: profile.name,
66
66
  releaseTarget,
67
67
  prompt: text,
@@ -70,7 +70,7 @@ export async function handleRelease({ domain, action, positional, flags }) {
70
70
  memoryMode: "off",
71
71
  wait,
72
72
  ...result,
73
- }, flags);
73
+ }, flags), flags);
74
74
  return true;
75
75
  }
76
76
  return false;
@@ -82,6 +82,33 @@ function releaseNotifyText(flags) {
82
82
  const value = typeof flags.text === "string" ? flags.text.trim() : "";
83
83
  return value || "发布";
84
84
  }
85
+ async function releaseNotifyOutput(client, data, flags) {
86
+ if (wantsVerbose(flags))
87
+ return data;
88
+ const decoded = await client.decodeEvents(data.events).catch(() => []);
89
+ const turnEvents = data.turnId ? decoded.filter((event) => event.turnId === data.turnId) : decoded;
90
+ const done = [...turnEvents].reverse().find((event) => event.decoded?.type === "done")?.decoded;
91
+ const finalOutput = turnEvents
92
+ .filter((event) => event.decoded?.type === "text_delta" && event.decoded.stream !== "thought")
93
+ .map((event) => event.decoded?.type === "text_delta" ? event.decoded.text : "")
94
+ .join("");
95
+ return {
96
+ profile: data.profile,
97
+ releaseTarget: data.releaseTarget,
98
+ promptPreview: data.promptPreview,
99
+ permissionMode: data.permissionMode,
100
+ memoryMode: data.memoryMode,
101
+ wait: data.wait,
102
+ requestId: data.requestId,
103
+ sessionId: data.sessionId,
104
+ turnId: data.turnId,
105
+ admission: data.admission,
106
+ dispatch: data.dispatch,
107
+ eventCount: data.events.length,
108
+ ...(done && typeof done === "object" && "status" in done ? { status: done.status } : {}),
109
+ ...(finalOutput ? { finalOutputPreview: compactText(finalOutput, 1200) } : {}),
110
+ };
111
+ }
85
112
  function tagsFromFlagValue(value) {
86
113
  if (typeof value !== "string")
87
114
  return [];
@@ -406,7 +406,7 @@ export async function handleSession({ domain, action, positional, flags }) {
406
406
  const visibleSessions = flags.all === true
407
407
  ? allSessions
408
408
  : (await Promise.all(allSessions.map(async (session) => ({ session, metadata: await client.decodeSessionMetadata(session) }))))
409
- .filter(({ session, metadata }) => session.status !== "closed" && metadata.hiddenFromWorkspace !== true)
409
+ .filter(({ session, metadata }) => session.status !== "closed" && metadata.hiddenFromWorkspace !== true && metadata.source !== "dogfood")
410
410
  .map(({ session }) => session);
411
411
  const { items: sessions, page } = selectWindow(visibleSessions, flags, 20);
412
412
  if (wantsJson(flags) || wantsVerbose(flags)) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves-cli",
3
- "version": "0.1.0-beta.310",
3
+ "version": "0.1.0-beta.312",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
@@ -17,6 +17,8 @@ export async function handleCreateSession(ws, config, command) {
17
17
  const metadata = await commandSessionMetadata(config, command.encryptedMetadata);
18
18
  const model = modelFromCommand(command.model ?? metadata?.model);
19
19
  const tags = normalizeSessionTags(metadata?.tags);
20
+ const hiddenFromWorkspace = metadata?.hiddenFromWorkspace === true;
21
+ const source = typeof metadata?.source === "string" && metadata.source.trim() ? metadata.source.trim() : undefined;
20
22
  const runtime = getRuntime(cwd);
21
23
  let handle = await runtime.ensureSession({
22
24
  sessionKey: command.sessionId,
@@ -44,6 +46,8 @@ export async function handleCreateSession(ws, config, command) {
44
46
  name: providerName ?? command.name,
45
47
  ...(model ? { model } : {}),
46
48
  ...(tags.length > 0 ? { tags } : {}),
49
+ ...(hiddenFromWorkspace ? { hiddenFromWorkspace: true } : {}),
50
+ ...(source ? { source } : {}),
47
51
  handle,
48
52
  };
49
53
  sessions.set(command.sessionId, state);
@@ -111,6 +115,15 @@ export async function handleImportHistoricalSession(ws, config, command) {
111
115
  handle,
112
116
  };
113
117
  const importedMetadata = await commandSessionMetadata(config, command.encryptedMetadata);
118
+ const importedTags = normalizeSessionTags(importedMetadata?.tags);
119
+ const importedHiddenFromWorkspace = importedMetadata?.hiddenFromWorkspace === true;
120
+ const importedSource = typeof importedMetadata?.source === "string" && importedMetadata.source.trim() ? importedMetadata.source.trim() : undefined;
121
+ if (importedTags.length > 0)
122
+ state.tags = importedTags;
123
+ if (importedHiddenFromWorkspace)
124
+ state.hiddenFromWorkspace = true;
125
+ if (importedSource)
126
+ state.source = importedSource;
114
127
  sessions.set(command.sessionId, state);
115
128
  const importedAt = new Date().toISOString();
116
129
  const importedMetadataBase = {
@@ -261,9 +274,12 @@ function historicalSessionPage(value) {
261
274
  export async function resumeSession(ws, config, sessionId, requestId, fallback, announceSuccess = true, syncDirection = "auto") {
262
275
  const existing = sessions.get(sessionId);
263
276
  if (existing) {
264
- const hydratedExisting = fallback?.tags && fallback.tags.length > 0
265
- ? { ...existing, tags: fallback.tags }
266
- : existing;
277
+ const hydratedExisting = {
278
+ ...existing,
279
+ ...(fallback?.tags && fallback.tags.length > 0 ? { tags: fallback.tags } : {}),
280
+ ...(fallback?.hiddenFromWorkspace === true ? { hiddenFromWorkspace: true } : {}),
281
+ ...(fallback?.source ? { source: fallback.source } : {}),
282
+ };
267
283
  if (hydratedExisting !== existing)
268
284
  sessions.set(sessionId, hydratedExisting);
269
285
  const refreshed = await refreshRuntimeSessionName(hydratedExisting);
@@ -324,6 +340,8 @@ export async function resumeSession(ws, config, sessionId, requestId, fallback,
324
340
  cwd,
325
341
  name,
326
342
  ...(fallback?.tags && fallback.tags.length > 0 ? { tags: fallback.tags } : {}),
343
+ ...(fallback?.hiddenFromWorkspace === true ? { hiddenFromWorkspace: true } : {}),
344
+ ...(fallback?.source ? { source: fallback.source } : {}),
327
345
  handle,
328
346
  });
329
347
  sessions.set(sessionId, state);
@@ -1,6 +1,6 @@
1
1
  import { type EncryptedEnvelope, type MachineCommand } from "../../../../packages/shared/dist/index.js";
2
2
  import type { DaemonConfig, SessionFallback, SessionMetadata, SessionState } from "../types.js";
3
- export declare function encryptedSessionMetadata(config: DaemonConfig, sessionId: string, state: Pick<SessionState, "agent" | "cwd" | "name" | "handle" | "model" | "tags">, extra: Record<string, unknown>): Promise<EncryptedEnvelope>;
3
+ export declare function encryptedSessionMetadata(config: DaemonConfig, sessionId: string, state: Pick<SessionState, "agent" | "cwd" | "name" | "handle" | "model" | "tags" | "hiddenFromWorkspace" | "source">, extra: Record<string, unknown>): Promise<EncryptedEnvelope>;
4
4
  export declare function commandSessionMetadata(config: DaemonConfig, encryptedMetadata?: EncryptedEnvelope): Promise<SessionMetadata | undefined>;
5
5
  export declare function sessionFallbackFromCommand(config: DaemonConfig, command: Extract<MachineCommand, {
6
6
  type: "machine:prompt" | "machine:resume" | "machine:rewind" | "machine:fork" | "machine:renameSession" | "machine:close";
@@ -6,6 +6,8 @@ export function encryptedSessionMetadata(config, sessionId, state, extra) {
6
6
  name: state.name,
7
7
  ...(state.model ? { model: state.model } : {}),
8
8
  ...(state.tags && state.tags.length > 0 ? { tags: state.tags, tagKeys: sessionTagKeys(state.tags) } : {}),
9
+ ...(state.hiddenFromWorkspace ? { hiddenFromWorkspace: true } : {}),
10
+ ...(state.source ? { source: state.source } : {}),
9
11
  runtime: {
10
12
  backend: state.handle.backend,
11
13
  runtimeSessionName: state.handle.runtimeSessionName,
@@ -41,6 +43,8 @@ export async function sessionFallbackFromCommand(config, command) {
41
43
  name: metadata.name ?? command.sessionId,
42
44
  ...(typeof metadata.model === "string" && metadata.model.trim() ? { model: metadata.model.trim() } : {}),
43
45
  ...(tags.length > 0 ? { tags } : {}),
46
+ ...(metadata.hiddenFromWorkspace === true ? { hiddenFromWorkspace: true } : {}),
47
+ ...(typeof metadata.source === "string" && metadata.source.trim() ? { source: metadata.source.trim() } : {}),
44
48
  emptyRuntimeSession: metadata.emptyRuntimeSession === true,
45
49
  historicalBackfill: historicalBackfillStateFromMetadata(metadata),
46
50
  runtime: metadata.runtime,
@@ -36,6 +36,14 @@ export async function handlePrompt(ws, config, command) {
36
36
  }
37
37
  if (fallback?.tags && fallback.tags.length > 0) {
38
38
  session = { ...session, tags: fallback.tags };
39
+ }
40
+ if (fallback?.hiddenFromWorkspace === true) {
41
+ session = { ...session, hiddenFromWorkspace: true };
42
+ }
43
+ if (fallback?.source) {
44
+ session = { ...session, source: fallback.source };
45
+ }
46
+ if (session !== sessions.get(command.sessionId)) {
39
47
  sessions.set(command.sessionId, session);
40
48
  }
41
49
  if (!claimSessionTurn(command.sessionId, command.requestId, command.turnId, "prompt")) {
@@ -33,6 +33,8 @@ export type SessionState = {
33
33
  name: string;
34
34
  model?: string;
35
35
  tags?: string[];
36
+ hiddenFromWorkspace?: boolean;
37
+ source?: string;
36
38
  handle: RuntimeHandle;
37
39
  historicalBackfill?: HistoricalBackfillState;
38
40
  };
@@ -58,6 +60,8 @@ export type SessionMetadata = {
58
60
  model?: string;
59
61
  tags?: string[];
60
62
  tagKeys?: string[];
63
+ hiddenFromWorkspace?: boolean;
64
+ source?: string;
61
65
  requestedRuntimeName?: string;
62
66
  permissionMode?: PermissionMode;
63
67
  importedFromRuntimeSessionId?: string;
@@ -90,6 +94,8 @@ export type SessionMetadata = {
90
94
  export type SessionFallback = Pick<SessionState, "agent" | "cwd" | "name"> & {
91
95
  model?: string;
92
96
  tags?: string[];
97
+ hiddenFromWorkspace?: boolean;
98
+ source?: string;
93
99
  emptyRuntimeSession?: boolean;
94
100
  historicalBackfill?: HistoricalBackfillState;
95
101
  runtime?: SessionMetadata["runtime"];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves-daemon",
3
- "version": "0.1.0-beta.310",
3
+ "version": "0.1.0-beta.312",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves",
3
- "version": "0.1.0-beta.310",
3
+ "version": "0.1.0-beta.312",
4
4
  "description": "Remote controller for local coding agents with hosted or self-hosted relay support.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,6 +5,7 @@ export type ParsedArgs = {
5
5
  export type ParseFlagsOptions = {
6
6
  valueFlags?: Iterable<string>;
7
7
  dashPrefixedValueFlags?: Iterable<string>;
8
+ repeatableValueFlags?: Iterable<string>;
8
9
  };
9
10
  export declare function parseFlags(argv: string[], options?: ParseFlagsOptions): ParsedArgs;
10
11
  //# sourceMappingURL=args.d.ts.map
@@ -1,6 +1,7 @@
1
1
  export function parseFlags(argv, options = {}) {
2
2
  const valueFlags = new Set(options.valueFlags ?? []);
3
3
  const dashPrefixedValueFlags = new Set(options.dashPrefixedValueFlags ?? []);
4
+ const repeatableValueFlags = new Set(options.repeatableValueFlags ?? []);
4
5
  const positional = [];
5
6
  const flags = {};
6
7
  for (let i = 0; i < argv.length; i++) {
@@ -19,17 +20,22 @@ export function parseFlags(argv, options = {}) {
19
20
  flags[key] = true;
20
21
  }
21
22
  else if (valueFlags.has(key) && (!next.startsWith("--") || dashPrefixedValueFlags.has(key))) {
22
- flags[key] = next;
23
+ flags[key] = appendFlagValue(flags[key], next, repeatableValueFlags.has(key));
23
24
  i++;
24
25
  }
25
26
  else if (next.startsWith("--")) {
26
27
  flags[key] = true;
27
28
  }
28
29
  else {
29
- flags[key] = next;
30
+ flags[key] = appendFlagValue(flags[key], next, repeatableValueFlags.has(key));
30
31
  i++;
31
32
  }
32
33
  }
33
34
  return { positional, flags };
34
35
  }
36
+ function appendFlagValue(previous, next, repeatable) {
37
+ if (!repeatable || previous === undefined || previous === true || previous === false)
38
+ return next;
39
+ return `${previous},${next}`;
40
+ }
35
41
  //# sourceMappingURL=args.js.map