@team-semicolon/semicolony-cli 4.18.75 → 4.18.77

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.
@@ -0,0 +1,77 @@
1
+ -- 182_worker_agent_installation_kind.sql
2
+ --
3
+ -- Distinguish human-operated worker devices from service-hive Agent runtime
4
+ -- installations without replacing the existing worker_devices compatibility
5
+ -- key or changing any existing Human row identity.
6
+
7
+ ALTER TABLE semicolony.worker_devices
8
+ ADD COLUMN IF NOT EXISTS installation_kind TEXT NOT NULL DEFAULT 'human-device';
9
+
10
+ ALTER TABLE semicolony.worker_devices
11
+ ADD CONSTRAINT worker_devices_installation_kind_check
12
+ CHECK (installation_kind IN ('human-device', 'agent-runtime')),
13
+ ADD CONSTRAINT worker_devices_agent_installation_identity_check
14
+ CHECK (
15
+ installation_kind <> 'agent-runtime'
16
+ OR (
17
+ device_id = 'hermes-principals@hermes'
18
+ AND installed_profile_id = 'semo-ops-worker-agent'
19
+ AND kb_access_mode = 'worker-gateway'
20
+ )
21
+ );
22
+
23
+ CREATE INDEX IF NOT EXISTS idx_worker_devices_installation_kind_profile
24
+ ON semicolony.worker_devices (installation_kind, installed_profile_id, status);
25
+
26
+ COMMENT ON COLUMN semicolony.worker_devices.installation_kind IS
27
+ 'human-device for a person-operated Worker; agent-runtime for a service-hive Agent installation.';
28
+
29
+ CREATE OR REPLACE VIEW semicolony.v_worker_environment_status AS
30
+ SELECT
31
+ wd.device_id,
32
+ wd.worker_id,
33
+ wd.display_name,
34
+ wd.hostname,
35
+ wd.os,
36
+ wd.workspace_root,
37
+ wd.installed_profile_id,
38
+ wd.installed_profile_version,
39
+ wd.tool_adapters_enabled,
40
+ wd.kb_access_mode,
41
+ wd.last_seen_at,
42
+ wd.last_doctor_status,
43
+ wd.last_doctor_summary,
44
+ wd.setup_progress,
45
+ wd.status,
46
+ wd.updated_at,
47
+ COALESCE(
48
+ jsonb_agg(
49
+ jsonb_build_object(
50
+ 'local_hive_id', lh.local_hive_id,
51
+ 'workload_type', lh.workload_type,
52
+ 'status', lh.status,
53
+ 'migration_target', lh.migration_target,
54
+ 'expiry_at', lh.expiry_at,
55
+ 'last_seen_at', lh.last_seen_at
56
+ )
57
+ ) FILTER (WHERE lh.local_hive_id IS NOT NULL),
58
+ '[]'::jsonb
59
+ ) AS local_hives,
60
+ wp.active_release_id,
61
+ active_release.version AS active_release_version,
62
+ wd.applied_release_id,
63
+ wd.applied_release_version,
64
+ wd.applied_at,
65
+ wd.installation_kind,
66
+ wd.device_id AS installation_id
67
+ FROM semicolony.worker_devices wd
68
+ LEFT JOIN semicolony.local_hives lh
69
+ ON lh.device_id = wd.device_id
70
+ LEFT JOIN semicolony.workspace_profiles wp
71
+ ON wp.profile_id = wd.installed_profile_id
72
+ LEFT JOIN semicolony.worker_profile_releases active_release
73
+ ON active_release.release_id = wp.active_release_id
74
+ GROUP BY wd.device_id, wp.active_release_id, active_release.version;
75
+
76
+ COMMENT ON COLUMN semicolony.v_worker_environment_status.installation_id IS
77
+ 'Runtime-neutral installation identity; backed by device_id for compatibility.';
@@ -0,0 +1,277 @@
1
+ -- 183_worker_agent_gateway_credentials.sql
2
+ --
3
+ -- Bind the fixed Hermes installation to one release-only host credential and
4
+ -- two KB-read-only actor credentials. Plaintext bearer material is never
5
+ -- stored in this schema; callers provide only a SHA-256 hash and short prefix.
6
+
7
+ CREATE TABLE semicolony.worker_agent_actor_bindings (
8
+ installation_id text NOT NULL
9
+ REFERENCES semicolony.worker_devices(device_id) ON DELETE CASCADE,
10
+ actor_id text NOT NULL CHECK (actor_id IN ('semi', 'colony')),
11
+ runtime_profile text NOT NULL,
12
+ profile_id text NOT NULL DEFAULT 'semo-ops-worker-agent',
13
+ runtime_kind text NOT NULL DEFAULT 'hermes-cli',
14
+ gateway_credential_id uuid NOT NULL UNIQUE
15
+ REFERENCES semicolony.gateway_credentials(id),
16
+ status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'revoked')),
17
+ issued_at timestamptz NOT NULL DEFAULT clock_timestamp(),
18
+ rotated_at timestamptz,
19
+ updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
20
+ PRIMARY KEY (installation_id, actor_id),
21
+ CONSTRAINT worker_agent_actor_fixed_installation_check CHECK (
22
+ installation_id = 'hermes-principals@hermes'
23
+ AND profile_id = 'semo-ops-worker-agent'
24
+ AND runtime_kind = 'hermes-cli'
25
+ ),
26
+ CONSTRAINT worker_agent_actor_runtime_profile_check CHECK (
27
+ (actor_id = 'semi' AND runtime_profile = 'semo-semi')
28
+ OR (actor_id = 'colony' AND runtime_profile = 'semo-colony')
29
+ )
30
+ );
31
+
32
+ CREATE INDEX idx_worker_agent_actor_bindings_credential
33
+ ON semicolony.worker_agent_actor_bindings (gateway_credential_id, status);
34
+
35
+ COMMENT ON TABLE semicolony.worker_agent_actor_bindings IS
36
+ 'Server-side binding for the fixed SEMO Ops Hermes actors; contains no bearer material.';
37
+
38
+ CREATE OR REPLACE FUNCTION semicolony.issue_worker_agent_host_credential(
39
+ p_team_tenant_id uuid,
40
+ p_token_hash text,
41
+ p_token_prefix text,
42
+ p_issued_by text,
43
+ p_expires_days integer
44
+ )
45
+ RETURNS uuid
46
+ LANGUAGE plpgsql
47
+ SECURITY DEFINER
48
+ SET search_path = pg_catalog
49
+ AS $function$
50
+ DECLARE
51
+ v_credential_id uuid;
52
+ v_previous_credential_id uuid;
53
+ v_worker_id text;
54
+ BEGIN
55
+ IF p_token_hash IS NULL OR p_token_hash !~ '^[0-9a-f]{64}$' THEN
56
+ RAISE EXCEPTION 'invalid worker-agent host credential token hash' USING ERRCODE = '22023';
57
+ END IF;
58
+ IF p_token_prefix IS NULL OR p_token_prefix !~ '^wck_hermes-host_[A-Za-z0-9_-]{6}$' THEN
59
+ RAISE EXCEPTION 'invalid worker-agent host credential token prefix' USING ERRCODE = '22023';
60
+ END IF;
61
+ IF p_issued_by IS NULL OR p_issued_by <> btrim(p_issued_by)
62
+ OR btrim(p_issued_by) = '' OR char_length(p_issued_by) > 128
63
+ OR p_issued_by ~ '[[:cntrl:]]' THEN
64
+ RAISE EXCEPTION 'invalid credential issuer' USING ERRCODE = '22023';
65
+ END IF;
66
+ IF p_expires_days IS NULL OR p_expires_days < 1 OR p_expires_days > 90 THEN
67
+ RAISE EXCEPTION 'worker-agent credential expiry must be between 1 and 90 days'
68
+ USING ERRCODE = '22023';
69
+ END IF;
70
+
71
+ PERFORM t.id
72
+ FROM public.tenants AS t
73
+ WHERE t.id = p_team_tenant_id
74
+ AND t.slug = 'team-semicolon'
75
+ AND t.tenant_type = 'team'
76
+ AND coalesce(t.metadata ->> 'status', 'active') = 'active';
77
+ IF NOT FOUND THEN
78
+ RAISE EXCEPTION 'active team-semicolon tenant not found' USING ERRCODE = 'P0002';
79
+ END IF;
80
+
81
+ SELECT wd.worker_id, wd.gateway_credential_id
82
+ INTO v_worker_id, v_previous_credential_id
83
+ FROM semicolony.worker_devices AS wd
84
+ WHERE wd.device_id = 'hermes-principals@hermes'
85
+ AND wd.installation_kind = 'agent-runtime'
86
+ AND wd.installed_profile_id = 'semo-ops-worker-agent'
87
+ AND wd.kb_access_mode = 'worker-gateway'
88
+ AND wd.status = 'active'
89
+ FOR UPDATE;
90
+ IF NOT FOUND THEN
91
+ RAISE EXCEPTION 'active Worker-agent installation not found' USING ERRCODE = 'P0002';
92
+ END IF;
93
+
94
+ INSERT INTO semicolony.gateway_credentials (
95
+ tenant_id, tenant_slug, token_hash, token_prefix, scopes, issued_by, expires_at, metadata
96
+ ) VALUES (
97
+ p_team_tenant_id,
98
+ 'team-semicolon',
99
+ p_token_hash,
100
+ p_token_prefix,
101
+ ARRAY['worker:release:read', 'worker:release:report']::text[],
102
+ p_issued_by,
103
+ clock_timestamp() + make_interval(days => p_expires_days),
104
+ jsonb_build_object(
105
+ 'credential_kind', 'worker-agent-host',
106
+ 'installation_id', 'hermes-principals@hermes',
107
+ 'profile_id', 'semo-ops-worker-agent',
108
+ 'runtime_kind', 'hermes-cli',
109
+ 'tenant_anchor', 'team-semicolon',
110
+ 'lineage_status', 'active'
111
+ )
112
+ )
113
+ RETURNING id INTO v_credential_id;
114
+
115
+ UPDATE semicolony.worker_devices
116
+ SET gateway_credential_id = v_credential_id
117
+ WHERE device_id = 'hermes-principals@hermes'
118
+ AND worker_id = v_worker_id;
119
+
120
+ IF v_previous_credential_id IS NOT NULL AND v_previous_credential_id <> v_credential_id THEN
121
+ UPDATE semicolony.gateway_credentials
122
+ SET status = 'revoked',
123
+ revoked_at = clock_timestamp(),
124
+ revoked_reason = 'worker-agent host credential rotated'
125
+ WHERE id = v_previous_credential_id
126
+ AND status = 'active';
127
+ END IF;
128
+
129
+ RETURN v_credential_id;
130
+ END;
131
+ $function$;
132
+
133
+ CREATE OR REPLACE FUNCTION semicolony.issue_worker_agent_actor_credential(
134
+ p_team_tenant_id uuid,
135
+ p_actor_id text,
136
+ p_token_hash text,
137
+ p_token_prefix text,
138
+ p_issued_by text,
139
+ p_expires_days integer
140
+ )
141
+ RETURNS uuid
142
+ LANGUAGE plpgsql
143
+ SECURITY DEFINER
144
+ SET search_path = pg_catalog
145
+ AS $function$
146
+ DECLARE
147
+ v_credential_id uuid;
148
+ v_previous_credential_id uuid;
149
+ v_runtime_profile text;
150
+ BEGIN
151
+ IF p_actor_id NOT IN ('semi', 'colony') THEN
152
+ RAISE EXCEPTION 'invalid Worker-agent actor' USING ERRCODE = '22023';
153
+ END IF;
154
+ v_runtime_profile := CASE p_actor_id WHEN 'semi' THEN 'semo-semi' ELSE 'semo-colony' END;
155
+ IF p_token_hash IS NULL OR p_token_hash !~ '^[0-9a-f]{64}$' THEN
156
+ RAISE EXCEPTION 'invalid worker-agent actor credential token hash' USING ERRCODE = '22023';
157
+ END IF;
158
+ IF p_token_prefix IS NULL
159
+ OR p_token_prefix !~ ('^wck_' || p_actor_id || '_[A-Za-z0-9_-]{6}$') THEN
160
+ RAISE EXCEPTION 'invalid worker-agent actor credential token prefix' USING ERRCODE = '22023';
161
+ END IF;
162
+ IF p_issued_by IS NULL OR p_issued_by <> btrim(p_issued_by)
163
+ OR btrim(p_issued_by) = '' OR char_length(p_issued_by) > 128
164
+ OR p_issued_by ~ '[[:cntrl:]]' THEN
165
+ RAISE EXCEPTION 'invalid credential issuer' USING ERRCODE = '22023';
166
+ END IF;
167
+ IF p_expires_days IS NULL OR p_expires_days < 1 OR p_expires_days > 90 THEN
168
+ RAISE EXCEPTION 'worker-agent credential expiry must be between 1 and 90 days'
169
+ USING ERRCODE = '22023';
170
+ END IF;
171
+
172
+ PERFORM t.id
173
+ FROM public.tenants AS t
174
+ WHERE t.id = p_team_tenant_id
175
+ AND t.slug = 'team-semicolon'
176
+ AND t.tenant_type = 'team'
177
+ AND coalesce(t.metadata ->> 'status', 'active') = 'active';
178
+ IF NOT FOUND THEN
179
+ RAISE EXCEPTION 'active team-semicolon tenant not found' USING ERRCODE = 'P0002';
180
+ END IF;
181
+
182
+ PERFORM wd.device_id
183
+ FROM semicolony.worker_devices AS wd
184
+ WHERE wd.device_id = 'hermes-principals@hermes'
185
+ AND wd.installation_kind = 'agent-runtime'
186
+ AND wd.installed_profile_id = 'semo-ops-worker-agent'
187
+ AND wd.kb_access_mode = 'worker-gateway'
188
+ AND wd.status = 'active'
189
+ FOR UPDATE;
190
+ IF NOT FOUND THEN
191
+ RAISE EXCEPTION 'active Worker-agent installation not found' USING ERRCODE = 'P0002';
192
+ END IF;
193
+
194
+ SELECT binding.gateway_credential_id
195
+ INTO v_previous_credential_id
196
+ FROM semicolony.worker_agent_actor_bindings AS binding
197
+ WHERE binding.installation_id = 'hermes-principals@hermes'
198
+ AND binding.actor_id = p_actor_id
199
+ FOR UPDATE;
200
+
201
+ INSERT INTO semicolony.gateway_credentials (
202
+ tenant_id, tenant_slug, token_hash, token_prefix, scopes, issued_by, expires_at, metadata
203
+ ) VALUES (
204
+ p_team_tenant_id,
205
+ 'team-semicolon',
206
+ p_token_hash,
207
+ p_token_prefix,
208
+ ARRAY['kb:read']::text[],
209
+ p_issued_by,
210
+ clock_timestamp() + make_interval(days => p_expires_days),
211
+ jsonb_build_object(
212
+ 'credential_kind', 'worker-agent-actor',
213
+ 'actor_id', p_actor_id,
214
+ 'installation_id', 'hermes-principals@hermes',
215
+ 'profile_id', 'semo-ops-worker-agent',
216
+ 'runtime_kind', 'hermes-cli',
217
+ 'runtime_profile', v_runtime_profile,
218
+ 'tenant_anchor', 'team-semicolon',
219
+ 'lineage_status', 'active'
220
+ )
221
+ )
222
+ RETURNING id INTO v_credential_id;
223
+
224
+ INSERT INTO semicolony.worker_agent_actor_bindings (
225
+ installation_id, actor_id, runtime_profile, profile_id, runtime_kind,
226
+ gateway_credential_id, status, issued_at, rotated_at, updated_at
227
+ ) VALUES (
228
+ 'hermes-principals@hermes', p_actor_id, v_runtime_profile,
229
+ 'semo-ops-worker-agent', 'hermes-cli', v_credential_id, 'active',
230
+ clock_timestamp(), CASE WHEN v_previous_credential_id IS NULL THEN NULL ELSE clock_timestamp() END,
231
+ clock_timestamp()
232
+ )
233
+ ON CONFLICT (installation_id, actor_id) DO UPDATE
234
+ SET runtime_profile = EXCLUDED.runtime_profile,
235
+ profile_id = EXCLUDED.profile_id,
236
+ runtime_kind = EXCLUDED.runtime_kind,
237
+ gateway_credential_id = EXCLUDED.gateway_credential_id,
238
+ status = 'active',
239
+ rotated_at = clock_timestamp(),
240
+ updated_at = clock_timestamp();
241
+
242
+ IF v_previous_credential_id IS NOT NULL AND v_previous_credential_id <> v_credential_id THEN
243
+ UPDATE semicolony.gateway_credentials
244
+ SET status = 'revoked',
245
+ revoked_at = clock_timestamp(),
246
+ revoked_reason = 'worker-agent actor credential rotated'
247
+ WHERE id = v_previous_credential_id
248
+ AND status = 'active';
249
+ END IF;
250
+
251
+ RETURN v_credential_id;
252
+ END;
253
+ $function$;
254
+
255
+ REVOKE ALL ON FUNCTION semicolony.issue_worker_agent_host_credential(
256
+ uuid, text, text, text, integer
257
+ ) FROM PUBLIC, sc_app, sc_provider;
258
+ GRANT EXECUTE ON FUNCTION semicolony.issue_worker_agent_host_credential(
259
+ uuid, text, text, text, integer
260
+ ) TO sc_app;
261
+
262
+ REVOKE ALL ON FUNCTION semicolony.issue_worker_agent_actor_credential(
263
+ uuid, text, text, text, text, integer
264
+ ) FROM PUBLIC, sc_app, sc_provider;
265
+ GRANT EXECUTE ON FUNCTION semicolony.issue_worker_agent_actor_credential(
266
+ uuid, text, text, text, text, integer
267
+ ) TO sc_app;
268
+
269
+ GRANT SELECT (
270
+ installation_id, actor_id, runtime_profile, profile_id, runtime_kind,
271
+ gateway_credential_id, status
272
+ ) ON TABLE semicolony.worker_agent_actor_bindings TO sc_app;
273
+
274
+ GRANT SELECT (
275
+ device_id, worker_id, gateway_credential_id, installation_kind,
276
+ installed_profile_id, status
277
+ ) ON TABLE semicolony.worker_devices TO sc_app;
@@ -21,6 +21,7 @@ SEMICOLONY CLI의 PostgreSQL 마이그레이션 파일들.
21
21
  | `174_worker_fleet_board_grants.sql` | 플릿 상황판 전용 롤 `sc_fleet_board` + 오퍼레이터 액션 감사 테이블 |
22
22
  | `178_feedback_capture_capability.sql` | `sc_app` 일반 DML 없이 KB+액션아이템을 원자 기록하는 `record_feedback` capability |
23
23
  | `179_worker_feedback_credential_scope.sql` | 워커 자격증명 `feedback:write` 허용 및 활성 디바이스 바인딩에 범위 백필 |
24
+ | `183_worker_agent_gateway_credentials.sql` | Hermes Agent host release credential과 Semi/Colony actor KB-read credential의 서버 바인딩 및 회전 |
24
25
 
25
26
  ## 007: Flat Skill Naming
26
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-semicolon/semicolony-cli",
3
- "version": "4.18.75",
3
+ "version": "4.18.77",
4
4
  "description": "SemiColony CLI - AI operations and agent orchestration installer",
5
5
  "main": "dist/bundle.js",
6
6
  "bin": {