@pippit-dev/cli 1.0.18 → 1.0.20

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.
@@ -13,8 +13,9 @@ import (
13
13
  var decimalIDPattern = regexp.MustCompile(`^[1-9][0-9]*$`)
14
14
 
15
15
  type ApplyOptions struct {
16
- ProjectID string
17
- Request ApplyRequest
16
+ ProjectID string
17
+ Request ApplyRequest
18
+ AllowNonAcknowledgedResults bool
18
19
  }
19
20
 
20
21
  type ApplyRequest struct {
@@ -47,6 +48,8 @@ type ApplyResult struct {
47
48
  BatchID string `json:"batch_id"`
48
49
  Results []PatchTransactionResult `json:"results"`
49
50
  LogID string `json:"log_id,omitempty"`
51
+
52
+ rawResults []json.RawMessage
50
53
  }
51
54
 
52
55
  type PatchTransactionResult struct {
@@ -58,7 +61,29 @@ type PatchTransactionResult struct {
58
61
  }
59
62
 
60
63
  type applyData struct {
61
- Results []PatchTransactionResult `json:"results"`
64
+ Results []json.RawMessage `json:"results"`
65
+ }
66
+
67
+ type decodedPatchTransactionResult struct {
68
+ result PatchTransactionResult
69
+ raw json.RawMessage
70
+ }
71
+
72
+ func (result ApplyResult) MarshalJSON() ([]byte, error) {
73
+ type applyResultJSON struct {
74
+ BatchID string `json:"batch_id"`
75
+ Results any `json:"results"`
76
+ LogID string `json:"log_id,omitempty"`
77
+ }
78
+ results := any(result.Results)
79
+ if len(result.rawResults) > 0 {
80
+ results = result.rawResults
81
+ }
82
+ return json.Marshal(applyResultJSON{
83
+ BatchID: result.BatchID,
84
+ Results: results,
85
+ LogID: result.LogID,
86
+ })
62
87
  }
63
88
 
64
89
  func Apply(ctx context.Context, opts ApplyOptions, runner *common.Runner) (*ApplyResult, error) {
@@ -90,18 +115,33 @@ func Apply(ctx context.Context, opts ApplyOptions, runner *common.Runner) (*Appl
90
115
  envelope.LogID,
91
116
  )
92
117
  }
93
- ordered, err := validateApplyResults(request.Transactions, data.Results)
118
+ decodedResults, err := decodeApplyResults(data.Results)
119
+ if err != nil {
120
+ return nil, common.NewLogIDError(
121
+ fmt.Sprintf("canvas apply returned invalid data: %v; query affected assets before retrying because outcome cannot be confirmed", err),
122
+ envelope.LogID,
123
+ )
124
+ }
125
+ ordered, rawResults, _, err := validateApplyResults(
126
+ request.Transactions,
127
+ decodedResults,
128
+ opts.AllowNonAcknowledgedResults,
129
+ )
94
130
  if err != nil {
95
131
  return nil, common.NewLogIDError(
96
132
  fmt.Sprintf("%s; query affected assets before retrying because outcome cannot be confirmed", err),
97
133
  envelope.LogID,
98
134
  )
99
135
  }
100
- return &ApplyResult{
136
+ result := &ApplyResult{
101
137
  BatchID: request.BatchID,
102
138
  Results: ordered,
103
139
  LogID: strings.TrimSpace(envelope.LogID),
104
- }, nil
140
+ }
141
+ if opts.AllowNonAcknowledgedResults {
142
+ result.rawResults = rawResults
143
+ }
144
+ return result, nil
105
145
  }
106
146
 
107
147
  func validateApplyRequest(request *ApplyRequest) error {
@@ -161,43 +201,79 @@ func validateApplyRequest(request *ApplyRequest) error {
161
201
  return nil
162
202
  }
163
203
 
164
- func validateApplyResults(expected []PatchTransaction, actual []PatchTransactionResult) ([]PatchTransactionResult, error) {
165
- byID := make(map[string]PatchTransactionResult, len(actual))
166
- for _, result := range actual {
204
+ func decodeApplyResults(rawResults []json.RawMessage) ([]decodedPatchTransactionResult, error) {
205
+ results := make([]decodedPatchTransactionResult, 0, len(rawResults))
206
+ for index, rawResult := range rawResults {
207
+ var result PatchTransactionResult
208
+ if err := json.Unmarshal(rawResult, &result); err != nil {
209
+ return nil, fmt.Errorf("decode transaction result[%d]: %w", index, err)
210
+ }
211
+ results = append(results, decodedPatchTransactionResult{result: result, raw: rawResult})
212
+ }
213
+ return results, nil
214
+ }
215
+
216
+ func validateApplyResults(
217
+ expected []PatchTransaction,
218
+ actual []decodedPatchTransactionResult,
219
+ allowNonAcknowledgedResults bool,
220
+ ) ([]PatchTransactionResult, []json.RawMessage, bool, error) {
221
+ byID := make(map[string]decodedPatchTransactionResult, len(actual))
222
+ for _, decoded := range actual {
223
+ result := decoded.result
167
224
  result.TransactionID = strings.TrimSpace(result.TransactionID)
168
225
  if result.TransactionID == "" {
169
- return nil, fmt.Errorf("canvas apply returned a result without transaction_id")
226
+ return nil, nil, false, fmt.Errorf("canvas apply returned a result without transaction_id")
170
227
  }
171
228
  if _, duplicate := byID[result.TransactionID]; duplicate {
172
- return nil, fmt.Errorf("canvas apply returned duplicate result for transaction %q", result.TransactionID)
229
+ return nil, nil, false, fmt.Errorf("canvas apply returned duplicate result for transaction %q", result.TransactionID)
173
230
  }
174
- byID[result.TransactionID] = result
231
+ decoded.result = result
232
+ byID[result.TransactionID] = decoded
175
233
  }
176
234
  if len(byID) != len(expected) {
177
- return nil, fmt.Errorf("canvas apply returned %d transaction results, want %d", len(byID), len(expected))
235
+ return nil, nil, false, fmt.Errorf("canvas apply returned %d transaction results, want %d", len(byID), len(expected))
178
236
  }
179
237
  ordered := make([]PatchTransactionResult, 0, len(expected))
238
+ orderedRaw := make([]json.RawMessage, 0, len(expected))
239
+ hasNonAcknowledgedResult := false
180
240
  for _, transaction := range expected {
181
- result, ok := byID[transaction.TransactionID]
241
+ decoded, ok := byID[transaction.TransactionID]
182
242
  if !ok {
183
- return nil, fmt.Errorf("canvas apply omitted transaction result %q", transaction.TransactionID)
184
- }
185
- if strings.ToLower(strings.TrimSpace(result.Status)) != "ack" {
186
- return nil, fmt.Errorf(
187
- "canvas apply transaction %q was not acknowledged: status=%q blocked_by=%q error=%q",
188
- transaction.TransactionID, result.Status, result.BlockedByTransaction, result.Error,
189
- )
243
+ return nil, nil, false, fmt.Errorf("canvas apply omitted transaction result %q", transaction.TransactionID)
190
244
  }
191
- for _, patch := range transaction.Patches {
192
- version, ok := result.AssetVersions[patch.AssetID]
193
- if !ok {
194
- return nil, fmt.Errorf("canvas apply transaction %q omitted version for asset %q", transaction.TransactionID, patch.AssetID)
245
+ result := decoded.result
246
+ status := strings.ToLower(strings.TrimSpace(result.Status))
247
+ switch status {
248
+ case "ack":
249
+ for _, patch := range transaction.Patches {
250
+ version, ok := result.AssetVersions[patch.AssetID]
251
+ if !ok {
252
+ if allowNonAcknowledgedResults && patch.Op == "add" && patch.Path == "" {
253
+ continue
254
+ }
255
+ return nil, nil, false, fmt.Errorf("canvas apply transaction %q omitted version for asset %q", transaction.TransactionID, patch.AssetID)
256
+ }
257
+ if version < 0 {
258
+ return nil, nil, false, fmt.Errorf("canvas apply transaction %q returned negative version for asset %q", transaction.TransactionID, patch.AssetID)
259
+ }
195
260
  }
196
- if version < 0 {
197
- return nil, fmt.Errorf("canvas apply transaction %q returned negative version for asset %q", transaction.TransactionID, patch.AssetID)
261
+ case "reject", "blocked", "skipped":
262
+ if !allowNonAcknowledgedResults {
263
+ return nil, nil, false, fmt.Errorf(
264
+ "canvas apply transaction %q was not acknowledged: status=%q blocked_by=%q error=%q",
265
+ transaction.TransactionID, result.Status, result.BlockedByTransaction, result.Error,
266
+ )
198
267
  }
268
+ hasNonAcknowledgedResult = true
269
+ default:
270
+ return nil, nil, false, fmt.Errorf(
271
+ "canvas apply transaction %q returned invalid status %q",
272
+ transaction.TransactionID, result.Status,
273
+ )
199
274
  }
200
275
  ordered = append(ordered, result)
276
+ orderedRaw = append(orderedRaw, decoded.raw)
201
277
  }
202
- return ordered, nil
278
+ return ordered, orderedRaw, hasNonAcknowledgedResult, nil
203
279
  }
@@ -336,6 +336,119 @@ func TestApplyRequiresAcknowledgementAndEveryAssetVersion(t *testing.T) {
336
336
  }
337
337
  }
338
338
 
339
+ func TestApplyAllowsExplicitTransportOutcomesWithoutHidingServerResultFields(t *testing.T) {
340
+ request := ApplyRequest{
341
+ BatchID: "batch-1", ClientID: "client-1",
342
+ Transactions: []PatchTransaction{{TransactionID: "tx-1", Patches: []PatchEntry{{
343
+ AssetID: "asset-1", Op: "replace", Path: "", Value: json.RawMessage(`{}`),
344
+ }}}},
345
+ }
346
+ for _, status := range []string{"reject", "blocked", "skipped"} {
347
+ t.Run(status, func(t *testing.T) {
348
+ client := &fakeClient{send: func(_ context.Context, _ string, _ any, out any) error {
349
+ response := fmt.Sprintf(
350
+ `{"ret":"0","log_id":"transport-log","data":{"results":[{"transaction_id":"tx-1","status":%q,"blocked_by":"tx-0","message":"server detail","current_asset_versions":{"asset-1":7},"server_detail":{"reason":"conflict"}}]}}`,
351
+ status,
352
+ )
353
+ return decodeInto(out, response)
354
+ }}
355
+ result, err := Apply(context.Background(), ApplyOptions{
356
+ Request: request,
357
+ AllowNonAcknowledgedResults: true,
358
+ }, runnerWithClient(client))
359
+ if err != nil || result.Results[0].Status != status {
360
+ t.Fatalf("Apply() = (%#v, %v), want structured %s result", result, err, status)
361
+ }
362
+ payload, err := json.Marshal(result)
363
+ if err != nil {
364
+ t.Fatalf("Marshal() error = %v", err)
365
+ }
366
+ var output map[string]any
367
+ if err := json.Unmarshal(payload, &output); err != nil {
368
+ t.Fatalf("Unmarshal() error = %v", err)
369
+ }
370
+ entry := output["results"].([]any)[0].(map[string]any)
371
+ if entry["message"] != "server detail" || entry["server_detail"] == nil || entry["current_asset_versions"] == nil {
372
+ t.Fatalf("result = %#v, want complete server transaction result", entry)
373
+ }
374
+ })
375
+ }
376
+ }
377
+
378
+ func TestApplyTransportResultAllowsRootAssetCreateAckWithoutVersion(t *testing.T) {
379
+ request := ApplyRequest{
380
+ BatchID: "batch-1", ClientID: "client-1",
381
+ Transactions: []PatchTransaction{{TransactionID: "tx-1", Patches: []PatchEntry{{
382
+ AssetID: "asset-new", Op: "add", Path: "", Value: json.RawMessage(`{"nodes":[]}`),
383
+ }}}},
384
+ }
385
+ client := &fakeClient{send: func(_ context.Context, _ string, _ any, out any) error {
386
+ return decodeInto(out, `{"ret":"0","data":{"results":[{"transaction_id":"tx-1","status":"ack","server_detail":{"created":true}}]}}`)
387
+ }}
388
+ result, err := Apply(context.Background(), ApplyOptions{
389
+ Request: request,
390
+ AllowNonAcknowledgedResults: true,
391
+ }, runnerWithClient(client))
392
+ if err != nil || result.Results[0].Status != "ack" {
393
+ t.Fatalf("Apply() = (%#v, %v), want transport ACK", result, err)
394
+ }
395
+ payload, err := json.Marshal(result)
396
+ if err != nil {
397
+ t.Fatalf("Marshal() error = %v", err)
398
+ }
399
+ var output map[string]any
400
+ if err := json.Unmarshal(payload, &output); err != nil {
401
+ t.Fatalf("Unmarshal() error = %v", err)
402
+ }
403
+ entry := output["results"].([]any)[0].(map[string]any)
404
+ if entry["asset_versions"] != nil || entry["server_detail"] == nil {
405
+ t.Fatalf("result = %#v, want unchanged ACK for SDK decoder", entry)
406
+ }
407
+ }
408
+
409
+ func TestApplyTransportResultStillRequiresVersionsForNonCreatePatches(t *testing.T) {
410
+ for _, patch := range []PatchEntry{
411
+ {AssetID: "asset-1", Op: "replace", Path: "", Value: json.RawMessage(`{}`)},
412
+ {AssetID: "asset-1", Op: "add", Path: "/nodes/0", Value: json.RawMessage(`{}`)},
413
+ } {
414
+ t.Run(patch.Op+patch.Path, func(t *testing.T) {
415
+ request := ApplyRequest{
416
+ BatchID: "batch-1", ClientID: "client-1",
417
+ Transactions: []PatchTransaction{{TransactionID: "tx-1", Patches: []PatchEntry{patch}}},
418
+ }
419
+ client := &fakeClient{send: func(_ context.Context, _ string, _ any, out any) error {
420
+ return decodeInto(out, `{"ret":"0","data":{"results":[{"transaction_id":"tx-1","status":"ack"}]}}`)
421
+ }}
422
+ _, err := Apply(context.Background(), ApplyOptions{
423
+ Request: request,
424
+ AllowNonAcknowledgedResults: true,
425
+ }, runnerWithClient(client))
426
+ if err == nil || !strings.Contains(err.Error(), "omitted version") {
427
+ t.Fatalf("Apply() error = %v, want missing version rejection", err)
428
+ }
429
+ })
430
+ }
431
+ }
432
+
433
+ func TestApplyTransportResultModeStillRejectsInvalidStatus(t *testing.T) {
434
+ request := ApplyRequest{
435
+ BatchID: "batch-1", ClientID: "client-1",
436
+ Transactions: []PatchTransaction{{TransactionID: "tx-1", Patches: []PatchEntry{{
437
+ AssetID: "asset-1", Op: "replace", Path: "", Value: json.RawMessage(`{}`),
438
+ }}}},
439
+ }
440
+ client := &fakeClient{send: func(_ context.Context, _ string, _ any, out any) error {
441
+ return decodeInto(out, `{"ret":"0","data":{"results":[{"transaction_id":"tx-1","status":"pending"}]}}`)
442
+ }}
443
+ _, err := Apply(context.Background(), ApplyOptions{
444
+ Request: request,
445
+ AllowNonAcknowledgedResults: true,
446
+ }, runnerWithClient(client))
447
+ if err == nil || !strings.Contains(err.Error(), "invalid status") {
448
+ t.Fatalf("Apply() error = %v, want invalid response rejection", err)
449
+ }
450
+ }
451
+
339
452
  func TestApplyBetaRejectsMultipleTransactionsBeforeRequest(t *testing.T) {
340
453
  requestCalls := 0
341
454
  client := &fakeClient{send: func(context.Context, string, any, any) error {
@@ -0,0 +1,61 @@
1
+ package common
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/Pippit-dev/pippit-cli/internal/config"
8
+ )
9
+
10
+ // GetCreditBalanceResult is the JSON output printed by `pippit-tool-cli get-credit-balance`.
11
+ type GetCreditBalanceResult struct {
12
+ TotalRemainAmount int64 `json:"total_remain_amount,string"`
13
+ LogID string `json:"-"`
14
+ }
15
+
16
+ type getCreditBalanceResponse struct {
17
+ Ret string `json:"ret"`
18
+ Errmsg string `json:"errmsg"`
19
+ LogID string `json:"log_id"`
20
+ Data *getCreditBalanceResponseData `json:"data"`
21
+ }
22
+
23
+ type getCreditBalanceResponseData struct {
24
+ TotalRemainAmount *int64 `json:"total_remain_amount,string"`
25
+ }
26
+
27
+ // GetCreditBalance queries the effective credit balance of the access-key owner.
28
+ func GetCreditBalance(ctx context.Context, runner *Runner) (*GetCreditBalanceResult, error) {
29
+ if runner == nil || runner.Client == nil {
30
+ return nil, fmt.Errorf("get_credit_balance 运行器客户端缺失")
31
+ }
32
+
33
+ var resp getCreditBalanceResponse
34
+ if err := runner.Client.SendRequest(ctx, getCreditBalancePath(runner), struct{}{}, &resp); err != nil {
35
+ return nil, fmt.Errorf("获取积分余额请求失败: %w", err)
36
+ }
37
+ if resp.Ret != "0" {
38
+ if resp.Errmsg == "" {
39
+ resp.Errmsg = "未知错误"
40
+ }
41
+ return nil, NewLogIDError(fmt.Sprintf("获取积分余额请求返回失败: ret=%s errmsg=%s", resp.Ret, resp.Errmsg), resp.LogID)
42
+ }
43
+ if resp.Data == nil {
44
+ return nil, NewLogIDError("get_credit_balance 响应缺少 data", resp.LogID)
45
+ }
46
+ if resp.Data.TotalRemainAmount == nil {
47
+ return nil, NewLogIDError("get_credit_balance 响应缺少 data.total_remain_amount", resp.LogID)
48
+ }
49
+
50
+ return &GetCreditBalanceResult{
51
+ TotalRemainAmount: *resp.Data.TotalRemainAmount,
52
+ LogID: resp.LogID,
53
+ }, nil
54
+ }
55
+
56
+ func getCreditBalancePath(runner *Runner) string {
57
+ if runner != nil && runner.Config != nil && runner.Config.Paths != nil && runner.Config.Paths.GetCreditBalance != "" {
58
+ return runner.Config.Paths.GetCreditBalance
59
+ }
60
+ return config.GetCreditBalancePath
61
+ }
@@ -0,0 +1,75 @@
1
+ package common
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "strings"
7
+ "testing"
8
+
9
+ "github.com/bytedance/sonic"
10
+ )
11
+
12
+ type creditBalanceFakeClient struct {
13
+ response string
14
+ path string
15
+ body any
16
+ }
17
+
18
+ func (c *creditBalanceFakeClient) SendRequest(_ context.Context, path string, body any, out any) error {
19
+ c.path = path
20
+ c.body = body
21
+ return sonic.Unmarshal([]byte(c.response), out)
22
+ }
23
+
24
+ func (c *creditBalanceFakeClient) SendRequestWithHeaders(ctx context.Context, path string, body any, _ map[string]string, out any) error {
25
+ return c.SendRequest(ctx, path, body, out)
26
+ }
27
+
28
+ func (c *creditBalanceFakeClient) SendMultipartRequest(context.Context, string, map[string]string, MultipartFile, any) error {
29
+ return fmt.Errorf("unexpected multipart request")
30
+ }
31
+
32
+ func TestGetCreditBalanceReturnsAmount(t *testing.T) {
33
+ client := &creditBalanceFakeClient{
34
+ response: `{"ret":"0","log_id":"log_balance","data":{"total_remain_amount":"4321"}}`,
35
+ }
36
+
37
+ result, err := GetCreditBalance(context.Background(), &Runner{Client: client})
38
+ if err != nil {
39
+ t.Fatalf("GetCreditBalance() error = %v", err)
40
+ }
41
+ if result.TotalRemainAmount != 4321 {
42
+ t.Fatalf("TotalRemainAmount = %d, want 4321", result.TotalRemainAmount)
43
+ }
44
+ if result.LogID != "log_balance" {
45
+ t.Fatalf("LogID = %q, want log_balance", result.LogID)
46
+ }
47
+ if client.path != "/api/biz/v1/skill/get_credit_balance" {
48
+ t.Fatalf("path = %q, want get_credit_balance path", client.path)
49
+ }
50
+ if _, ok := client.body.(struct{}); !ok {
51
+ t.Fatalf("body = %#v, want an empty POST body", client.body)
52
+ }
53
+ }
54
+
55
+ func TestGetCreditBalanceRequiresOptionalAmount(t *testing.T) {
56
+ client := &creditBalanceFakeClient{
57
+ response: `{"ret":"0","log_id":"log_missing","data":{}}`,
58
+ }
59
+
60
+ _, err := GetCreditBalance(context.Background(), &Runner{Client: client})
61
+ if err == nil || !strings.Contains(err.Error(), "data.total_remain_amount") || !strings.Contains(err.Error(), "log_id=log_missing") {
62
+ t.Fatalf("GetCreditBalance() error = %v, want missing amount and log_id", err)
63
+ }
64
+ }
65
+
66
+ func TestGetCreditBalanceBusinessErrorIncludesLogID(t *testing.T) {
67
+ client := &creditBalanceFakeClient{
68
+ response: `{"ret":"16008","errmsg":"查询失败","log_id":"log_123"}`,
69
+ }
70
+
71
+ _, err := GetCreditBalance(context.Background(), &Runner{Client: client})
72
+ if err == nil || !strings.Contains(err.Error(), "ret=16008 errmsg=查询失败") || !strings.Contains(err.Error(), "log_id=log_123") {
73
+ t.Fatalf("GetCreditBalance() error = %v, want business error and log_id", err)
74
+ }
75
+ }
@@ -12,6 +12,7 @@ const (
12
12
  DefaultAuthTTL = 30 * time.Second
13
13
  DefaultAuthStoreServiceName = "pippit-cli"
14
14
  SubmitRunPath = "/api/biz/v1/skill/submit_run"
15
+ GetCreditBalancePath = "/api/biz/v1/skill/get_credit_balance"
15
16
  GetThreadPath = "/api/biz/v1/skill/get_thread"
16
17
  UploadFilePath = "/api/biz/v1/skill/upload_file"
17
18
  ListThreadFilePath = "/api/biz/v1/skill/list_thread_file"
@@ -29,10 +30,11 @@ type Config struct {
29
30
  }
30
31
 
31
32
  type Paths struct {
32
- SubmitRun string
33
- GetThread string
34
- UploadFile string
35
- ListThreadFile string
33
+ SubmitRun string
34
+ GetCreditBalance string
35
+ GetThread string
36
+ UploadFile string
37
+ ListThreadFile string
36
38
  }
37
39
 
38
40
  // Load resolves the built-in runtime config.
@@ -43,10 +45,11 @@ func Load() *Config {
43
45
  AuthTTL: DefaultAuthTTL,
44
46
  AccessKey: strings.TrimSpace(os.Getenv(EnvXYQAccessKey)),
45
47
  Paths: &Paths{
46
- SubmitRun: SubmitRunPath,
47
- GetThread: GetThreadPath,
48
- UploadFile: UploadFilePath,
49
- ListThreadFile: ListThreadFilePath,
48
+ SubmitRun: SubmitRunPath,
49
+ GetCreditBalance: GetCreditBalancePath,
50
+ GetThread: GetThreadPath,
51
+ UploadFile: UploadFilePath,
52
+ ListThreadFile: ListThreadFilePath,
50
53
  },
51
54
  }
52
55
  }
@@ -20,6 +20,9 @@ func TestLoadUsesDefaultConfig(t *testing.T) {
20
20
  if cfg.Paths.SubmitRun != SubmitRunPath {
21
21
  t.Fatalf("SubmitRun path = %q, want %q", cfg.Paths.SubmitRun, SubmitRunPath)
22
22
  }
23
+ if cfg.Paths.GetCreditBalance != GetCreditBalancePath {
24
+ t.Fatalf("GetCreditBalance path = %q, want %q", cfg.Paths.GetCreditBalance, GetCreditBalancePath)
25
+ }
23
26
  }
24
27
 
25
28
  func TestLoadReadsAccessKey(t *testing.T) {
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@pippit-dev/cli",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Pippit CLI",
5
5
  "bin": {
6
6
  "pippit-tool-cli": "scripts/run.js"
7
7
  },
8
8
  "scripts": {
9
9
  "postinstall": "node scripts/install.js",
10
- "test": "node scripts/version-check.test.js && node scripts/skills.test.js && node scripts/install-wizard.test.js && go test ./... && go vet ./..."
10
+ "prepack": "node scripts/prepare-canvas-runtime.js",
11
+ "prepare:canvas-runtime": "node scripts/prepare-canvas-runtime.js",
12
+ "test": "node scripts/version-check.test.js && node scripts/skills.test.js && node scripts/install-wizard.test.js && node scripts/canvas-command.test.js && node scripts/prepare-canvas-runtime.test.js && go test ./... && go vet ./...",
13
+ "verify:canvas-runtime": "node scripts/prepare-canvas-runtime.js --check-only"
11
14
  },
12
15
  "os": [
13
16
  "darwin",
@@ -32,11 +35,15 @@
32
35
  "skills",
33
36
  "scripts/install.js",
34
37
  "scripts/install-wizard.js",
38
+ "scripts/canvas-command.js",
35
39
  "scripts/platform.js",
36
40
  "scripts/run.js",
37
41
  "scripts/skills.js",
38
42
  "scripts/telemetry.js",
39
43
  "scripts/version-check.js",
44
+ "dist/xyq-canvas-command-runtime.cjs",
45
+ "dist/xyq-canvas-command-runtime.cjs.LEGAL.txt",
46
+ "dist/xyq-canvas-command-runtime.cjs.sha256",
40
47
  "checksums.txt",
41
48
  "README.md",
42
49
  "LICENSE"