@uns-kit/cli 0.0.24 → 0.0.27

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/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Command line scaffolding tool for the UNS toolkit. It bootstraps a new project with `@uns-kit/core` preconfigured and ready to extend with additional plugins.
4
4
 
5
+ ## Prerequisites
6
+
7
+ - Node.js 18 or newer (ES2022 runtime features)
8
+ - A package manager such as `pnpm`, `npm`, or `yarn`
9
+ - `git` available on PATH (required for `configure-devops`, used to initialize new projects automatically)
10
+
5
11
  ## Usage
6
12
 
7
13
  ```bash
@@ -21,6 +27,8 @@ pnpm install
21
27
  pnpm run dev
22
28
  ```
23
29
 
30
+ When `git` is available on your PATH the scaffold also initializes a fresh repository so you can commit immediately.
31
+
24
32
  ## Commands
25
33
 
26
34
  - `uns-kit create <name>` – create a new UNS project in the specified directory.
package/dist/index.js CHANGED
@@ -155,11 +155,61 @@ async function createProject(projectName) {
155
155
  await patchPackageJson(targetDir, pkgName);
156
156
  await patchConfigJson(targetDir, pkgName);
157
157
  await replacePlaceholders(targetDir, pkgName);
158
+ const initializedGit = await initGitRepository(targetDir);
158
159
  console.log(`\nCreated ${pkgName} in ${path.relative(process.cwd(), targetDir)}`);
159
160
  console.log("Next steps:");
160
161
  console.log(` cd ${projectName}`);
161
162
  console.log(" pnpm install");
162
163
  console.log(" pnpm run dev");
164
+ if (initializedGit) {
165
+ console.log(" git status # verify the new repository");
166
+ }
167
+ }
168
+ async function initGitRepository(targetDir) {
169
+ try {
170
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
171
+ cwd: targetDir,
172
+ encoding: "utf8",
173
+ });
174
+ if (stdout.trim() === "true") {
175
+ return false;
176
+ }
177
+ }
178
+ catch (error) {
179
+ if (isGitCommandNotFoundError(error)) {
180
+ console.log("Git not found on PATH. Skipping repository initialization.");
181
+ return false;
182
+ }
183
+ const execError = error;
184
+ const stderr = typeof execError.stderr === "string" ? execError.stderr : "";
185
+ if (stderr && !stderr.includes("not a git repository")) {
186
+ console.warn("Unable to determine git repository status:", stderr.trim());
187
+ return false;
188
+ }
189
+ }
190
+ try {
191
+ await execFileAsync("git", ["init"], {
192
+ cwd: targetDir,
193
+ encoding: "utf8",
194
+ });
195
+ console.log("Initialized empty Git repository.");
196
+ return true;
197
+ }
198
+ catch (error) {
199
+ if (isGitCommandNotFoundError(error)) {
200
+ console.log("Git not found on PATH. Skipping repository initialization.");
201
+ return false;
202
+ }
203
+ const execError = error;
204
+ const stderr = typeof execError.stderr === "string" ? execError.stderr.trim() : "";
205
+ if (stderr) {
206
+ console.warn(`Failed to initialize git repository: ${stderr}`);
207
+ }
208
+ else {
209
+ console.warn(`Failed to initialize git repository: ${error.message}`);
210
+ }
211
+ return false;
212
+ }
163
213
  }
