@dinoxx/dinox-cli 2.3.0 → 2.4.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.
Files changed (47) hide show
  1. package/README.md +17 -3
  2. package/dist/cli.js +2 -0
  3. package/dist/commands/gateway/index.d.ts +4 -0
  4. package/dist/commands/gateway/index.js +300 -0
  5. package/dist/commands/schema/renderSkills.js +4 -0
  6. package/dist/commands/schema/spec.js +2 -0
  7. package/dist/config/paths.d.ts +1 -0
  8. package/dist/config/paths.js +1 -1
  9. package/dist/daemon/server.d.ts +1 -0
  10. package/dist/daemon/server.js +31 -0
  11. package/dist/gateway/adapters/acp.d.ts +42 -0
  12. package/dist/gateway/adapters/acp.js +285 -0
  13. package/dist/gateway/adapters/echo.d.ts +12 -0
  14. package/dist/gateway/adapters/echo.js +51 -0
  15. package/dist/gateway/adapters/index.d.ts +9 -0
  16. package/dist/gateway/adapters/index.js +30 -0
  17. package/dist/gateway/assistantProjection.d.ts +32 -0
  18. package/dist/gateway/assistantProjection.js +193 -0
  19. package/dist/gateway/config.d.ts +50 -0
  20. package/dist/gateway/config.js +253 -0
  21. package/dist/gateway/detect.d.ts +40 -0
  22. package/dist/gateway/detect.js +145 -0
  23. package/dist/gateway/deviceHost.d.ts +82 -0
  24. package/dist/gateway/deviceHost.js +530 -0
  25. package/dist/gateway/mcpServer.d.ts +4 -0
  26. package/dist/gateway/mcpServer.js +118 -0
  27. package/dist/gateway/protocol.d.ts +29 -0
  28. package/dist/gateway/protocol.js +127 -0
  29. package/dist/gateway/runExecutor.d.ts +67 -0
  30. package/dist/gateway/runExecutor.js +316 -0
  31. package/dist/gateway/service.d.ts +26 -0
  32. package/dist/gateway/service.js +82 -0
  33. package/dist/gateway/streamPublisher.d.ts +33 -0
  34. package/dist/gateway/streamPublisher.js +93 -0
  35. package/dist/gateway/threadStore.d.ts +157 -0
  36. package/dist/gateway/threadStore.js +430 -0
  37. package/dist/gateway/types.d.ts +117 -0
  38. package/dist/gateway/types.js +9 -0
  39. package/dist/vendor/electron/domain/properties.js +12 -1
  40. package/dist/vendor/electron/domain/saved-views.js +4 -1
  41. package/dist/vendor/electron/shared/external-types.d.ts +18 -0
  42. package/dist/vendor/electron/shared/note-properties.d.ts +16 -0
  43. package/dist/vendor/electron/shared/note-properties.js +45 -0
  44. package/dist/vendor/electron/shared/powersync-read-models.d.ts +7 -1
  45. package/dist/vendor/electron/shared/remote-device-agent.d.ts +373 -0
  46. package/dist/vendor/electron/shared/remote-device-agent.js +191 -0
  47. package/package.json +5 -2
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Dinox agent gateway: hosts external agent runtimes (Hermes, OpenClaw, pi,
3
+ * Claude Code, Codex, ... over ACP) as remote devices the Dinox app can drive.
4
+ *
5
+ * The app sees each gateway as one `dinox.remote-device.v2` device. The agent
6
+ * owns its conversation context (gateway-session mode): each turn sends only
7
+ * the new user message, while Dinox keeps a display/sync copy of the transcript.
8
+ */
9
+ export type GatewayKind = 'acp' | 'echo';
10
+ /** Local-only gateway definition (never synced: it may carry commands and env). */
11
+ export type GatewayDefinition = {
12
+ /** Stable id; also the `c_robot.id` the app binds agents to. */
13
+ id: string;
14
+ name: string;
15
+ kind: GatewayKind;
16
+ /** Detected agent type (`cursor`, `opencode`, ...); one gateway per type per machine. */
17
+ agentId?: string;
18
+ /** NATS device id the app addresses (`[a-z0-9][a-z0-9_-]{0,63}`). */
19
+ deviceId: string;
20
+ /** ACP: executable started over stdio, e.g. `hermes` with args `["acp"]`. */
21
+ command?: string;
22
+ args?: string[];
23
+ /** Working directory handed to the agent's sessions. */
24
+ cwd?: string;
25
+ /** Extra environment for the agent process (secrets stay on this machine). */
26
+ env?: Record<string, string>;
27
+ enabled: boolean;
28
+ /** Offer the read-only Dinox knowledge tools (MCP) to the agent; default true. */
29
+ dinoxTools?: boolean;
30
+ createdAt: string;
31
+ };
32
+ export type GatewayToolStatus = 'pending' | 'running' | 'completed' | 'failed';
33
+ /** Normalized agent output, independent of the adapter protocol. */
34
+ export type GatewayEvent = {
35
+ type: 'text-delta';
36
+ text: string;
37
+ } | {
38
+ type: 'reasoning-delta';
39
+ text: string;
40
+ } | {
41
+ type: 'tool';
42
+ toolCallId: string;
43
+ title?: string;
44
+ status?: GatewayToolStatus;
45
+ input?: unknown;
46
+ output?: unknown;
47
+ } | {
48
+ type: 'approval-request';
49
+ approvalId: string;
50
+ toolCallId: string;
51
+ toolName: string;
52
+ input?: unknown;
53
+ } | {
54
+ type: 'approval-resolved';
55
+ approvalId: string;
56
+ approved: boolean;
57
+ } | {
58
+ type: 'usage';
59
+ inputTokens?: number | null;
60
+ outputTokens?: number | null;
61
+ };
62
+ /**
63
+ * A choice the agent exposes per session (an ACP select config option), e.g.
64
+ * its model or mode. Values are the agent's own ids; `group` is set when the
65
+ * agent groups its values (OpenCode groups models by provider).
66
+ */
67
+ export type GatewayConfigOption = {
68
+ id: string;
69
+ name: string;
70
+ /** ACP semantic category: `model`, `mode`, `thought_level`, ... (UX only). */
71
+ category: string | null;
72
+ description: string | null;
73
+ currentValue: string;
74
+ values: Array<{
75
+ value: string;
76
+ name: string;
77
+ description: string | null;
78
+ group: string | null;
79
+ }>;
80
+ };
81
+ export type GatewayStopReason = 'end_turn' | 'cancelled' | 'max_tokens' | 'refusal' | 'error';
82
+ export type GatewayTurnInput = {
83
+ /** Agent session to continue; absent starts a new one. */
84
+ sessionId: string | null;
85
+ prompt: string;
86
+ /** Attachments the user sent with this turn (trusted, https). */
87
+ resources: Array<{
88
+ uri: string;
89
+ name: string;
90
+ mediaType?: string | null;
91
+ }>;
92
+ signal: AbortSignal;
93
+ /** Option id → value to apply to the session before the prompt; unknown entries are skipped. */
94
+ config?: Readonly<Record<string, string>>;
95
+ onEvent: (event: GatewayEvent) => void;
96
+ /** Resolves with the user's decision for an `approval-request` event. */
97
+ awaitApproval: (approvalId: string) => Promise<boolean>;
98
+ };
99
+ export type GatewayTurnResult = {
100
+ sessionId: string;
101
+ stopReason: GatewayStopReason;
102
+ error?: string;
103
+ };
104
+ export type GatewayAdapterInfo = {
105
+ kind: GatewayKind;
106
+ agentName: string | null;
107
+ agentVersion: string | null;
108
+ };
109
+ /** One agent runtime. Implementations keep their own process/connection state. */
110
+ export interface GatewayAdapter {
111
+ readonly kind: GatewayKind;
112
+ info(): GatewayAdapterInfo;
113
+ runTurn(input: GatewayTurnInput): Promise<GatewayTurnResult>;
114
+ /** The agent's session settings (models, modes); absent when it has none. */
115
+ describeConfig?(): Promise<GatewayConfigOption[]>;
116
+ dispose(): Promise<void>;
117
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Dinox agent gateway: hosts external agent runtimes (Hermes, OpenClaw, pi,
3
+ * Claude Code, Codex, ... over ACP) as remote devices the Dinox app can drive.
4
+ *
5
+ * The app sees each gateway as one `dinox.remote-device.v2` device. The agent
6
+ * owns its conversation context (gateway-session mode): each turn sends only
7
+ * the new user message, while Dinox keeps a display/sync copy of the transcript.
8
+ */
9
+ export {};
@@ -3,7 +3,7 @@
3
3
  * Inputs: typed PowerSync read-model and command payloads for property definitions and note values.
4
4
  * Outputs: catalog rows plus transactional definition, migration, and note-property mutations.
5
5
  */
6
- import { ACTIVE_PROPERTY_TYPES, applyNotePropertySlotOperation, buildPropertyDefConfigKey, coercePropertyValue, DEFAULT_STATUS_OPTION_INPUTS, findPropertyOptionById, findPropertyOptionByLabel, getDefaultPropertyDefConfig, isIssuedUniqueId, isPropertyKeyValid, isPropertyType, mergeNotePropertiesPatch, NOTE_PROPERTY_LIMITS, normalizeDatePropertyValue, normalizeFilePropertyValue, normalizeNumberPropertyValue, normalizePlacePropertyValue, normalizePropertyDefConfig, normalizePropertyOptionLabel, normalizePropertyOptions, normalizeUniqueIdPrefix, normalizeUrlPropertyValue, parsePropertyDefConfigEnvelope, parsePropertyDefOptionsEnvelope, parseStoredNoteProperties, parseUniqueIdPropertyConfig, propertyOptionLabelKey, PROPERTY_DEF_CONFIG_KEY_PREFIX, PROPERTY_OPTION_COLORS, PROPERTY_OPTION_LABEL_MAX_LENGTH, PROPERTY_OPTION_LIMITS, savedViewFilterSchema, serializePropertyDefConfig, serializePropertyDefOptions, serializeRawNoteProperties, serializeUniqueIdPropertyConfig, STATUS_PROPERTY_GROUPS, tryParseRawNoteProperties, } from "../shared/index.js";
6
+ import { ACTIVE_PROPERTY_TYPES, applyNotePropertySlotOperation, buildPropertyDefConfigKey, coercePropertyValue, DEFAULT_STATUS_OPTION_INPUTS, findPropertyOptionById, findPropertyOptionByLabel, getDefaultPropertyDefConfig, isIssuedUniqueId, isNumberPropertyValueAllowed, isPropertyKeyValid, isPropertyType, mergeNotePropertiesPatch, NOTE_PROPERTY_LIMITS, NUMBER_PROPERTY_PRESETS, normalizeDatePropertyValue, normalizeFilePropertyValue, normalizeNumberPropertyValue, normalizePlacePropertyValue, normalizePropertyDefConfig, normalizePropertyOptionLabel, normalizePropertyOptions, normalizeUniqueIdPrefix, normalizeUrlPropertyValue, parseNumberPropertyPreset, parsePropertyDefConfigEnvelope, parsePropertyDefOptionsEnvelope, parseStoredNoteProperties, parseUniqueIdPropertyConfig, propertyOptionLabelKey, PROPERTY_DEF_CONFIG_KEY_PREFIX, PROPERTY_OPTION_COLORS, PROPERTY_OPTION_LABEL_MAX_LENGTH, PROPERTY_OPTION_LIMITS, savedViewFilterSchema, serializeNumberPropertyPreset, serializePropertyDefConfig, serializePropertyDefOptions, serializeRawNoteProperties, serializeUniqueIdPropertyConfig, STATUS_PROPERTY_GROUPS, tryParseRawNoteProperties, } from "../shared/index.js";
7
7
  import { uuidv7 } from "uuidv7";
8
8
  import { z } from "zod";
9
9
  import { defineCommand, defineReadModel, } from "../rpc.js";
@@ -78,6 +78,7 @@ const PROPERTY_DEF_ENSURE_INPUT = z.object({
78
78
  type: z.enum(ACTIVE_PROPERTY_TYPES).nullable().optional(),
79
79
  optionItems: z.array(PROPERTY_OPTION_INPUT_SCHEMA).max(PROPERTY_OPTION_LIMITS.items).optional(),
80
80
  uniqueIdPrefix: z.string().max(64).nullable().optional(),
81
+ numberPreset: z.enum(NUMBER_PROPERTY_PRESETS).nullable().optional(),
81
82
  config: PROPERTY_DEF_CONFIG_INPUT_SCHEMA.optional(),
82
83
  description: z.string().max(2000).nullable().optional(),
83
84
  icon: z.string().max(120).nullable().optional(),
@@ -487,6 +488,11 @@ function normalizeExplicitConfig(config, type) {
487
488
  return normalized;
488
489
  }
489
490
  function optionsJsonForNewDefinition(type, input) {
491
+ if (type === "number") {
492
+ return input.numberPreset && input.numberPreset !== "number"
493
+ ? serializeNumberPropertyPreset(input.numberPreset)
494
+ : null;
495
+ }
490
496
  if (type === "unique_id") {
491
497
  return serializeUniqueIdPropertyConfig({
492
498
  prefix: normalizeUniqueIdPrefix(input.uniqueIdPrefix, normalizeOptionalString(input.name)),
@@ -1224,6 +1230,11 @@ function normalizeKnownPropertyValue(state, value, allowedArchivedIds) {
1224
1230
  const normalized = normalizeNumberPropertyValue(value, state.config);
1225
1231
  if (normalized === null)
1226
1232
  throw new Error("PROPERTY_NUMBER_INVALID");
1233
+ // Presets set by dinox-app (rating 0–5 integers, progress 0–1) bind here too.
1234
+ const preset = parseNumberPropertyPreset(state.row.options);
1235
+ if (!isNumberPropertyValueAllowed(normalized, preset)) {
1236
+ throw new Error("PROPERTY_NUMBER_INVALID");
1237
+ }
1227
1238
  return normalized;
1228
1239
  }
1229
1240
  case "date": {
@@ -862,10 +862,13 @@ function assertConditionSemantics(condition, semantic) {
862
862
  normalizeDatePropertyValue(value, semantic.dateMode ?? "date") === null) {
863
863
  throw new Error("SAVED_VIEW_INVALID_DATE_VALUE");
864
864
  }
865
+ // Only property-backed selects carry an option list. System list fields such as
866
+ // `sys.tags` match free values (tag paths), so they must not be checked here.
865
867
  if ((semantic.propertyType === "select" ||
866
868
  semantic.propertyType === "multi_select") &&
869
+ semantic.options !== undefined &&
867
870
  typeof value === "string" &&
868
- !(semantic.options ?? []).some((option) => option.id === value)) {
871
+ !semantic.options.some((option) => option.id === value)) {
869
872
  throw new Error("SAVED_VIEW_OPTION_NOT_FOUND");
870
873
  }
871
874
  }
@@ -1,2 +1,20 @@
1
1
  export type ChatMessageAnchorFields = Record<string, unknown>;
2
2
  export type SystemAgentKey = string;
3
+ export type ChatUIMessage = {
4
+ id: string;
5
+ role: 'system' | 'user' | 'assistant';
6
+ parts: Array<Record<string, unknown>>;
7
+ metadata?: Record<string, unknown>;
8
+ };
9
+ export interface ThreadRunCreateThreadRequest {
10
+ id: string;
11
+ name: string;
12
+ kind?: string | null;
13
+ userId?: string | null;
14
+ projectId?: string | null;
15
+ cmdId?: string | null;
16
+ metaData?: Record<string, unknown> | null;
17
+ config?: Record<string, unknown> | null;
18
+ }
19
+ export type ThreadRunEventStatus = 'queued' | 'running' | 'waiting_approval' | 'finalizing' | 'succeeded' | 'failed' | 'cancelled' | 'interrupted' | 'expired';
20
+ export type ThreadRunUsageSnapshot = Record<string, unknown>;
@@ -155,6 +155,20 @@ export type UrlPropertyDefConfig = {
155
155
  type: 'url';
156
156
  schemes?: Array<'http' | 'https'>;
157
157
  };
158
+ /**
159
+ * Display/input preset for `number` properties, shared with dinox-app. Stored in
160
+ * the definition's `options` column as `{ version: 1, number: { preset } }`
161
+ * (number defs have no select options, so the envelopes never collide).
162
+ * - progress: stored 0–1, shown as a percentage bar
163
+ * - rating: integer 0–5, shown as stars
164
+ */
165
+ export declare const NUMBER_PROPERTY_PRESETS: readonly ["number", "progress", "rating"];
166
+ export type NumberPropertyPreset = (typeof NUMBER_PROPERTY_PRESETS)[number];
167
+ export declare const RATING_PROPERTY_MAX = 5;
168
+ export declare function parseNumberPropertyPreset(raw: string | null | undefined): NumberPropertyPreset | undefined;
169
+ export declare function serializeNumberPropertyPreset(preset: NumberPropertyPreset): string;
170
+ /** Range rules every client enforces for a preset (mirrors dinox-app). */
171
+ export declare function isNumberPropertyValueAllowed(value: unknown, preset?: NumberPropertyPreset): value is number;
158
172
  export type PropertyDefConfig = DatePropertyDefConfig | NumberPropertyDefConfig | SelectPropertyDefConfig | MultiSelectPropertyDefConfig | UrlPropertyDefConfig;
159
173
  export declare function isPropertyDefSource(value: unknown): value is PropertyDefSource;
160
174
  /** Domain form of a synced property-definition catalog entry (camelCase). */
@@ -169,6 +183,8 @@ export interface PropertyDef {
169
183
  options?: PropertyOption[];
170
184
  /** Counter stored in `c_property_def.options` for `unique_id` (not the select envelope). */
171
185
  uniqueId?: UniqueIdPropertyConfig;
186
+ /** `number` only: progress / rating presentation (absent = plain number). */
187
+ numberPreset?: NumberPropertyPreset;
172
188
  /** Effective type configuration, including legacy defaults when no companion row exists. */
173
189
  config?: PropertyDefConfig;
174
190
  /** Explicit companion config exists but is malformed, unsupported, or type-mismatched. */
@@ -64,6 +64,47 @@ export function buildPropertyDefConfigKey(definitionId) {
64
64
  throw new Error('INVALID_PROPERTY_DEF_ID');
65
65
  return `${PROPERTY_DEF_CONFIG_KEY_PREFIX}${id}`;
66
66
  }
67
+ /**
68
+ * Display/input preset for `number` properties, shared with dinox-app. Stored in
69
+ * the definition's `options` column as `{ version: 1, number: { preset } }`
70
+ * (number defs have no select options, so the envelopes never collide).
71
+ * - progress: stored 0–1, shown as a percentage bar
72
+ * - rating: integer 0–5, shown as stars
73
+ */
74
+ export const NUMBER_PROPERTY_PRESETS = ['number', 'progress', 'rating'];
75
+ export const RATING_PROPERTY_MAX = 5;
76
+ export function parseNumberPropertyPreset(raw) {
77
+ if (!raw || !raw.trim())
78
+ return undefined;
79
+ try {
80
+ const config = JSON.parse(raw);
81
+ if (!isRecord(config) || config.version !== 1 || !isRecord(config.number))
82
+ return undefined;
83
+ const preset = config.number.preset;
84
+ return NUMBER_PROPERTY_PRESETS.includes(preset)
85
+ ? preset
86
+ : undefined;
87
+ }
88
+ catch {
89
+ return undefined;
90
+ }
91
+ }
92
+ export function serializeNumberPropertyPreset(preset) {
93
+ if (!NUMBER_PROPERTY_PRESETS.includes(preset))
94
+ throw new Error('INVALID_NUMBER_PROPERTY_PRESET');
95
+ return JSON.stringify({ version: 1, number: { preset } });
96
+ }
97
+ /** Range rules every client enforces for a preset (mirrors dinox-app). */
98
+ export function isNumberPropertyValueAllowed(value, preset) {
99
+ if (typeof value !== 'number' || !Number.isFinite(value))
100
+ return false;
101
+ if (preset === 'progress')
102
+ return value >= 0 && value <= 1;
103
+ if (preset === 'rating') {
104
+ return Number.isInteger(value) && value >= 0 && value <= RATING_PROPERTY_MAX;
105
+ }
106
+ return true;
107
+ }
67
108
  export function isPropertyDefSource(value) {
68
109
  return value === 'manual' || value === 'ai';
69
110
  }
@@ -936,6 +977,9 @@ export function serializePropertyDefConfig(config) {
936
977
  throw new Error('INVALID_PROPERTY_DEF_CONFIG');
937
978
  return JSON.stringify(normalized);
938
979
  }
980
+ function numberPresetField(preset) {
981
+ return preset && preset !== 'number' ? { numberPreset: preset } : {};
982
+ }
939
983
  /** Map a raw c_property_def row into the domain object (unknown type -> text). */
940
984
  export function mapPropertyDef(record) {
941
985
  const rawType = record.type ?? 'text';
@@ -960,6 +1004,7 @@ export function mapPropertyDef(record) {
960
1004
  },
961
1005
  }
962
1006
  : {}),
1007
+ ...(type === 'number' ? numberPresetField(parseNumberPropertyPreset(record.options)) : {}),
963
1008
  config: parsedConfig.status === 'empty'
964
1009
  ? getDefaultPropertyDefConfig(type, 'legacy')
965
1010
  : explicitConfigMatches
@@ -1,5 +1,5 @@
1
1
  import type { ChatMessageAnchorFields } from './external-types.js';
2
- import type { ActivePropertyType, NoteProperties, NotePropertiesPatch, NotePropertySlotOperation, PropertyDefConfig, PropertyDefSource, PropertyOptionInput } from './note-properties.js';
2
+ import type { ActivePropertyType, NoteProperties, NotePropertiesPatch, NotePropertySlotOperation, NumberPropertyPreset, PropertyDefConfig, PropertyDefSource, PropertyOptionInput } from './note-properties.js';
3
3
  import type { SystemAgentKey } from './external-types.js';
4
4
  export interface ResourceUserListInput {
5
5
  userId: string;
@@ -284,6 +284,10 @@ export interface NoteSoftDeleteInput {
284
284
  noteId: string;
285
285
  updatedAt: string;
286
286
  }
287
+ export interface NotePermanentDeleteInput {
288
+ /** Only notes already in the trash (`is_del = 1`) are removed; others are skipped. */
289
+ noteIds: string[];
290
+ }
287
291
  export interface NoteCommentsReplaceInput {
288
292
  noteId: string;
289
293
  comments: string;
@@ -1141,6 +1145,8 @@ export interface PropertyDefEnsureInput {
1141
1145
  optionItems?: PropertyOptionInput[];
1142
1146
  /** Prefix for a new `unique_id` counter. Empty falls back to the property name. */
1143
1147
  uniqueIdPrefix?: string | null;
1148
+ /** New `number` definitions only: progress / rating presentation. */
1149
+ numberPreset?: NumberPropertyPreset | null;
1144
1150
  config?: PropertyDefConfig;
1145
1151
  description?: string | null;
1146
1152
  icon?: string | null;