164
214
  async function configureDevops(targetPath) {
165
215
  const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
@@ -191,7 +241,7 @@ async function configureDevops(targetPath) {
191
241
  const remoteUrl = await getGitRemoteUrl(targetDir, "origin");
192
242
  const remoteInfo = remoteUrl ? parseAzureRemote(remoteUrl) : undefined;
193
243
  const repositoryName = inferRepositoryNameFromPackage(pkg.name) || inferRepositoryNameFromPackage(path.basename(targetDir));
194
- const defaultOrganization = config.devops?.organization?.trim() || remoteInfo?.organization || "sijit";
244
+ const defaultOrganization = config.devops?.organization?.trim() || remoteInfo?.organization || "example-org";
195
245
  const organization = await promptWithDefault(defaultOrganization ? `Azure DevOps organization [${defaultOrganization}]: ` : "Azure DevOps organization: ", defaultOrganization, "Azure DevOps organization is required.");
196
246
  const defaultProject = config.devops?.project?.trim() || remoteInfo?.project || "";
197
247
  const project = await promptWithDefault(defaultProject ? `Azure DevOps project [${defaultProject}]: ` : "Azure DevOps project: ", defaultProject, "Azure DevOps project is required.");
@@ -887,8 +937,12 @@ async function patchPackageJson(targetDir, packageName) {
887
937
  const raw = await readFile(pkgFile, "utf8");
888
938
  const pkg = JSON.parse(raw);
889
939
  pkg.name = packageName;
890
- if (pkg.dependencies && pkg.dependencies["@uns-kit/core"]) {
891
- pkg.dependencies["@uns-kit/core"] = `^${coreVersion}`;
940
+ const dependencies = (pkg.dependencies ??= {});
941
+ if (dependencies["@uns-kit/core"]) {
942
+ dependencies["@uns-kit/core"] = resolveUnsPackageSpecifier("@uns-kit/core", "../../uns-core/package.json");
943
+ }
944
+ else {
945
+ dependencies["@uns-kit/core"] = `^${coreVersion}`;
892
946
  }
893
947
  await writeFile(pkgFile, JSON.stringify(pkg, null, 2) + "\n", "utf8");
894
948
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uns-kit/cli",
3
- "version": "0.0.24",
3
+ "version": "0.0.27",
4
4
  "description": "Command line scaffolding tool for UNS applications",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "azure-devops-node-api": "^15.1.1",
29
- "@uns-kit/core": "0.0.25"
29
+ "@uns-kit/core": "0.0.28"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsc -p tsconfig.build.json",
@@ -32,7 +32,7 @@ const apiInput = await unsProxyProcess.createApiProxy("templateUnsApiInput", api
32
32
  /**
33
33
  * Register an API endpoint and event handler
34
34
  */
35
- apiInput.get("sij/", "summary-1",{
35
+ apiInput.get("example/", "summary-1",{
36
36
  tags: ["Tag1"],
37
37
  apiDescription: "Test API endpoint 1",
38
38
  queryParams: [
@@ -41,7 +41,7 @@ apiInput.get("sij/", "summary-1",{
41
41
  ]
42
42
  });
43
43
 
44
- apiInput.get("sij/", "summary-2",{
44
+ apiInput.get("example/", "summary-2",{
45
45
  tags: ["Tag2"],
46
46
  apiDescription: "Test API endpoint 2",
47
47
  queryParams: [
@@ -33,7 +33,7 @@ cronInput.event.on("cronEvent", async (event: UnsEvents["cronEvent"]) => {
33
33
  const time = UnsPacket.formatToISO8601(new Date());
34
34
  const numberValue: number = 42;
35
35
  const message: IUnsMessage = { data: { time, value: numberValue, uom: PhysicalMeasurements.MiliVolt } };
36
- const topic: UnsTopics = "sij/";
36
+ const topic: UnsTopics = "example/";
37
37
  const tags: UnsTags[] = [];
38
38
  const packet = await UnsPacket.unsPacketFromUnsMessage(message);
39
39
  mqttOutput.publishMqttMessage({ topic, attribute: "data-number", packet, description: "Number value", tags });
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "devops": {
21
21
  "provider": "azure-devops",
22
- "organization": "sijit",
23
- "project": "industry40"
22
+ "organization": "example-org",
23
+ "project": "example-project"
24
24
  }
25
25
  }
@@ -35,7 +35,7 @@ mqttInput.event.on("input", async (event) => {
35
35
  const values = event.message.split(",");
36
36
  const numberValue: number = parseFloat(values[0]);
37
37
  const message: IUnsMessage = { data: { dataGroup:"electricity", time, value: numberValue, uom: PhysicalMeasurements.MiliVolt } };
38
- const topic: UnsTopics = "sij/";
38
+ const topic: UnsTopics = "example/";
39
39
  const tags: UnsTags[] = [];
40
40
  const packet = await UnsPacket.unsPacketFromUnsMessage(message);
41
41
  mqttOutput.publishMqttMessage({ topic, attribute: "data-number", packet, description: "Number value", tags });
@@ -19,7 +19,7 @@ const config = await ConfigFile.loadConfig();
19
19
  */
20
20
  const unsProxyProcess = new UnsProxyProcess(config.infra.host!, {processName: config.uns.processName!});
21
21
  const mqttInput = await unsProxyProcess.createUnsMqttProxy((config.input?.host)!, "templateUnsRttInput", config.uns.instanceMode!, config.uns.handover!, {
22
- mqttSubToTopics: ["iba/zrm"],
22
+ mqttSubToTopics: ["integration/raw-table"],
23
23
  publishThrottlingDelay:0,
24
24
  subscribeThrottlingDelay:0
25
25
  });
@@ -29,23 +29,23 @@ const mqttOutput = await unsProxyProcess.createUnsMqttProxy((config.output?.host
29
29
  });
30
30
 
31
31
  /**
32
- * The input worker connects to the IBA broker and listens for incoming messages.
32
+ * The input worker connects to the upstream broker and listens for incoming messages.
33
33
  * It processes the messages and transforms them into a table-type IUnsMessage.
34
34
  * The resulting message is published to the output broker.
35
35
  */
36
36
  mqttInput.event.on("input", async (event) => {
37
37
  try {
38
- if (event.topic === "iba/zrm") {
38
+ if (event.topic === "integration/raw-table") {
39
39
  const jsonObject = JSON.parse(event.message);
40
40
  const timestamp = jsonObject.Timestamp;
41
41
  delete(jsonObject.Timestamp);
42
42
 
43
43
  const time = UnsPacket.formatToISO8601(new Date(timestamp));
44
- const message: IUnsMessage = { table: {dataGroup:"iba_test", values:jsonObject, time}};
45
- const topic: UnsTopics = "sij/acroni/hv/";
44
+ const message: IUnsMessage = { table: {dataGroup:"demo_table", values:jsonObject, time}};
45
+ const topic: UnsTopics = "example/factory-a/line-1/";
46
46
  const tags: UnsTags[] = [];
47
47
  const packet = await UnsPacket.unsPacketFromUnsMessage(message);
48
- mqttOutput.publishMqttMessage({ topic, attribute: "zrm", packet, description: "Table", tags });
48
+ mqttOutput.publishMqttMessage({ topic, attribute: "table-sample", packet, description: "Table", tags });
49
49
  }
50
50
  } catch (error) {
51
51
  const reason = error instanceof Error ? error : new Error(String(error));
@@ -31,28 +31,28 @@ Readiness: Examples call the gateway’s `Ready()` RPC to wait for the requested
31
31
  ## Scripts
32
32
 
33
33
  - `data_transformer.py`
34
- - Subscribes to input topics (default `raw/#`) and publishes UNS data packets (`sij/` + `data-number`).
34
+ - Subscribes to input topics (default `raw/#`) and publishes UNS data packets (`example/` + `data-number`).
35
35
  - Examples:
36
- - Unix/macOS: `python python/examples/data_transformer.py --addr unix:/tmp/uns-gateway.sock --auto --in raw/# --out-topic sij/ --attribute data-number --uom mV --group electricity`
37
- - Windows: `python python/examples/data_transformer.py --addr 127.0.0.1:50051 --auto --in raw/# --out-topic sij/ --attribute data-number --uom mV --group electricity`
36
+ - Unix/macOS: `python python/examples/data_transformer.py --addr unix:/tmp/uns-gateway.sock --auto --in raw/# --out-topic example/ --attribute data-number --uom mV --group electricity`
37
+ - Windows: `python python/examples/data_transformer.py --addr 127.0.0.1:50051 --auto --in raw/# --out-topic example/ --attribute data-number --uom mV --group electricity`
38
38
 
39
39
  - `table_transformer.py`
40
- - Subscribes to `iba/zrm` JSON and publishes a UNS table packet (`sij/acroni/hv/` + `zrm`).
40
+ - Subscribes to `integration/raw-table` JSON and publishes a UNS table packet (`example/factory-a/line-1/` + `table-sample`).
41
41
  - Examples:
42
- - Unix/macOS: `python python/examples/table_transformer.py --addr unix:/tmp/uns-gateway.sock --auto --in-topic iba/zrm --out-topic sij/acroni/hv/ --attribute zrm`
43
- - Windows: `python python/examples/table_transformer.py --addr 127.0.0.1:50051 --auto --in-topic iba/zrm --out-topic sij/acroni/hv/ --attribute zrm`
42
+ - Unix/macOS: `python python/examples/table_transformer.py --addr unix:/tmp/uns-gateway.sock --auto --in-topic integration/raw-table --out-topic example/factory-a/line-1/ --attribute table-sample`
43
+ - Windows: `python python/examples/table_transformer.py --addr 127.0.0.1:50051 --auto --in-topic integration/raw-table --out-topic example/factory-a/line-1/ --attribute table-sample`
44
44
 
45
45
  - `data_publisher_loop.py`
46
46
  - Cron-like loop that publishes a data value periodically (default 1000 ms).
47
47
  - Examples:
48
- - Unix/macOS: `python python/examples/data_publisher_loop.py --addr unix:/tmp/uns-gateway.sock --auto --out-topic sij/ --attribute data-number --uom mV --period-ms 1000`
49
- - Windows: `python python/examples/data_publisher_loop.py --addr 127.0.0.1:50051 --auto --out-topic sij/ --attribute data-number --uom mV --period-ms 1000`
48
+ - Unix/macOS: `python python/examples/data_publisher_loop.py --addr unix:/tmp/uns-gateway.sock --auto --out-topic example/ --attribute data-number --uom mV --period-ms 1000`
49
+ - Windows: `python python/examples/data_publisher_loop.py --addr 127.0.0.1:50051 --auto --out-topic example/ --attribute data-number --uom mV --period-ms 1000`
50
50
 
51
51
  - `data_publish_once.py`
52
52
  - Sends a single UNS data packet.
53
53
  - Examples:
54
- - Unix/macOS: `python python/examples/data_publish_once.py --addr unix:/tmp/uns-gateway.sock --auto --out-topic sij/ --attribute data-number --value 42 --uom mV --group electricity`
55
- - Windows: `python python/examples/data_publish_once.py --addr 127.0.0.1:50051 --auto --out-topic sij/ --attribute data-number --value 42 --uom mV --group electricity`
54
+ - Unix/macOS: `python python/examples/data_publish_once.py --addr unix:/tmp/uns-gateway.sock --auto --out-topic example/ --attribute data-number --value 42 --uom mV --group electricity`
55
+ - Windows: `python python/examples/data_publish_once.py --addr 127.0.0.1:50051 --auto --out-topic example/ --attribute data-number --value 42 --uom mV --group electricity`
56
56
 
57
57
  - `data_subscribe.py`
58
58
  - Subscribes to topics via the gateway and prints messages.
@@ -61,11 +61,11 @@ Readiness: Examples call the gateway’s `Ready()` RPC to wait for the requested
61
61
  - Windows: `python python/examples/data_subscribe.py --addr 127.0.0.1:50051 --auto raw/# some/other/topic`
62
62
 
63
63
  - `api_register_and_serve.py`
64
- - Registers `/api/sij/summary-1` and `/api/sij/summary-2` endpoints and serves requests over a bidirectional stream.
64
+ - Registers `/api/example/summary-1` and `/api/example/summary-2` endpoints and serves requests over a bidirectional stream.
65
65
  - Examples:
66
66
  - Unix/macOS: `python python/examples/api_register_and_serve.py --addr unix:/tmp/uns-gateway.sock --auto`
67
67
  - Windows: `python python/examples/api_register_and_serve.py --addr 127.0.0.1:50051 --auto`
68
- - Then call: `http://<gateway-host>:<gateway-port>/api/sij/summary-1?filter=foo&limit=10`
68
+ - Then call: `http://<gateway-host>:<gateway-port>/api/example/summary-1?filter=foo&limit=10`
69
69
 
70
70
  - `api_handler.py`
71
71
  - Another API handler demo that returns structured JSON responses. Similar to `api_register_and_serve.py`.
@@ -22,7 +22,7 @@ def make_channel(addr: str) -> grpc.Channel:
22
22
 
23
23
 
24
24
  def register_endpoints(stub: gw.UnsGatewayStub) -> None:
25
- # Mirror TS api-example.ts for two GET endpoints under /api/sij/summary-1 and -2
25
+ # Mirror TS api-example.ts for two GET endpoints under /api/example/summary-1 and -2
26
26
  qp = [
27
27
  pb2.ApiQueryParam(name="filter", type="string", required=True, description="Filter za podatke"),
28
28
  pb2.ApiQueryParam(name="limit", type="number", required=False, description="Koliko podatkov želiš"),
@@ -30,7 +30,7 @@ def register_endpoints(stub: gw.UnsGatewayStub) -> None:
30
30
 
31
31
  for idx, tag in [(1, "Tag1"), (2, "Tag2")]:
32
32
  req = pb2.RegisterApiGetRequest(
33
- topic="sij/",
33
+ topic="example/",
34
34
  attribute=f"summary-{idx}",
35
35
  api_description=f"Test API endpoint {idx}",
36
36
  tags=[tag],
@@ -54,7 +54,7 @@ def serve_requests(stub: gw.UnsGatewayStub, echo: bool = False) -> None:
54
54
  stream = stub.ApiEventStream(req_iter())
55
55
  try:
56
56
  for ev in stream:
57
- # ev.path e.g. /sij/summary-1; ev.query is a map<string,string>
57
+ # ev.path e.g. /example/summary-1; ev.query is a map<string,string>
58
58
  path = ev.path
59
59
  query: Dict[str, str] = dict(ev.query)
60
60
  filt = query.get("filter", "")
@@ -102,7 +102,7 @@ def register(stub: gw.UnsGatewayStub) -> None:
102
102
  ]
103
103
  for idx, tag in [(1, "Tag1"), (2, "Tag2")]:
104
104
  res = stub.RegisterApiGet(pb2.RegisterApiGetRequest(
105
- topic="sij/",
105
+ topic="example/",
106
106
  attribute=f"summary-{idx}",
107
107
  api_description=f"Test API endpoint {idx}",
108
108
  tags=[tag],
@@ -126,7 +126,7 @@ def main():
126
126
  parser = argparse.ArgumentParser(description="Publish one UNS data packet")
127
127
  parser.add_argument("--addr", default=None)
128
128
  parser.add_argument("--auto", action="store_true")
129
- parser.add_argument("--out-topic", default="sij/")
129
+ parser.add_argument("--out-topic", default="example/")
130
130
  parser.add_argument("--attribute", default="data-number")
131
131
  parser.add_argument("--value", type=float, default=42.0)
132
132
  parser.add_argument("--uom", default="mV")
@@ -129,7 +129,7 @@ def main():
129
129
  parser = argparse.ArgumentParser(description="Publish UNS data packets periodically (cron-like)")
130
130
  parser.add_argument("--addr", default=None)
131
131
  parser.add_argument("--auto", action="store_true")
132
- parser.add_argument("--out-topic", default="sij/")
132
+ parser.add_argument("--out-topic", default="example/")
133
133
  parser.add_argument("--attribute", default="data-number")
134
134
  parser.add_argument("--uom", default="mV")
135
135
  parser.add_argument("--period-ms", type=int, default=1000)
@@ -134,7 +134,7 @@ def main():
134
134
  parser.add_argument("--addr", default=None, help="Gateway address (unix:/path.sock or host:port); defaults to unique per-script")
135
135
  parser.add_argument("--auto", action="store_true", help="auto-start gateway if not running")
136
136
  parser.add_argument("--in", dest="in_topics", nargs="+", default=["raw/#"], help="input topics to subscribe")
137
- parser.add_argument("--out-topic", default="sij/", help="output UNS base topic")
137
+ parser.add_argument("--out-topic", default="example/", help="output UNS base topic")
138
138
  parser.add_argument("--attribute", default="data-number", help="output attribute")
139
139
  parser.add_argument("--uom", default="mV", help="unit of measure")
140
140
  parser.add_argument("--group", default="electricity", help="dataGroup")
@@ -140,7 +140,7 @@ def transform(addr: str | None, in_topic: str, out_topic: str, out_attribute: st
140
140
  req = pb2.PublishRequest(
141
141
  topic=out_topic,
142
142
  attribute=out_attribute,
143
- table=pb2.Table(time=time_iso, values=tv_list, data_group="iba_test"),
143
+ table=pb2.Table(time=time_iso, values=tv_list, data_group="demo_table"),
144
144
  )
145
145
  res = stub.Publish(req)
146
146
  if not res.ok:
@@ -148,12 +148,12 @@ def transform(addr: str | None, in_topic: str, out_topic: str, out_attribute: st
148
148
 
149
149
 
150
150
  def main():
151
- parser = argparse.ArgumentParser(description="Subscribe to IBA topic and publish UNS table packets")
151
+ parser = argparse.ArgumentParser(description="Subscribe to an upstream topic and publish UNS table packets")
152
152
  parser.add_argument("--addr", default=None, help="Gateway address (unix:/path.sock or host:port); defaults to unique per-script")
153
153
  parser.add_argument("--auto", action="store_true", help="auto-start gateway if not running")
154
- parser.add_argument("--in-topic", default="iba/zrm")
155
- parser.add_argument("--out-topic", default="sij/acroni/hv/")
156
- parser.add_argument("--attribute", default="zrm")
154
+ parser.add_argument("--in-topic", default="integration/raw-table")
155
+ parser.add_argument("--out-topic", default="example/factory-a/line-1/")
156
+ parser.add_argument("--attribute", default="table-sample")
157
157
  args = parser.parse_args()
158
158
 
159
159
  transform(args.addr, args.in_topic, args.out_topic, args.attribute, args.auto)
@@ -19,16 +19,16 @@ const config = await ConfigFile.loadConfig();
19
19
  const unsProxyProcess = new UnsProxyProcess(config.infra.host!, {processName:config.uns.processName}) as UnsProxyProcessWithTemporal;
20
20
  const temporalTopic: ITemporalTopic = {
21
21
  attribute: "temporal-data",
22
- topic: "sij/",
22
+ topic: "example/",
23
23
  attributeType: UnsAttributeType.Data,
24
24
  attributeNeedsPersistence: true,
25
25
  dataGroup: "temporal",
26
26
  description: "Temporal data example",
27
27
  tags: ["temporal"],
28
28
  }
29
- const temporalProxy = await unsProxyProcess.createTemporalProxy("templateUnsTemporal", "temporal-1.sij.digital:7233", "hv");
29
+ const temporalProxy = await unsProxyProcess.createTemporalProxy("templateUnsTemporal", "temporal.example.local:7233", "line-namespace");
30
30
  await temporalProxy.initializeTemporalProxy(temporalTopic);
31
31
 
32
32
  // Start temporal workflow
33
- const result = await temporalProxy.startWorkflow("TransformHvSclData", {'coil_id': "42"}, "ETL_HV_SCL_TASK_QUEUE");
34
- logger.info(`Workflow result: ${JSON.stringify(result)}`);
33
+ const result = await temporalProxy.startWorkflow("TransformLineData", {'coil_id': "42"}, "ETL_LINE_TASK_QUEUE");
34
+ logger.info(`Workflow result: ${JSON.stringify(result)}`);